배열을 이용한 배열지향 프로그래밍
NumPy 배열을 사용하면 반복문을 작성하지 않고 간결한 배열 연산을 사용해 많은 종류의 데이터 처리 작업을 할 수 있다.
배열 연산을 사용해서 반복문을 명시적으로 제거하는 기법을 흔히 벡터화라고 부르는데 일반적으로 벡터화된 배열에 대한
산술 연산은 순수 파이썬 연산에 비해 2~3배에서 많게는 수십,수백 배까지 빠르다.
처음으로 다룰 브로드캐스팅은 아주 강력한 벡터 연산 방법이다.
예를 들어 값이 놓여 있는 그리드에 sqrt(x^2 + y^2)을 계산을 한다고 하자. np.meshgrid 함수는 두 개의 1차원 배열을 받아서
가능한 모든(x,y)짝을 만들 수 있는 2차원 배열 두개를 반환한다.
in : points = np.arange(-5,5,0.01) # -5부터 4.99까지 0.01씩 증가하는 값들의 배열
xs, ys = np.meshgrid(points, points)
xs, ys
out : (array([[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
...,
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99]]),
array([[-5. , -5. , -5. , ..., -5. , -5. , -5. ],
[-4.99, -4.99, -4.99, ..., -4.99, -4.99, -4.99],
[-4.98, -4.98, -4.98, ..., -4.98, -4.98, -4.98],
...,
[ 4.97, 4.97, 4.97, ..., 4.97, 4.97, 4.97],
[ 4.98, 4.98, 4.98, ..., 4.98, 4.98, 4.98],
[ 4.99, 4.99, 4.99, ..., 4.99, 4.99, 4.99]]))
이제 그리드 상의 두 포인트로 간단하게 계산을 적용할 수 있다.
in : z = np.sqrt(xs**2 + ys **2)
z
out : array([[7.07106781, 7.06400028, 7.05693985, ..., 7.04988652, 7.05693985,
7.06400028],
[7.06400028, 7.05692568, 7.04985815, ..., 7.04279774, 7.04985815,
7.05692568],
[7.05693985, 7.04985815, 7.04278354, ..., 7.03571603, 7.04278354,
7.04985815],
...,
[7.04988652, 7.04279774, 7.03571603, ..., 7.0286414 , 7.03571603,
7.04279774],
[7.05693985, 7.04985815, 7.04278354, ..., 7.03571603, 7.04278354,
7.04985815],
[7.06400028, 7.05692568, 7.04985815, ..., 7.04279774, 7.04985815,
7.05692568]])
matplotlib을 사용해서 2차원 배열을 시각화 할 수 있다.
in : import matplotlib.pyplot as plt
plt.imshow(z, cmap=plt.cm.gray); plt.colorbar()
배열 연산으로 조건절 표현하기
numpy.where 함수는 x if 조건 else y 가튼 삼항식의 벡터화된 버전이다.
다음과 같은 불리언 배열 하나와 값이 들어 있는 두 개의 배열이 있다고 하자.
in : xarr = np.array([1.1,1.2,1.3,1.4,1.5])
yarr = np.array([2.1,2.2,2.3,2.4,2.5])
cond = np.array([True,False,True,True,False])
cond의 값이 True일 때는 xarr의 값을 취하고 아니면 yarr의 값을 취하고 싶다면 리스트 표기법을 이용해서 다음처럼 작성이 가능하다.
in :result = [(x if c else y) for x,y,c in zip(xarr,yarr,cond)]
result
out : [1.1, 2.2, 1.3, 1.4, 2.5]
이 방법에는 몇 가지 문제가 있는데, 순수 파이썬으로 수행되기 때문에 큰 배열을 빠르게 처리하지 못한다.
또한 다차원 배열에서는 사용할 수 없는 문제가 있다. np.where을 사용하면 아주 간결하게 작성할 수 있다.
in : result = np.where(cond, xarr, yarr)
result
out : array([1.1, 2.2, 1.3, 1.4, 2.5])
np.where의 두 번째와 세 번째 인자는 배열이 아니어도 상관없다. 둘 중 하나 혹은 둘다 스칼라값이어도 동작한다.
데이터 분석에서 일반적인 where의 사용은 다른 배열에 기반한 새로운 배열을 생성한다.
임의로 생선된 데이터가 들어 있는 행렬이 있고 양수는 모두 2로, 음수는 모두 -2로 바꾸려면 np.where를 사용해서 쉽게 가능하다.
in : arr = np.random.randn(4,4)
arr
out : array([[-0.83185274, -0.67194219, -0.06129791, 0.04525119],
[-0.46098457, 1.41308202, -1.603621 , 0.98616444],
[ 0.12001107, -0.46709253, -0.00776444, 1.59149794],
[ 1.66777 , -0.08016008, -0.38260291, -1.66683148]])
in : arr > 0
out : array([[False, False, False, True],
[False, True, False, True],
[ True, False, False, True],
[ True, False, False, False]])
in : np.where(arr> 0, 2,-2)
out : array([[-2, -2, -2, 2],
[-2, 2, -2, 2],
[ 2, -2, -2, 2],
[ 2, -2, -2, -2]])
np.where를 사용할 때 스칼라값과 배열을 조합할 수 있다. 예를 들어 arr의 모든 양수를 2로 바꿀 수 있다.
in : np.where(arr > 0, 2, arr)
out : array([[-0.83185274, -0.67194219, -0.06129791, 2. ],
[-0.46098457, 2. , -1.603621 , 2. ],
[ 2. , -0.46709253, -0.00776444, 2. ],
[ 2. , -0.08016008, -0.38260291, -1.66683148]])
np.where로 넘기는 배열은 그냥 크기만 같은 배열이거나 스칼라값이 될 수 있다.
수학 메서드와 통계 메서드
배열 전체 혹은 배열에서 한 축을 따르는 자료애 대한 통계를 계산하는 수학 함수는 배열 메서드로 사용할 수 있다.
전체의 합(sum)이나 평균(mean), 표준편차(std)는 NumPy의 최상위 함수를 이용하거나
배열의 인스턴스 메서드를 사용해서 구할 수 있다.
in : arr = np.random.randn(5,4)
arr
out : array([[ 1.07992527, -1.19768322, -0.19407906, 0.81104059],
[-0.68860156, -0.43083032, -0.85813298, -0.77360107],
[-0.48780068, 0.14328868, 1.18127116, 1.53023032],
[-0.43660321, -0.58241972, 1.62065865, -0.57232035],
[ 0.13224502, -1.434484 , -0.07593917, 0.24894199]])
in : arr.mean()
out : -0.049244683531922
in : np.mean(arr)
out : -0.049244683531922
in : arr.sum()
out : -0.9848936706384399
mean이나 sum같은 함수는 선택적으로 axis 인자를 받아서 해당 axis에 대한 통계를 계산하고 한 차수 낮은 배열을 반환한다.
in : arr.mean(axis=1)
out : array([ 0.12480089, -0.68779148, 0.59174737, 0.00732884, -0.28230904])
in : arr.sum(axis=0)
out : array([-0.40083517, -3.50212858, 1.6737786 , 1.24429148])
여기서 arr.sum(0)은 로우의 합을 구하라는 의미이며, arr.mean(1)은 모든 컬럼에서 평균을 구하라는 의미이다.
cumsum과 cumprod 메서드는 중간 계산값을 담고 있는 배열을 반환한다.
in : arr = np.arange(8)
arr
out : array([0, 1, 2, 3, 4, 5, 6, 7])
in : arr.cumsum()
out : array([ 0, 1, 3, 6, 10, 15, 21, 28], dtype=int32)
다차원 배열에서 cumsum 같은 누산 함수는 같은 크기의 배열을 반환한다.
하지만 축을 지정하여 부분적으로 계산하면 낮은 차수의 슬라이스를 반환한다.
in : arr = np.array([[0,1,2],[3,4,5],[6,7,8]])
arr
out : array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
in : arr.cumsum(axis=0)
out : array([[ 0, 1, 2],
[ 3, 5, 7],
[ 9, 12, 15]], dtype=int32)
in : arr.cumprod(axis=1)
out : array([[ 0, 0, 0],
[ 3, 12, 60],
[ 6, 42, 336]], dtype=int32)
기본 배열 통계 메서드를 지원하는 모든 함수를 확인해보자.
- sum: 배열 전체 혹은 특징 축에 대한 모든 원소의 합을 계산한다. 크기가 0인 배열에 대한 sum결과는 0이다.
- mean: 산술평균을 구한다. 크기가 0인 배열에 대한 mean결과는 NaN이다.
- std, var: 각 표준편차(std)와 분산(var)을 구한다. 선택적으로 자유도를 줄 수 있으며 분모의 기본 값은 n이다.
- min, max: 최솟값과 최댓값
- argmin, argmax: 최소 원소의 색인값과 최대 원소의 색인값
- cumsum: 각 원소의 누적합
- cumprod: 각 원소의 누적곱
불리언 배열을 위한 메서드
이전 메서드의 불리언값을 1(True) 또는 0(False)으로 강제할 수 있다. 따라서 sum 메서드를
실행하면 불리언 배열에서 True인 원소의 개수를 셀 수 있다.
in : arr = np.random.randn(100)
(arr > 0).sum()
out : 48
any와 all 메서드는 불리언 배열에 특히 유용하다.
any 메서드는 하나 이상의 값이 True인지 검사하고, all 메서드는 모든 원소가 True인지 검사한다.
in : bools = np.array([False, False, True, False])
bools.any()
out : True
in : bools.all()
out : False
이들 메서드는 불리언 배열이 아니어도 동작하는데, 0이 아닌 원소는 모두 True로 간주한다.
정렬
파이썬 내장 리스트형처럼 NumPy 배열 역시 sort 메서드를 이용해서 정렬할 수 있다.
in : arr = np.random.randn(6)
arr
out : array([ 0.53413419, -1.70386072, 1.2909524 , 1.52257074, -0.9137089 ,
0.48683424])
in : arr.sort()
arr
out : array([-1.70386072, -0.9137089 , 0.48683424, 0.53413419, 1.2909524 ,
1.52257074])
in : arr = np.random.randn(5,3)
arr
out : array([[-1.16230913, -0.63633837, -1.08670598],
[ 1.22065311, -0.88332837, 0.6161566 ],
[ 0.16367177, -1.6620613 , -1.45580973],
[-0.35872157, -1.77557839, 0.96856771],
[-0.53185819, 0.42271495, -0.39729175]])
in : arr.sort() # 다차원 배열의 정렬은 sort 메서드에 넘긴 축의 값에 따라 1차원 부분을 정렬한다.
arr
out : array([[-1.16230913, -1.08670598, -0.63633837],
[-0.88332837, 0.6161566 , 1.22065311],
[-1.6620613 , -1.45580973, 0.16367177],
[-1.77557839, -0.35872157, 0.96856771],
[-0.53185819, -0.39729175, 0.42271495]])
np.sort 메서드는 배열을 직접 변경하지 않고 정렬된 결과를 가지고 있는 복사본을 반환한다.
배열의 분위수를 구하는 쉽고 빠른 방법은 우섭 배열을 정렬한 후 특정 분위의 값을 선택하는 것이다.
in : large_arr = np.random.randn(1000)
large_arr.sort()
large_arr[int(0.05 * len(large_arr))] # 5% 분위수
out : -1.6520591855863298
'머신러닝 > numpy' 카테고리의 다른 글
NumPy의 기초(5) (0) | 2021.10.06 |
---|---|
NumPy의 기초(3) (0) | 2021.09.09 |
NumPy의 기초(2) (0) | 2021.09.09 |
NumPy의 기초(1) (0) | 2021.09.09 |