Pythonデータサイエンス入門: NumPy・Pandas・Scikit-Learnの実践ガイド2026
Python 3.12とNumPy 2.1、Pandas 2.2、Scikit-Learn 1.6を使ったデータサイエンスの実践チュートリアルです。データ前処理から特徴量エンジニアリング、機械学習パイプラインの構築まで、コード付きで解説します。

2026年のPythonデータサイエンスにおいて、実務の90%をカバーするライブラリは3つに集約されます。数値計算のNumPy、表形式データ操作のPandas、そして機械学習のScikit-Learnです。本記事では、それぞれのライブラリの基本操作を実践的なコードとともに解説し、最終的に生データの読み込みからクリーニング、特徴量エンジニアリング、モデル学習までを一貫して行うパイプラインを構築します。
すべてのコードはPython 3.12以降、NumPy 2.1、Pandas 2.2、Scikit-Learn 1.6で動作確認済みです。マイナーバージョン間でAPIの互換性は維持されていますが、NumPy 2.xでは大規模配列操作のパフォーマンスが大幅に改善されているため、アップグレードを推奨します。
NumPyの配列操作による高速な数値計算
NumPyは、Pythonのリストを連続したメモリブロックに格納されるn次元配列に置き換えます。このパフォーマンス差は非常に大きく、100万要素に対するNumPyのベクトル化演算は、等価なPythonループと比較して50〜100倍高速に動作します。PandasのDataFrameやScikit-Learnのモデルも、内部的にはすべてNumPy配列を使用しています。
# numpy_basics.py
import numpy as np
# Create arrays from different sources
prices = np.array([29.99, 49.99, 19.99, 99.99, 39.99])
quantities = np.arange(1, 6) # [1, 2, 3, 4, 5]
# Vectorized arithmetic — no loops needed
revenue = prices * quantities
print(revenue) # [29.99, 99.98, 59.97, 399.96, 199.95]
# Statistical aggregations
print(f"Total revenue: ${revenue.sum():.2f}") # $789.85
print(f"Mean price: ${prices.mean():.2f}") # $47.99
print(f"Std deviation: ${prices.std():.2f}") # $27.64
# Boolean indexing — filter without loops
premium_mask = prices > 40
premium_items = prices[premium_mask] # [49.99, 99.99]ブールインデックスは、実際のデータサイエンスコードで最も頻繁に登場するパターンです。ループ内でif文を記述する代わりに、ブールマスクを使うことで条件を満たす要素を1回の操作で抽出できます。
NumPyのリシェイプとブロードキャスト
2次元以上の操作では、NumPyが異なる形状の配列をどのようにブロードキャストするかを理解する必要があります。ブロードキャストのルールにより、行列とベクトルの間で明示的なタイリングなしに演算を行うことが可能です。
# numpy_reshape.py
import numpy as np
# Monthly sales data: 4 products x 3 months
sales = np.array([
[120, 150, 130], # Product A
[200, 180, 220], # Product B
[90, 110, 95], # Product C
[300, 280, 310], # Product D
])
# Column-wise mean (average per month)
monthly_avg = sales.mean(axis=0) # [177.5, 180.0, 188.75]
# Row-wise sum (total per product)
product_totals = sales.sum(axis=1) # [400, 600, 295, 890]
# Normalize each product relative to its own max
normalized = sales / sales.max(axis=1, keepdims=True)
# keepdims=True preserves the shape for broadcasting
print(normalized[0]) # [0.8, 1.0, 0.867] — Product A relative to its peak
# Reshape for Scikit-Learn (requires 2D input)
flat_sales = sales.flatten() # 1D array of 12 values
reshaped = flat_sales.reshape(-1, 1) # 12x1 column vectoraxisパラメータは集約の方向を制御します。axis=0は行を畳み込み(列方向の操作)、axis=1は列を畳み込みます(行方向の操作)。keepdims=True引数を指定することで、ブロードキャスト時の形状不一致を防ぐことができます。
PandasによるDataFrame操作とデータクリーニング
PandasはNumPy配列にラベル付きの軸とSQL風の操作を付加したライブラリです。DataFrameが中心的なデータ構造であり、各列が異なるデータ型を保持できるテーブルとして機能します。Pandas 2.2では、PyArrowバックエンドの文字列カラムがデフォルトとなり、従来のobject型文字列と比較してメモリ使用量が大幅に削減されています。
# pandas_cleaning.py
import pandas as pd
import numpy as np
# Load and inspect raw data
df = pd.read_csv("candidates.csv")
print(df.shape) # (1500, 8)
print(df.dtypes) # Check column types
print(df.isna().sum()) # Count missing values per column
# Clean in a reproducible chain
df_clean = (
df
.dropna(subset=["salary", "experience_years"]) # Drop rows missing critical fields
.assign(
salary=lambda x: x["salary"].clip(lower=20000, upper=500000), # Cap outliers
experience_years=lambda x: x["experience_years"].astype(int),
hired_date=lambda x: pd.to_datetime(x["hired_date"], errors="coerce"),
)
.drop_duplicates(subset=["email"]) # Remove duplicate candidates
.query("experience_years >= 0") # Filter invalid entries
.reset_index(drop=True)
)
print(f"Cleaned: {len(df)} -> {len(df_clean)} rows").assign()を使ったメソッドチェーンにより、各変換処理を可読性高く再現可能な形で記述できます。各ステップが何をどのような理由で変更するのかが明確に文書化されます。.assign()内のlambda x:パターンは、チェーン内のその時点でのDataFrameを参照するため、古いデータへの参照を回避できます。
Pandas 2.2では、Copy-on-Write(CoW)がデフォルトで有効になっています。これによりSettingWithCopyWarningが解消され、メソッドチェーンの操作がより安全になります。スライスへの変更が元のDataFrameをサイレントに変更することはなくなり、Pandasは実際に書き込みが発生した場合にのみコピーを作成します。
PandasのGroupBy集約と特徴量エンジニアリング
GroupByはデータをサブセットに分割し、各サブセットに関数を適用し、結果を結合する操作です。このパターンは、表形式データの機械学習における特徴量エンジニアリングの大部分を担っています。
# pandas_groupby.py
import pandas as pd
# Aggregate candidate stats by department
dept_stats = (
df_clean
.groupby("department")
.agg(
avg_salary=("salary", "mean"),
median_experience=("experience_years", "median"),
headcount=("email", "count"),
max_salary=("salary", "max"),
)
.sort_values("avg_salary", ascending=False)
)
print(dept_stats.head())
# Create features for ML: encode categorical + add aggregated stats
df_features = (
df_clean
.assign(
# Ratio of individual salary to department average
salary_ratio=lambda x: x["salary"] / x.groupby("department")["salary"].transform("mean"),
# Time since hire in days
tenure_days=lambda x: (pd.Timestamp.now() - x["hired_date"]).dt.days,
# Binary encoding
is_senior=lambda x: (x["experience_years"] >= 5).astype(int),
)
).transform()メソッドは元のインデックスに揃えたSeriesを返すため、.assign()内で安全に使用できます。これにより、集約結果を手動で元のDataFrameに結合するという一般的な落とし穴を回避できます。
Data Science & MLの面接対策はできていますか?
インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。
Scikit-Learnパイプラインのゼロからの構築
Scikit-Learnのパイプラインは、前処理ステップとモデルを単一のオブジェクトにチェーンします。パイプラインを使用することで、学習データにのみトランスフォーマーをフィッティングし、同一の変換をテストデータに適用するため、データリーケージを防止できます。
# sklearn_pipeline.py
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import classification_report
import pandas as pd
# Define column groups
numeric_features = ["salary", "experience_years", "salary_ratio", "tenure_days"]
categorical_features = ["department", "role_level"]
target = "promoted"
# Split before any preprocessing
X = df_features[numeric_features + categorical_features]
y = df_features[target]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# Build the preprocessing + model pipeline
preprocessor = ColumnTransformer(
transformers=[
("num", StandardScaler(), numeric_features),
("cat", OneHotEncoder(handle_unknown="ignore"), categorical_features),
]
)
pipeline = Pipeline([
("preprocessor", preprocessor),
("classifier", GradientBoostingClassifier(
n_estimators=200,
learning_rate=0.1,
max_depth=4,
random_state=42,
)),
])
# Train and evaluate
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
print(classification_report(y_test, y_pred))ColumnTransformerは、異なるカラム型に対して異なる前処理を1ステップで適用します。StandardScalerは数値特徴量を平均0、分散1に正規化します。OneHotEncoderはカテゴリ文字列をバイナリカラムに変換します。handle_unknown="ignore"パラメータを指定することで、テストデータに学習データに存在しないカテゴリが含まれていてもパイプラインがエラーを起こさなくなります。
交差検証とハイパーパラメータチューニング
単一のtrain/test分割では、どのサンプルがどちらのセットに割り当てられるかによって、誤解を招く結果が生じる可能性があります。交差検証は複数の分割を実行してスコアを平均化することで、より信頼性の高いパフォーマンス推定値を提供します。
# sklearn_tuning.py
from sklearn.model_selection import cross_val_score, GridSearchCV
import numpy as np
# 5-fold cross-validation on the full pipeline
scores = cross_val_score(pipeline, X_train, y_train, cv=5, scoring="f1")
print(f"F1 scores: {scores}")
print(f"Mean F1: {scores.mean():.3f} (+/- {scores.std() * 2:.3f})")
# Grid search over hyperparameters
param_grid = {
"classifier__n_estimators": [100, 200, 300],
"classifier__max_depth": [3, 4, 5],
"classifier__learning_rate": [0.05, 0.1, 0.2],
}
grid_search = GridSearchCV(
pipeline,
param_grid,
cv=5,
scoring="f1",
n_jobs=-1,
verbose=1,
)
grid_search.fit(X_train, y_train)
print(f"Best params: {grid_search.best_params_}")
print(f"Best F1: {grid_search.best_score_:.3f}")
# Evaluate the best model on held-out test set
best_model = grid_search.best_estimator_
print(classification_report(y_test, best_model.predict(X_test)))ダブルアンダースコア構文(classifier__max_depth)は、パイプライン内のネストされたパラメータを参照します。n_jobs=-1を指定すると、利用可能なすべてのCPUコアで検索が並列化されます。
データセット全体にスケーラーをフィッティングしてから分割すると、検証フォールドの情報が学習フォールドに漏洩します。スケーラーをPipeline内に配置することで、各フォールドの学習部分のみでスケーリングがフィッティングされることが保証されます。これは機械学習の面接で最も頻繁に問われるミスの一つです。
モデルの保存と本番環境へのデプロイ
学習済みパイプラインは、デプロイのためにシリアライズする必要があります。joblibライブラリは、Pythonの組み込みpickleよりもNumPy配列を効率的に処理します。
# sklearn_export.py
import joblib
from pathlib import Path
# Save the complete pipeline (preprocessor + model)
model_dir = Path("models")
model_dir.mkdir(exist_ok=True)
joblib.dump(best_model, model_dir / "promotion_model_v1.joblib")
# Load and predict in a different process
loaded_model = joblib.load(model_dir / "promotion_model_v1.joblib")
new_data = pd.DataFrame({
"salary": [75000],
"experience_years": [4],
"salary_ratio": [1.05],
"tenure_days": [730],
"department": ["Engineering"],
"role_level": ["Mid"],
})
prediction = loaded_model.predict(new_data)
probability = loaded_model.predict_proba(new_data)[:, 1]
print(f"Promoted: {bool(prediction[0])}, Confidence: {probability[0]:.2%}")モデル単体ではなくパイプライン全体を保存することで、推論時にも学習時と同一の前処理ステップが確実に適用されます。
まとめ
- NumPyのベクトル化演算は、Pythonループを50〜100倍高速な配列計算に置き換えます。ブールインデックスとブロードキャストにより、フィルタリングや変換の大部分を処理できます
- Pandasの
.assign()と.transform()を用いたメソッドチェーンは、可読性と再現性の高いデータクリーニングパイプラインを実現します。Pandas 2.2のCopy-on-Writeにより、サイレントなデータ変異バグが解消されます - Scikit-LearnのPipelineは前処理とモデリングを単一のオブジェクトにまとめ、学習データとテストデータ間のデータリーケージを防止します
GridSearchCVを用いた交差検証は、信頼性の高いパフォーマンス推定と、すべてのCPUコアを活用した自動ハイパーパラメータチューニングを提供しますjoblibによるパイプライン全体のシリアライズにより、学習時と推論時で同一の前処理が保証されます- これらのパターンをデータサイエンスの面接問題で実践し、コーディング力と統計的基礎の理解の両方を鍛えることを推奨します
今すぐ練習を始めましょう!
面接シミュレーターと技術テストで知識をテストしましょう。
タグ
共有

