Day 034 — numpy入門(配列操作・数値計算)

2026-05-13 白 / Phase 1 理論 numpy入門(配列操作・数値計算)

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

numpyとは?

numpyは「数値計算のためのPythonライブラリ」です。Pythonのリストと似ていますが、数値計算に特化した多次元配列(ndarray)を扱えます。

なぜKaggleで必要なのか?

  • データは最終的に「数値の塊(行列)」として扱う
  • numpyはPythonリストより100〜1000倍速く計算できる
  • pandas・scikit-learn・TensorFlowなど全てのMLライブラリがnumpyを土台にしている

配列の「次元」を直感的に理解する

次元numpyの言い方身近な例
0次元スカラー1つの数(例: 42)
1次元ベクトル1行のデータ(例: [1, 2, 3])
2次元行列Excelのシート(行 × 列)
3次元テンソル複数のExcelシート(チャンネル × 行 × 列)

shape・dtype・ndim の意味

import numpy as np
arr = np.array([[1, 2, 3],
                [4, 5, 6]])

arr.shape   # (2, 3) → 2行3列
arr.ndim    # 2 → 2次元
arr.dtype   # int64 → 整数型
arr.size    # 6 → 要素の総数
  • shape: 配列の形(行数, 列数)。Excelで言えば「何行×何列か」
  • ndim: 次元数(shapeのタプルの長さと同じ)
  • dtype: データの型(int=整数、float=小数、bool=真偽値)
  • size: 全要素の数(shapeの全要素を掛け算した値)

よく使うnumpy配列の生成方法

np.zeros((3, 4))      # 全要素が0の3行4列の配列
np.ones((2, 5))       # 全要素が1の2行5列の配列
np.arange(0, 10, 2)   # [0, 2, 4, 6, 8](0から10未満を2ステップ)
np.linspace(0, 1, 5)  # [0.0, 0.25, 0.5, 0.75, 1.0](0〜1を5等分)
np.random.seed(42)
np.random.rand(3, 3)  # 0〜1の乱数で3×3の配列

ブロードキャストとは

numpyの強力な機能で、異なる形の配列同士でも自動的にサイズを合わせて演算する仕組みです。

arr = np.array([1, 2, 3])
arr + 10   # [11, 12, 13] → スカラー10が自動で全要素に足される
arr * 2    # [2, 4, 6]

Excelで「全セルに+10する」のと同じイメージです。


📝 問題

以下の3つの設問に答えてください。コードや説明で回答してください。

設問1: shape・ndim・sizeの読み方

以下のコードを見て、各出力値を予測してください(実際に実行して確認してもOKです)。

▶ 出力を見る
import numpy as np

a = np.array([[[1, 2], [3, 4]],
              [[5, 6], [7, 8]]])

print(a.shape)   # → ?
print(a.ndim)    # → ?
print(a.size)    # → ?
print(a.dtype)   # → ?

各値が何を意味するかを日本語で説明してください。


設問2: 配列生成の使い分け

以下の状況では np.zeros / np.ones / np.arange / np.linspace のどれが最適ですか?理由も答えてください。

  1. モデルの重みを全て「0」で初期化したい(4×5の行列)
  2. 0〜1の範囲で100個の等間隔な数値を生成したい(グラフのx軸用)
  3. 1から始まって2ずつ増える数列(1, 3, 5, 7, ..., 19)を生成したい

設問3: ブロードキャストの動作予測

以下のコードは正常に動作しますか?動作する場合は出力を、動作しない場合はなぜエラーになるかを答えてください。

▶ 出力を見る
import numpy as np

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

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

print(A + b)

また、もし b = np.array([10, 20]) だった場合(shape: (2,))は動作しますか?


🔍 ヒント(段階的開示)

ヒント1(方向性)

設問1: a の形を「何層 × 何行 × 何列」という3次元的なイメージで考えてみてください。

設問2: arangelinspace の違いは「ステップ幅を指定するか、個数を指定するか」です。

設問3: ブロードキャストは「末尾から次元を合わせる」ルールに従います。

ヒント2(アプローチ)

設問1:

  • shapeは (layer数, row数, col数) の順
  • ndimはshapeのタプルの長さ
  • sizeは全次元を掛け算した値

設問2:

# 参考
np.arange(start, stop, step)   # ステップ幅で指定
np.linspace(start, stop, num)  # 個数で指定(stopを含む)

設問3: ブロードキャストのルール

  • 末尾の次元から比較する
  • 両方の次元が同じ、または片方が1の場合のみ可能
ヒント3(コード骨格)

設問1の確認コード:

import numpy as np
a = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
print(f"shape: {a.shape}")     # (?, ?, ?)
print(f"ndim: {a.ndim}")       # ?
print(f"size: {a.size}")       # ?
print(f"dtype: {a.dtype}")     # ?

設問2の骨格:

# 1. zerosで初期化
w = np.zeros((4, 5))

# 2. 0〜1の100点(linspaceを使う)
x = np.linspace(0, 1, ___)

# 3. 奇数列(arangeを使う)
odd = np.arange(1, ___, 2)

模範解答

設問1の答え

import numpy as np

a = np.array([[[1, 2], [3, 4]],
              [[5, 6], [7, 8]]])

print(a.shape)   # (2, 2, 2)
print(a.ndim)    # 3
print(a.size)    # 8
print(a.dtype)   # int64

各値の意味:

  • shape = (2, 2, 2): 3次元配列で「2層 × 2行 × 2列」。複数のExcelシートが2枚あり、各シートが2行2列のイメージ
  • ndim = 3: 3次元配列(shapeのタプルの長さが3)
  • size = 8: 全要素数 = 2 × 2 × 2 = 8
  • dtype = int64: 整数型(64ビット整数)

設問2の答え

import numpy as np

# 1. np.zeros を使う
w = np.zeros((4, 5))
# 理由: 全要素を0で初期化したい → np.zeros が最適
# np.ones は1で初期化するため目的と合わない

# 2. np.linspace を使う
x = np.linspace(0, 1, 100)
# 理由: 「0〜1の範囲で100個の点」という個数指定 → linspace
# np.arange(0, 1, 0.01) でも似た結果だが、浮動小数点誤差で
# ちょうど100個にならない場合があるため linspace が安全

# 3. np.arange を使う
odd = np.arange(1, 20, 2)
# 結果: [ 1  3  5  7  9 11 13 15 17 19]
# 理由: 「2ずつ増える」というステップ幅が決まっている → arange
print(odd)

使い分けまとめ:

関数向いている場面
np.zeros重み・バイアスの初期化(全0)
np.onesマスク・正規化の初期値(全1)
np.arangeステップ幅が決まっているとき
np.linspace個数が決まっているとき(グラフ用途に多い)

設問3の答え

import numpy as np

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

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

print(A + b)
# 正常動作する!
# 出力:
# [[11 22 33]
#  [14 25 36]]

なぜ動作するか:

  • A の shape は (2, 3)b の shape は (3,)
  • ブロードキャストは末尾から次元を合わせる
  • 末尾次元: 3 == 3 → 一致 ✓
  • b は自動的に [[10, 20, 30], [10, 20, 30]] のように拡張されてAの各行に加算される

b = np.array([10, 20]) の場合(shape: (2,)):

# エラーになる!
A = np.array([[1, 2, 3], [4, 5, 6]])   # shape: (2, 3)
b = np.array([10, 20])                  # shape: (2,)
print(A + b)  # ValueError: operands could not be broadcast together

なぜエラーか:

  • 末尾次元: 3 ≠ 2 かつ どちらも1ではない → ブロードキャスト不可
  • b(2, 1) にreshapeすれば動作する:
b = np.array([10, 20]).reshape(2, 1)  # shape: (2, 1)
print(A + b)
# [[11 12 13]
#  [24 25 26]]

🪜 Step-by-Step 解説

1
3次元配列を視覚化する
a = np.array([[[1, 2], [3, 4]],
              [[5, 6], [7, 8]]])

視覚的に整理すると:

層0:            層1:
[[1, 2],        [[5, 6],
 [3, 4]]         [7, 8]]

→ 2層 × 2行 × 2列 = shape (2, 2, 2)

理由: numpyのshapeは「外側の括弧の数から順に次元を数える」。最も外側が2要素 → 2層、各層が2行 → 2行、各行が2列 → 2列。

2
arange vs linspace の選び方

実際に動かして違いを体感する:

# arange: ステップ幅0.1で0〜1
arr1 = np.arange(0, 1, 0.1)
print(len(arr1), arr1)  # 10個、1.0が含まれない

# linspace: 0〜1を10等分
arr2 = np.linspace(0, 1, 10)
print(len(arr2), arr2)  # 10個、1.0が含まれる

重要な違い:

  • arange(0, 1, 0.1) → 1.0は含まれない(stop未満)
  • linspace(0, 1, 10) → 0と1両端を含む

グラフのx軸など「両端含む」が自然な場面では linspace が便利。

3
ブロードキャストを行列演算として理解する
A = np.array([[1, 2, 3],
              [4, 5, 6]])

b = np.array([10, 20, 30])

numpyは内部で b を以下のように「仮想的に」拡張して計算する:

b (仮想拡張後):
[[10, 20, 30],
 [10, 20, 30]]

実際には拡張したコピーは作らず、メモリ効率よく計算する。これがブロードキャストが高速な理由。


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

行列とは何か

行列(マトリックス)は「数値を長方形に並べた表」です。

  • 「2行3列の行列」= Excelシートの2行×3列の範囲
  • 行列の足し算 = 対応するセル同士を足す
  • ブロードキャストは「足りない行を自動でコピーして足す」仕組み

浮動小数点とは

コンピュータは小数を「近似値」で保存します。例えば 0.1 + 0.20.30000000000000004 になります(これは仕様で誤りではない)。np.linspace はこの問題を避けるため、等間隔の点を内部で整数ベースで計算してから変換します。


🏆 Kaggleでの実践的な使い方

1. 特徴量行列の作成

Kaggleでは「全データ = numpy配列」として扱うことが多い。

# Titanicデータを想定
X = np.array([
    [22, 1, 0],   # [年齢, 性別, 生存]
    [38, 0, 1],
    [26, 0, 1],
])
print(X.shape)  # (3, 3) → 3サンプル × 3特徴量

2. 正規化(標準化)

機械学習モデルに入力する前に「値の範囲を揃える」処理でnumpyを使う:

X_mean = X.mean(axis=0)   # 各列の平均(ブロードキャスト活用)
X_std  = X.std(axis=0)    # 各列の標準偏差
X_norm = (X - X_mean) / X_std   # 標準化(全列一括でできる!)

axis=0 の意味は「列方向(縦方向)に計算する」。axis=1 なら行方向。

3. ランダムシードの固定

Kaggleコンペでは再現性が重要。乱数の結果を固定するために使う:

np.random.seed(42)   # 必ず最初に書く
idx = np.random.permutation(1000)  # 毎回同じシャッフル順序になる

⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
np.arange(0, 1, 0.1) で1.0が含まれると思うPythonの range と混同arange は stop 未満なので1.0は含まれない
shape(行, 列) ではなく (列, 行) と読む直感と逆に感じるshape[0] が行数(縦)、shape[1] が列数(横)
ブロードキャストで次元が合えば何でも動くと思う「末尾から合わせる」ルールを忘れるshape (2,3) と (2,) はエラー。(2,1) にreshapeが必要
dtype を気にしない整数配列に小数を入れると切り捨て小数を扱うなら dtype=float を明示か np.float64 を使う
a.sizelen(a) を混同Pythonリストの感覚のままlen(a) は最初の次元のサイズ(2次元配列なら行数)、size は全要素数

🚀 次のステップ

  • 発展: numpyのスライシング・インデックス参照(a[1:3, ::2] など)、fancy indexingを学ぶ
  • 次回予告: pandas入門(DataFrame・Series)— numpyの上に構築されたデータ分析ツール。Excelのような操作がPythonでできるようになる

🎯 自己評価

自分の回答

気づき・メモ