부록 A(p. 587)의 내용 중 가장 많이 사용되는 고급 기능을 다룬다.
numpy
모듈과 시각화 도구 모듈인 matplotlib.pyplot
에 대한 기본 설정을 지정한다.
# 넘파이
import numpy as np
# 램덤 시드
np.random.seed(12345)
# 어레이 사용되는 부동소수점들의 정확도 지정
np.set_printoptions(precision=4, suppress=True)
# 파이플롯
import matplotlib.pyplot as plt
# 도표 크기 지정
plt.rc('figure', figsize=(10, 6))
어레이를 조작하는 몇 가지 중요한 고급 기법을 살펴본다.
reshape()
메서드¶reshape()
메서드를 활용하여 지정된 튜플의 모양으로 주어진 어레이의 모양을 변형한다.
단, 항목의 수가 변하지 않도록 모양을 지정해야 한다.
예를 들어, 길이가 8인 1차원 어레이가 다음과 같다.
arr = np.arange(8)
arr
array([0, 1, 2, 3, 4, 5, 6, 7])
이제 (4, 2) 모양의 2차원 어레이로 모양을 변형할 수 있다.
arr.reshape((4, 2))
array([[0, 1], [2, 3], [4, 5], [6, 7]])
항목의 수만 같으면 임의의 차원의 어레이를 임의의 차원의 어레이로 변형시킬 수 있다.
arr.reshape((4, 2)).reshape((2, 2, 2))
array([[[0, 1], [2, 3]], [[4, 5], [6, 7]]])
-1
의 역할¶어레의 모양을 지정할 때 튜플의 특정 위치에 -1을 사용할 수 있다. 그러면 그 위치의 값은 튜플의 다른 항목의 정보를 이용하여 자동으로 지정된다. 예를 들어, 아래 코드에서 -1은 4를 의미한다. 이유는 20개의 항목을 5개의 행으로 이루어진 2차원 어레이로 지정하려면 열은 4개 있어야 하기 때문이다.
arr = np.arange(20)
arr.reshape((5, -1))
array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15], [16, 17, 18, 19]])
arr.reshape((5, 4))
array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15], [16, 17, 18, 19]])
동일한 이유로 아래에서 -1은 5를 의미한다.
arr.reshape((2, -1, 2))
array([[[ 0, 1], [ 2, 3], [ 4, 5], [ 6, 7], [ 8, 9]], [[10, 11], [12, 13], [14, 15], [16, 17], [18, 19]]])
arr.reshape((2, 5, 2))
array([[[ 0, 1], [ 2, 3], [ 4, 5], [ 6, 7], [ 8, 9]], [[10, 11], [12, 13], [14, 15], [16, 17], [18, 19]]])
어레이에 임의의 축을 추가하는 방식으로 1차원 커진 어레이를 생성할 수 있다. 어느 축을 지정하느냐에 따라 다른 모양을 갖게 된다.
다음 길이가 3인 1차원 어레이를 이용하자.
np.random.normal()
함수는 np.random.randn()
함수를 일반화하여 정균분포를 따르면서
무작위 수를 생성한다.
평균값과 표준편차를 지정할 수 있으며, 기본값은 평균값 0, 표준편차 1로 표준 정규분포를 따르도록 한다.arr_1d = np.random.normal(size=3)
arr_1d
array([-0.2047, 0.4789, -0.5194])
아래 코드는 열 관련 축을 추가한다.
arr_1d[:, np.newaxis]
array([[-0.2047], [ 0.4789], [-0.5194]])
reshape()
메소드로도 동일한 결과를 얻을 수 있다.
arr_1d.reshape((3, 1))
array([[-0.2047], [ 0.4789], [-0.5194]])
아래 코드는 행 관련 축을 추가한다.
arr_1d[np.newaxis, :]
array([[-0.2047, 0.4789, -0.5194]])
arr_1d.reshape((1,3))
array([[-0.2047, 0.4789, -0.5194]])
2차원 어레이에 축을 추가하면 3차원 어레이가 생성되며, 작동방식은 앞서와 동일하다.
arr = np.random.randn(4, 3)
arr
array([[-0.5557, 1.9658, 1.3934], [ 0.0929, 0.2817, 0.769 ], [ 1.2464, 1.0072, -1.2962], [ 0.275 , 0.2289, 1.3529]])
arr[:,:,np.newaxis].shape
(4, 3, 1)
arr[:,:,np.newaxis]
array([[[-0.5557], [ 1.9658], [ 1.3934]], [[ 0.0929], [ 0.2817], [ 0.769 ]], [[ 1.2464], [ 1.0072], [-1.2962]], [[ 0.275 ], [ 0.2289], [ 1.3529]]])
arr[:,np.newaxis,:].shape
(4, 1, 3)
arr[:,np.newaxis,:]
array([[[-0.5557, 1.9658, 1.3934]], [[ 0.0929, 0.2817, 0.769 ]], [[ 1.2464, 1.0072, -1.2962]], [[ 0.275 , 0.2289, 1.3529]]])
ravel()
메서드와 flatten()
메서드¶두 메서드 모두 1차원 어레이를 반환한다. 즉, 차원을 모두 없앤다.
arr = np.arange(15).reshape((5, 3))
arr
array([[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8], [ 9, 10, 11], [12, 13, 14]])
arr1 = arr.ravel()
arr1
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
arr2 = arr.flatten()
arr2
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
차이점은 ravel()
메서드는 뷰(view)를 사용하는 반면에 flatten()
메서드는 어레이를 새로 생성한다.
예를 들어, 아래처럼 arr1
의 항목을 변경하면 arr
의 항목도 함께 변경된다.
arr1[0] = -1
arr
array([[-1, 1, 2], [ 3, 4, 5], [ 6, 7, 8], [ 9, 10, 11], [12, 13, 14]])
arr2
은 arr
과 전혀 상관이 없다.
arr2[0] = -7
arr
array([[-1, 1, 2], [ 3, 4, 5], [ 6, 7, 8], [ 9, 10, 11], [12, 13, 14]])
np.split()
함수¶어레이를 지정된 기준에 따라 여러 개의 어레이로 쪼갠다. 반환값은 쪼개진 어레이들의 리스트다.
아래 예제를 살펴보자.
arr = np.random.randn(7, 5)
arr
array([[ 0.8864, -2.0016, -0.3718, 1.669 , -0.4386], [-0.5397, 0.477 , 3.2489, -1.0212, -0.5771], [ 0.1241, 0.3026, 0.5238, 0.0009, 1.3438], [-0.7135, -0.8312, -2.3702, -1.8608, -0.8608], [ 0.5601, -1.2659, 0.1198, -1.0635, 0.3329], [-2.3594, -0.1995, -1.542 , -0.9707, -1.307 ], [ 0.2863, 0.378 , -0.7539, 0.3313, 1.3497]])
np.split()
함수의 인자는 정수이거나 정수들의 리스트가 사용된다.
먼저, 정수 리스트가 들어오면 축이 정한 방향으로 리스트에 포함된 정수를 이용하여 여러 개의 구간으로 쪼갠다.
아래 코드는 행을 기준으로 행의 인덱스를 0-1, 2, 3-4, 5-7 네 개의 구간으로 쪼갠다. 따라서 결과는 네 개의 어레이로 이루어진 리스트가 되며, 각 어레의 모양은 다음과 같다.
(2, 5), (1, 5), (2, 5), (2, 5)
np.split(arr, [2, 3, 5],axis=0)
[array([[ 0.8864, -2.0016, -0.3718, 1.669 , -0.4386], [-0.5397, 0.477 , 3.2489, -1.0212, -0.5771]]), array([[0.1241, 0.3026, 0.5238, 0.0009, 1.3438]]), array([[-0.7135, -0.8312, -2.3702, -1.8608, -0.8608], [ 0.5601, -1.2659, 0.1198, -1.0635, 0.3329]]), array([[-2.3594, -0.1995, -1.542 , -0.9707, -1.307 ], [ 0.2863, 0.378 , -0.7539, 0.3313, 1.3497]])]
반면에 열을 기준으로 0, 1-2, 3-4 3개의 구간으로 쪼개면 다음과 같으며, 각 어레이의 모양은 다음과 같다.
(7, 1) (7, 2), (7, 2)
np.split(arr, [1, 3],axis=1)
[array([[ 0.8864], [-0.5397], [ 0.1241], [-0.7135], [ 0.5601], [-2.3594], [ 0.2863]]), array([[-2.0016, -0.3718], [ 0.477 , 3.2489], [ 0.3026, 0.5238], [-0.8312, -2.3702], [-1.2659, 0.1198], [-0.1995, -1.542 ], [ 0.378 , -0.7539]]), array([[ 1.669 , -0.4386], [-1.0212, -0.5771], [ 0.0009, 1.3438], [-1.8608, -0.8608], [-1.0635, 0.3329], [-0.9707, -1.307 ], [ 0.3313, 1.3497]])]
np.vsplit()
/np.hsplit()
함수¶두 함수는 np.split()
함수에 축을 각각 0과 1로 지정한 함수이다.
np.vsplit(arr, z)
= np.split(arr, z, axis=0)
np.vsplit(arr, [2, 3, 5])
[array([[ 0.8864, -2.0016, -0.3718, 1.669 , -0.4386], [-0.5397, 0.477 , 3.2489, -1.0212, -0.5771]]), array([[0.1241, 0.3026, 0.5238, 0.0009, 1.3438]]), array([[-0.7135, -0.8312, -2.3702, -1.8608, -0.8608], [ 0.5601, -1.2659, 0.1198, -1.0635, 0.3329]]), array([[-2.3594, -0.1995, -1.542 , -0.9707, -1.307 ], [ 0.2863, 0.378 , -0.7539, 0.3313, 1.3497]])]
np.hsplit(arr, z)
= np.split(arr, z, axis=1)
np.hsplit(arr, [1, 3])
[array([[ 0.8864], [-0.5397], [ 0.1241], [-0.7135], [ 0.5601], [-2.3594], [ 0.2863]]), array([[-2.0016, -0.3718], [ 0.477 , 3.2489], [ 0.3026, 0.5238], [-0.8312, -2.3702], [-1.2659, 0.1198], [-0.1995, -1.542 ], [ 0.378 , -0.7539]]), array([[ 1.669 , -0.4386], [-1.0212, -0.5771], [ 0.0009, 1.3438], [-1.8608, -0.8608], [-1.0635, 0.3329], [-0.9707, -1.307 ], [ 0.3313, 1.3497]])]
np.concatenate()
함수¶두 개의 어레이를 이어 붙이다. 지정되는 축에 따라 좌우로 또는 상하로 이어붙인다.
아래 두 어레이가 주어졌다고 가정하다.
arr1 = np.array([[1, 2, 3], [4, 5, 6]])
arr1
array([[1, 2, 3], [4, 5, 6]])
arr2 = np.array([[7, 8, 9], [10, 11, 12]])
arr2
array([[ 7, 8, 9], [10, 11, 12]])
위아래로 이어붙이려면 축을 0으로 정한다.
주의사항: 인자가 길이가 2인 리스트이다.
np.concatenate([arr1, arr2], axis=0)
array([[ 1, 2, 3], [ 4, 5, 6], [ 7, 8, 9], [10, 11, 12]])
좌우로 이어붙이려면 축을 1로 정한다.
np.concatenate([arr1, arr2], axis=1)
array([[ 1, 2, 3, 7, 8, 9], [ 4, 5, 6, 10, 11, 12]])
np.vstack()
/np.hstack()
함수¶두 함수는 np.concatenate()
함수에 축을 각각 0과 1로 지정한 함수이다.
주의사항: 인자가 길이가 2인 튜플이다.
np.vstack((x, y))
= np.concatenate([x, y], axis=0)
np.vstack((arr1, arr2))
array([[ 1, 2, 3], [ 4, 5, 6], [ 7, 8, 9], [10, 11, 12]])
np.hstack((x, y))
= np.concatenate([x, y], axis=1)
np.hstack((arr1, arr2))
array([[ 1, 2, 3, 7, 8, 9], [ 4, 5, 6, 10, 11, 12]])
np.r_[]
/np.c_[]
객체¶vstack()
/hstack()
과 동일한 기능을 수행하는 특수한 객체들이다.
아래 세 개의 어레이를 이용하여 사용법을 살펴본다.
arr = np.arange(6)
arr
array([0, 1, 2, 3, 4, 5])
arr1 = np.arange(6).reshape((3, 2))
arr1
array([[0, 1], [2, 3], [4, 5]])
arr2 = np.random.randn(3, 2)
arr2
array([[ 0.0699, 0.2467], [-0.0119, 1.0048], [ 1.3272, -0.9193]])
아래 코드는 np.vstack((arr1, arr2))
와 동일하다.
np.r_[arr1, arr2]
array([[ 0. , 1. ], [ 2. , 3. ], [ 4. , 5. ], [ 0.0699, 0.2467], [-0.0119, 1.0048], [ 1.3272, -0.9193]])
np.vstack([arr1, arr2])
array([[ 0. , 1. ], [ 2. , 3. ], [ 4. , 5. ], [ 0.0699, 0.2467], [-0.0119, 1.0048], [ 1.3272, -0.9193]])
아래 코드는 np.hstack((arr1, arr2))
와 동일하다.
np.c_[arr1, arr2]
array([[ 0. , 1. , 0.0699, 0.2467], [ 2. , 3. , -0.0119, 1.0048], [ 4. , 5. , 1.3272, -0.9193]])
np.hstack((arr1, arr2))
array([[ 0. , 1. , 0.0699, 0.2467], [ 2. , 3. , -0.0119, 1.0048], [ 4. , 5. , 1.3272, -0.9193]])
행 또는 열의 크기를 적절하게 맞출 수 있는 어떤 조합도 가능하다.
np.c_[np.r_[arr1, arr2], arr]
array([[ 0. , 1. , 0. ], [ 2. , 3. , 1. ], [ 4. , 5. , 2. ], [ 0.0699, 0.2467, 3. ], [-0.0119, 1.0048, 4. ], [ 1.3272, -0.9193, 5. ]])
arr = np.arange(6).reshape((2,3))
arr
array([[0, 1, 2], [3, 4, 5]])
arr * 4
아래 어레이의 곱셈과 동일하다.
arr * 4
array([[ 0, 4, 8], [12, 16, 20]])
arr2 = np.arange(4).reshape((4,1)).repeat(3,axis=1)
arr2
array([[0, 0, 0], [1, 1, 1], [2, 2, 2], [3, 3, 3]])
arr3 = np.arange(1, 4)
arr3
array([1, 2, 3])
arr2 + arr3
array([[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]])
arr3a = np.arange(1, 4).reshape((1,3))
arr3a
array([[1, 2, 3]])
arr2 + arr3a
array([[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]])
arr4 = np.arange(1, 5).reshape((4,1))
arr4
array([[1], [2], [3], [4]])
arr2 + arr4
array([[1, 1, 1], [3, 3, 3], [5, 5, 5], [7, 7, 7]])
arr6 = np.arange(24).reshape((3, 4, 2))
arr6
array([[[ 0, 1], [ 2, 3], [ 4, 5], [ 6, 7]], [[ 8, 9], [10, 11], [12, 13], [14, 15]], [[16, 17], [18, 19], [20, 21], [22, 23]]])
arr7 = np.arange(8).reshape((4, 2))
arr7
array([[0, 1], [2, 3], [4, 5], [6, 7]])
arr6 + arr7
array([[[ 0, 2], [ 4, 6], [ 8, 10], [12, 14]], [[ 8, 10], [12, 14], [16, 18], [20, 22]], [[16, 18], [20, 22], [24, 26], [28, 30]]])
arr = np.random.randn(4, 3)
arr
array([[-1.5491, 0.0222, 0.7584], [-0.6605, 0.8626, -0.01 ], [ 0.05 , 0.6702, 0.853 ], [-0.9559, -0.0235, -2.3042]])
arr.mean(0)
array([-0.7789, 0.3829, -0.1757])
demeaned = arr - arr.mean(0)
demeaned
array([[-0.7702, -0.3607, 0.9341], [ 0.1183, 0.4797, 0.1657], [ 0.8289, 0.2873, 1.0287], [-0.177 , -0.4064, -2.1285]])
demeaned.mean(0)
array([ 0., 0., -0.])
arr
array([[-1.5491, 0.0222, 0.7584], [-0.6605, 0.8626, -0.01 ], [ 0.05 , 0.6702, 0.853 ], [-0.9559, -0.0235, -2.3042]])
row_means = arr.mean(1)
row_means
array([-0.2562, 0.064 , 0.5244, -1.0945])
row_means.reshape((4, 1))
array([[-0.2562], [ 0.064 ], [ 0.5244], [-1.0945]])
demeaned = arr - row_means.reshape((4, 1))
demeaned.mean(1)
array([-0., -0., -0., -0.])
arr = np.zeros((4, 3))
arr
array([[0., 0., 0.], [0., 0., 0.], [0., 0., 0.], [0., 0., 0.]])
arr[:] = 5
arr
array([[5., 5., 5.], [5., 5., 5.], [5., 5., 5.], [5., 5., 5.]])
col = np.array([1.28, -0.42, 0.44, 1.6])
arr[:] = col[:, np.newaxis]
arr
array([[ 1.28, 1.28, 1.28], [-0.42, -0.42, -0.42], [ 0.44, 0.44, 0.44], [ 1.6 , 1.6 , 1.6 ]])
arr[:2] = [[-1.37], [0.509]]
arr
array([[-1.37 , -1.37 , -1.37 ], [ 0.509, 0.509, 0.509], [ 0.44 , 0.44 , 0.44 ], [ 1.6 , 1.6 , 1.6 ]])