# Pythonデータサイエンス入門: NumPy・Pandas・Scikit-Learnの実践ガイド2026 > Python 3.12とNumPy 2.1、Pandas 2.2、Scikit-Learn 1.6を使ったデータサイエンスの実践チュートリアルです。データ前処理から特徴量エンジニアリング、機械学習パイプラインの構築まで、コード付きで解説します。 - Published: 2026-04-07 - Updated: 2026-04-07 - Author: Anthony Fillion-Maillet - Tags: data-science, python, numpy, pandas, scikit-learn, machine-learning, tutorial - Reading time: 10 min --- 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配列を使用しています。 ```python # 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が異なる形状の配列をどのようにブロードキャストするかを理解する必要があります。ブロードキャストのルールにより、行列とベクトルの間で明示的なタイリングなしに演算を行うことが可能です。 ```python # 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 vector ``` `axis`パラメータは集約の方向を制御します。`axis=0`は行を畳み込み(列方向の操作)、`axis=1`は列を畳み込みます(行方向の操作)。`keepdims=True`引数を指定することで、ブロードキャスト時の形状不一致を防ぐことができます。 ## PandasによるDataFrame操作とデータクリーニング PandasはNumPy配列にラベル付きの軸とSQL風の操作を付加したライブラリです。DataFrameが中心的なデータ構造であり、各列が異なるデータ型を保持できるテーブルとして機能します。Pandas 2.2では、PyArrowバックエンドの文字列カラムがデフォルトとなり、従来のobject型文字列と比較してメモリ使用量が大幅に削減されています。 ```python # 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** > > Pandas 2.2では、Copy-on-Write(CoW)がデフォルトで有効になっています。これにより`SettingWithCopyWarning`が解消され、メソッドチェーンの操作がより安全になります。スライスへの変更が元のDataFrameをサイレントに変更することはなくなり、Pandasは実際に書き込みが発生した場合にのみコピーを作成します。 ## PandasのGroupBy集約と特徴量エンジニアリング GroupByはデータをサブセットに分割し、各サブセットに関数を適用し、結果を結合する操作です。このパターンは、表形式データの機械学習における特徴量エンジニアリングの大部分を担っています。 ```python # 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に結合するという一般的な落とし穴を回避できます。 ## Scikit-Learnパイプラインのゼロからの構築 Scikit-Learnのパイプラインは、前処理ステップとモデルを単一のオブジェクトにチェーンします。パイプラインを使用することで、学習データにのみトランスフォーマーをフィッティングし、同一の変換をテストデータに適用するため、データリーケージを防止できます。 ```python # 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分割では、どのサンプルがどちらのセットに割り当てられるかによって、誤解を招く結果が生じる可能性があります。交差検証は複数の分割を実行してスコアを平均化することで、より信頼性の高いパフォーマンス推定値を提供します。 ```python # 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配列を効率的に処理します。 ```python # 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`によるパイプライン全体のシリアライズにより、学習時と推論時で同一の前処理が保証されます - これらのパターンを[データサイエンスの面接問題](/technologies/data-science)で実践し、コーディング力と統計的基礎の理解の両方を鍛えることを推奨します --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ja/blog/data-science/python-data-science-numpy-pandas-scikit-learn-2026