Day 038 — numpy ブロードキャスト・ユニバーサル関数

2026-05-17 白 / Phase 1 理論 numpy入門

📚 背景知識(読んでから問題へ)

ブロードキャストとは?

「形の違う配列同士を自動的に合わせて計算する仕組み」です。
スーパーのレシート(複数の金額)に消費税率(1つの数)を掛けるとき、税率を同じ数だけ用意する必要はありませんよね。numpy も同じことを自動でやります。

a   shape (2, 3)
1 2 3 4 5 6
+
b   shape (3,)
10 20 30 10 20 30 ↓ 引き伸ばし
result   shape (2, 3)
11 22 33 14 25 36

ブロードキャストのルール

末尾の次元から順番に比較して、サイズが同じか、どちらかが 1 であれば OK。

✓ OK
(3, 4) + (4,)
末尾の 4 が一致
✓ OK
(3, 4) + (1, 4)
1 は引き伸ばされる
✗ NG
(3, 4) + (3,)
末尾が 4 vs 3 で不一致

ユニバーサル関数(ufunc)一覧

関数意味使用例結果
np.sqrt()平方根np.sqrt(4)2.0
np.abs()絶対値np.abs(-5)5
np.exp()指数関数 e^xnp.exp(1)2.718…
np.log()自然対数np.log(1)0.0
np.sum()合計np.sum([1,2,3])6
np.mean()平均np.mean([1,2,3])2.0
np.std()標準偏差np.std([1,2,3])0.816…
np.max() / np.min()最大・最小np.max([3,1,4])4

📝 問題

次のコードを 実行前に 予測して、理由を言葉で説明してください。

import numpy as np

a = np.array([[1, 2, 3],
              [4, 5, 6]])  # shape: (2, 3)

b = np.array([10, 20, 30])  # shape: (3,)

result = a + b
  1. result の shape は何になる?
  2. result の中身(値)は何になる?
  3. なぜそうなるのか、言葉で説明する

以下のデータを 標準化(Zスコア化) してください。
標準化とは「平均を引いて、標準偏差で割る」処理です。Kaggleの特徴量エンジニアリングで最頻出の前処理の1つ。

import numpy as np

data = np.array([150, 160, 170, 180, 190])  # 身長データ(cm)

# TODO: dataを標準化して z_scores に格納する
# ヒント: np.mean(), np.std() を使う

z_scores = ???
print(z_scores)
💡
期待される出力の特徴: 平均が(ほぼ)0 になり、各値が平均から「標準偏差の何個分」ずれているかを表す。

🔍 ヒント(段階的開示)

ヒント1 — 方向性
問1: b の shape (3,)a の shape (2, 3)各行に対して適用されます。「各行に同じ操作をする」イメージで考えてみましょう。

問2: 数式を思い出してください。標準化 = (値 - 平均) ÷ 標準偏差。numpyでは -/ を使うだけです。
ヒント2 — アプローチ
問1: b = [10, 20, 30]a の各行 [1,2,3][4,5,6] にそれぞれ足されます。

問2:
  • 平均: mean = np.mean(data)
  • 標準偏差: std = np.std(data)
  • 標準化: z_scores = (data - mean) / std
ヒント3 — コード骨格(ほぼ答え)
問1の答え:
# shape は (2, 3) になる
# [[1+10, 2+20, 3+30],   →  [[11, 22, 33],
#  [4+10, 5+20, 6+30]]       [14, 25, 36]]
問2の骨格:
mean = np.mean(data)
std  = np.std(data)
z_scores = (data - mean) / std

模範解答

問1 — ブロードキャスト

import numpy as np

a = np.array([[1, 2, 3],
              [4, 5, 6]])
b = np.array([10, 20, 30])

result = a + b
print("shape:", result.shape)
print(result)
▶ 出力を見る
shape: (2, 3)
[[11 22 33]
 [14 25 36]]
📐
理由: b の shape (3,)a の shape (2, 3) の末尾次元と一致するため、ブロードキャストが適用されます。b が2行分にコピーされ(引き伸ばされ)、各行との足し算が行われます。

問2 — 標準化(Zスコア)

import numpy as np

data = np.array([150, 160, 170, 180, 190])

mean = np.mean(data)    # 170.0
std  = np.std(data)     # 14.142...

z_scores = (data - mean) / std

print("平均:", mean)
print("標準偏差:", std)
print("Zスコア:", z_scores)
print("確認 - Zスコアの平均(≈0):", np.mean(z_scores))
▶ 出力を見る
平均: 170.0
標準偏差: 14.142135623730951
Zスコア: [-1.41421356 -0.70710678  0.          0.70710678  1.41421356]
確認 - Zスコアの平均(≈0): 0.0

Zスコア ビジュアライゼーション

0 +1.5 -1.5 150 cm 160 cm 170 cm 180 cm 190 cm −1.41 −0.71 0 +0.71 +1.41 身長データのZスコア(平均170cm)

🪜 Step-by-Step 解説

1
ブロードキャストの仕組みを理解する
b = [10, 20, 30] が内部的には [[10, 20, 30], [10, 20, 30]] に引き伸ばされてから a と足し算されます。
「引き伸ばす」といっても実際にはメモリをコピーしないため、非常に効率的。大量データを扱うKaggleではループより何十倍も速くなります。
2
標準化の数学的意味を理解する
data = [150, 160, 170, 180, 190] の場合:
平均 = 170.0(全員の平均身長)、標準偏差 ≈ 14.14(平均からの典型的なズレ)
(data - mean) で「平均からどれだけズレているか」を計算し、/ std で「標準偏差の何個分か」に換算します。
3
標準化の中にもブロードキャストが使われている!
# data は shape (5,) の配列
# mean は スカラー(単一の数値)
# → スカラーが自動的に shape (5,) に引き伸ばされる

diff = data - mean   # [-20, -10, 0, 10, 20]  ← ブロードキャスト!
z    = diff / std    # [-1.41, -0.71, 0, 0.71, 1.41]  ← ブロードキャスト!

📐 数学・統計の補足(文系向け)

Zスコアとは

🎯
「自分は平均から標準偏差の何個分 離れているか」 を表す数値です。
Zスコア意味偏差値換算
0ちょうど平均50
+1平均より「典型的なズレ1個分」上60
-1平均より「典型的なズレ1個分」下40
+2平均より「典型的なズレ2個分」上(上位約2.3%)70

偏差値 = Zスコア × 10 + 50

なぜKaggleで標準化が必要か

標準化なし 体重 50〜100 kg 年収 300〜1000万 → 年収の影響が圧倒的に大きくなってしまう 標準化後: 両方とも平均0・標準偏差1 のスケールに統一 ✓

機械学習モデル(特に距離を使うもの: KNN, SVM, 線形回帰など)は値の「スケール」に敏感です。標準化すれば両方が「平均0、標準偏差1」のスケールに統一されます。

🏆 Kaggleでの実践的な使い方

特徴量の標準化(前処理の基本)

import numpy as np
import pandas as pd

# Titanicのage列を標準化する例
df = pd.DataFrame({'age': [22, 38, 26, 35, float('nan')]})

# 欠損値を除いて計算
mean_age = np.mean(df['age'].dropna())
std_age  = np.std(df['age'].dropna())

# 欠損値はそのままにして標準化
df['age_scaled'] = (df['age'] - mean_age) / std_age
print(df)

ブロードキャストを使った複数列の一括処理

# 複数の特徴量を一括で標準化(Kaggleでよく使うパターン)
X = np.array([[150, 60],   # [身長, 体重]
              [160, 55],
              [170, 70],
              [180, 80]])

means = np.mean(X, axis=0)  # 各列の平均 → shape (2,)
stds  = np.std(X, axis=0)   # 各列の標準偏差 → shape (2,)

X_scaled = (X - means) / stds  # ブロードキャストで一括標準化!
print(X_scaled)
🔧
実際のコンペでは sklearn.preprocessing.StandardScaler を使うことが多いですが、その内部でもこれと同じ計算をしています。numpyで理解しておくとカスタム処理が書けるようになります。

⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
np.std()ddof を気にしなくて良い? サンプル標準偏差(ddof=1)と母標準偏差(ddof=0)がある Kaggleの前処理では ddof=0(デフォルト)でOK。統計的推定が目的なら ddof=1
ブロードキャストでエラーが出た shapeが合わない エラーメッセージのshapeを確認し、末尾次元から一致するか確認する
標準化すると元の値に戻せない? 変換は可逆と知らない original = z_scores * std + mean で元に戻せる(逆変換)
np.mean().mean() は同じ? どちらも使われるので混乱 arr.mean()np.mean(arr) は基本的に同じ。pandas DataFrameでは .mean() が便利

🚀 次のステップ

  • 発展: axis パラメータを使った行方向・列方向の集計(np.sum(X, axis=0) vs axis=1
  • 次回予告: pandas入門(DataFrame・Series)— Kaggleでのデータ操作の主役

🎯 自己評価

自分の回答

気づき・メモ