NumPy

  • 그냥 python for 돌면 무조건 느리다. C가 아니니까... 파이썬으로 아무리 해봐야 크게 개선이 안됨.
  • 그래서 Pandas나 Numpy가 제공하는 방법 대로 접근하는게 제일 효과적인 성능 개선 방법.
 

```python

import numpy as np

 

x = np.array([1, 2, 3])

print(type(x))

> <class 'numpy.ndarray'>

```

 

특정 값을 가진 index들을 반환받는 방법 : where

```python
>>> arr = np.array([[1,2,0],[0,1,2]])
>>> np.where(arr == 0)
(array([0, 1], dtype=int64), array([2, 0], dtype=int64))
-> 0,2와 1,0
```

 

 

산술 연산과 브로드캐스트

넘파이 배열에 대한 산술 연산자의 동작은 파이썬 기본 리스트와 다르다.

 

* 연산

```python

print(x*2)        - 넘파이 배열 * 2

> [2 4 6]

l = [1, 2, 3]

print(l*2)        - 파이썬 기본 리스트 * 2

> [1, 2, 3, 1, 2, 3] 

```

 

+ 연산

```python

print(x+1)

> [2 3 4]

print(x + [3, 3, 3])

> [4 5 6]

파이썬 기본 리스트에 배열이 아닌 스칼라값(수치 하나)을 더할 수는 없다.

print(l + [3, 3, 3])

> [1, 2, 3, 3, 3, 3]

[3, 3] 같이 원소 수가 다른 배열을 더하면 넘파이는 오류, 

파이썬 기본 리스트는 끝에 [3, 3]이 추가된다.

```

 

파이썬 리스트가 지원하는 연산자는 `` *, +`` 뿐이라 `` -, /``는 사용할 수 없다.

넘파이는 모든 연산자를 지원하며 원소별(element-wise) 연산을 수행한다.

넘파이 배열을 스칼라값과 산술 연산 하는 경우 넘파이 배열의 원소별로 한 번씩 계산이 수행된다. 이 기능을 브로드캐스트라고 한다.

브로드캐스트는 유용한 기능이지만 배열의 형상, 차원 순서를 주의깊게 맞춰주지 않으면 에러가 발생하기 쉽다.

또는 에러가 발생하지 않고 그냥 진행되어 이상하게 동작하거나.

 

리스트로 원소 선택하기 ( 인덱스로 리스트 넘기기 )

인덱스로 리스트나 투플을 넘겨 해당 원소들을 선택 및 조작할 수 있다.
```python
nparr = np.array([10, 11, 12, 13, 14, 15])
print(nparr[[0, 2]])
> [10 12]
 
nparr[[0, 2]] = 0
print(nparr)
> [ 0 11  0 13 14 15]
```
 

map

map을 사용하지 않아도 배열에서 원소들이 조건을 만족하는지를 ``python True False``로 나타내는 리스트를 반환할 수 있다.
```python
x = np.array([1, -2, 3, -2, -5, 10])
print(x > 0)   x > 0 연산에 대한 결과로 브로드캐스트가 작동하여 bool 배열이 생성된다.
> [ True False  True False False  True]
 
#이를 이용하면 반복문을 사용하지 않아도 조건을 만족하는(True에 해당하는) 원소만 조작할 수 있다.
#리스트로 원소를 선택할 수 있기 때문에, 위 bool 배열을 리스트로 넘기면 True에 대응되는 원소만 반환한다.
nparr = np.array([10, 11, 12, 13, 14, 15])
mask = x >0
print(nparr[mask])
> [10 12 15]
 
nparr[mask] = 0
print(nparr)
> [ 0 11  0 13 14  0]
```

 

N차원 배열

파이썬 리스트와 같은 방법으로 작성한다. 

행렬의 형상은 shape으로, 행렬에 담긴 원소의 자료형은 dtype으로 알아낼 수 있다.

행렬의 차수는 np.ndim()으로 알 수 있다.

```python

x = np.array([[1, 2], [4, 5]])

print(x.shape)

> (2, 2)

print(x.dtype)

> int32

print(x.ndim())

> 2

```

 

차원이 서로 다른(형상이 다른) 넘파이 배열 간의 산술 연산 시에도 브로드캐스트가 작동한다. 

 

x[a, b]는 x[a][b]와 같다. 그러나 x[:a, :b]는 x[:a][:b]와 다르다.

```python

print(x[0, 1])

> 2

print(x[0][1])

> 2

```

 

flatten() 를 이용해 1차원 배열로 변경할 수 있다. 이를 평탄화 라고 부른다.

```python

x = x.flatten()

print(x)

> [1 2 4 5]

```

 

reshape() 를 이용해 형상을 변경할 수 있다.

```python

x = np.array([[1, 2, 3], [4, 5, 6]])

print(x.reshape(3, 2))

> [[1 2]

   [3 4]

   [5 6]]

print(x.reshape(x.size, -1)) #-1을 넣으면 알아서 나머지 크기만큼 묶어준다

> [[1]

   [2]

   [3]

   [4]

   [5]

   [6]]

print(x = x.reshape(2, -1))

> [[1 2 3]

   [4 5 6]]

```

 

transpose() 를 사용해 다차원 배열의 차원 순서를 변경할 수 있다.

선형대수에서 말하는 ^T 변환은 보통 .T를 사용한다.

단, `` .T``는 1차원 배열에는 동작하지 않는다! ``py [[1, 2, 3]]`` 이런 식으로 두 번 이상 묶어야 동작한다.

`` .reshape()``는 1차원 배열에도 동작한다.

```python

print(x.T)

> [[1 4]

   [2 5]

   [3 6]]

 

x = np.ones((1,2,3)) #형상이 (1, 2, 3)인 행렬 생성

print(x.shape)

> (1, 2, 3)

print(x.transpose(2, 0, 1).shape)

> (3, 1, 2)

```

 

행렬곱

수학에서 의미하는 일반적인 행렬곱(내적)을 수행하려면 그냥 * 연산이 아니라 np.dot()을 사용한다.
```python
A = np.array([[1, 2], [3, 4]])
B = np.array([[1, 0], [0, 1]])
print(A*B)
>[[1 0]
  [0 4]]
print(np.dot(A, B))
>[[1 2]
  [3 4]]
```

Note ) 3.5 버전부터 자체적으로 행렬곱 연산자 ``python @``를 지원한다.

 

numpy.nditer 다차원 배열 순회

다차원 배열을 다차원 iterator로 변환할 수 있다.
 
반환받은 iterator에는 다양한 method가 있는데, 
그 중 `` it.multi_index``를사용하면 다차원 배열의 인덱스를 투플로 반환받을 수 있다.
예를 들어 `` 2x3`` 행렬일 경우 iterator를 반복할 때 마다 다음이 차례로 반환된다.
```
(0, 0)
(0, 1)
(0, 2)
(1, 0)
(1, 1)
(1, 2)
```
이를 이용하면 for문 중첩 등의 복잡한 방법을 사용하지 않아도 다차원 배열을 간단히 순회할 수 있다.
 

초기화 및 선언

```python
np.empty()
np.zeros()
np.ones()
np.empty_like()
...
```
 

shape이 분명 2차원이어야 하는데 (11351,) 과 같이 나올 때 해결방법

 

Pandas 

  • Group by: split-apply-combine 
  • https://pandas.pydata.org/pandas-docs/stable/user_guide/groupby.html
  • Series에는 map이 있으나 DataFrame에는 map이 없다. 대신 apply가 있다.
    • apply는 기본적으로 한 열 마다 적용되지만, axis 지정으로 한 행 마다 적용으로 바꿀 수 있다. 
    • 그래서 dataframe에서  한 행 을 가져와 여러 속성을 이용해 람다를 적용 하고 싶은 경우 axis=1 apply,
    • 단순히 한 행에 람다를 적용하고 싶은 경우 Series.map 을 사용.
  • transform은 한 열마다 적용 되는 것이므로, 두 개의 열을 묶어서 transform으로 넘기는 것은 불가능하다. (애초에 이런 경우 apply를 쓰는 것이 맞다)
 

```py

# 이런게 가능. 

id_interval = csv['Timestamp'] - csv.groupby('Arbitration_ID')['Timestamp'].shift(1)

result['IdInterval'] = id_interval.fillna(-1)

```

 

https://stackoverflow.com/questions/64954022/python-pandas-groupby-filter-and-apply-according-to-condition-on-values

https://stackoverflow.com/questions/48997350/pandas-dataframe-groupby-for-separate-groups-of-same-value

 

MultiIndex

실세계의 많은 데이터가 MultiIndex 형태다. (학교 -> 학과 -> 학번)

https://data-make.tistory.com/126  

 

```py

train_x_df = pd.read_csv(DATA_DIR  + "train_x_df.csv", index_col=['coin_index', 'sample_id', 'time'])

# read_csv 말고도 다양한 방법으로 MultiIndex df로 만들 수 있음.

 

# (coin_index, sample_id) 별로, time을 1차원으로 쭉 붙이는 식으로 가공하려면

train_x_df.unstack()

# 컬럼 이름이 중복되는 것 끼리 열 단위 MultiIndex를 추가로 생성해서 묶어줌

```

https://pandas.pydata.org/docs/user_guide/reshaping.html#reshaping-by-stacking-and-unstacking

 

 

scikit-learn

차원 축소 ( dimension reduction ) : PCA

몇 차원으로 줄일지 직접 n을 지정하는 것도 가능하고
설명 분산량이 몇 이상임을 만족하는 차원 개수를 알아서 선택하도록 하는 것도 가능 { sum(pca.explained_variance_ratio_) }
 
```py
pca = PCA()
pca.fit(normal_data_df)

data_df_reducted = pd.DataFrame(data=pca.transform(test_data_df))

```

 

KernelPCA

라는 녀석도 있음.
 

LDA

당연히 LDA도 있다.