[Numpy] #5 요소별 연산, 브로드캐스팅(Broadcasting) ndarray 가지고놀기 기초문법 공부하기 5
- numpy 연산
앞선 포스팅에서 다뤘지만, 오늘은 조금 더 복잡하게 들어갈 예정이니, numpy 연산을 복습하고 가보도록 하자
import numpy as np
a = np.random.randint(1, 5, (5, ))
b = np.random.randint(1, 5, (5, ))
print("a: ", a) # a: [1 1 3 3 1]
print("b: ", b, '\n') # b: [1 1 2 4 3]
print("a + b: ", a + b) # a + b: [2 2 5 7 4]
변수 a, b는 np.random.randint(1, 5, (5, )) 1<=x<5 사이의 임의의 정수를 (5, )의 모양으로 배열 생성
이후 a + b 하게 되면
a + b = [ a[0]+b[0],
a[1]+b[1],
a[2]+b[2],
a[3]+b[3],
a[4]+b[4] ]
위의 코드와 같이 연산이 진행된다.
이러한 맥락에서 다른 연산자들을 활용해보면
a - b = [ a[0]-b[0],
a[1]-b[1],
a[2]-b[2],
a[3]-b[3],
a[4]-b[4] ]
a * b = [ a[0]*b[0],
a[1]*b[1],
a[2]*b[2],
a[3]*b[3],
a[4]*b[4] ]
a / b = [ a[0]/b[0],
a[1]/b[1],
a[2]/b[2],
a[3]/b[3],
a[4]/b[4] ]
a // b = [ a[0]//b[0],
a[1]//+b[1],
a[2]//b[2],
a[3]//b[3],
a[4]//b[4] ]
a % b = [ a[0]%b[0],
a[1]%b[1],
a[2]%b[2],
a[3]%b[3],
a[4]%b[4] ]
a ** b = [ a[0]**b[0],
a[1]**b[1],
a[2]**b[2],
a[3]**b[3],
a[4]**b[4] ]
이와 같은 방식으로 동일하게 연산이 진행되고,
a = np.arange(2*2).reshape((2, 2))
b = np.arange(2*2).reshape((2, 2))
print(a + b) # [[0 2]
# [4 6]]
a = np.arange(3*3).reshape((3, 3))
b = np.arange(3*3).reshape((3, 3))
print(a + b) # [[ 0 2 4]
# [ 6 8 10]
# [12 14 16]]
다차원 배열 역시 같은 방식으로 연산이 가능하다.
- Broadcasting(브로드캐스팅)
- 앞선 방법도 행렬, 배열간의 연산을 간단히 구현할 수 있게 하지만, 더 활용도가 높은 개념이 있다.
- 그것이 Broadcasting이다. 한국말로 정확히 번역이 애매해 "브로드캐스팅"으로 칭하겠다.
- 브로드캐스팅을 설명하기 위해 하나의 상황을 가정하겠다.
- 문제1
어느 학생 3명의 국어, 영어, 수학시험을 보았고, 점수가 나왔다.
그런데, 국어, 영어, 수학 시험문제에 오류가 있어 과목에 따라 보정점수가 주어졌다.
해당 상황을 코드로 구현해보아라.
# student[0] : 국어, student[1] : 영어, student[2] : 수학
student_a = np.random.randint(0,100+1,3)
student_b = np.random.randint(0,100+1,3)
student_c = np.random.randint(0,100+1,3)
# 최초 점수
scores = np.array((student_a,student_b,student_c))
print(scores)
# 국 영 수
# 학생a [[21 29 75]
# 학생b [ 6 51 66]
# 학생c [33 7 91]]
# 보정 점수, 국어 : 15점, 영어 : 10점, 수학 : 5점
comp = np.array([15, 10, 5])
print(scores + comp)
# 국 영 수
# 학생a [[36 39 80]
# 학생b [21 61 71]
# 학생c [48 17 96]]
print(scores.shape, comp.shape) # (3, 3) (3, )
문제는 위의 코드와 같이 구현할 수 있겠다.
생각보다 코드가 간단하지 않은가? 그런데 여기서 처음보는 코드 구조가 있다.
print(scores.shape, comp.shape) # (3, 3) (3, )
scores와 comp의 모양이 다른데 어떻게 연산이 되는걸까?
관련 그림을 깔끔하게 그리고 싶었으나, 표로 대체하겠슴당.
random.randint()를 통해 생성한 값을 기준으로 설명해보겠다.
기존점수 | 국어 | 영어 | 수학 |
학생a | 21 | 29 | 75 |
학생b | 6 | 51 | 66 |
학생c | 33 | 7 | 91 |
국어 | 영어 | 수학 | |
보정점수 | 15 | 10 | 5 |
표로 표현해보자면, 기존점수와 보정점수는 이런식으로 그려질 것이다.
그리고 행렬로 표현하면, 아래와 같이 그려진다.
$$ \left[\begin{matrix}21 & 29 & 75 \\ 6 & 51 & 66 \\ 33 & 7 & 91\\\end{matrix}\right] + \left[\begin{matrix} 15 & 10 & 5\\\end{matrix}\right]$$
이러한 식으로 서로 모양이 같지 않은 행렬은 어떻게 계산되는 것일까?
$$ \left[\begin{matrix}21 & 29 & 75 \\ 6 & 51 & 66 \\ 33 & 7 & 91\\\end{matrix}\right] + \left[\begin{matrix}15 & 10 & 5 \\ 15 & 10 & 5 \\ 15 & 10 & 5\\\end{matrix}\right] = \left[\begin{matrix}36 & 39 & 80 \\ 21 & 61 & 71 \\ 48 & 17 & 96\\\end{matrix}\right]$$
- (3, 3)행렬과 (3, )행렬의 합은 뒷행렬이 (3, ) -> (3, 3)으로 늘어진 후, 각각의 요소에 대응하여 합계산을 하게 된다.
- 그렇다면 (3, 3)행렬과 (3, 1)행렬의 합도 가능할까?
a = np.arange(3*3).reshape((3, 3))
b = np.arange(3).reshape((3, 1))
print(a)
# [[0 1 2]
# [3 4 5]
# [6 7 8]]
print(b)
# [[0]
# [1]
# [2]]
print(a + b)
# [[ 0 1 2]
# [ 4 5 6]
# [ 8 9 10]]
$$\left[\begin{matrix}0&1&2\\3&4&5\\6&7&8\\\end{matrix}\right] + \left[\begin{matrix}0\\1\\ 2\\\end{matrix}\right]$$
이러한 식으로 행렬, 배열간의 합을 구해보고자 하면
$$\left[\begin{matrix}0&1&2\\3&4&5\\6&7&8\\\end{matrix}\right] + \left[\begin{matrix}0&0&0\\1&1&1\\2&2&2\\\end{matrix}\right] = \left[\begin{matrix}0&1&2\\4&5&6\\8&9&10\\\end{matrix}\right]$$
- (3, 3)행렬과 (3, 1)행렬의 합은 뒷행렬이 (3, 1) -> (3, 3)으로 늘어진 후, 각각의 요소에 대응하여 합계산을 하게 된다.