6.10. 브로드캐스팅

이항 연산자가 형태가 정확히 일치하지 않는 두 배열을 받으면, numpy 는 오류를 발생시키지 않습니다 – 브로드캐스팅 을 합니다. 브로드캐스팅은 두 형태가 호환되는지, 그리고 호환된다면 작은 쪽이 큰 쪽에 맞도록 어떻게 가상으로 늘려지는지를 결정하는 작은 규칙 집합입니다.

6.10.1. 규칙

두 피연산자가 형태 AB 를 가질 때, numpy 는 두 단계로 이를 처리합니다.

  1. 랭크를 맞춥니다. 한 피연산자가 다른 쪽보다 축이 더 적으면, numpy 는 두 형태가 같은 수의 축을 가질 때까지 그 형태의 앞쪽을 크기 1인 축으로 가상으로 채웁니다. 형태 (3,) 인 1차원 피연산자가 형태 (2, 3) 인 2차원 피연산자와 짝지어지면 (2, 3) 에 대해 (1, 3) 이 됩니다.

  2. 각 축을 확인합니다. 이제 길이가 같아진 형태를 축별로 따라가면, 각 크기 쌍은 두 조건 중 하나를 만족해야 합니다: 크기가 같거나, 둘 중 하나가 1이어야 합니다. 크기 1인 축은 연산을 위해 다른 쪽의 크기로 가상으로 늘려집니다. (2, 3) 에 대한 (1, 3) 쌍은 첫 번째 축이 1을 가지고(2로 늘어남) 두 번째 축이 일치하므로(3 == 3) 호환됩니다. 결과는 형태 (2, 3) 을 가집니다.

어떤 축 쌍이라도 두 조건 중 어느 것도 만족하지 못하면, 형태는 호환되지 않으며 연산자는 ValueError 를 발생시킵니다.

6.10.2. 예제

스칼라 대 임의의 배열. 스칼라는 형태 (1,) 처럼 작동하며 무엇이든 맞게 늘어납니다:

a = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float)
a + 10                 # (2, 3) + scalar -> (2, 3)

2차원 행렬을 가로지르는 1차원 벡터. 규칙 1은 크기 1인 축을 앞에 추가하여 (3,)(1, 3) 으로 만들고, 규칙 2는 그 행을 a 의 각 열 아래로 늘립니다:

row = np.array([100, 200, 300], dtype=np.float)
a + row                # (2, 3) + (3,) -> (2, 3)

길이가 같은 두 1차원 배열 은 요소별로 더해집니다 – 브로드캐스팅이 필요 없습니다:

np.arange(4) + np.arange(4)

행 벡터에 대한 열 벡터 는 2차원 “외적” 형태를 만듭니다: (4, 1)(3,) 과 짝지어지면 랭크 추가 후 (1, 3) 에 대한 (4, 1) 이 되고, 규칙 2는 각 피연산자를 그 크기 1인 축을 따라 늘립니다:

x = np.array([1, 2, 3, 4]).reshape((4, 1))     # column
y = np.array([10, 20, 30])                      # row
x + y                                           # (4, 3) matrix

동일한 형태 규칙이 arctan2() 를 포함한 모든 두 인수 ufunc에 적용됩니다:

np.arctan2(y, 1.0)
np.arctan2(y, x)

6.10.3. 브로드캐스팅이 할당하지 않는

늘림은 가상입니다. numpy 는 두 피연산자를 함께 따라가며, 작은 쪽을 복사하는 대신 그 브로드캐스트 축을 따라 다시 읽습니다. 더 짧은 배열의 데이터는 메모리에서 결코 복제되지 않습니다.

메모리에 중요한 것은 출력 배열의 크기입니다. a + rowa 의 형태에 row 의 형태를 더한 것이 아니라, a 의 형태를 가진 출력을 할당합니다. 긴 브로드캐스팅 연쇄는 여전히 큰 중간 결과를 만들어낼 수 있습니다.

6.10.4. 브로드캐스팅이 잘못될 때

전형적인 실패는 어느 쪽도 늘릴 크기 1인 축을 갖지 않고 크기가 일치하지 않는 두 형태입니다 – 예를 들어 (4, 3) 에 대한 (3, 4) 입니다. 규칙 2는 3을 4에 맞출 수 없으므로 numpyValueError 를 발생시킵니다.

더 미묘한 문제는 브로드캐스팅이 성공하지만 애플리케이션이 의도한 방식이 아닌 경우입니다. (5, 1) 에 대한 (5,) 가 대표적인 사례입니다: 랭크 추가가 (5,)(1, 5) 로 바꾸고, 이것이 (5, 1) 에 대해 브로드캐스트되어 (5, 5) 행렬을 만듭니다 – 이는 두 벡터의 외적 조합이지, 애플리케이션이 아마도 원했을 길이 5의 요소별 결과가 아닙니다. 의심스러울 때는 reshape()transpose() 에 손을 대기 전에 양쪽에서 shape 를 출력하고 규칙을 단계별로 따라가십시오.