3.2.4 配列演算

- ベクトル化演算の概念と利点を理解する
- 要素ごとの演算と汎用関数(ufunc)を身につける
- ブロードキャスト機構(Broadcasting)のルールを理解する
- 集約関数を使って統計計算を行えるようにする
ベクトル化演算:ループからの卒業
Section titled “ベクトル化演算:ループからの卒業”ベクトル化演算は NumPy の核心的な考え方です。つまり、配列全体に対して操作し、ループを書かずに処理します。
純 Python vs NumPy
Section titled “純 Python vs NumPy”import numpy as np
# 純 Python:1つずつ計算prices = [100, 200, 300, 400, 500]discounted = []for p in prices: discounted.append(p * 0.8)print(discounted) # [80.0, 160.0, 240.0, 320.0, 400.0]
# NumPy:1行で完了prices = np.array([100, 200, 300, 400, 500])discounted = prices * 0.8print(discounted) # [ 80. 160. 240. 320. 400.]要素ごとの演算
Section titled “要素ごとの演算”NumPy 配列の算術演算は要素ごとに行われます。
a = np.array([1, 2, 3, 4])b = np.array([10, 20, 30, 40])
print(a + b) # [11 22 33 44] 対応する位置どうしを足すprint(a - b) # [ -9 -18 -27 -36]print(a * b) # [ 10 40 90 160] 対応する位置どうしを掛ける(行列積ではありません!)print(a / b) # [0.1 0.1 0.1 0.1]print(a ** 2) # [ 1 4 9 16] 2乗print(b % 3) # [1 2 0 1] 余りprint(b // 3) # [ 3 6 10 13] 切り捨て除算スカラーとの演算
Section titled “スカラーとの演算”配列と 1 つの数値(スカラー)を計算するときは、スカラーが自動的に各要素へ適用されます。
arr = np.array([10, 20, 30, 40])
print(arr + 5) # [15 25 35 45]print(arr * 2) # [20 40 60 80]print(arr / 10) # [1. 2. 3. 4.]print(1 / arr) # [0.1 0.05 0.033 0.025]arr = np.array([15, 23, 8, 42, 31])
print(arr > 20) # [False True False True True]print(arr == 23) # [False True False False False]print(arr != 8) # [ True True False True True]汎用関数(ufunc)
Section titled “汎用関数(ufunc)”NumPy にはたくさんの汎用関数があり、配列の各要素に数学的な演算を適用できます。
よく使う数学関数
Section titled “よく使う数学関数”arr = np.array([1, 4, 9, 16, 25])
# 平方根print(np.sqrt(arr)) # [1. 2. 3. 4. 5.]
# 絶対値neg = np.array([-3, -1, 0, 2, 5])print(np.abs(neg)) # [3 1 0 2 5]
# べき乗print(np.power(arr, 0.5)) # sqrt と同じ
# 指数と対数print(np.exp([0, 1, 2])) # [1. 2.718 7.389] e のべき乗print(np.log([1, np.e, 10])) # [0. 1. 2.303] 自然対数print(np.log10([1, 10, 100])) # [0. 1. 2.] 10 を底とするprint(np.log2([1, 2, 8, 64])) # [0. 1. 3. 6.] 2 を底とする# 0 から 2π までの角度を作成angles = np.linspace(0, 2 * np.pi, 5) # [0, π/2, π, 3π/2, 2π]
print(np.sin(angles)) # [ 0. 1. 0. -1. 0.] ← 正弦print(np.cos(angles)) # [ 1. 0. -1. 0. 1.] ← 余弦切り上げ・切り捨て関数
Section titled “切り上げ・切り捨て関数”arr = np.array([1.2, 2.5, 3.7, -1.3, -2.8])
print(np.floor(arr)) # [ 1. 2. 3. -2. -3.] 切り捨てprint(np.ceil(arr)) # [ 2. 3. 4. -1. -2.] 切り上げprint(np.round(arr)) # [ 1. 2. 4. -1. -3.] 四捨五入print(np.trunc(arr)) # [ 1. 2. 3. -1. -2.] 小数点以下を切り捨て2 つの配列間の演算
Section titled “2 つの配列間の演算”a = np.array([3, 5, 7, 9])b = np.array([1, 4, 2, 8])
print(np.maximum(a, b)) # [3 5 7 9] 対応する位置で大きい方を取るprint(np.minimum(a, b)) # [1 4 2 8] 対応する位置で小さい方を取るprint(np.where(a > b, a, b)) # maximum と同じだが、より柔軟ブロードキャスト機構(Broadcasting)
Section titled “ブロードキャスト機構(Broadcasting)”形状の異なる 2 つの配列で演算するとき、NumPy は自動的に小さい配列を「ブロードキャスト」して、形状をそろえます。
いちばん簡単な例
Section titled “いちばん簡単な例”arr = np.array([1, 2, 3])
# スカラー + 配列 → スカラーが [10, 10, 10] にブロードキャストされるprint(arr + 10) # [11 12 13]これはブロードキャストの例です。NumPy は 10 を [10, 10, 10] に広げてから、要素ごとに足しています。
2 次元配列 + 1 次元配列
Section titled “2 次元配列 + 1 次元配列”matrix = np.array([ [1, 2, 3], [4, 5, 6], [7, 8, 9]])
row = np.array([10, 20, 30])
# row が各行にブロードキャストされるresult = matrix + rowprint(result)# [[11 22 33]# [14 25 36]# [17 28 39]]ブロードキャストの流れは次のように理解できます。
matrix: row (ブロードキャスト前): row (ブロードキャスト後):[[1, 2, 3], [10, 20, 30] → [[10, 20, 30], [4, 5, 6], [10, 20, 30], [7, 8, 9]] [10, 20, 30]]列ベクトル + 行ベクトル
Section titled “列ベクトル + 行ベクトル”col = np.array([[1], [2], [3]]) # shape: (3, 1) 列ベクトルrow = np.array([10, 20, 30]) # shape: (3,) 行ベクトル
# 両方がブロードキャストされるresult = col + rowprint(result)# [[11 21 31]# [12 22 32]# [13 23 33]]ブロードキャストのルール
Section titled “ブロードキャストのルール”flowchart TD A["2つの配列の形状は違う?"] -->|はい| B["末尾から次元をそろえて比較する"] B --> C{"各次元は次を満たす?<br/>1. 等しい<br/>2. どちらかが 1"} C -->|すべて満たす| D["ブロードキャスト成功 ✅<br/>次元が 1 のものがコピーされて広がる"] C -->|満たさないものがある| E["ブロードキャスト失敗 ❌<br/>ValueError を出す"] A -->|いいえ| F["形状が同じなので、そのまま演算する"]覚え方は簡単です。次元は後ろから比べて、同じか、どちらかが 1 なら OK です。
# ✅ ブロードキャストできる# (3, 4) + (4,) → (3, 4) 最後の次元はどちらも 4# (3, 4) + (1, 4) → (3, 4) 1 行目の 3 と 1 → 3 に広がる# (3, 1) + (1, 4) → (3, 4) 両方の次元がブロードキャストされる
# ❌ ブロードキャストできない# (3, 4) + (3,) → エラー!最後の次元は 4 ≠ 3 で、どちらも 1 ではないブロードキャストの実用例
Section titled “ブロードキャストの実用例”# データの標準化:各列からその列の平均を引くdata = np.array([ [85, 170, 60], [92, 180, 75], [78, 165, 55], [90, 175, 70]]) # 4 人の学生:成績、身長、体重
# 各列の平均を計算col_mean = data.mean(axis=0) # [86.25 172.5 65. ] shape: (3,)
# ブロードキャスト:(4, 3) - (3,) → (4, 3)centered = data - col_meanprint(centered)# [[-1.25 -2.5 -5. ]# [ 5.75 7.5 10. ]# [-8.25 -7.5 -10. ]# [ 3.75 2.5 5. ]]集約関数は、複数のデータを 1 つ、または少数の値に「まとめる」関数です。
よく使う集約関数
Section titled “よく使う集約関数”arr = np.array([4, 7, 2, 9, 1, 5, 8, 3, 6])
print(np.sum(arr)) # 45 合計print(np.mean(arr)) # 5.0 平均print(np.median(arr)) # 5.0 中央値print(np.std(arr)) # 2.58 標準偏差print(np.var(arr)) # 6.67 分散print(np.min(arr)) # 1 最小値print(np.max(arr)) # 9 最大値print(np.argmin(arr)) # 4 最小値のインデックスprint(np.argmax(arr)) # 3 最大値のインデックスprint(np.cumsum(arr)) # [ 4 11 13 22 23 28 36 39 45] 累積和print(np.cumprod(arr[:5])) # [ 4 28 56 504 504] 累積積axis を使った集約
Section titled “axis を使った集約”多次元配列では、axis パラメータでどの方向に沿って集約するかを指定します。
matrix = np.array([ [1, 2, 3], [4, 5, 6], [7, 8, 9]])
# axis を指定しない:すべての要素をまとめるprint(np.sum(matrix)) # 45
# axis=0:行方向に沿って(列ごとに)集約する —— 上下にまとめるprint(np.sum(matrix, axis=0)) # [12 15 18]
# axis=1:列方向に沿って(行ごとに)集約する —— 左右にまとめるprint(np.sum(matrix, axis=1)) # [ 6 15 24]axis の考え方は、axis=0 は行を消す、axis=1 は列を消す です。
| axis | 考え方 | 結果 |
|---|---|---|
axis=0 | 上下にまとめ、列を残す | [12, 15, 18] |
axis=1 | 左右にまとめ、行を残す | [6, 15, 24] |
迷ったら、まず matrix.shape を表示して、どの次元を消したいかを考えます。
実践:成績分析
Section titled “実践:成績分析”# 5 人の学生の 3 科目の成績scores = np.array([ [85, 92, 78], # 学生1:国語、数学、英語 [90, 88, 95], # 学生2 [72, 65, 80], # 学生3 [95, 98, 92], # 学生4 [60, 55, 70] # 学生5])
subjects = ["国語", "数学", "英語"]
# 各学生の合計点total = np.sum(scores, axis=1)print("各学生の合計点:", total) # [255 273 217 285 185]
# 各学生の平均点avg_per_student = np.mean(scores, axis=1)print("各学生の平均点:", avg_per_student)
# 各科目の平均点avg_per_subject = np.mean(scores, axis=0)for sub, avg in zip(subjects, avg_per_subject): print(f" {sub}平均点: {avg:.1f}")
# クラス全体で最高点は誰のどの科目かmax_idx = np.unravel_index(np.argmax(scores), scores.shape)print(f"最高点: {scores[max_idx]} (学生{max_idx[0]+1}の{subjects[max_idx[1]]})")
# 合計点がいちばん高い学生best_student = np.argmax(total)print(f"合計点が最高: 学生{best_student + 1}, 合計点 {total[best_student]}")np.where:条件による選択
Section titled “np.where:条件による選択”np.where は、NumPy 版の三項演算子です。
arr = np.array([85, 42, 91, 67, 55, 78])
# 合格は "PASS"、不合格は "FAIL" とするresult = np.where(arr >= 60, "PASS", "FAIL")print(result) # ['PASS' 'FAIL' 'PASS' 'PASS' 'FAIL' 'PASS']
# 不合格の点数を 60 にするadjusted = np.where(arr >= 60, arr, 60)print(adjusted) # [85 60 91 67 60 78]このページを終えたら、この evidence card を残します。
- 配列状態
- 操作前の shape、dtype、axis、サンプル値
- 操作
- indexing、slicing、broadcasting、reshape、線形代数、またはランダム/stat関数
- 出力
- 結果の配列形状、値、または統計量
- 失敗確認
- 軸の混同、view/copy の落とし穴、ブロードキャスト不一致、または誤った形状
- 期待される成果
- 配列操作を確認できる出力形状と値
| カテゴリ | 内容 | 例 |
|---|---|---|
| ベクトル化演算 | 配列全体をまとめて計算し、ループ不要 | arr * 2, a + b |
| 汎用関数 | 要素ごとの数学関数 | np.sqrt(), np.exp(), np.log() |
| ブロードキャスト | 形状の異なる配列を自動で拡張する | (3,4) + (4,) → (3,4) |
| 集約関数 | 統計量をまとめて計算する | np.sum(), np.mean(), np.std() |
| axis パラメータ | 集約する方向を指定する | axis=0 は列ごと、axis=1 は行ごと |
| np.where | 条件による選択 | np.where(arr > 0, arr, 0) |
手を動かしてみよう
Section titled “手を動かしてみよう”練習 1:ベクトル化計算
Section titled “練習 1:ベクトル化計算”# 華氏温度を摂氏温度に変換する# 公式:C = (F - 32) × 5/9import numpy as np
fahrenheit = np.array([32, 68, 100, 212, 72, 98.6])
# ベクトル化演算で 1 行で変換するcelsius = (fahrenheit - 32) * 5 / 9練習 2:ブロードキャストの練習
Section titled “練習 2:ブロードキャストの練習”# 3 つの商品の元の価格import numpy as np
prices = np.array([100, 200, 300])
# 3 種類の割引率(列ベクトル)discounts = np.array([[0.9], [0.8], [0.7]])
# ブロードキャストを使って、各商品が各割引率のときの価格を計算する(3×3 行列)final_prices = discounts * prices# 期待結果:# [[ 90. 180. 270.]# [ 80. 160. 240.]# [ 70. 140. 210.]]練習 3:成績統計
Section titled “練習 3:成績統計”# 50 人の学生のランダムな成績を生成する(40〜100 の範囲)rng = np.random.default_rng(seed=42)scores = rng.integers(40, 101, size=50)
# 1. 平均、中央値、標準偏差を計算する# 2. 最高点、最低点とその位置を見つける# 3. 各点数帯の人数を集計する:不合格(<60)、合格(60-69)、普通(70-79)、良い(80-89)、優秀(90+)# 4. 合格率を計算する参考実装と解説
- 華氏から摂氏への変換は
(fahrenheit - 32) * 5 / 9を使います。よくある例では、およそ[0, 20, 37.78, 100, 22.22, 37]になります。 - Broadcasting は右側の次元からそろえて考えます。行ベクトルと列ベクトルを足す典型例では、各行の値と各列の値が組み合わさるため 3x3 行列になります。
- スコア統計では、平均、中央値、標準偏差、最高と最低の位置または名前、合格率、ビンごとの件数を出します。手書きループではなく配列演算で説明できることがポイントです。