【NumPy入門】ブロードキャストの仕組み!計算できる形状とエラーの原因を解説

Python

こんにちは、ぼりたそです!

今回は、NumPyで形状が異なる配列同士を計算できる「ブロードキャスト」について解説します。

配列に1つの数値を足したり、表の列ごとに異なる値を掛けたりするときに使われる仕組みです。一方で、形状の組み合わせによってはエラーになったり、意図とは違う位置の値で計算されたりします。

Point
  • ブロードキャストとは何か
  • 2次元配列に列ごと・行ごとの値を足す方法
  • 計算できる形状を右端から確認するルール
  • (3,)と(3, 1)の違い
  • エラーになる場合と、エラーにならない間違い

それでは順番に確認していきましょう!

スポンサーリンク

ブロードキャストとは?

ブロードキャストは、一定のルールに従って、形状が異なる配列の要素を対応させて計算する仕組みです。

まずはNumPyを読み込みます。以降のコードは、この読み込みを実行した状態を前提とします。

import numpy as np

配列と1つの数値の計算もブロードキャスト

次の計算では、1つの数値10を配列の各要素に足しています。

a = np.array([10, 20, 30])
print(a + 10)

# 実行結果:
# [20 30 40]

計算結果は、aに[10, 10, 10]を足した場合と同じです。このように、必要な位置で同じ値を繰り返し使うイメージで考えられます。

ただし、**説明図のように、入力データを毎回すべて複製しているわけではありません。**ブロードキャストでは、同じ入力値を必要な位置で参照できます。通常のa + 10の結果を保存する配列は別に必要です。

列ごとに異なる値を足す

2行3列の配列に、要素数3の配列を足してみましょう。

data = np.array([
    [10, 20, 30],
    [40, 50, 60]
])
offset = np.array([1, 2, 3])

print(data + offset)

# 実行結果:
# [[11 22 33]
#  [41 52 63]]

1列目には1、2列目には2、3列目には3が加わります。offsetの3つの値が、各行で繰り返し使われています。

data.shapeは(2, 3)、offset.shapeは(3,)です。形状は異なりますが、右端のサイズ3が対応するため計算できます。

列ごとに補正値を足す、列ごとの基準値を引く、といった処理に使える書き方です。

行ごとに異なる値を足す

今度は、1行目に100、2行目に200を足してみましょう。

行ごとの値を使う場合は、2行1列の形状にします。

data = np.array([[10, 20, 30], [40, 50, 60]])
offset = np.array([100, 200]).reshape(2, 1)

print(offset)
print(data + offset)

# 実行結果:
# [[100]
#  [200]]
# [[110 120 130]
#  [240 250 260]]

offsetの形状は(2, 1)です。各行の1つの値が、その行の3列すべてに使われます。

reshape(2, 1)は、2つの値を2行1列にする操作です。行ごとの値を使いたい場合は、長さ1の列の軸を残すことがポイントです。

ブロードキャストできる形状のルール

ブロードキャストでは、2つの配列の形状を右端から比較します。対応する各軸が、次のどちらかを満たす必要があります。

  1. サイズが同じ
  2. どちらか一方のサイズが1

次元数が異なる場合は、少ない側の左にサイズ1の軸があるものとして考えます。サイズ1の軸は、相手のサイズに対応します。

例えば、(2, 3)と(3,)は、判定上、(2, 3)と(1, 3)として比較できます。

比較する位置左の配列右の配列判定
右端33同じなので計算できる
その左21一方が1なので計算できる

結果の形状は(2, 3)です。これは判定のための考え方であり、元のoffset.shapeが自動的に(1, 3)へ書き換わるわけではありません。

よく使う形状の組み合わせ

配列1の形状配列2の形状結果の形状2次元の例での使い方
(2, 3)スカラー()(2, 3)全要素に同じ値を使う
(2, 3)(3,)(2, 3)列ごとに異なる値を使う
(2, 3)(1, 3)(2, 3)列ごとに異なる値を使う
(2, 3)(2, 1)(2, 3)行ごとに異なる値を使う
(2, 3)(2,)計算不可右端の3と2が合わない

この記事では、要素数が0ではない通常の配列を使っています。

スポンサーリンク

計算できない場合:要素数だけでは判断できない

2行3列の配列に、要素数2の1次元配列を足すとエラーになります。

data = np.array([[10, 20, 30], [40, 50, 60]])
offset = np.array([100, 200])

print(data.shape)
print(offset.shape)

# data + offset はValueErrorになるため、ここでは実行しません。

# 実行結果:
# (2, 3)
# (2,)

行数が2だから要素数2でよさそうに見えますが、比較は右端から行います。(2, 3)と(2,)の右端は3と2で、同じでも、どちらかが1でもありません。

行ごとに足したいなら、先ほどのようにoffset.reshape(2, 1)へ変えます。配列全体の要素数が同じでも、各軸の条件が合わなければ計算できません。

両方の配列が広がる組み合わせ

片方だけでなく、両方の配列が異なる軸で対応範囲を広げる場合もあります。

column = np.array([10, 20]).reshape(2, 1)
row = np.array([1, 2, 3]).reshape(1, 3)

print(column + row)
print((column + row).shape)

# 実行結果:
# [[11 12 13]
#  [21 22 23]]
# (2, 3)

columnの10と20それぞれに対して、rowの1・2・3を足しています。

形状は(2, 1)と(1, 3)で、結果は(2, 3)です。両方の入力より要素数の多い結果ができることもわかります。

集計結果と組み合わせる:行ごとの平均を引く

行ごとの平均を引きたい場合も、平均値を1列の形状にします。集計時にkeepdims=Trueを指定すると、その形状を保てます。

data = np.array([[10, 20, 30], [40, 50, 60]])
row_mean = np.mean(data, axis=1, keepdims=True)

print(row_mean)
print(row_mean.shape)
print(data - row_mean)

# 実行結果:
# [[20.]
#  [50.]]
# (2, 1)
# [[-10.   0.  10.]
#  [-10.   0.  10.]]

1行目からは20、2行目からは50が引かれます。

keepdims=Trueを省略した行平均の形状は(2,)で、今回の(2, 3)とは計算できません。集計後にどの配列と計算するかまで考えると、形状を決めやすくなります。

ブロードキャストの注意点

エラーにならなくても、意図した方向とは限らない

2行2列の配列に、要素数2の1次元配列を足す場合はエラーになりません。しかし、値は列ごとに対応します。

data = np.array([[10, 20], [30, 40]])
offset = np.array([100, 200])

print(data + offset)
print(data + offset.reshape(2, 1))

# 実行結果:
# [[110 220]
#  [130 240]]
# [[110 120]
#  [230 240]]

1つ目は1列目に100、2列目に200を足しています。2つ目は1行目に100、2行目に200を足しています。

**計算できたことと、意図した計算になったことは別です。**行と列が同じサイズだと間違いを見落としやすいため、小さな配列で対応関係を確認しましょう。

大きな結果や中間配列にはメモリが必要

入力の値を繰り返し使えるとしても、通常の計算結果や途中の配列には保存領域が必要です。

例えば、(10000, 1)と(1, 10000)の足し算は、結果が1億要素になります。各要素が8バイトのfloat64なら、結果だけで約800 MBが必要です。

大きな配列を計算するときは、入力だけでなく、結果の形状と要素数も確認してください。

NumPyを基礎から学びたい方へ

ブロードキャストは、配列を使った計算・集計・形状変更をつなぐ仕組みです。これらを組み合わせると、列ごとの標準化など、データ分析に役立つ処理を書けるようになります。

体系的に学びたい方向けに、Kindle書籍『機械学習・データ分析のためのNumPy入門 ― 配列操作と数値計算の基本をPythonで学ぶ ―』を執筆しました。

本書では、配列の基本操作から集計やブロードキャストまで、コード例と演習問題を通じて学べます。

「説明を読むだけでなく、形状を自分で考える練習をしたい」という方は、ぜひチェックしてみてください!

まとめ

今回は、NumPyのブロードキャストの仕組みと、形状の確認方法について解説しました。

確認することポイント
比較する順番形状の右端から確認する
計算できる条件各軸でサイズが同じ、または一方が1
次元数が違う場合少ない側の左にサイズ1の軸があると考える
列ごとの計算(列数,)または(1, 列数)を使う
行ごとの計算(行数, 1)を使う
集計結果の利用keepdims=Trueで必要な軸を残せる

ブロードキャストでは、形状を見ながら、どの値がどの位置で使われるかを考えることが大切です。

まずはreshape()で形状を変えて、同じ値でも計算結果がどう変わるかを確かめてみてください!

スポンサーリンク
タイトルとURLをコピーしました