📚 背景知識(読んでから問題へ)
処理パイプラインのイメージ
今日の問題は「生データ → クリーニング → 分類 → 集計 → ソート」という典型的な前処理パイプラインです。
np.clip — 値を範囲内に収める
スーパーの会員ポイントが「マイナスになれない」「上限10000点」のように、値の範囲を制限するイメージです。
np.clip(arr, 0, 100) # 0未満 → 0, 100超 → 100
np.where — 条件によって値を切り替える
「赤信号なら止まれ、青なら進め」のような条件分岐を、配列全体に一括で適用します。
np.where(条件, Trueの値, Falseの値)
ブール配列の sum() — 条件を満たす個数を数える
Trueは1、Falseは0として合計されます。「何人が合格したか」を数えるのに使います。
(arr >= 60).sum() # 60以上の要素数を返す
🗂️ データスキーマ
| 変数名 | 型 | 説明 | 値の範囲 | 備考 |
|---|---|---|---|---|
scores |
ndarray[int] | 生の試験得点データ(10名分) | -5 〜 101(入力ミス含む) | 前処理前の生データ |
scores_clipped |
ndarray[int] | クリッピング後の得点 | 0 〜 100 | np.clip 適用後 |
grade |
ndarray[str] | 評価(A / B) | "A" or "B" | 80以上 → A |
mean_score |
float64 | 平均点 | 0.0 〜 100.0 | クリッピング後で計算 |
pass_count |
int | 合格者数(60点以上) | 0 〜 10 | ブール配列の sum() |
pass_rate |
float64 | 合格率(%) | 0.0 〜 100.0 | pass_count / 10 × 100 |
scores_sorted |
ndarray[int] | 降順ソート済み得点 | 0 〜 100 | 高得点→低得点の順 |
📝 問題
101点と-5点は入力ミスです(本来は0〜100点)。以下の4問を順番に実装してください。import numpy as np
scores = np.array([45, 78, 92, 33, 67, 85, 55, 101, -5, 70])
問1 — クリッピング
scores の値を 0以上100以下 に制限し、scores_clipped に格納してください。
[ 45 78 92 33 67 85 55 100 0 70]問2 — 条件分類(np.where)
scores_clipped を使い、np.where で評価を付けて grade に格納してください。
| 得点 | 評価 |
|---|---|
| 80点以上 | "A" |
| 80点未満 | "B" |
問3 — 統計計算
scores_clipped から以下を計算してください:
mean_score: 平均点(np.meanを使う)pass_count: 60点以上の人数(ブールインデックスと.sum()を使う)pass_rate: 合格率(%)=pass_count / 全人数 × 100
問4 — 降順ソート
scores_clipped を 降順(大→小) に並べた配列 scores_sorted を作ってください。
[100 92 85 78 70 67 55 45 33 0]np.sort は昇順(小→大)で返します。降順にするにはひと工夫が必要です。🔍 ヒント(段階的開示)
ヒント1 — 方向性
- 問1: numpyには「値を範囲内に収める」専用関数があります
- 問2: 条件によって文字列を切り替えるには
np.whereが便利です - 問3: ブール配列の
sum()はTrueの個数を返します - 問4: 昇順配列を逆にすれば降順になります
ヒント2 — アプローチ
- 問1:
np.clip(arr, a_min, a_max) - 問2:
np.where(scores_clipped >= 80, "A", "B") - 問3:
(scores_clipped >= 60).sum()でTrueの数が数えられます - 問4:
np.sort(arr)[::-1]で降順になります
ヒント3 — コード骨格(ほぼ答え)
# 問1
scores_clipped = np.clip(scores, ___, ___)
# 問2
grade = np.where(scores_clipped >= ___, "A", "B")
# 問3
mean_score = np.mean(scores_clipped)
pass_count = (scores_clipped >= ___).sum()
pass_rate = pass_count / len(scores_clipped) * 100
# 問4
scores_sorted = np.sort(scores_clipped)[::___]
✅ 模範解答
import numpy as np
scores = np.array([45, 78, 92, 33, 67, 85, 55, 101, -5, 70])
# 問1: クリッピング
scores_clipped = np.clip(scores, 0, 100)
print("クリッピング後:", scores_clipped)
# → [ 45 78 92 33 67 85 55 100 0 70]
# 問2: 評価分類
grade = np.where(scores_clipped >= 80, "A", "B")
print("評価:", grade)
# → ['B' 'B' 'A' 'B' 'B' 'A' 'B' 'A' 'B' 'B']
# 問3: 統計計算
mean_score = np.mean(scores_clipped)
pass_count = (scores_clipped >= 60).sum()
pass_rate = pass_count / len(scores_clipped) * 100
print(f"平均点: {mean_score:.1f}")
print(f"合格者数: {pass_count}")
print(f"合格率: {pass_rate:.1f}%")
# → 平均点: 62.5
# → 合格者数: 6
# → 合格率: 60.0%
# 問4: 降順ソート
scores_sorted = np.sort(scores_clipped)[::-1]
print("降順:", scores_sorted)
# → [100 92 85 78 70 67 55 45 33 0]
🪜 Step-by-Step 解説
Step 1: np.clip でデータ品質を守る
scores_clipped = np.clip(scores, 0, 100)
101 → 100、-5 → 0 に丸められます。
なぜ clip を使うか? if文 で1要素ずつ処理するより10〜100倍速く、コードも1行で済むからです。
Step 2: np.where で条件分岐を配列全体に適用
grade = np.where(scores_clipped >= 80, "A", "B")
Pythonの通常の if は1つの値にしか使えませんが、np.where は配列全体に同時に適用できます。
Step 3: ブールインデックスで合格者を数える
pass_count = (scores_clipped >= 60).sum()
scores_clipped >= 60 は [F, T, T, F, T, T, F, T, F, T] のようなブール配列を返します。sum() はTrueを1、Falseを0として合計するので、Trueの個数(=合格者数 6)が得られます。
Step 4: スライスで降順ソート
scores_sorted = np.sort(scores_clipped)[::-1]
np.sort は昇順(小→大)で返します。[::-1] は「逆から全部」を意味するスライスで、降順(大→小)にできます。
📊 得点分布(クリッピング後)
クリッピング後の10名の得点を棒グラフで確認しましょう。緑色が合格(60点以上)、グレーが不合格です。
🔢 数学・統計の補足(文系向け)
合格率の計算式:
合格率 = 合格者数 ÷ 全体の人数 × 100
これは「全体の中で条件を満たす割合」を求める基本的な割り算です。numpyでは sum() と len() を組み合わせるだけで計算できます。
平均点 62.5 の求め方: クリッピング後の合計 45+78+92+33+67+85+55+100+0+70 = 625 を人数 10 で割ると 62.5。
🏆 Kaggleでの実践的な使い方
| 操作 | Kaggle場面 | 具体例 |
|---|---|---|
np.clip |
外れ値の制限 | House Prices の面積データ(0〜4000 sqft に制限) |
np.where |
特徴量の二値化 | Titanic:年齢60歳以上 → 高齢者フラグ(1/0) |
| ブールインデックス | 条件フィルタリング | 欠損なし行だけ抽出して統計計算 |
np.sort[::-1] |
ランキング生成 | 予測スコア上位N件を取り出してアンサンブル |
⚠️ よくある誤解・ミス
| 誤解・ミス | なぜ起こるか | 正しい理解 |
|---|---|---|
np.sort が降順だと思う |
"sort"=整列のイメージ | np.sort は常に昇順。降順は [::-1] を付ける |
(arr > 60).sum() の意味が不明 |
ブール配列の概念が未理解 | Trueは1、Falseは0として合計するので条件一致数になる |
np.clip を知らずに if文 でループ |
numpy以前のPython習慣 | numpyには配列全体を一括処理する専用関数がある |
pass_rate の分母に pass_count を使う |
割合の分母を間違える | 分母は全体(len(arr))、分子が部分(合格者数) |
🚀 次のステップ
- 発展: 3段階評価(A/B/C)を実装するには
np.selectが便利。np.searchsortedで区間分類も可能 - 次回予告: pandas入門(DataFrame・Series)— numpy配列をベースにした表形式データ処理