Python | ファイル・OS 操作:NumPy のブロードキャスト

Python Python
スポンサーリンク

概要(ブロードキャストは「形が違っても自然に計算させる」仕組み)

NumPy のブロードキャストは、形状(shape)が違う配列どうしの演算を可能にする仕組みです。ポイントは「末尾の次元から見て、同じ長さか長さ1なら合わせられる」「長さ1の次元は自動的に“伸びる”」「実データを複製せずに仮想拡張で高速」。初心者は「shape を確認→合わせたい軸に長さ1の次元を足す→そのまま演算する」型を覚えると、for ループを書かずにスッと解決できます。


基本の使い方(ここが重要)

スカラーと配列の演算(全要素に同じ値を適用)

import numpy as np

x = np.array([1, 2, 3])
print(x + 10)   # → [11 12 13]
print(x * 2)    # → [2 4 6]
Python

スカラーは自動で全要素に“伸びる”ので、配列全体へ一括で適用できます。

行列と行ベクトル(列方向に伸びる)

import numpy as np

M = np.array([[1, 2, 3],
              [4, 5, 6]])          # shape=(2,3)
v = np.array([10, 20, 30])          # shape=(3,)
print(M + v)
# 各行の3列へ [10,20,30] が足される
Python

末尾の次元が一致しているので、列方向へ自動拡張されて加算されます。

行列と列ベクトル(行方向に伸びる)

import numpy as np

M = np.array([[1, 2, 3],
              [4, 5, 6]])          # shape=(2,3)
u = np.array([100, 200]).reshape(2, 1)  # shape=(2,1)
print(M + u)
# 1行目に100、2行目に200を各列へ足す
Python

長さ1の次元は“伸びる”ので、行方向に拡張されて計算されます。


ブロードキャストのルール(形合わせの考え方を深掘り)

ルールの要約(末尾から見て整合性チェック)

  • 次元数をそろえる: 次元の少ない配列は、先頭側に長さ1の次元があるとみなされます(仮想的に追加される)。
  • 各次元の整合性: 対応する次元で「サイズが同じ」または「どちらかが1」ならOK。両方とも1以外で違うならNG。
  • 拡張の仕方: サイズ1の次元は、相手のサイズに合わせて仮想的に繰り返される(メモリコピーはしない)。

形を意図的に合わせるテクニック(np.newaxis / reshape)

import numpy as np

x = np.array([1, 2, 3])        # shape=(3,)
col = x[:, np.newaxis]         # shape=(3,1) 列ベクトル化
row = x[np.newaxis, :]         # shape=(1,3) 行ベクトル化

M = np.arange(9).reshape(3, 3) # shape=(3,3)
print(M + col)                 # 各行に [1,2,3] を縦方向で足す
print(M + row)                 # 各列に [1,2,3] を横方向で足す
Python

np.newaxis は「次元を1つ増やす」ための最短手。reshape(…, 1) でも同じ効果が得られます。


典型パターン(前処理・正規化・距離計算)

列方向・行方向の標準化(平均0・分散1)

import numpy as np

X = np.array([[1.0, 2.0, 3.0],
              [4.0, 5.0, 6.0],
              [7.0, 8.0, 9.0]])   # shape=(3,3)

# 列方向(各列の平均と標準偏差)
mu = X.mean(axis=0)               # shape=(3,)
sigma = X.std(axis=0)             # shape=(3,)
Z = (X - mu) / sigma              # 列ごとに標準化

# 行方向(各行の平均と標準偏差)
mu_row = X.mean(axis=1)[:, None]  # shape=(3,1)
sigma_row = X.std(axis=1)[:, None]
Z_row = (X - mu_row) / sigma_row
Python

平均・標準偏差の shape を「列なら (n,)」「行なら (n,1)」に合わせるだけで、一括標準化できます。

min-max 正規化(0〜1へ)

import numpy as np

X = np.array([[10.0, 20.0, 30.0],
              [15.0, 25.0, 35.0]])
mn = X.min(axis=0)  # 各列の最小
mx = X.max(axis=0)  # 各列の最大
Y = (X - mn) / (mx - mn)
Python

列ごとの最小・最大を列ベクトル形にすると、配列全体を一発でスケーリングできます。

すべての組み合わせ距離(行どうしの差の二乗和)

import numpy as np

A = np.array([[1.0, 2.0], [3.0, 4.0]])  # shape=(2,2)
B = np.array([[5.0, 6.0], [7.0, 8.0]])  # shape=(2,2)

# A の各行と B の各行のユークリッド距離を 2x2 で出す
diff = A[:, None, :] - B[None, :, :]  # shape=(2,2,2)
dist2 = (diff ** 2).sum(axis=2)       # 各行の二乗和 → shape=(2,2)
print(dist2)
Python

[:, None, :] や [None, :, :] で「行数を交差させる軸」を増やすのがコツ。二次元の距離行列がループ無しに作れます。


よくある落とし穴(形不一致・意図しない拡張・keepdims)

両方が1以外でサイズが違うとエラー

import numpy as np

A = np.ones((2, 3))
B = np.ones((4, 3))
# A + B  # 2 と 4(行)が合わない → エラー
Python

「どちらかが1」か「同じ長さ」でない軸は合わせられません。足りない側に長さ1の次元を足す発想を持つと回避できます。

意図しない方向へ拡張される(軸の取り違え)

import numpy as np

X = np.arange(6).reshape(2, 3)  # (2行,3列)
v = np.array([10, 20])          # (2,)
# X + v は「列方向」に合わない(3 と 2 が不一致)
# 行方向に足したいなら v を (2,1) にする
print(X + v[:, None])           # 行方向の拡張で意図通り
Python

「どの軸で合わせたいか」を shape で表現する癖をつけると、取り違えが消えます。

集計後の形が変わって困る(keepdims を使う)

import numpy as np

X = np.arange(6).reshape(2, 3)
col_sum = X.sum(axis=0)             # shape=(3,)
# そのままだと (2,3) と (3,) のブロードキャストになる(列方向)
# 行方向に合わせたい等、軸を明示したいときは keepdims
col_sum_keep = X.sum(axis=0, keepdims=True)  # shape=(1,3)
print(X - col_sum_keep)                       # 明快に列方向で拡張
Python

keepdims=True は「集計後もその軸を長さ1で残す」。拡張方向を明示できて安全です。


例題で身につける(定番から一歩先まで)

例題1:列ごとの標準化(列方向)

import numpy as np

X = np.array([[1.0, 2.0, 3.0],
              [4.0, 5.0, 6.0],
              [7.0, 8.0, 9.0]])

mu = X.mean(axis=0)
sd = X.std(axis=0)
Z = (X - mu) / sd
print(Z.round(2))
Python

例題2:行ごとのセンタリング(行方向)

import numpy as np

X = np.array([[10.0, 11.0, 12.0],
              [20.0, 21.0, 22.0]])
row_mean = X.mean(axis=1)[:, None]  # (2,1)
Xc = X - row_mean
print(Xc)
Python

例題3:列オフセットの一括加算

import numpy as np

M = np.array([[1, 2, 3],
              [4, 5, 6]])
offs = np.array([10, 20, 30])  # (3,)
print(M + offs)
Python

例題4:全組み合わせの差分二乗和(距離行列)

import numpy as np

A = np.array([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]])  # 3点
B = np.array([[1.0, 1.0], [2.0, 0.0]])              # 2点

d = A[:, None, :] - B[None, :, :]   # (3,2,2)
dist2 = (d ** 2).sum(axis=2)        # (3,2)
print(dist2)
Python

まとめ

ブロードキャストの核は「末尾の次元から見て、同じ長さか長さ1なら合わせられる」「長さ1の次元は自動拡張」「実データは複製しない」。shape を常に確認し、合わせたい方向に長さ1の次元を追加(np.newaxis や reshape)する型を身につけると、ループなしで前処理・正規化・距離計算まで一気に書けます。keepdims で集計後の形を整え、軸の取り違えをなくす。これを体に入れれば、初心者でも短く、速く、壊れない数値計算が自然に書けるようになります。

タイトルとURLをコピーしました