NumPyでshapeのエラーが出たら、まず「各軸は何を表すか」を決める。行ごとの基準値を引くなら基準値を列方向へ広げ、列ごとの基準値を引くなら行方向へ広げればよい。ただし、エラーが出ないことと意図した計算であることは別である。ここでは測定回数×センサー数という対応を保ったまま補正する。
最小例で確かめる
以下は説明用に作った小さなデータである。実測データや実行速度の測定結果ではない。コード全体をexample.pyとして保存すれば、入力ファイルを別途用意せずに実行できる。assertは、この例で成り立つべき形や値を確認するために入れてある。
import numpy as np
x = np.array([[10., 20., 30.], [40., 50., 60.]])
row_base = np.array([1., 2.])
col_base = np.array([10., 20., 30.])
try:
x - row_base # 意図的な失敗例
except ValueError:
print("expected: ValueError")
else:
raise AssertionError("shape mismatch was not detected")
by_row = x - row_base[:, None]
by_col = x - col_base[None, :]
centered = x - x.mean(axis=1, keepdims=True)
print("row:", by_row.tolist())
print("column:", by_col.tolist())
print("centered:", centered.tolist())
a = np.array([1., 2., 3.])
accidental = a[:, None] - a
print("accidental shape:", accidental.shape)
assert by_row.shape == by_col.shape == centered.shape == (2, 3)
np.testing.assert_array_equal(by_row, [[9, 19, 29], [38, 48, 58]])
np.testing.assert_array_equal(by_col, [[0, 0, 0], [30, 30, 30]])
np.testing.assert_allclose(centered.mean(axis=1), 0, atol=1e-12)
assert accidental.shape == (3, 3)
実行結果
expected: ValueError
row: [[9.0, 19.0, 29.0], [38.0, 48.0, 58.0]]
column: [[0.0, 0.0, 0.0], [30.0, 30.0, 30.0]]
centered: [[-10.0, 0.0, 10.0], [-10.0, 0.0, 10.0]]
accidental shape: (3, 3)
右端の軸から照合する
broadcastingでは、shapeを右端から比べ、長さが同じか、どちらかが1であれば演算できる。xのshapeは(2, 3)、row_baseは(2,)なので、まず3と2が衝突する。測定が2回だから長さ2の配列を引けそうに見えても、NumPyは「測定回数」という意味を知らない。長さを照合する規則だけで処理する。
row_base[:, None]は(2, 1)である。各行にある一つの基準値が3列へ広がり、1行目から1、2行目から2を引く。一方、col_base[None, :]は(1, 3)なので、センサーごとの3個の基準値が2行へ広がる。col_baseは(3,)のままでも同じ結果になるが、ここでは補正方向を見える形で明示した。
平均を引くときは軸を残す
axis=1は各行の列方向を集約する指定である。そのまま平均すると長さ2の配列になり、先ほどと同じ問題が起きる。keepdims=Trueを付ければ集約した軸の長さが1のまま残り、(2, 1)から自然に各行へ引き戻せる。axis=0なら列ごとの平均になり、残る形は(1, 3)である。
どのaxisを指定するかは、図や変数名からではなく入力配列の契約から決める。「時刻×センサー」を「センサー×時刻」へ変更したら、集約軸も変更する必要がある。前処理の各段階でshapeを表示し、期待する行数・列数と照合すると、後段で大きな誤りになる前に気付ける。
通ってしまう誤計算にも注意する
a[:, None] – aは(3, 1)と(3,)の演算なので、結果は(3, 3)となる。これは全組合せの差を作る正当な演算だが、同じ位置の値だけを引くつもりなら誤りである。正方形のデータでは、誤った軸の長さが偶然一致して例外が出ないこともある。例のように行数と列数を変えた小配列で確認すると見分けやすい。
補正後のshapeだけでなく、特定の行・列の値も確認するのがよい。例では1行目の差し引き値と2行目の差し引き値を別々に検査し、中心化後の行平均も確かめた。shapeを合わせるためだけにreshapeや転置を追加すると、計算が通る代わりに測定値の対応を壊すことがある。
broadcastingは入力の小配列を必ず物理的に複製する仕組みではないが、演算結果には大きな配列が必要になる。例えば本来1次元で済む処理が全組合せへ広がれば、要素数が急増する。大きなデータへ適用する前に、出力shapeと要素数を紙の上でも見積もっておこう。
動作確認環境と参考資料
Linux・CPython 3.12.14、NumPy 2.3.5、pandas 2.2.3、SciPy 1.17.0、Matplotlib 3.10.8の環境で掲載コードを実行した。使用するライブラリはコード冒頭のimportを参照してほしい。公式資料の最新版と、この実行確認版は区別している。数値の末尾や表の表示幅は環境によって変わることがある。
