numpyの配列のブロードキャスト

ラベル:

前の関連記事:pandasのパネルのお勉強


Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理を購入して1週間経ってようやく最後の12章にたどり着きました。p410のブロードキャストが理解しにくかったのでわかったことを整理しておきます。

この記事の図の一部の角括弧が横幅がありすぎて正しく表示されていません。

私の環境ではChromeでは正しく表示されましたが、FirefoxとiOS Safariでは正しく表示されませんでした。

numpyの配列のブロードキャスト.ipynb · GitHubでみてもChromeは正しく表示されましたが、FirefoxではLatexの数式部分が全く表示されておらず、iOS Safariではrawデータしかみることができませんでした。

In [49]:
import numpy as np

同次元の同じ形状の配列の算術計算

まず同じ形状の多次元配列の算術計算をしてみます。
同じ形状の配列の算術計算は単純に各要素の算術計算になります。
例として形状(2,3,4)の3次元配列Aを作ります。
In [50]:
A=np.arange(24).reshape(2,3,4)
A
Out[50]:
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]]])
この(2,3,4)の3次元配列同士を加算します。
In [51]:
A+A
Out[51]:
array([[[ 0,  2,  4,  6],
        [ 8, 10, 12, 14],
        [16, 18, 20, 22]],

       [[24, 26, 28, 30],
        [32, 34, 36, 38],
        [40, 42, 44, 46]]])
単純に配列の各要素が加算されて各要素が2倍になっています。

同次元の異なる形状の配列の算術計算

今度はAと形状の異なる3次元配列Bを作成して、異なる形状の同次元の配列の算術計算をします。
形状の異なる配列同士の算術計算は各軸の配列の長さが同じか、同じでない場合はどちらかの配列の長さが1である必要があります。
In [52]:
B=np.arange(8).reshape(2,1,4)
B
Out[52]:
array([[[0, 1, 2, 3]],

       [[4, 5, 6, 7]]])
形状(2,3,4)の3次元配列Aとこの形状(2,1,4)の3次元配列Bの各軸の配列の長さを比較して算術計算が可能かを考えます。
第0軸と第2軸はどちらも同じ長さなので問題ありません。
第1軸の長さが異なりますが一方の長さが1なので第1軸はブロードキャストで対応できます。
ということでAとBは算術計算が可能です。
In [53]:
C=A+B
C.shape
Out[53]:
(2, 3, 4)
Cの形状は(2,3,4)になります。
第1軸はBの要素がブロードキャストされてAの要素に適応されるので第1軸はAと同じ配列の長さ3になります。
In [54]:
C
Out[54]:
array([[[ 0,  2,  4,  6],
        [ 4,  6,  8, 10],
        [ 8, 10, 12, 14]],

       [[16, 18, 20, 22],
        [20, 22, 24, 26],
        [24, 26, 28, 30]]])
Bの第2軸の要素は1つしかないので3つにブロードキャスト、つまり複製されてAの第2軸と加算されています。
numpyの多次元配列の「軸を入れ換える」ということについての学習でやったように配列の各要素に添字をつけたものでどういう計算結果になっているのか見てみます。
$$A\\=\begin{bmatrix}\begin{bmatrix} \left[{a}_{(1,1,1)},\;{a}_{(1,1,2)},\;{a}_{(1,1,3)},\;{a}_{(1,1,4)}\right]\\ \left[{a}_{(1,2,1)},\;{a}_{(1,2,2)},\;{a}_{(1,2,3)},\;{a}_{(1,2,4)}\right]\\ \left[{a}_{(1,3,1)},\;{a}_{(1,3,2)},\;{a}_{(1,3,3)},\;{a}_{(1,3,4)}\right] \end{bmatrix}\\\begin{bmatrix} \left[{a}_{(2,1,1)},\;{a}_{(2,1,2)},\;{a}_{(2,1,3)},\;{a}_{(2,1,4)}\right]\\ \left[{a}_{(2,2,1)},\;{a}_{(2,2,2)},\;{a}_{(2,2,3)},\;{a}_{(2,2,4)}\right]\\ \left[{a}_{(2,3,1)},\;{a}_{(2,3,2)},\;{a}_{(2,3,3)},\;{a}_{(2,3,4)}\right] \end{bmatrix}\end{bmatrix}$$$$B\\=\begin{bmatrix}\begin{bmatrix} \left[{b}_{(1,1,1)},\;{b}_{(1,1,2)},\;{b}_{(1,1,3)},\;{b}_{(1,1,4)}\right] \end{bmatrix}\\\begin{bmatrix} \left[{b}_{(2,1,1)},\;{b}_{(2,1,2)},\;{b}_{(2,1,3)},\;{b}_{(2,1,4)}\right]\\ \end{bmatrix}\end{bmatrix}$$$$C=A+B=\\\begin{bmatrix}\begin{bmatrix} \left[{a}_{(1,1,1)}+{b}_{(1,1,1)},\;{a}_{(1,1,2)}+{b}_{(1,1,2)},\;{a}_{(1,1,3)}+{b}_{(1,1,3)},\;{a}_{(1,1,4)}+{b}_{(1,1,4)}\right]\\ \left[{a}_{(1,2,1)}+{b}_{(1,1,1)},\;{a}_{(1,2,2)}+{b}_{(1,1,2)},\;{a}_{(1,2,3)}+{b}_{(1,1,3)},\;{a}_{(1,2,4)}+{b}_{(1,1,4)}\right]\\ \left[{a}_{(1,3,1)}+{b}_{(1,1,1)},\;{a}_{(1,3,2)}+{b}_{(1,1,2)},\;{a}_{(1,3,3)}+{b}_{(1,1,3)},\;{a}_{(1,3,4)}+{b}_{(1,1,4)}\right] \end{bmatrix}\\\begin{bmatrix} \left[{a}_{(2,1,1)}+{b}_{(2,1,1)},\;{a}_{(2,1,2)}+{b}_{(2,1,2)},\;{a}_{(2,1,3)}+{b}_{(2,1,3)},\;{a}_{(2,1,4)}+{b}_{(2,1,4)}\right]\\ \left[{a}_{(2,2,1)}+{b}_{(2,1,1)},\;{a}_{(2,2,2)}+{b}_{(2,1,2)},\;{a}_{(2,2,3)}+{b}_{(2,1,3)},\;{a}_{(2,2,4)}+{b}_{(2,1,4)}\right]\\ \left[{a}_{(2,3,1)}+{b}_{(2,1,1)},\;{a}_{(2,3,2)}+{b}_{(2,1,2)},\;{a}_{(2,3,3)}+{b}_{(2,1,3)},\;{a}_{(2,3,4)}+{b}_{(2,1,4)}\right] \end{bmatrix}\end{bmatrix}$$
Bの第1軸の要素が複製されてAの第1軸の3つの要素と加算されていることがわかります。

異なる次元の配列の算術計算

今度は次元の異なる配列同士の算術計算をします。
先ほど使った形状(2,3,4)の3次元配列Aに形状(1,4)の2次元配列Bを加算します。
In [55]:
B=np.arange(4).reshape(1,4)
B
Out[55]:
array([[0, 1, 2, 3]])
次元が異なる配列同士の算術計算の場合は次元が高い方、つまり高い軸の方から配列の個数を比較します。
Aの第2軸とBの第1軸の配列の長さは同じなので問題ありません。
Aの第1軸とBの第0軸の配列の長さは異なりますが、Bの第0軸の配列の長さは1なので問題ありません。
Aの第0軸に対応する軸はBにありませんが足りない次元に対してはBの最後の次元、つまりBの第0軸が適応されます。
In [56]:
C=A+B
C.shape
Out[56]:
(2, 3, 4)
In [57]:
C
Out[57]:
array([[[ 0,  2,  4,  6],
        [ 4,  6,  8, 10],
        [ 8, 10, 12, 14]],

       [[12, 14, 16, 18],
        [16, 18, 20, 22],
        [20, 22, 24, 26]]])
$$C=A+B=\\\begin{bmatrix}\begin{bmatrix} \left[{a}_{(1,1,1)}+{b}_{(1,1)},\;{a}_{(1,1,2)}+{b}_{(1,2)},\;{a}_{(1,1,3)}+{b}_{(1,3)},\;{a}_{(1,1,4)}+{b}_{(1,4)}\right]\\ \left[{a}_{(1,2,1)}+{b}_{(1,1)},\;{a}_{(1,2,2)}+{b}_{(1,2)},\;{a}_{(1,2,3)}+{b}_{(1,3)},\;{a}_{(1,2,4)}+{b}_{(1,4)}\right]\\ \left[{a}_{(1,3,1)}+{b}_{(1,1)},\;{a}_{(1,3,2)}+{b}_{(1,2)},\;{a}_{(1,3,3)}+{b}_{(1,3)},\;{a}_{(1,3,4)}+{b}_{(1,4)}\right] \end{bmatrix}\\\begin{bmatrix} \left[{a}_{(2,1,1)}+{b}_{(1,1)},\;{a}_{(2,1,2)}+{b}_{(1,2)},\;{a}_{(2,1,3)}+{b}_{(1,3)},\;{a}_{(2,1,4)}+{b}_{(1,4)}\right]\\ \left[{a}_{(2,2,1)}+{b}_{(1,1)},\;{a}_{(2,2,2)}+{b}_{(1,2)},\;{a}_{(2,2,3)}+{b}_{(1,3)},\;{a}_{(2,2,4)}+{b}_{(1,4)}\right]\\ \left[{a}_{(2,3,1)}+{b}_{(1,1)},\;{a}_{(2,3,2)}+{b}_{(1,2)},\;{a}_{(2,3,3)}+{b}_{(1,3)},\;{a}_{(2,3,4)}+{b}_{(1,4)}\right] \end{bmatrix}\end{bmatrix}$$
Bの要素がAの形状に合わせて足りない部分はブロードキャストされて複製されて使われていることがわかります。

np.newaxisで新しい軸を挿入する

In [58]:
A.shape
Out[58]:
(2, 3, 4)
In [59]:
A
Out[59]:
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]]])
In [60]:
B=np.arange(3)
B
Out[60]:
array([0, 1, 2])
In [61]:
B.shape
Out[61]:
(3,)
(2,3,4)のAと(3,)のBはブロードキャストの規則に合わないので算術計算できません。
そこでBにnp.newaxis第1軸を挿入します。
In [62]:
B=B[:,np.newaxis]
B.shape
Out[62]:
(3, 1)
In [63]:
B
Out[63]:
array([[0],
       [1],
       [2]])
これでBの形状が(3,1)になって(2,3,4)のAと算術計算できるようになりました。
In [64]:
C=A+B
C.shape
Out[64]:
(2, 3, 4)
In [65]:
C
Out[65]:
array([[[ 0,  1,  2,  3],
        [ 5,  6,  7,  8],
        [10, 11, 12, 13]],

       [[12, 13, 14, 15],
        [17, 18, 19, 20],
        [22, 23, 24, 25]]])
$$A=\\\begin{bmatrix}\begin{bmatrix} \left[{a}_{(1,1,1)},\;{a}_{(1,1,2)},\;{a}_{(1,1,3)},\;{a}_{(1,1,4)}\right]\\ \left[{a}_{(1,2,1)},\;{a}_{(1,2,2)},\;{a}_{(1,2,3)},\;{a}_{(1,2,4)}\right]\\ \left[{a}_{(1,3,1)},\;{a}_{(1,3,2)},\;{a}_{(1,3,3)},\;{a}_{(1,3,4)}\right] \end{bmatrix}\\\begin{bmatrix} \left[{a}_{(2,1,1)},\;{a}_{(2,1,2)},\;{a}_{(2,1,3)},\;{a}_{(2,1,4)}\right]\\ \left[{a}_{(2,2,1)},\;{a}_{(2,2,2)},\;{a}_{(2,2,3)},\;{a}_{(2,2,4)}\right]\\ \left[{a}_{(2,3,1)},\;{a}_{(2,3,2)},\;{a}_{(2,3,3)},\;{a}_{(2,3,4)}\right] \end{bmatrix}\end{bmatrix}$$$$B=\\\begin{bmatrix}\begin{bmatrix} \left[{b}_{(1,1)}\right] \end{bmatrix}\\\begin{bmatrix} \left[{b}_{(2,1)}\right] \end{bmatrix}\\\begin{bmatrix} \left[{b}_{(3,1)}\right] \end{bmatrix}\end{bmatrix}$$
$$C=A+B=\\\begin{bmatrix}\begin{bmatrix} \left[{a}_{(1,1,1)}+{b}_{(1,1)},\;{a}_{(1,1,2)}+{b}_{(1,1)},\;{a}_{(1,1,3)}+{b}_{(1,1)},\;{a}_{(1,1,4)}+{b}_{(1,1)}\right]\\ \left[{a}_{(1,2,1)}+{b}_{(2,1)},\;{a}_{(1,2,2)}+{b}_{(2,1)},\;{a}_{(1,2,3)}+{b}_{(2,1)},\;{a}_{(1,2,4)}+{b}_{(2,1)}\right]\\ \left[{a}_{(1,3,1)}+{b}_{(3,1)},\;{a}_{(1,3,2)}+{b}_{(3,1)},\;{a}_{(1,3,3)}+{b}_{(3,1)},\;{a}_{(1,3,4)}+{b}_{(3,1)}\right] \end{bmatrix}\\\begin{bmatrix} \left[{a}_{(2,1,1)}+{b}_{(1,1)},\;{a}_{(2,1,2)}+{b}_{(1,1)},\;{a}_{(2,1,3)}+{b}_{(1,1)},\;{a}_{(2,1,4)}+{b}_{(1,1)}\right]\\ \left[{a}_{(2,2,1)}+{b}_{(2,1)},\;{a}_{(2,2,2)}+{b}_{(2,1)},\;{a}_{(2,2,3)}+{b}_{(2,1)},\;{a}_{(2,2,4)}+{b}_{(2,1)}\right]\\ \left[{a}_{(2,3,1)}+{b}_{(3,1)},\;{a}_{(2,3,2)}+{b}_{(3,1)},\;{a}_{(2,3,3)}+{b}_{(3,1)},\;{a}_{(2,3,4)}+{b}_{(3,1)}\right] \end{bmatrix}\end{bmatrix}$$
添字の要素の位置を表すタプルをみるとブロードキャストの規則性がよくわかります。
左上の(1,1,1)の位置から対応する位置を右揃えにして比較して、ない位置はそのまま次の位置に流用していることがわかります。
ブロードキャストの規則とはそれができる規則ということになります。
PR

0 件のコメント:

コメントを投稿