[Python] Numpy
- 본 포스팅은
Numpy
라이브러리의 배열의 인덱싱, 연산, 그리고 Broadcasting에 관하여 다룹니다. - 정수 배열 인덱싱 / boolean 배열 인덱싱 / 배열의 자료형
- 배열 연산 :
add
/ subtract
/ multiply
/ divide
/ sqrt
/ dot
- Broadcasting
Hello! Numpy
정수 배열 인덱싱
Numpy
배열을 슬라이싱하면, 결과로 얻어지는 배열은 언제나 기존 배열의 부분 배열이다.
그러나 정수 배열 인덱싱을 한다면, 원본과 다른 배열을 만들 수 있다.
1
2
| a = np.array([[1, 2], [3, 4], [4, 5]])
a
|
1
2
3
| array([[1, 2],
[3, 4],
[4, 5]])
|
array_obj[[row1, row2, row3], [col1, col2, col3]]
1
| a[[0, 1, 2], [0, 1, 0]]
|
np.array([ array_obj[row1, col1], array_obj[row2, col2], array_obj[row3, col3] ])
1
| np.array([a[0, 0], a[1, 1], a[2, 0]])
|
다음과 같이 선언하여 객체에 넣게되면 튜플형태로 반환된다.
1
2
3
| b = a[0, 0], a[1, 1], a[2, 0]
print (b)
print (type(b))
|
1
2
| (1, 4, 4)
<class 'tuple'>
|
정수 배열 인덱싱을 가장 유용하게 사용하는 방법 중 하나는
행렬의 각 행에서 하나의 요소를 선택하거나 바꾸는 것이다
요소를 선택할 새로운 배열을 생성한다.
1
2
3
| a = np.arange(1, 13)
a.shape = (4, 3)
a
|
1
2
3
4
| array([[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12]])
|
인덱스를 저장할 배열 생성
1
2
| b = np.array([0, 2, 0, 1])
b
|
b에 저장된 인덱스를 이용해 각 행에서 하나의 요소를 선택한다.
다음의 예시는
1
| a[[0, 1, 2, 3], [0, 2, 0, 1]]
|
과 동일하다
이를 응용하여, b에 저장된 인덱스를 이용해 각 행에서 하나의 요소를 변경할 수 있다.
1
2
| a[np.arange(4), b] += 10
a
|
1
2
3
4
| array([[31, 2, 3],
[ 4, 5, 36],
[37, 8, 9],
[10, 41, 12]])
|
boolean 배열 인덱싱
Boolean 배열 인덱싱을 통해 배열안에 있는 요소들을 선택할 수 있다.
해당 인덱싱은 특정 조건을 만족하게하는 요소만 선택하고자 할 때 자주 사용된다.
1
2
| a = np.array([[1, 2], [3, 4], [4, 5]])
a
|
1
2
3
| array([[1, 2],
[3, 4],
[4, 5]])
|
다음의 예시는 boolean배열 인덱싱의 예시이다.
2보다 큰 a의 요소를 찾고자 할때 다음과 같이 수행한다.
1
2
| bool_idx = (a > 2)
bool_idx
|
1
2
3
| array([[False, False],
[ True, True],
[ True, True]])
|
이를 기존 배열에서 True인 값을 가지는 요소로 구성되는 rank 1인 배열을 출력할 수 있다.
위에서 설명된 것들을 한 단락으로 정리하면 다음과 같다.
자료형
Numpy
가 자료형을 추측해서 선택한다.
1
2
3
| # 정수형
x = np.array([1, 2])
x.dtype
|
1
2
3
| # 실수형
x = np.array([1. , 2.])
x.dtype
|
특정 자료형을 명시적으로 지칭하여 사용할 수도 있다.
1
2
3
| # 정수형으로 작성했지만, 자료형을 float으로 명시하여 float자료형의 value가 x값에 대입된다.
x = np.array([1, 2], dtype = np.float64)
x
|
배열 연산
기본적인 수학 함수들은 배열의 각 요소별로 동작하며
연산자를 통해 동작하거나 numpy 함수 모듈을 통해 동작한다.
1
2
3
4
5
| # 연습용 배열 생성
x = np.arange(4, dtype = np.float64).reshape(2,2)
y = np.arange(4, 8, dtype = np.float64).reshape(2,2)
print(x)
print(y)
|
1
2
3
4
| [[0. 1.]
[2. 3.]]
[[4. 5.]
[6. 7.]]
|
- Array 합
- 연산자 :
+
- numpy 함수 :
np.add()
1
2
| array([[ 4., 6.],
[ 8., 10.]])
|
1
2
| array([[ 4., 6.],
[ 8., 10.]])
|
- Array 차
- 연산자 :
-
- numpy 함수 :
np.subtract()
1
2
| array([[-4., -4.],
[-4., -4.]])
|
1
2
| array([[-4., -4.],
[-4., -4.]])
|
- Array 곱
- 연산자 :
*
- numpy 함수 :
np.multiply()
1
2
| array([[ 0., 5.],
[12., 21.]])
|
1
2
| array([[ 0., 5.],
[12., 21.]])
|
- Array 나눗셈
- 연산자 :
/
- numpy 함수 :
np.divide()
1
2
| array([[0. , 0.2 ],
[0.33333333, 0.42857143]])
|
1
2
| array([[0. , 0.2 ],
[0.33333333, 0.42857143]])
|
1
2
| array([[0. , 1. ],
[1.41421356, 1.73205081]])
|
1
2
| array([[2. , 2.23606798],
[2.44948974, 2.64575131]])
|
또한 Numpy
는 백터의 내적이나 백터 곱 그리고 열과 행을 합산하는 등의 유용한 함수를 제공한다.
1
2
3
4
5
6
| # 백터의 내적/곱을 실습하기위한 배열 생성
x = np.array([[1,2], [3, 4]])
y = np.array([[5,6], [7, 8]])
v = np.array([9, 10])
w = np.array([11, 12])
|
1
2
3
| # 백터의 내적
print(v.dot(w))
print(np.dot(v, w))
|
1
2
3
| # rank 2 행렬과 rank 1 백터 곱
print(x.dot(v))
print(np.dot(x, v))
|
1
2
3
| # rank 2 행렬과 rank 2 행렬 곱
print(x.dot(y))
print(np.dot(x, y))
|
1
2
3
4
| [[19 22]
[43 50]]
[[19 22]
[43 50]]
|
sum()
- 배열안의 요소들을 합산하고 싶을 때 사용한다.
axis = 0
: 각 열에 대한 합산을 연산하고 싶을 경우axis = 1
: 각 행에 대한 합산을 연산하고 싶을 경우
1
2
| # axis 옵션을 사용하지 않을 경우 단순히 모든 요소들을 합산한다.
np.sum(x)
|
1
2
| # 열에 대한 합산
np.sum(x, axis = 0)
|
1
2
| # 행에 대한 합산
np.sum(x, axis = 1)
|
Numpy Broadcasting
파이썬에서 말하는 Broadcasting은 방송이라는 명사적 의미보다는 spread라는 동사적의미가 강하다.
일반적으로는 다음과 같이 numpy로 생성된 사이즈가 다른 배열끼리는 연산이 불가능하다.
1
2
3
4
| x = np.array([[1, 2, 3, 4]])
y = np.array([[1, 2]])
x + y
|
1
2
3
4
5
6
7
8
9
10
11
| ---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-104-6295914b1607> in <module>
2 y = np.array([[1, 2]])
3
----> 4 x + y
ValueError: operands could not be broadcast together with shapes (1,4) (1,2)
|
위의 에러 문구를 읽어보면 broadcast가 되지 못했다는 의미를 가지고 있다.
즉, 어떤 조건을 만족한다면 사이즈가 다른 배열 끼리의 연산도 가능하다는 말이다.
이때, Broadcasting
은 numpy
에서 사이즈가 다른 배열 간에도 산술 연산이 가능하게한다.
종종 작은 배열과 큰 배열이 있을 때, 큰 배열을 대상으로 작은 배열을 여러 번 연산하고자 할 때 사용한다.
예를 들자면, 행렬의 각 행에 상수 벡터를 더하는 것을 생각하면된다.
다음의 같은 결과들을 출력하는 순차적인 예제들을 살펴보자
행렬 x의 각 행에 백터 v를 더한 뒤,
그 결과를 행렬 y에 저장하고자 한다.
[Process1] 반복문을 이용한 방법
일반적으로는 for문을 이용해서 간단하게 결과를 출력할 수 있다.
1
2
3
| x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
v = np.array([1, 0, 1])
y = np.empty_like(x) # x와 동일한 shape를 가지며 비어있는 행렬 생성
|
반복문을 통하여 행렬 x의 각 행에 v를 더한다
1
2
| for i in range(4):
y[i, :] = x[i, :] + v
|
1
2
3
4
| array([[ 2, 2, 4],
[ 5, 5, 7],
[ 8, 8, 10],
[11, 11, 13]])
|
하지만, 위의 방식대로하면 `x`가 만약 매우 큰 행렬이라면,
위처럼 반복문을 이용하면 매우 느려질 수 있다.
[Process2] tile
함수를 이용한 방법
이때, 백터v
를 행렬x
의 각 행에 대하는 것을
v
를 여러개 복사해 수직으로 쌓는 행렬을 만들고 앞과 같은 과정을 수행가능하다.
1
2
3
| x = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
v = np.array([1, 0, 1])
x
|
1
2
3
4
| array([[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12]])
|
v를 차곡차곡 tile
함수를 이용하여 쌓는다.
1
2
| vv = np.tile(v, (4, 1))
vv
|
1
2
3
4
| array([[1, 0, 1],
[1, 0, 1],
[1, 0, 1],
[1, 0, 1]])
|
1
2
3
4
| array([[ 2, 2, 4],
[ 5, 5, 7],
[ 8, 8, 10],
[11, 11, 13]])
|
하지만, Numpy
의 Broadcasting
을 이용하면 위처럼 v의 복사본을 여러 개 쌓아서 만들지 않아도 같은 연산을 수행할 수 있다.
[Process3] Numpy의 Broadcasting을 이용한 방법
1
2
| x = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])
v = np.array([1, 0, 1])
|
x
의 사이즈가 (4, 3)이고, v
의 사이즈가 (3, )지만 Broadcasting
으로 인해 문제없이 아래와 같이 연산이 수행된다.
1
2
3
4
| array([[ 2, 2, 4],
[ 5, 5, 7],
[ 8, 8, 10],
[11, 11, 13]])
|
이때 v
는 v
의 복사본이 차곡차곡 쌓인 shape(4,3) 처럼 간주되어 x
와 동일한 사이즈가 되며 두 배열 간의 요소별 덧셈연산이 가능해진 것이다.
또한, Broadcasting
은 다음의 규칙을 따른다.
두 배열이 동일한 차원을 가지고 있지 않다면, 낮은 차원의 배열이 높은 차원의 배열과 같은 차원의 배열로 인식된다.
반환된 배열은 연산을 수행한 배열 중 rank가 가장 큰 배열과 같다.
broadcasting이 적용된 배열의 사이즈는 연산에 사용된 배열들의 사이즈에 대한 최소 공배수 값을 사용한다.
요소가 하나인 배열은 어떤 배열에나 broadcasting을 적용할 수 있다. ex) 4X4 + 1
하나의 배열이 차원이 1인 경우 broadcasting을 적용할 수 있다. ex) 4X4 + 1X4
차원의 짝이 맞을 때 broadcasting을 적용할 수 있다. ex) 3X1 + 1X3
References
- http://aikorea.org/cs231n/python-numpy-tutorial/#numpy-arrays
- https://sacko.tistory.com/16
- https://appia.tistory.com/184
- https://076923.github.io/posts/Python-numpy-12/