📚 背景知識(読んでから問題へ)
numpyとは?
numpy(ナンパイ)は、Pythonで「数値の集まり(配列)」を高速に扱うためのライブラリです。
普通のPythonリストでも数値を扱えますが、numpyを使うと:
- 速い: 裏側でC言語が動いているため、100万個のデータも一瞬で計算
- 便利: 「全部の数値を2倍にして」などが1行で書ける
- Kaggle必須: pandas・scikit-learnなど機械学習ライブラリはすべてnumpyを基盤にしている
配列のイメージ
普通のリスト: [1, 2, 3, 4, 5]
numpy配列: array([1, 2, 3, 4, 5]) ← 見た目は似てるが全然違う!
Excel表の「1列」がnumpyの1次元配列、「表全体」が2次元配列(行列)のイメージです。
よく使う操作
| 操作 | コード例 | 意味 |
|---|---|---|
| 配列作成 | np.array([1,2,3]) | リストをnumpy配列に変換 |
| 連番生成 | np.arange(0, 10, 2) | 0から10未満を2刻みで |
| 形状確認 | arr.shape | 行数・列数を確認 |
| 型確認 | arr.dtype | データ型(int, float等)を確認 |
| 全要素に演算 | arr * 2 | 全要素を2倍(ブロードキャスト) |
| 条件でフィルタ | arr[arr > 3] | 3より大きい要素だけ取り出す |
| 統計計算 | arr.mean() | 平均値 |
📝 問題
Titanicデータの「年齢」を模した配列を使って、以下のタスクをすべて実装してください。
import numpy as np
# Titanicの乗客年齢(一部サンプル)
ages = np.array([22, 38, 26, 35, 35, np.nan, 54, 2, 27, 14,
4, 58, 20, 39, 14, 55, 2, np.nan, 31, 35,
34, 15, 28, 8, 38, np.nan, 19, 40, 66, 28])
タスク1: agesの形状(要素数)・データ型を確認してください。
タスク2: np.nan(欠損値)が何個あるか数えてください。
タスク3: 欠損値を除外した配列 ages_clean を作成してください。
タスク4: ages_clean の平均・最小・最大・中央値を計算してください。
タスク5: 18歳未満(未成年)の乗客の年齢を抽出してください。
タスク6: 年齢を標準化(平均0・標準偏差1にスケーリング)してください。
🔍 ヒント(段階的開示)
ヒント1(方向性)
欠損値は np.nan という特別な値で表現されます。numpyにはnan対応の関数(np.isnan(), np.nanmean()等)が用意されています。
ヒント2(アプローチ)
- 形状:
.shape、型:.dtype - nanの個数:
np.isnan(ages).sum() - nan除外: ブールインデックス
ages[~np.isnan(ages)] - 統計:
np.mean(),np.min(),np.max(),np.median() - フィルタリング: ブールインデックス
agesclean[agesclean < 18] - 標準化:
(x - mean) / std
ヒント3(コード骨格)
# タスク1
print("形状:", ages.shape)
print("型:", ages.dtype)
# タスク2
nan_count = np.isnan(ages).sum()
# タスク3
ages_clean = ages[~np.isnan(ages)]
# タスク4
print("平均:", np.mean(ages_clean))
print("最小:", np.min(ages_clean))
print("最大:", np.max(ages_clean))
print("中央値:", np.median(ages_clean))
# タスク5
minors = ages_clean[ages_clean < 18]
# タスク6
ages_scaled = (ages_clean - ages_clean.mean()) / ages_clean.std()
✅ 模範解答
import numpy as np
# Titanicの乗客年齢(一部サンプル)
ages = np.array([22, 38, 26, 35, 35, np.nan, 54, 2, 27, 14,
4, 58, 20, 39, 14, 55, 2, np.nan, 31, 35,
34, 15, 28, 8, 38, np.nan, 19, 40, 66, 28])
# タスク1: 形状と型
print("=== タスク1: 形状・データ型 ===")
print(f"形状: {ages.shape}") # (30,) → 30要素の1次元配列
print(f"データ型: {ages.dtype}") # float64(nanを含むとfloatになる)
# タスク2: 欠損値の個数
print("\n=== タスク2: 欠損値 ===")
nan_count = np.isnan(ages).sum()
print(f"欠損値の個数: {nan_count}個") # 3個
# タスク3: 欠損値を除外
print("\n=== タスク3: 欠損値除外 ===")
ages_clean = ages[~np.isnan(ages)]
print(f"元の要素数: {len(ages)}")
print(f"クリーン後の要素数: {len(ages_clean)}")
# タスク4: 基本統計
print("\n=== タスク4: 基本統計 ===")
print(f"平均: {np.mean(ages_clean):.2f}歳")
print(f"最小: {np.min(ages_clean):.0f}歳")
print(f"最大: {np.max(ages_clean):.0f}歳")
print(f"中央値: {np.median(ages_clean):.1f}歳")
# タスク5: 未成年フィルタリング
print("\n=== タスク5: 未成年(18歳未満) ===")
minors = ages_clean[ages_clean < 18]
print(f"未成年の年齢: {minors}")
print(f"未成年の人数: {len(minors)}人")
# タスク6: 標準化(Zスコア正規化)
print("\n=== タスク6: 標準化 ===")
mean = ages_clean.mean()
std = ages_clean.std()
ages_scaled = (ages_clean - mean) / std
print(f"標準化後の平均: {ages_scaled.mean():.10f}") # ほぼ0
print(f"標準化後の標準偏差: {ages_scaled.std():.10f}") # ほぼ1
print(f"最初の5要素(標準化後): {ages_scaled[:5].round(3)}")
実行結果の例:
▶ 出力を見る
=== タスク1: 形状・データ型 ===
形状: (30,)
データ型: float64
=== タスク2: 欠損値 ===
欠損値の個数: 3個
=== タスク3: 欠損値除外 ===
元の要素数: 30
クリーン後の要素数: 27
=== タスク4: 基本統計 ===
平均: 28.96歳
最小: 2歳
最大: 66歳
中央値: 28.0歳
=== タスク5: 未成年(18歳未満) ===
未成年の年齢: [ 2. 14. 4. 14. 2. 15. 8.]
未成年の人数: 7人
=== タスク6: 標準化 ===
標準化後の平均: 0.0000000000
標準化後の標準偏差: 1.0000000000
最初の5要素(標準化後): [-0.637 0.753 -0.253 0.521 0.521]🪜 Step-by-Step 解説
print(ages.shape) # (30,) → タプルで返る。30要素の1次元配列
print(ages.dtype) # float64 → nanが入るとfloatになる!
ポイント: nanはfloat型なので、整数の配列にnanを混ぜるとすべてfloat64になります。Kaggleのデータでよく遭遇するパターンです。
np.isnan() で欠損値を検出mask = np.isnan(ages)
# array([False, False, ..., True, ..., False])
# Trueの部分がnanの場所
nan_count = mask.sum() # TrueはIntとして1、FalseはIntとして0なのでsumで合計できる
なぜsum()で数えられるのか: Pythonでは True = 1, False = 0 として扱えるため、sum() でTrueの個数(=nanの個数)を数えられます。
ages_clean = ages[~np.isnan(ages)]
# ↑ ~ は「否定」。Trueをまるごと反転する
~np.isnan(ages) は「nanじゃない場所だけTrue」のマスクになります。これを配列の添字に使うと、Trueの要素だけが残ります。
np.mean(ages_clean) # 算術平均
np.min(ages_clean) # 最小値
np.max(ages_clean) # 最大値
np.median(ages_clean) # 中央値
注意: nanが含まれている場合は np.nanmean(), np.nanmin() 等の nan対応版 を使わないと結果がnanになります。
minors = ages_clean[ages_clean < 18]
ages_clean < 18 は比較演算子で各要素を評価し、True/Falseの配列を返します。これを添字に使って条件に合う要素だけ取り出せます。
ages_scaled = (ages_clean - ages_clean.mean()) / ages_clean.std()
標準化の意味: 「元のデータから平均を引いて、標準偏差で割る」操作です。
- 平均からどれだけ離れているかを、標準偏差を単位として表現します
- 結果として「平均=0, 標準偏差=1」のデータになります
📐 数学・統計の補足(文系向け)
ブロードキャストとは
ages_clean - ages_clean.mean()
これは「配列の全要素から同じ値(平均)を引く」操作です。
普通の算数なら [22, 38, 26, ...] - 28.96 という計算はできませんが、numpyは自動的に「28.96を全要素に適用」してくれます。これを ブロードキャスト と呼びます。
標準化の直感的な理解
テストの点数で考えましょう:
- Aさん: 数学80点(クラス平均70点、標準偏差10点) → Zスコア = (80-70)/10 = +1.0
- Bさん: 英語90点(クラス平均85点、標準偏差2点) → Zスコア = (90-85)/2 = +2.5
数学で80点(平均+10点)より英語で90点(平均+5点)の方が実は「クラス内での優秀さ」は低いことが分かります。Zスコアで比較すると異なるスケールのデータを公平に比較できます。
🏆 Kaggleでの実践的な使い方
1. 欠損値の確認(必ずやること)
# 実際のKaggleコードでよく書くパターン
import pandas as pd
df = pd.read_csv('train.csv')
print(df.isnull().sum()) # 列ごとの欠損値数
numpyの np.isnan() と同じ概念で、pandasでは isnull() を使います。
2. 特徴量の標準化(機械学習の前処理)
線形回帰・ニューラルネットワークなど、多くのモデルは特徴量のスケールに敏感です。年齢(0-100)と収入(0-100万)を同じモデルに入れると、スケールの大きい収入が支配的になります。標準化でスケールを揃えることが重要です。
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_train)
手動標準化 (x - mean) / std と全く同じ計算をライブラリがやってくれます。
3. ブールインデックスによるデータ分析
# Titanicコンペでよく使うパターン
survived = ages_clean[survived_mask == 1]
died = ages_clean[survived_mask == 0]
print(f"生存者の平均年齢: {survived.mean():.1f}")
print(f"死亡者の平均年齢: {died.mean():.1f}")
⚠️ よくある誤解・ミス
| 誤解・ミス | なぜ起こるか | 正しい理解 |
|---|---|---|
ages.shape を ages.shape() と書く | メソッドと属性を混同 | shape は属性(関数ではない)なので () 不要 |
nanが入ると mean() がnanになる | nanは「不明な値」なので、不明を含む計算は不明 | nanを除外するか np.nanmean() を使う |
ages * 2 がリストと同じと思う | リストの * は繰り返し、numpyは全要素に演算 | [1,2,3] 2 = [1,2,3,1,2,3] vs np.array([1,2,3]) 2 = [2,4,6] |
~ の意味が分からない | ビット演算子を知らない | ~mask はブール配列の全要素を反転(True↔False)する |
| 標準化と正規化を混同 | どちらも「スケール調整」だが方法が異なる | 標準化: (x-mean)/std → 平均0, std1 / 正規化: (x-min)/(max-min) → 0〜1の範囲 |
🚀 次のステップ
- 発展: 2次元配列(行列)の操作。
np.reshape(),np.concatenate(), 行列積(np.dot()) - 次回予告: pandas入門(DataFrame・Series)— numpyの上に乗った表形式データ操作ライブラリ。Kaggleでのデータ前処理の主役