머신러닝/numpy

NumPy의 기초(4)

빈코더 2021. 10. 6. 11:10
728x90

배열을 이용한 배열지향 프로그래밍

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
728x90

'머신러닝 > 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