[NumPy] ブロードキャスト

[NumPy] ブロードキャスト

ブロードキャスト

 配列の演算規則の記事で解説したように、配列に 1 を加えると、配列のすべての要素に 1 を加えるという処理が行われます。

# PYTHON_NUMPY_BROADCASTING_01-1

# NumPyをインポート
import numpy as np

# 1次元配列の定義
x = np.array([10, 20, 30])

# 配列の各要素に1を加える
print(x + 1)
[11 21 31]

 これはとても便利な記法です。なぜなら、こうした記法が許されていないとすれば、配列のすべての要素に 1 を加えるためには、もう1つ別の配列を用意しなければならないからです。

# PYTHON_NUMPY_BROADCASTING_01-2

# 1次元配列の定義
x = np.array([10, 20, 30])
y = np.array([1, 1, 1])

# 配列xの各要素に1を加える
print(x + y)
[11 21 31]

 あるいは、こうした操作を行なうための関数やメソッドを用意するという方法もあるかもしれませんが、いずれにしても「+1」ほど簡易な記述にはならないでしょう。

 実は、この操作は NumPy の ブロードキャスト (broadcasting) とよばれる機能の一例です。ブロードキャストは形状の異なる配列同士で演算の実行を可能にします。NumPy において、Python の数値(整数や浮動小数点数)はスカラーとよばれる 0 次元配列として扱われます。1 次元配列にスカラーを加える場合、下図のようにスカラーを引き伸ばして仮想的な 1 次元配列を作って演算処理を実行することになります。
 
 
 NumPy ブロードキャスト01
 
 様々な形状の配列同士でブロードキャストが適用されます。たとえば、2 次元配列と 1 次元配列を加える場合、1 次元配列を 2 次元配列の形状に合わせてブロードキャストします(引き伸ばします)。
 
 
 NumPy ブロードキャスト02
 
 実際にコードを書いて確認してみましょう。

# PYTHON_NUMPY_BROADCASTING_01-3

# 1次元配列の定義
x = np.array([[10, 20, 30],
              [40, 50, 60],
              [70, 80, 90]])

y = np.array([1, 1, 1])

# x + y
print(x + y)
[[11 21 31]
 [41 51 61]
 [71 81 91]]

 次は列ベクトルと 1 次元配列を掛けてみます。
 この場合、お互いが他方の配列の形状に合わせてブロードキャストされます。
 
 NumPy ブロードキャスト配列積
 
 この操作もコードで確認しておきます。

# PYTHON_NUMPY_BROADCASTING_01-4

# 列ベクトルを定義
x = np.array([[10],
              [20],
              [30]])

# 1次元配列を定義
y = np.array([1, 2, 3])

# x * y
print(x * y)
[[10 20 30]
 [20 40 60]
 [30 60 90]]

 実際には、ブロードキャストは図に描いたようなコピーを行なうことなく(メモリを割り当てることなく)配列演算を処理します(だからこそ高速なのです)。上の説明はあくまで概念的なもの(あるいはブロードキャストの演算ルール)と考えてください。
 

numpy.broadcast_to()

 numpy.broadcast_to() は受け取った配列 array を、shape で指定した形にブロードキャストします。

numpy.broadcast_to(array, shape, subok=False)

 shape にはブロードキャスト可能な形状を渡す必要があります。

# PYTHON_NUMPY_BROADCASTING_02-1

import numpy as np

x = np.array([0, 1, 2])

# xを5行3列にブロードキャスト
y = np.broadcast_to(x, (5, 3))

print(y)
[[0 1 2]
 [0 1 2]
 [0 1 2]
 [0 1 2]
 [0 1 2]]

 shape にブロードキャストできない形状を渡すと ValuError を送出します。
 たとえば、1 行 3 列の配列 x は 3 行 5 列にはブロードキャストできません。

# PYTHON_NUMPY_BROADCASTING_02-2

# xを5行3列にブロードキャスト
z = np.broadcast_to(x, (3, 5))

print(z)
ValueError: operands could not be broadcast together with remapped shapes [original->remapped]: (3,) and requested shape (3,5)

 

numpy.broadcast_arrays()

 numpy.broadcast_arrays() は複数の配列を受け取って、(可能であれば) ブロードキャストのルールにしたがって互いの形状を揃えます。

numpy.broadcast_arrays(*args, **kwargs)
# PYTHON_NUMPY_BROADCASTING_03-1

import numpy as np
from pprint import pprint

x = np.array([0, 1, 2])

y = np.array([[10],
              [20],
              [30]])

z = 1

# x,y,zをブロードキャストして形を揃える
w = np.broadcast_arrays(x, y, z)

pprint(w)
[array([[0, 1, 2],
       [0, 1, 2],
       [0, 1, 2]]),
 array([[10, 10, 10],
       [20, 20, 20],
       [30, 30, 30]]),
 array([[1, 1, 1],
       [1, 1, 1],
       [1, 1, 1]])]

 渡した配列にミスマッチ (互いにブロードキャストできない状態) があると、ValueError が発生します。

# PYTHON_NUMPY_BROADCASTING_03-2

x = np.array([0, 1, 2])

y = np.array([3, 4, 5, 6])

# x,yをブロードキャストして形を揃える
w = np.broadcast_arrays(x, y)

pprint(w)
ValueError: shape mismatch: objects cannot be broadcast to a single shape