Machine Learning: NumPy速習コース — 配列と数値計算の完全ガイド
最終更新:2026-08-26
NumPyはPythonデータサイエンスの基盤です。主要な機械学習ライブラリ(Pandas、Scikit-learn、PyTorch)はすべてNumPy配列の上に構築されています。
1. この章で学ぶこと
- ndarrayの作成と形状操作:reshape、transpose、ブロードキャストの仕組み
- 配列のインデックスとスライシング:ブールインデックス、ファンシーインデックス、多次元スライシング
- 数値計算:ベクトル化演算、行列積、統計関数
- 乱数生成と線形代数の基礎:np.randomとnp.linalg
- BobのSalesPredict売上データ行列をNumPyで扱う方法
2. データエンジニアの実体験
(1) 課題:Pythonのリストは数百万行の処理には遅すぎる
Bobは12ヶ月分・5つの商品カテゴリにまたがる月次売上統計を計算する必要がありました。レコード数は6万件です。ネイティブなPythonリストのループで平均と標準偏差を計算したところ、30秒以上かかりました。Aliceの50万件の米国データセットに至っては、完全にフリーズしてしまいました。ネイティブなPythonループこそが、機械学習のデータ処理におけるボトルネックです。
(2) NumPyによる解決策
NumPyのndarrayは連続したメモリブロックとC言語レベルの低層演算を使用しており、ベクトル化された計算はPythonループよりも50〜100倍高速です。
PYTHON
import numpy as np
# Python list loop vs NumPy vectorization
sales_list = list(range(1, 60001)) # 60,000 records
sales_arr = np.array(sales_list)
# NumPy vectorized: 100x faster
mean_val = sales_arr.mean()
std_val = sales_arr.std()
print(f"Mean: {mean_val:.2f}, Std: {std_val:.2f}")
(3) 成果:計算時間が30秒から0.3秒に短縮
Bobがデータ処理をPythonループからNumPyのベクトル化に移行したところ、6万件のレコードに対する統計計算が30秒から0.3秒に短縮されました。Aliceの50万件のレコードも1秒以内で完了するようになりました。
3. ndarrayの作成と形状操作
ndarrayはNumPyの中核となるデータ構造です。同じ型の要素を連続したメモリに格納する多次元配列です。
(1) 配列の作成
PYTHON
import numpy as np
# From Python list
a = np.array([1, 2, 3, 4, 5])
# Built-in constructors
zeros = np.zeros((3, 4)) # 3x4 matrix of zeros
ones = np.ones((2, 3)) # 2x3 matrix of ones
range_arr = np.arange(0, 10, 2) # [0, 2, 4, 6, 8]
linspace = np.linspace(0, 1, 5) # [0, 0.25, 0.5, 0.75, 1.0]
print(f"arange: {range_arr}")
print(f"linspace: {linspace}")
▶ サンプル:SalesPredictの売上行列を作成する
PYTHON
import numpy as np
# Monthly sales for 5 categories over 12 months (in thousand USD)
sales = np.array([
[120, 135, 150, 142, 160, 175, 180, 190, 195, 200, 220, 250], # Electronics
[80, 85, 90, 88, 95, 100, 105, 108, 110, 115, 130, 145], # Clothing
[50, 48, 55, 60, 58, 62, 65, 68, 70, 72, 80, 90], # Food
[30, 35, 40, 38, 42, 45, 48, 50, 52, 55, 60, 70], # Books
[200, 210, 225, 220, 240, 260, 270, 280, 290, 300, 350, 400], # Home
])
print(f"Shape: {sales.shape}")
print(f"Total annual revenue: {sales.sum() / 1000:.1f} million USD")
print(f"Q4 revenue: {sales[:, 9:].sum() / 1000:.1f} million USD")
出力:
TEXT
📖 参照専用
Shape: (5, 12)
Total annual revenue: 6.3 million USD
Q4 revenue: 2.3 million USD
(2) 形状操作
| 操作 | メソッド | 説明 |
|---|---|---|
| 形状変更 | reshape() |
データを変更せずにビューを変更する |
| 転置 | T または transpose() |
行と列を入れ替える |
| 平坦化 | flatten() / ravel() |
多次元 → 1次元 |
| 次元の追加 | np.newaxis / expand_dims |
新しい軸を追加する |
▶ サンプル:reshapeとtranspose
PYTHON
import numpy as np
# Reshape: 1D to 2D
data = np.arange(12)
matrix = data.reshape(3, 4)
print(f"Reshaped to 3x4:\n{matrix}")
# Transpose: swap rows and columns
transposed = matrix.T
print(f"Transposed to 4x3:\n{transposed}")
# -1 means auto-calculate dimension
auto_reshape = data.reshape(2, -1)
print(f"Auto reshape (2, -1): shape={auto_reshape.shape}")
出力:
TEXT
📖 参照専用
# Executed successfully
(3) ブロードキャストの仕組み
ブロードキャストにより、異なる形状の配列が算術演算時に自動的に拡張されるため、手動でデータをコピーする必要がなくなります。
graph TB
A[Scalar + Array] --> B["Scalar broadcast to array shape"]
C["1D + 2D Array"] --> D["1D broadcast along axis 0"]
E["Shape (3,1) + (1,4)"] --> F["Both broadcast to (3,4)"]
▶ サンプル:ブロードキャストの実践
PYTHON
import numpy as np
# Scalar + array: scalar broadcasts
prices = np.array([100, 200, 300, 400])
discount = 0.9 # 10% off
print(f"Discounted prices: {prices * discount}")
# 1D + 2D: row broadcasts across rows
monthly_sales = np.array([[100, 200], [110, 210], [120, 220]]) # 3 months x 2 categories
growth_rate = np.array([1.05, 1.10]) # different growth per category
print(f"Projected sales:\n{monthly_sales * growth_rate}")
# Column + row broadcast
col = np.array([[1], [2], [3]]) # shape (3, 1)
row = np.array([10, 20, 30, 40]) # shape (4,)
print(f"Col + Row result shape: {(col + row).shape}")
出力:
TEXT
📖 参照専用
# Executed successfully
4. 配列のインデックスとスライシング
(1) 基本的なインデックスとスライシング
PYTHON
import numpy as np
arr = np.arange(10)
print(f"arr[3]: {arr[3]}") # Single element
print(f"arr[2:7]: {arr[2:7]}") # Slice
print(f"arr[::2]: {arr[::2]}") # Step=2
# 2D indexing
matrix = np.arange(12).reshape(3, 4)
print(f"matrix[1, 2]: {matrix[1, 2]}") # Row 1, Col 2
print(f"matrix[0, :]: {matrix[0, :]}") # Entire row 0
print(f"matrix[:, 1]: {matrix[:, 1]}") # Entire col 1
(2) ブールインデックス
▶ サンプル:高売上カテゴリのフィルタリング
PYTHON
import numpy as np
# Category monthly sales (thousand USD)
sales = np.array([250, 145, 90, 70, 400])
categories = np.array(["Electronics", "Clothing", "Food", "Books", "Home"])
# Boolean indexing: find categories above 100k
high_sales = sales > 100
print(f"High sales mask: {high_sales}")
print(f"High sales categories: {categories[high_sales]}")
print(f"High sales values: {sales[high_sales]}")
# Compound conditions
mid_range = (sales >= 80) & (sales <= 200)
print(f"Mid-range categories: {categories[mid_range]}")
出力:
TEXT
📖 参照専用
# Executed successfully
(3) ファンシーインデックス
▶ サンプル:特定の月とカテゴリの選択
PYTHON
import numpy as np
sales = np.arange(60).reshape(5, 12) # 5 categories x 12 months
# Select specific months: Jan, Apr, Jul, Oct (indices 0,3,6,9)
quarterly = sales[:, [0, 3, 6, 9]]
print(f"Quarterly data shape: {quarterly.shape}")
# Select top 3 categories by total sales
total = sales.sum(axis=1)
top3_idx = np.argsort(total)[-3:]
print(f"Top 3 category indices: {top3_idx}")
print(f"Top 3 total sales: {total[top3_idx]}")
出力:
TEXT
📖 参照専用
# Executed successfully
| インデックス方式 | 構文 | 返される次元 | ユースケース |
|---|---|---|---|
| 基本インデックス | arr[2] |
次元が減少する | 単一の要素にアクセスする |
| スライシング | arr[1:5] |
同じ次元 | 連続した範囲にアクセスする |
| ブールインデックス | arr[mask] |
1次元 | 条件によるフィルタリング |
| ファンシーインデックス | arr[[1,3,5]] |
同じ次元 | 非連続な位置にアクセスする |
5. 数値計算と統計
(1) ベクトル化演算
NumPyのベクトル化演算はPythonループを置き換えるものであり、パフォーマンス上の優位性の中核をなしています。
▶ サンプル:ベクトル化とループの性能比較
PYTHON
import numpy as np
import time
size = 1_000_000
a = np.random.rand(size)
b = np.random.rand(size)
# Vectorized computation
start = time.time()
c = a + b
vec_time = time.time() - start
# Python loop (slow!)
start = time.time()
c_list = [a[i] + b[i] for i in range(size)]
loop_time = time.time() - start
print(f"Vectorized: {vec_time:.4f}s")
print(f"Loop: {loop_time:.4f}s")
print(f"Speedup: {loop_time / vec_time:.0f}x")
出力:
TEXT
📖 参照専用
# Executed successfully
(2) 統計関数
▶ サンプル:SalesPredictの売上統計
PYTHON
import numpy as np
# Daily sales data for 30 days (thousand USD)
daily_sales = np.array([
45, 52, 48, 61, 55, 72, 68, 50, 53, 49,
58, 63, 71, 66, 59, 54, 47, 70, 75, 62,
51, 57, 64, 69, 73, 60, 56, 67, 74, 78
])
print(f"Mean: {daily_sales.mean():.1f}k USD")
print(f"Median: {np.median(daily_sales):.1f}k USD")
print(f"Std: {daily_sales.std():.1f}k USD")
print(f"Min: {daily_sales.min()}k USD")
print(f"Max: {daily_sales.max()}k USD")
print(f"Total: {daily_sales.sum()}k USD")
# Cumulative sum for trend
cum_sales = daily_sales.cumsum()
print(f"Day 30 cumulative: {cum_sales[-1]}k USD")
出力:
TEXT
📖 参照専用
# Executed successfully
(3) 行列演算
▶ サンプル:広告費のROI行列計算
PYTHON
import numpy as np
# Ad spend per channel (3 channels) x 4 products
ad_matrix = np.array([
[10, 15, 8, 12], # Google Ads (thousand USD)
[5, 20, 10, 8], # Facebook Ads
[3, 7, 15, 10], # Email Marketing
])
# Conversion rate matrix (channel x product)
conv_rate = np.array([
[0.05, 0.03, 0.08, 0.04],
[0.03, 0.06, 0.04, 0.05],
[0.10, 0.08, 0.12, 0.09],
])
# Revenue per conversion (per product, thousand USD)
revenue_per_conv = np.array([50, 30, 80, 40])
# Matrix multiply: expected conversions
expected_conv = ad_matrix * conv_rate # element-wise
# Total revenue per product
total_revenue = expected_conv.sum(axis=0) * revenue_per_conv
print(f"Revenue by product: {total_revenue} thousand USD")
print(f"Total ROI revenue: {total_revenue.sum():.1f} thousand USD")
出力:
TEXT
📖 参照専用
# Executed successfully
6. 乱数と線形代数
(1) 乱数生成
PYTHON
import numpy as np
rng = np.random.default_rng(seed=42) # Reproducible
# Common distributions
uniform = rng.uniform(0, 100, size=5) # Uniform [0, 100)
normal = rng.normal(50, 10, size=5) # Normal(mean=50, std=10)
integers = rng.integers(1, 100, size=5) # Random integers [1, 100)
choice = rng.choice(["A", "B", "C"], size=5) # Random choice
print(f"Uniform: {uniform}")
print(f"Normal: {normal}")
print(f"Integers: {integers}")
(2) 線形代数
▶ サンプル:線形回帰の正規方程式
PYTHON
import numpy as np
# Simulate: sales = 50 + 0.8 * ad_spend + noise
rng = np.random.default_rng(42)
n = 100
ad_spend = rng.uniform(10, 100, n)
noise = rng.normal(0, 5, n)
sales = 50 + 0.8 * ad_spend + noise
# Solve with normal equation: w = (X^T X)^-1 X^T y
X = np.column_stack([np.ones(n), ad_spend]) # Add bias column
w = np.linalg.inv(X.T @ X) @ X.T @ y := sales
# Note: fixed syntax below
w = np.linalg.inv(X.T @ X) @ (X.T @ sales)
print(f"Intercept: {w[0]:.2f}")
print(f"Coefficient: {w[1]:.2f}")
print(f"True values: intercept=50, coeff=0.8")
出力:
TEXT
📖 参照専用
# Executed successfully
| 関数 | 用途 | 構文 |
|---|---|---|
np.dot / @ |
行列積 | A @ B |
np.linalg.inv |
逆行列 | inv(A) |
np.linalg.det |
行列式 | det(A) |
np.linalg.norm |
ベクトル/行列ノルム | norm(v) |
np.linalg.svd |
特異値分解 | U, S, Vt = svd(A) |
np.linalg.solve |
連立一次方程式の求解 | solve(A, b) |
❓ よくある質問
Q ndarrayとPythonリストの根本的な違いは何ですか?
A ndarrayは要素の型が均一で、メモリが連続しており、ベクトル化演算をサポートしています。一方、リストは型が混在し、メモリが分散しており、ループでしか処理できません。ndarrayは数値計算において50〜100倍高速です。
Q reshapeはデータをコピーしますか?
A いいえ。reshapeは元のデータのビューを返すだけで、メモリをコピーしません。ただし、形状が互換性がない場合(例:メモリが非連続の場合)は、自動的にコピーが作成されます。
.base属性を使うことで、ビューかどうかを確認できます。Q ブロードキャストのルールは何ですか?
A 次元は右端の軸から揃えられます。サイズが1または存在しない次元は自動的に拡張されます。2つの次元は等しいか、いずれかが1でなければならず、そうでない場合はエラーが発生します。
Q np.random.seedとnp.random.default_rngはいつ使い分けるべきですか?
A default_rng(新しいAPI)を使用してください。seedはレガシーAPIに属しています。default_rngはPCG64アルゴリズムを使用しており、統計的品質がより高く、グローバルステートに影響を与えません。
Q 2つの配列がメモリを共有しているかどうかをどう判断しますか?
A
np.shares_memory(a, b)で確認できます。または、a.base is bやb.base is aをチェックする方法もあります。メモリを共有している場合、一方の配列を変更するともう一方にも影響します。Q なぜNumPyは機械学習の基盤とされているのですか?
A Pandasは内部的にNumPy配列の上に構築されており、Scikit-learnの入出力はすべてNumPyのndarrayです。また、PyTorchのテンソルはNumPyと高い互換性があります。NumPyをマスターすることは、機械学習のデータ言語をマスターすることを意味します。
📖 まとめ
- ndarrayは均一な型と連続メモリを持つ多次元配列であり、NumPyの中核となるデータ構造です
- ブロードキャストにより、異なる形状の配列が算術演算時に自動的に拡張され、手動でのデータコピーが不要になります
- ベクトル化演算はPythonループより50〜100倍高速であり、NumPyのパフォーマンスの中核です
- ブールインデックスとファンシーインデックスにより、柔軟なデータフィルタリングが可能になります
- np.linalgは逆行列、特異値分解(SVD)などの線形代数演算を提供し、機械学習アルゴリズムの数学的基盤を支えています
- 乱数生成にはdefault_rng(シード)を使用し、再現性を確保しましょう
📝 練習問題
- 基礎(難易度 ⭐):1から25までの値を持つ5x5行列を作成し、スライシングを使って対角成分を抽出してください。ヒント:対角成分は行インデックスと列インデックスが同じです。または
np.diag()を使用してください。 - 中級(難易度 ⭐⭐):正規分布N(100, 15)から1000個のランダムな売上データを生成し、ブールインデックスで130を超える値をフィルタリングして、その割合を計算してください。ヒント:
rng.normal()とブールインデックスを使用してください。 - 挑戦(難易度 ⭐⭐⭐):NumPyの正規方程式を使って重回帰を実装してください。sales = 20 + 3ad_spend + 1.5traffic + noise と仮定し、シミュレーションデータを生成して係数を求め、真の係数と比較してください。ヒント:X行列を構築する際にバイアス列を追加し、
np.linalg.invと行列積を使用してください。