# Python pour la Data Science : NumPy, Pandas et Scikit-Learn en 2026 > Guide pratique de Python pour la data science en 2026. Couverture complète de NumPy pour le calcul vectorisé, Pandas pour le nettoyage et la transformation de données, et Scikit-Learn pour construire des pipelines de machine learning robustes. - 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 --- Python s'est imposé comme le langage de référence de la data science, non pas par hasard, mais parce que son écosystème de bibliothèques résout des problèmes réels avec une concision remarquable. NumPy, Pandas et Scikit-Learn forment la triade fondamentale que tout data scientist professionnel manipule quotidiennement : NumPy pour le calcul numérique vectorisé, Pandas pour la manipulation et le nettoyage de données tabulaires, Scikit-Learn pour construire, entraîner et déployer des modèles de machine learning. Ce guide parcourt les trois bibliothèques de façon progressive, avec du code directement exécutable, pour aborder les entretiens techniques de 2026 avec une base solide. > **Versions utilisées dans ce guide** > > Les exemples de ce guide utilisent **NumPy 2.0**, **Pandas 2.2** et **Scikit-Learn 1.5**, les versions stables en production au premier trimestre 2026. L'installation se fait via `pip install numpy pandas scikit-learn` ou via un environnement conda. Python 3.11+ est recommandé pour les performances optimales. ## NumPy : le calcul vectorisé au coeur de la data science NumPy est la fondation invisible de l'écosystème data science Python. Pandas, Scikit-Learn et même TensorFlow s'appuient sur ses tableaux multidimensionnels (`ndarray`) pour effectuer des calculs performants. La différence entre un code Python pur avec des boucles `for` et du code NumPy peut atteindre un facteur 100 en termes de vitesse d'exécution, grâce à la vectorisation : les opérations s'appliquent à l'ensemble du tableau en une seule instruction, sans itération explicite. L'exemple suivant illustre les opérations de base : création de tableaux, arithmétique vectorisée, agrégations statistiques et filtrage booléen. ```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] ``` Le filtrage booléen (`prices > 40`) produit un tableau de booléens qu'on utilise comme masque pour sélectionner des éléments. Cette syntaxe est omniprésente dans le code data science : elle remplace des boucles conditionnelles verbeuses par une expression lisible et performante. En entretien, expliquer la différence entre indexation par tableau booléen et indexation par indices entiers est une question fréquente. ## Reshape et broadcasting : maîtriser les dimensions des tableaux Deux opérations reviennent systématiquement lors de la préparation de données pour des modèles : le reshaping (changement de forme d'un tableau) et le broadcasting (propagation automatique d'opérations entre tableaux de formes compatibles). Scikit-Learn exige notamment que les données d'entrée soient des tableaux 2D, ce qui nécessite souvent un `reshape(-1, 1)` pour passer d'un vecteur 1D à une matrice colonne. ```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 ``` L'argument `axis` contrôle la direction de l'agrégation : `axis=0` opère sur les lignes (résultat par colonne), `axis=1` opère sur les colonnes (résultat par ligne). Le paramètre `keepdims=True` est indispensable pour le broadcasting : sans lui, le diviseur perd sa structure matricielle et la division produirait des résultats incorrects. Ces subtilités sont régulièrement testées lors des entretiens techniques en data science. ## Pandas : nettoyage et transformation de données réelles Les données réelles sont rarement propres. Valeurs manquantes, doublons, types incorrects, valeurs aberrantes : chaque jeu de données présente ses propres anomalies. Pandas offre une API expressive pour traiter ces problèmes de manière reproductible. L'approche préconisée en 2026 est le chaînage de méthodes (method chaining) : une seule expression enchaîne toutes les transformations, évitant les variables intermédiaires et facilitant le débogage et la relecture du code. ```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") ``` L'utilisation de `assign` avec des fonctions lambda garantit que chaque transformation s'applique à la colonne dans son état courant dans la chaîne, et non à une variable externe qui pourrait avoir été modifiée entre-temps. La méthode `clip` écrête les valeurs aberrantes sans supprimer les lignes concernées, ce qui préserve le volume de données tout en bornant l'influence des outliers sur les modèles statistiques. ## GroupBy et feature engineering avec Pandas Après le nettoyage, l'étape suivante consiste à créer des variables pertinentes pour le modèle de machine learning. Le `groupby` de Pandas permet de calculer des statistiques agrégées par groupe, puis de les propager sur tout le DataFrame via `transform`. Cette technique est au coeur du [feature engineering pour les entretiens](/technologies/data-science/interview-questions/feature-engineering) : enrichir les données individuelles avec un contexte groupé améliore systématiquement la performance des modèles. ```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), ) ) ``` La variable `salary_ratio` illustre un pattern particulièrement puissant : diviser la valeur individuelle par la moyenne du groupe produit une variable relative qui capture la position de chaque candidat dans son département, indépendamment des écarts salariaux inter-départements. Ce type de feature améliore systématiquement la performance des modèles de classification sur des données RH ou commerciales. La colonne `tenure_days` encode l'ancienneté sous forme numérique continue, bien plus exploitable qu'une date brute pour un algorithme de machine learning. > **Feature engineering et classification supervisée** > > Le DataFrame `df_features` construit ici sert directement d'entrée aux [tâches d'apprentissage supervisé](/technologies/data-science/interview-questions/ml-supervised-classification). La qualité du feature engineering détermine souvent davantage la performance finale que le choix de l'algorithme Scikit-Learn lui-même. Un feature engineering soigné avec un modèle simple surpasse fréquemment un modèle complexe appliqué à des données brutes. ## Scikit-Learn : construire un pipeline de machine learning robuste Scikit-Learn unifie l'interface de tous ses algorithmes sous un contrat commun : `fit`, `predict`, `transform`. Son atout le plus sous-estimé est la classe `Pipeline`, qui enchaîne preprocessing et modélisation en un objet unique. Cette approche élimine le data leakage — la fuite de statistiques du jeu d'entraînement vers le jeu de test — garantit la reproductibilité et simplifie considérablement le déploiement en production. Le `ColumnTransformer` permet d'appliquer des transformations différentes aux colonnes numériques (standardisation) et catégorielles (encodage one-hot) en une seule étape déclarative, sans dupliquer la logique de preprocessing. ```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), # Scale numeric columns ("cat", OneHotEncoder(handle_unknown="ignore"), categorical_features), # Encode categories ] ) 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)) ``` L'argument `stratify=y` dans `train_test_split` est crucial sur des classes déséquilibrées : il garantit que la proportion de promotions dans le jeu de test reflète celle de l'ensemble complet. Sans stratification, le jeu de test pourrait contenir proportionnellement beaucoup plus ou moins de cas positifs que le jeu d'entraînement, faussant l'évaluation. Le paramètre `handle_unknown="ignore"` dans `OneHotEncoder` protège le pipeline des catégories inconnues à l'inférence, un problème courant en production. ## Validation croisée et optimisation des hyperparamètres L'évaluation d'un modèle sur le même jeu de test pour choisir ses hyperparamètres constitue une forme de data leakage. La validation croisée (cross-validation) résout ce problème en évaluant les performances sur plusieurs sous-ensembles disjoints du jeu d'entraînement. Le `GridSearchCV` automatise la recherche exhaustive des meilleures combinaisons d'hyperparamètres via validation croisée, et s'applique directement au pipeline complet — preprocessing inclus. ```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, # Use all CPU cores 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))) ``` La notation `classifier__n_estimators` (double underscore) est la convention Scikit-Learn pour accéder aux paramètres d'une étape imbriquée dans un pipeline. L'argument `n_jobs=-1` parallélise la recherche sur tous les coeurs CPU disponibles, réduisant considérablement le temps d'exécution. Le score F1 est préféré à l'accuracy car le jeu de données de promotions est vraisemblablement déséquilibré — un aspect central des [questions d'entretien en machine learning](/blog/data-science/machine-learning-algorithms-explained). > **Ne jamais évaluer sur le jeu de test avant la sélection du modèle final** > > Une erreur classique consiste à consulter les scores sur `X_test` pendant la phase de tuning pour orienter le choix des hyperparamètres. Cela introduit un data leakage indirect : le modèle final devient optimiste car il a, de façon détournée, été sélectionné en fonction des données de test. Le jeu de test doit être utilisé **une seule fois**, uniquement pour l'évaluation finale du meilleur modèle sélectionné par cross-validation sur le jeu d'entraînement. ## Sérialisation et déploiement du pipeline Un modèle entraîné n'a de valeur que s'il peut être utilisé en production. La bibliothèque `joblib` sérialise le pipeline complet — preprocessing et modèle — en un seul fichier binaire. Cette approche garantit que les transformations appliquées lors de l'entraînement sont automatiquement reproduites lors de l'inférence, sans aucune duplication de code et sans risque d'inconsistance entre les environnements. ```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%}") ``` La méthode `predict_proba` retourne les probabilités de classe, pas seulement la décision binaire. En contexte RH, la confiance du modèle est souvent plus exploitable que la prédiction brute : un score de 62% mérite une revue humaine, là où un score de 94% peut justifier une action automatisée. Cette distinction entre classification dure et probabilité calibrée est une notion essentielle pour les [questions d'entretien en data science](/technologies/data-science), notamment lorsque les coûts d'erreur sont asymétriques. ## Conclusion La maîtrise de NumPy, Pandas et Scikit-Learn constitue le socle technique attendu pour tout rôle de data scientist ou ML engineer en 2026. Ces trois bibliothèques couvrent la totalité du cycle de vie d'un projet data : du calcul numérique brut à la mise en production d'un modèle, en passant par le nettoyage et la transformation des données. Les points essentiels à retenir : - **NumPy vectorisation** : préférer systématiquement les opérations vectorisées aux boucles Python. Maîtriser `axis`, `keepdims` et le broadcasting pour manipuler des tableaux multidimensionnels sans erreur de dimension. - **Pandas method chaining** : enchaîner les transformations avec `assign`, `query`, `drop_duplicates` et `reset_index` produit un code lisible et reproductible, sans effets de bord entre étapes. - **Feature engineering** : `groupby` + `transform` est le pattern clé pour enrichir les données individuelles avec des statistiques de groupe. La qualité des features prime généralement sur le choix de l'algorithme. - **Scikit-Learn Pipeline** : toujours encapsuler preprocessing et modèle dans un `Pipeline` combiné à un `ColumnTransformer` pour éliminer le data leakage et garantir la cohérence entre entraînement et inférence. - **Cross-validation** : sélectionner les hyperparamètres exclusivement via `cross_val_score` et `GridSearchCV` sur le jeu d'entraînement ; utiliser `stratify` sur les classes déséquilibrées pour des évaluations fiables. - **Déploiement** : `joblib.dump` sérialise le pipeline complet. `predict_proba` fournit des scores de confiance exploitables, plus utiles que des prédictions binaires dans la plupart des contextes décisionnels. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/fr/blog/data-science/python-data-science-numpy-pandas-scikit-learn-2026