📚 背景知識(読んでから問題へ)
numpyとは?
numpyは「数値計算のためのPythonライブラリ」です。Pythonの普通のリストと比べると、以下の点が優れています:
- 速い: 内部でC言語を使っているため、リストの10〜100倍速い
- 便利: 数学的な操作(足し算・掛け算・統計)を一度に全要素に適用できる
- 標準: pandas・scikit-learn・TensorFlowなど全てのデータサイエンスライブラリの基盤
直感的なイメージ
普通のリストで「全ての要素を2倍にする」とき:
data = [1, 2, 3, 4, 5]
result = [x * 2 for x in data] # ループが必要
numpyなら:
import numpy as np
data = np.array([1, 2, 3, 4, 5])
result = data * 2 # そのまま掛けるだけ!
重要な概念
| 用語 | 意味 | 例 |
|---|---|---|
| ndarray | numpyの配列 | np.array([1,2,3]) |
| shape | 配列の形(行×列) | (3, 4) = 3行4列 |
| dtype | データの型 | int64, float64 |
| axis | 操作の方向 | axis=0→列方向, axis=1→行方向 |
| ブロードキャスト | 異なる形の配列を自動的に合わせる機能 | array + 10 |
よく使う操作
import numpy as np
# 配列作成
a = np.array([1, 2, 3, 4, 5])
zeros = np.zeros(5) # [0, 0, 0, 0, 0]
ones = np.ones((2, 3)) # 2行3列の1の配列
arange = np.arange(0, 10, 2) # [0, 2, 4, 6, 8]
# 統計
a.mean() # 平均
a.std() # 標準偏差
a.min() # 最小値
a.max() # 最大値
a.sum() # 合計
📝 問題
あなたはEコマース会社のデータアナリストです。先月の売上データが以下のようにあります。
データ: 30日分の日次売上(万円)
import numpy as np
np.random.seed(42)
sales = np.random.randint(50, 200, size=30) # 50〜200万円の間でランダム生成
以下の分析タスクを全てnumpyのみで実装してください(pandasは使用しない):
タスク1: 基本統計の計算
- 月間合計売上
- 日次平均売上
- 最高売上額と最低売上額
- 売上の標準偏差(日々のばらつき)
タスク2: 条件フィルタリング
- 平均売上を上回った日数を計算
- 平均売上を上回った日のデータのみ抽出
タスク3: 正規化(Normalization)
- 売上データを0〜1の範囲に正規化する
- 正規化の公式:
(値 - 最小値) / (最大値 - 最小値) - 正規化後の最小値が0、最大値が1になることを確認
タスク4: 週次集計
- 30日を4週(各7日)と残り2日に分けて考える
- 最初の28日を
(4, 7)の形に reshape する - 各週の合計売上を計算する(axis=1 を使う)
最終的に、以下の形式で結果を出力してください:
▶ 出力を見る
=== 月次売上分析レポート ===
合計売上: XXX万円
平均売上: XX.X万円/日
最高売上: XXX万円
最低売上: XX万円
標準偏差: XX.X万円
平均超え日数: XX日 / 30日🔍 ヒント(段階的開示)
ヒント1(方向性)
numpyの配列に対して統計関数(.mean(), .sum()など)を使うと、全要素を一度に計算できます。条件フィルタリングは「ブール配列」を使います。
ヒント2(アプローチ)
- タスク1:
sales.sum(),sales.mean(),sales.max(),sales.min(),sales.std() - タスク2:
sales > sales.mean()でTrue/Falseの配列が作れる。sum()でTrueの数を数えられる - タスク3:
(sales - sales.min()) / (sales.max() - sales.min()) - タスク4:
sales[:28].reshape(4, 7)で形を変換。.sum(axis=1)で各行の合計
ヒント3(コード骨格)
import numpy as np
np.random.seed(42)
sales = np.random.randint(50, 200, size=30)
# タスク1: 基本統計
total = sales.___()
avg = sales.___()
max_sales = sales.___()
min_sales = sales.___()
std_sales = sales.___()
# タスク2: 条件フィルタリング
above_avg_mask = sales > ___ # ブール配列
above_avg_count = above_avg_mask.___() # Trueの数
above_avg_days = sales[above_avg_mask] # フィルタリング
# タスク3: 正規化
normalized = (sales - ___) / (___ - ___)
# タスク4: 週次集計
weekly = sales[:28].reshape(___, ___)
weekly_totals = weekly.sum(axis=___)
print(f"=== 月次売上分析レポート ===")
print(f"合計売上: {total}万円")
print(f"平均売上: {avg:.1f}万円/日")
print(f"最高売上: {max_sales}万円")
print(f"最低売上: {min_sales}万円")
print(f"標準偏差: {std_sales:.1f}万円")
print(f"\n平均超え日数: {above_avg_count}日 / 30日")
✅ 模範解答
import numpy as np
# データ生成
np.random.seed(42)
sales = np.random.randint(50, 200, size=30)
print("生成された売上データ(万円):")
print(sales)
# ============================
# タスク1: 基本統計の計算
# ============================
total = sales.sum()
avg = sales.mean()
max_sales = sales.max()
min_sales = sales.min()
std_sales = sales.std()
print("\n=== 月次売上分析レポート ===")
print(f"合計売上: {total}万円")
print(f"平均売上: {avg:.1f}万円/日")
print(f"最高売上: {max_sales}万円")
print(f"最低売上: {min_sales}万円")
print(f"標準偏差: {std_sales:.1f}万円")
# ============================
# タスク2: 条件フィルタリング
# ============================
above_avg_mask = sales > avg # True/Falseの配列
above_avg_count = above_avg_mask.sum() # Trueの数 = 平均超えの日数
above_avg_days = sales[above_avg_mask] # 平均超えの日の売上のみ
print(f"\n平均超え日数: {above_avg_count}日 / 30日")
print(f"平均超えの日の売上: {above_avg_days}")
# ============================
# タスク3: 正規化
# ============================
normalized = (sales - sales.min()) / (sales.max() - sales.min())
print(f"\n正規化後のデータ(先頭5件): {normalized[:5].round(3)}")
print(f"正規化後の最小値: {normalized.min():.1f}") # 0.0
print(f"正規化後の最大値: {normalized.max():.1f}") # 1.0
# ============================
# タスク4: 週次集計
# ============================
weekly = sales[:28].reshape(4, 7) # 28日を4週×7日に変換
weekly_totals = weekly.sum(axis=1) # axis=1: 各行(各週)の合計
print(f"\n週次売上合計:")
for week, total_w in enumerate(weekly_totals, 1):
print(f" 第{week}週: {total_w}万円")
🪜 Step-by-Step 解説
np.random.seed(42)
sales = np.random.randint(50, 200, size=30)
np.random.seed(42) は「ランダムの種」を固定します。同じseedを使えば、何度実行しても同じ「ランダムな」数が生成されます。これはデータサイエンスで「再現性」を保つために非常に重要です。
np.random.randint(50, 200, size=30) は「50以上200未満の整数を30個ランダムに生成」という意味です。
total = sales.sum() # 全て足す
avg = sales.mean() # 合計÷個数
max_sales = sales.max() # 最大値
min_sales = sales.min() # 最小値
std_sales = sales.std() # 標準偏差
これらはnumpy配列のメソッド(配列が持つ関数)です。Pythonのリストでは使えませんが、numpyのarrayなら使えます。全30要素を一瞬で計算します。
above_avg_mask = sales > avg
# → array([False, True, True, False, ...]) のようになる
above_avg_count = above_avg_mask.sum()
# Trueは1、Falseは0として計算される
# sum() = Trueの個数 = 平均超えの日数
above_avg_days = sales[above_avg_mask]
# ブール配列でインデックスすると、Trueの位置の要素だけ取り出せる
このパターン(条件 → ブール配列 → フィルタリング)はKaggleで毎日使う最重要テクニックです。pandasでも全く同じ考え方が使えます。
normalized = (sales - sales.min()) / (sales.max() - sales.min())
正規化の直感的な意味:
- 「最小値からの距離」÷「最大値と最小値の差」
- 最小値の場合:
(最小 - 最小) / (最大 - 最小) = 0 / 差 = 0 - 最大値の場合:
(最大 - 最小) / (最大 - 最小) = 1 - 間の値は0〜1の間に収まる
これはMin-Max正規化(またはMin-Max スケーリング)と呼ばれます。
weekly = sales[:28].reshape(4, 7)
# sales[:28] → 最初の28要素(インデックス0〜27)
# .reshape(4, 7) → 4行7列の2次元配列に変換
weekly_totals = weekly.sum(axis=1)
# axis=0 → 列方向(↓)に計算
# axis=1 → 行方向(→)に計算、各行の合計を出す
reshapeのイメージ:
[d1, d2, d3, d4, d5, d6, d7, d8, d9, ...]
↓ reshape(4, 7)
[[d1, d2, d3, d4, d5, d6, d7 ], ← 第1週
[d8, d9, d10, d11, d12, d13, d14], ← 第2週
[d15, d16, d17, d18, d19, d20, d21], ← 第3週
[d22, d23, d24, d25, d26, d27, d28]] ← 第4週
📐 数学・統計の補足(文系向け)
ブール(Boolean)とは?
「True(真)」か「False(偽)」の2択しかない値のことです。条件を満たせばTrue、満たさなければFalse。プログラミングの世界では「Yes/No」の答えをこの形で表現します。
axisの覚え方
表計算ソフト(Excelなど)をイメージしてください:
- axis=0(行方向): 列ごとに計算(↓方向)→ 各列の合計・平均を出す
- axis=1(列方向): 行ごとに計算(→方向)→ 各行の合計・平均を出す
🏆 Kaggleでの実践的な使い方
1. 特徴量の正規化
機械学習モデル(特に線形回帰・ニューラルネットワーク)は、特徴量のスケールが揃っていないと性能が落ちます。今回学んだMin-Max正規化は特徴量エンジニアリングの基本中の基本です。
# Kaggleの前処理でよく見るパターン
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X_scaled = scaler.fit_transform(X)
# これは内部でnumpyの演算を使っている
2. ブール配列によるデータフィルタリング
外れ値の除去・特定条件のサンプル抽出など、コンペの前処理で毎日使います。
3. reshapeとベクトル化
画像データ(縦×横×チャンネル)や時系列データ(時間×特徴量)の操作にreshapeは必須です。
⚠️ よくある誤解・ミス
| 誤解・ミス | なぜ起こるか | 正しい理解 |
|---|---|---|
sales > avg の結果をループで処理しようとする | Pythonリストの習慣 | ブール配列をそのままインデックスに使える |
reshape でエラーが出る | 元の要素数と一致しない形を指定 | 4×7=28 のように積が一致する必要がある |
axis=0 と axis=1 を逆に使う | 直感と逆になることがある | axis=1で「各行」の集計 |
std() がExcelと違う値になる | Excelはデフォルトで不偏標準偏差(n-1) | numpyはデフォルトで母標準偏差(n)。ddof=1 で一致 |
ブール配列の.sum()を.count()と思う | pandasのcountと混同 | numpyにはcount()なし。True=1として.sum()を使う |
🚀 次のステップ
- 発展: 標準化(Standardization)=
(値 - 平均) / 標準偏差。Min-Max正規化との使い分けを学ぶ - 次回予告: pandas入門(DataFrame・Series) — numpyの上に構築されたデータ分析の主力ライブラリ。表形式データの操作が格段に楽になる