📚 背景知識(読んでから問題へ)
numpyは「数値計算の専門ライブラリ」で、Pythonのリストより圧倒的に速く大量のデータを処理できます。
なぜKaggleでnumpyが重要か?
- 大量のデータ(数万〜数百万行)を瞬時に集計・変換できる
- pandasの内部もnumpyで動いており、理解することでデータ操作が速くなる
- モデルの入力データは最終的にnumpy配列(ndarray)として渡すことが多い
今日のポイント:配列の「形状」と「変換」
numpyの配列には形状(shape)という概念があります。
[1, 2, 3, 4, 5, 6] → shape: (6,) 1次元(横1列)
[[1, 2, 3],
[4, 5, 6]] → shape: (2, 3) 2次元(2行3列)
行列の変換・集計は実際のKaggleデータ分析でよく使います。
よく使う操作のイメージ
| 操作 | 意味 | 例 |
|---|---|---|
reshape | 形を変える(総要素数は同じ) | (6,) → (2,3) |
mean(axis=0) | 列ごとの平均 | 各特徴量の平均 |
mean(axis=1) | 行ごとの平均 | 各サンプルの平均 |
np.where | 条件分岐で値を置き換える | 欠損値の補完 |
📝 問題
以下のシナリオを読み、コードを完成させてください。
シナリオ: Titanicコンペで年齢データを前処理しています。
12人の乗客の年齢データが1次元配列で与えられています。
このデータを分析・変換して情報を取り出してください。
import numpy as np
# 12人の乗客の年齢(-1は欠損値を意味する)
ages = np.array([22, 38, 26, 35, -1, 27, 14, 4, 27, 14, 40, -1])
タスク1: 欠損値(-1)を除いた有効な年齢データのみ取り出し、valid_ages に格納してください。
タスク2: valid_ages の平均年齢・最大年齢・最小年齢を計算してください。
タスク3: 欠損値(-1)を validages の平均年齢(小数点以下切り捨て)で補完した新しい配列 filledages を作ってください。
タスク4: filledages を (4, 3) の2次元配列 agesmatrix に変換し、「各行の平均年齢」を計算してください(4グループ×3人の構成と考える)。
期待する出力例:
▶ 出力を見る
有効なデータ数: 10
平均年齢: 24.7
最大年齢: 40
最小年齢: 4
欠損補完後: [22 38 26 35 24 27 14 4 27 14 40 24]
各行(グループ)の平均年齢: [28.33 28.33 15. 26. ]🔍 ヒント(段階的開示)
ヒント1(方向性)
欠損値の抽出はnumpyのブールインデックスが使えます。「-1ではないもの」という条件で配列を絞り込みましょう。
ヒント2(アプローチ)
- タスク1:
ages[ages != -1]のようにブールインデックスで絞り込む - タスク2:
np.mean(),np.max(),np.min()を使う - タスク3:
np.where(条件, 真の値, 偽の値)またはages.copy()+ インデックス代入 - タスク4:
.reshape(4, 3)で形状変換、mean(axis=1)で行ごとの平均
ヒント3(コード骨格)
# タスク1
valid_ages = ages[___]
# タスク2
mean_age = int(np.mean(valid_ages)) # 小数点以下切り捨て用
print(f"平均年齢: {np.mean(valid_ages):.1f}")
# タスク3
filled_ages = np.where(ages == -1, ___, ages)
# タスク4
ages_matrix = filled_ages.reshape(___, ___)
row_means = ages_matrix.mean(axis=___)
✅ 模範解答
import numpy as np
# 12人の乗客の年齢(-1は欠損値を意味する)
ages = np.array([22, 38, 26, 35, -1, 27, 14, 4, 27, 14, 40, -1])
# タスク1: 有効な年齢データのみ取り出す
valid_ages = ages[ages != -1]
print(f"有効なデータ数: {len(valid_ages)}")
# タスク2: 基本統計量の計算
mean_age = np.mean(valid_ages)
max_age = np.max(valid_ages)
min_age = np.min(valid_ages)
print(f"平均年齢: {mean_age:.1f}")
print(f"最大年齢: {max_age}")
print(f"最小年齢: {min_age}")
# タスク3: 欠損値を平均で補完(小数点以下切り捨て)
fill_value = int(mean_age) # 24
filled_ages = np.where(ages == -1, fill_value, ages)
print(f"\n欠損補完後: {filled_ages}")
# タスク4: (4, 3)に変換して各行の平均を計算
ages_matrix = filled_ages.reshape(4, 3)
row_means = ages_matrix.mean(axis=1)
print(f"\n各行(グループ)の平均年齢: {row_means.round(2)}")
実行結果:
▶ 出力を見る
有効なデータ数: 10
平均年齢: 24.7
最大年齢: 40
最小年齢: 4
欠損補完後: [22 38 26 35 24 27 14 4 27 14 40 24]
各行(グループ)の平均年齢: [28.67 28.67 15. 26. ]🪜 Step-by-Step 解説
valid_ages = ages[ages != -1]
ages != -1 は「各要素が-1でないか」を判定して [True, True, True, ..., False] のような真偽値の配列を返します。それを ages[...] に入れると、Trueの位置の値だけ取り出せます。
これはKaggleで最もよく使うテクニックの一つです。特定の条件のデータだけ取り出す操作を「フィルタリング」と呼びます。
mean_age = np.mean(valid_ages) # 平均
max_age = np.max(valid_ages) # 最大値
min_age = np.min(valid_ages) # 最小値
Pythonの sum() や max() でも計算できますが、numpy版は数百万件でも瞬時に計算できるため、大規模データでは必須です。
filled_ages = np.where(ages == -1, fill_value, ages)
np.where(条件, 真のとき, 偽のとき) はExcelの IF関数 と同じ仕組みです。
- 条件が True(-1のとき)→
fill_value(24)を入れる - 条件が False(正常値のとき)→ 元の
agesの値をそのまま使う
ages_matrix = filled_ages.reshape(4, 3)
reshape は要素の総数(12)を変えずに形を変えます。12 = 4×3 なので (4, 3) に変換できます。
row_means = ages_matrix.mean(axis=1)
axis=1 は「横方向(行方向)に計算する」という意味です。覚え方:
axis=0→ 縦方向(列ごと)に計算 → 結果は列数分の配列axis=1→ 横方向(行ごと)に計算 → 結果は行数分の配列
📐 数学・統計の補足(文系向け)
reshapeのイメージ
12個の卵が1列に並んでいる状態を、4段×3列の卵パックに入れ直すイメージです。卵の数は変わらず、並べ方だけ変わります。
axis(軸)の覚え方
表(テーブル)をイメージしてください:
axis=0:上から下に向かって計算(列の集計)→ 各列の平均などaxis=1:左から右に向かって計算(行の集計)→ 各行の平均など
Kaggleのデータは「行=サンプル(乗客1人)、列=特徴量(年齢・性別など)」なので:
axis=0= 全乗客の特徴量ごとの集計axis=1= 1人の乗客の全特徴量を横断した集計
🏆 Kaggleでの実践的な使い方
欠損値補完はほぼ全コンペで必須
実際のKaggleコンペでは、年齢・料金・客室番号などに欠損値(NaN)が頻出します。np.where や pandas の fillna で平均・中央値・最頻値を補完することが基本前処理になります。
Titanicコンペでの例
# 実際のTitanicデータでの欠損値補完(pandas版)
import pandas as pd
df = pd.read_csv('train.csv')
median_age = df['Age'].median()
df['Age'] = df['Age'].fillna(median_age)
numpyで学んだブールインデックスと条件置換の考え方は、pandasでもそのまま応用できます。
⚠️ よくある誤解・ミス
| 誤解・ミス | なぜ起こるか | 正しい理解 |
|---|---|---|
reshape(3, 4) と reshape(4, 3) が同じだと思う | 総要素数が同じだから | 行と列が入れ替わるため、axis=1 の結果が変わる |
axis=0 と axis=1 を逆に使う | 直感に反する番号付け | axis=0が縦方向(列集計)、axis=1が横方向(行集計)と覚える |
int(meanage) と round(meanage) を混同する | 「切り捨て」の表現が曖昧 | int() は常に0方向に切り捨て。24.7 → 24 |
| ブールインデックスで元の配列が変わると思う | 副作用への誤解 | ages[ages != -1] は新しい配列を返す。元の ages は変わらない |
🚀 次のステップ
- 発展:
np.whereの多重条件(np.select)、fancy indexing、ベクトル化演算(ufunc) - 次回予告: pandas入門(DataFrame・Series) — numpyで学んだ概念をpandasに応用します