Skip to content

Pandas fillna() : Gérer les valeurs manquantes dans les DataFrames

Updated on

Les valeurs manquantes sont le saboteur silencieux de l'analyse de données. Un seul NaN caché dans une colonne critique peut faire qu'une agrégation renvoie NaN, qu'un modèle de machine learning lance une erreur lors de l'entraînement, ou qu'un graphique de tableau de bord affiche un espace vide là où devrait se trouver une ligne de tendance. Les jeux de données du monde réel contiennent presque toujours des lacunes -- les relevés de capteurs s'interrompent, les répondants à des enquêtes sautent des questions, les réponses d'API renvoient des champs nuls, et les exports CSV arrivent avec des cellules vides. La question n'est jamais de savoir si vous rencontrerez des données manquantes, mais comment vous les gérerez.

La méthode fillna() de pandas est l'outil principal pour remplacer les valeurs manquantes par quelque chose de significatif. Ce guide couvre chaque paramètre, démontre les stratégies de remplissage courantes (scalaire, dictionnaire, forward fill, backward fill, moyenne/médiane/mode), compare fillna() à dropna() et interpolate(), et montre comment chaîner ces opérations dans un pipeline de données propre. Chaque exemple de code est prêt à copier avec la sortie attendue.

📚

Détecter les valeurs manquantes avant de les remplir

Avant de remplir quoi que ce soit, vous devez savoir où se trouvent les lacunes. Pandas fournit trois fonctions de détection :

import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    'name': ['Alice', 'Bob', None, 'Diana', 'Eve'],
    'age': [28, np.nan, 35, np.nan, 42],
    'salary': [55000, 62000, np.nan, 48000, np.nan]
})
 
# isna() renvoie True pour les valeurs manquantes (alias : isnull())
print(df.isna())

Sortie :

    name    age  salary
0  False  False   False
1  False   True   False
2   True  False    True
3  False   True   False
4  False  False    True

Résumé rapide des comptages de valeurs manquantes

# Compter les valeurs manquantes par colonne
print(df.isna().sum())

Sortie :

name      1
age       2
salary    2
dtype: int64

notna() pour la vérification inverse

# notna() renvoie True pour les valeurs non manquantes
print(df.notna().sum())

Sortie :

name      4
age       3
salary    3
dtype: int64
FonctionRenvoie True quandAlias
isna()La valeur est NaN, None ou NaTisnull()
notna()La valeur n'est pas manquantenotnull()

Ces fonctions fonctionnent aussi bien sur les DataFrames que sur les Series individuelles. Utilisez-les pour auditer vos données avant de décider d'une stratégie de remplissage.

fillna() basique avec une valeur scalaire

L'utilisation la plus simple de fillna() remplace chaque NaN du DataFrame par une seule valeur :

import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    'product': ['Widget', 'Gadget', 'Gizmo'],
    'price': [19.99, np.nan, 29.99],
    'stock': [100, 50, np.nan]
})
 
print("Avant :")
print(df)
 
df_filled = df.fillna(0)
 
print("\nAprès fillna(0) :")
print(df_filled)

Sortie :

Avant :
  product  price  stock
0  Widget  19.99  100.0
1  Gadget    NaN   50.0
2   Gizmo  29.99    NaN

Après fillna(0) :
  product  price  stock
0  Widget  19.99  100.0
1  Gadget   0.00   50.0
2   Gizmo  29.99    0.0

Cela fonctionne, mais remplir une colonne de prix avec 0 est trompeur -- cela suggère que le produit est gratuit. Pour les colonnes de texte, vous pourriez remplir avec "Inconnu". La clé est de choisir une valeur de remplissage qui a un sens sémantique pour chaque colonne.

Signature complète de la méthode

DataFrame.fillna(value=None, method=None, axis=None, inplace=False, limit=None)
ParamètreTypeDéfautDescription
valuescalaire, dict, Series ou DataFrameNoneLa valeur pour remplir les entrées manquantes
method'ffill', 'bfill' ou NoneNoneMéthode de propagation pour remplir les lacunes
axis0 ou 1NoneRemplir le long des lignes (0) ou des colonnes (1)
inplaceboolFalseSi True, modifie le DataFrame sur place
limitintNoneNombre maximum de NaN consécutifs à remplir

fillna() avec un dictionnaire : Différentes valeurs par colonne

Dans la plupart des jeux de données réels, chaque colonne représente un type de mesure différent, et une seule valeur de remplissage n'a pas de sens partout. Passez un dictionnaire à fillna() pour spécifier les valeurs de remplissage par colonne :

import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    'name': ['Alice', None, 'Charlie', 'Diana'],
    'age': [28, 34, np.nan, 45],
    'department': ['Engineering', 'Sales', None, 'Marketing'],
    'salary': [75000, np.nan, 68000, np.nan]
})
 
fill_values = {
    'name': 'Unknown',
    'age': df['age'].median(),
    'department': 'Unassigned',
    'salary': df['salary'].mean()
}
 
df_filled = df.fillna(fill_values)
print(df_filled)

Sortie :

      name   age   department   salary
0    Alice  28.0  Engineering  75000.0
1  Unknown  34.0        Sales  71500.0
2  Charlie  34.0   Unassigned  68000.0
3    Diana  45.0    Marketing  71500.0

C'est l'approche recommandée pour les pipelines de données en production car elle vous donne un contrôle explicite sur ce que chaque colonne reçoit.

Forward Fill (ffill) et Backward Fill (bfill)

Les données de séries temporelles et les jeux de données ordonnés bénéficient souvent d'un remplissage basé sur la propagation. Le forward fill transmet la dernière valeur connue vers l'avant ; le backward fill prend la prochaine valeur connue vers l'arrière.

import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    'date': pd.date_range('2026-01-01', periods=7, freq='D'),
    'temperature': [22.1, np.nan, np.nan, 24.5, np.nan, 26.0, np.nan]
})
 
print("Original :")
print(df)
 
print("\nForward fill (ffill) :")
print(df.fillna(method='ffill'))
 
print("\nBackward fill (bfill) :")
print(df.fillna(method='bfill'))

Sortie :

Original :
        date  temperature
0 2026-01-01         22.1
1 2026-01-02          NaN
2 2026-01-03          NaN
3 2026-01-04         24.5
4 2026-01-05          NaN
5 2026-01-06         26.0
6 2026-01-07          NaN

Forward fill (ffill) :
        date  temperature
0 2026-01-01         22.1
1 2026-01-02         22.1
2 2026-01-03         22.1
3 2026-01-04         24.5
4 2026-01-05         24.5
5 2026-01-06         26.0
6 2026-01-07         26.0

Backward fill (bfill) :
        date  temperature
0 2026-01-01         22.1
1 2026-01-02         24.5
2 2026-01-03         24.5
3 2026-01-04         24.5
4 2026-01-05         26.0
5 2026-01-06         26.0
6 2026-01-07          NaN

Remarquez que le backward fill laisse la dernière ligne comme NaN car il n'y a pas de valeur suivante à extraire. Vous pouvez combiner les deux méthodes pour combler toutes les lacunes :

df_filled = df.fillna(method='ffill').fillna(method='bfill')
print(df_filled)

À partir de pandas 2.1, vous pouvez également utiliser directement les méthodes autonomes df.ffill() et df.bfill(), qui sont des raccourcis pour fillna(method='ffill') et fillna(method='bfill').

Limiter la propagation avec limit

Quand un capteur tombe en panne pendant des jours, le forward fill indéfini peut masquer de véritables lacunes dans les données. Le paramètre limit plafonne le nombre de NaN consécutifs qui sont remplis :

import pandas as pd
import numpy as np
 
s = pd.Series([1.0, np.nan, np.nan, np.nan, 5.0])
 
print("limit=1 :")
print(s.fillna(method='ffill', limit=1))
 
print("\nlimit=2 :")
print(s.fillna(method='ffill', limit=2))

Sortie :

limit=1 :
0    1.0
1    1.0
2    NaN
3    NaN
4    5.0
dtype: float64

limit=2 :
0    1.0
1    1.0
2    1.0
3    NaN
4    5.0
dtype: float64

C'est essentiel pour les données de séries temporelles où vous voulez combler les petites lacunes mais signaler les pannes plus longues pour une révision manuelle.

fillna() avec moyenne, médiane et mode

L'imputation statistique remplace les valeurs manquantes par une statistique résumée calculée à partir des valeurs non manquantes de cette colonne. C'est la stratégie la plus courante pour les caractéristiques numériques avant d'alimenter un modèle en données :

import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    'math_score': [85, np.nan, 92, 78, np.nan, 88],
    'reading_score': [np.nan, 76, 81, np.nan, 90, 85],
    'grade': ['A', 'B', 'A', np.nan, 'B', np.nan]
})
 
# Remplir les colonnes numériques avec leur moyenne de colonne
df['math_score'] = df['math_score'].fillna(df['math_score'].mean())
df['reading_score'] = df['reading_score'].fillna(df['reading_score'].median())
 
# Remplir la colonne catégorielle avec le mode (valeur la plus fréquente)
df['grade'] = df['grade'].fillna(df['grade'].mode()[0])
 
print(df)

Sortie :

   math_score  reading_score grade
0       85.00          83.00     A
1       85.75          76.00     B
2       92.00          81.00     A
3       78.00          83.00     A
4       85.75          90.00     B
5       88.00          85.00     A
StratégieIdéal pourNotes
mean()Données numériques avec des distributions approximativement symétriquesSensible aux valeurs aberrantes
median()Données numériques avec des distributions asymétriques ou des valeurs aberrantesPlus robuste que la moyenne
mode()Données catégorielles ou valeurs numériques discrètesRenvoie la valeur la plus courante ; mode()[0] prend la première en cas d'égalité

Pour les pipelines de machine learning, envisagez d'utiliser sklearn.impute.SimpleImputer qui s'intègre aux pipelines scikit-learn et gère correctement l'imputation dans la séparation train/test.

interpolate() pour les données numériques

Quand les données suivent une tendance (prix des actions, relevés de capteurs, métriques de croissance), interpolate() estime les valeurs manquantes en se basant sur les points de données environnants plutôt qu'en utilisant un remplissage plat :

import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    'day': range(1, 8),
    'revenue': [1000, np.nan, np.nan, 1600, np.nan, 2000, np.nan]
})
 
df['fillna_ffill'] = df['revenue'].fillna(method='ffill')
df['interpolated'] = df['revenue'].interpolate(method='linear')
 
print(df)

Sortie :

   day  revenue  fillna_ffill  interpolated
0    1   1000.0        1000.0        1000.0
1    2      NaN        1000.0        1200.0
2    3      NaN        1000.0        1400.0
3    4   1600.0        1600.0        1600.0
4    5      NaN        1600.0        1800.0
5    6   2000.0        2000.0        2000.0
6    7      NaN        2000.0        2000.0

Remarquez comment interpolate() produit une progression linéaire lisse (1000, 1200, 1400, 1600, 1800, 2000) tandis que ffill crée des plateaux plats. Pandas prend en charge plusieurs méthodes d'interpolation :

MéthodeDescription
'linear'Par défaut. Trace une ligne droite entre les points connus.
'time'Interpolation linéaire pondérée par l'index temporel.
'index'Utilise les valeurs numériques réelles de l'index.
'polynomial'Ajuste un polynôme de l'ordre spécifié.
'spline'Ajuste une spline de l'ordre spécifié pour des courbes lisses.

Utilisez interpolate() quand les données ont un ordre naturel et une tendance. Utilisez fillna() quand vous avez une valeur de remplacement connue ou avez besoin d'un remplissage basé sur la propagation.

Le paramètre inplace

Comme la plupart des méthodes pandas, fillna() renvoie un nouveau DataFrame par défaut. Définir inplace=True modifie l'original :

import pandas as pd
import numpy as np
 
df = pd.DataFrame({'a': [1, np.nan, 3], 'b': [np.nan, 5, 6]})
 
# Méthode 1 : assignation (recommandé)
df_new = df.fillna(0)
print(f"Original inchangé : {df.isna().sum().sum()} NaNs")
print(f"Nouvelle copie : {df_new.isna().sum().sum()} NaNs")
 
# Méthode 2 : inplace (modifie l'original)
df.fillna(0, inplace=True)
print(f"Après inplace : {df.isna().sum().sum()} NaNs")

Sortie :

Original inchangé : 2 NaNs
Nouvelle copie : 0 NaNs
Après inplace : 0 NaNs

La bonne pratique moderne de pandas favorise l'assignation plutôt que inplace=True car l'assignation fonctionne naturellement dans les chaînes de méthodes et rend le flux de données explicite.

Comparaison : fillna() vs dropna() vs interpolate()

Le choix de la bonne stratégie pour les données manquantes dépend de votre jeu de données, du schéma de données manquantes et de votre cas d'utilisation en aval. Voici une comparaison côte à côte :

Aspectfillna()dropna()interpolate()
Ce que ça faitRemplace NaN par une valeur spécifiéeSupprime les lignes ou colonnes contenant NaNEstime NaN à partir des valeurs environnantes
Nombre de lignesPréservéRéduitPréservé
Idéal pourValeurs de remplacement connues, données catégorielles, imputation statistiquePetit pourcentage de lignes manquantes, ou quand l'imputation fausserait l'analyseDonnées numériques ordonnées/séries temporelles avec tendance naturelle
RisqueIntroduit un biais si la valeur de remplissage est mal choisiePerd des données ; peut biaiser les résultats si les données ne manquent pas aléatoirementSuppose un schéma sous-jacent lisse qui peut ne pas exister
Cas d'utilisation typiqueRemplir les réponses d'enquête manquantes avec "Pas de réponse", remplir les prix avec la moyenne de la colonneSupprimer les lignes sans variable cible avant l'entraînement du modèleCombler les lacunes dans les prix quotidiens d'actions ou les relevés de température
Gère les données catégoriellesOuiOui (par suppression)Non (numérique uniquement)
Compatible avec le chaînageOuiOuiOui

Règle empirique pour la décision :

  1. Si moins de 5% des lignes manquent et que les données manquent complètement au hasard, dropna() est sûr.
  2. Si vous avez une valeur par défaut significative ou pouvez calculer une statistique raisonnable, utilisez fillna().
  3. Si les données sont ordonnées et numériques avec une tendance, utilisez interpolate().

fillna() sur des colonnes spécifiques

Vous ne voulez pas toujours remplir l'ensemble du DataFrame. Appliquez fillna() à des colonnes individuelles ou à un sous-ensemble :

import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    'city': ['NYC', None, 'LA', None, 'Chicago'],
    'temperature': [32.1, np.nan, 75.3, np.nan, 28.5],
    'humidity': [45, 60, np.nan, np.nan, 55]
})
 
# Remplir uniquement la colonne ville
df['city'] = df['city'].fillna('Unknown')
 
# Remplir uniquement la colonne température avec sa moyenne
df['temperature'] = df['temperature'].fillna(df['temperature'].mean())
 
# Laisser les NaN d'humidité intacts pour l'instant
print(df)

Sortie :

      city  temperature  humidity
0      NYC    32.100000      45.0
1  Unknown    45.300000      60.0
2       LA    75.300000       NaN
3  Unknown    45.300000       NaN
4  Chicago    28.500000      55.0

Cette approche sélective est importante quand différentes colonnes nécessitent un traitement différent -- ou quand certaines valeurs manquantes sont intentionnelles (par exemple, l'humidité pourrait ne pas s'appliquer aux mesures intérieures).

Chaîner fillna() avec d'autres opérations

Le chaînage de méthodes pandas permet de construire des pipelines de données lisibles. fillna() s'intègre naturellement dans ces chaînes :

import pandas as pd
import numpy as np
 
raw = pd.DataFrame({
    'customer_id': [101, 102, 101, 103, 102, 104],
    'purchase': [25.0, np.nan, 30.0, np.nan, 15.0, np.nan],
    'channel': ['web', 'store', None, 'web', None, 'store']
})
 
result = (
    raw
    .fillna({'purchase': 0, 'channel': 'unknown'})
    .drop_duplicates(subset=['customer_id'], keep='first')
    .sort_values('customer_id')
    .reset_index(drop=True)
)
 
print(result)

Sortie :

   customer_id  purchase  channel
0          101      25.0      web
1          102       0.0    store
2          103       0.0      web
3          104       0.0    store

Ce pipeline remplit les valeurs manquantes, déduplique par identifiant client, trie et réinitialise l'index en une seule expression lisible.

Pipeline concret : Nettoyage des données de ventes

Voici une chaîne plus réaliste qui combine plusieurs étapes de nettoyage :

import pandas as pd
import numpy as np
 
sales = pd.DataFrame({
    'date': ['2026-01-01', '2026-01-02', '2026-01-03', '2026-01-04', '2026-01-05'],
    'product': ['Widget', None, 'Widget', 'Gadget', None],
    'units': [10, np.nan, 15, np.nan, 8],
    'unit_price': [9.99, 9.99, np.nan, 14.99, np.nan],
    'region': ['East', 'East', None, 'West', 'West']
})
 
clean = (
    sales
    .assign(date=lambda d: pd.to_datetime(d['date']))
    .fillna({
        'product': 'Unknown',
        'region': 'Unassigned',
        'units': sales['units'].median(),
        'unit_price': sales['unit_price'].median()
    })
    .assign(total=lambda d: d['units'] * d['unit_price'])
    .sort_values('date')
    .reset_index(drop=True)
)
 
print(clean)

Sortie :

        date  product  units  unit_price      region   total
0 2026-01-01   Widget   10.0        9.99        East   99.90
1 2026-01-02  Unknown   10.0        9.99        East   99.90
2 2026-01-03   Widget   15.0        9.99  Unassigned  149.85
3 2026-01-04   Gadget   10.0       14.99        West  149.90
4 2026-01-05  Unknown    8.0        9.99        West   79.92

Les appels assign() créent ou transforment des colonnes, fillna() gère les lacunes, et la chaîne s'écoule de haut en bas dans un ordre logique.

Visualiser les patterns de données manquantes avec PyGWalker

Avant de choisir une stratégie de remplissage, il est utile de voir où les valeurs manquantes sont concentrées. Sont-elles dispersées aléatoirement, regroupées dans certaines colonnes, ou corrélées avec des périodes spécifiques ? L'inspection visuelle révèle souvent des patterns que les statistiques résumées manquent.

PyGWalker (opens in a new tab) est une bibliothèque Python open source qui transforme n'importe quel DataFrame pandas en une interface de visualisation interactive de type Tableau directement dans Jupyter Notebook. Vous pouvez glisser-déposer des colonnes sur les axes, changer les types de graphiques et filtrer les données par clics au lieu d'écrire du code matplotlib répétitif.

import pandas as pd
import pygwalker as pyg
 
# Chargez vos données et marquez les patterns manquants
df = pd.read_csv('your_data.csv')
 
# Ajouter une colonne comptant les valeurs manquantes par ligne
df['missing_count'] = df.isna().sum(axis=1)
 
# Lancer l'explorateur interactif
walker = pyg.walk(df)

Dans l'interface PyGWalker, vous pouvez créer des diagrammes en barres montrant le comptage des valeurs manquantes par colonne, des cartes de chaleur révélant quelles lignes ont le plus de lacunes, et des nuages de points pour vérifier si les données manquantes sont corrélées avec d'autres variables. Ce type d'audit visuel change souvent la stratégie de remplissage que vous choisissez.

Installez PyGWalker avec pip install pygwalker ou essayez-le dans Google Colab (opens in a new tab).

FAQ

Quelle est la différence entre fillna() et dropna() ?

fillna() remplace les valeurs manquantes par une valeur que vous spécifiez, en conservant toutes les lignes intactes. dropna() supprime les lignes entières (ou colonnes) qui contiennent des valeurs manquantes. Utilisez fillna() quand vous avez une valeur de remplacement raisonnable et voulez préserver votre nombre de lignes. Utilisez dropna() quand les lignes manquantes sont peu nombreuses et que l'imputation introduirait un biais inacceptable.

Puis-je remplir les valeurs NaN avec la moyenne d'une colonne ?

Oui. Utilisez df['column'] = df['column'].fillna(df['column'].mean()). Cela calcule la moyenne à partir des valeurs non manquantes et remplit chaque NaN dans cette colonne avec le résultat. Pour des données asymétriques, median() est souvent un meilleur choix car elle est moins affectée par les valeurs aberrantes extrêmes.

Que fait le paramètre limit dans fillna() ?

Le paramètre limit plafonne le nombre maximum de valeurs NaN consécutives qui sont remplies. Par exemple, df.fillna(method='ffill', limit=2) fera un forward fill d'au maximum 2 lacunes consécutives. Toute séquence plus longue de valeurs manquantes ne sera que partiellement remplie, laissant les lacunes restantes comme NaN. C'est utile pour les données de séries temporelles où vous voulez combler les petites lacunes mais signaler les pannes prolongées.

Comment remplir NaN avec différentes valeurs pour différentes colonnes ?

Passez un dictionnaire à fillna() où les clés sont les noms de colonnes et les valeurs sont les valeurs de remplissage : df.fillna({'age': 0, 'name': 'Unknown', 'salary': df['salary'].median()}). Chaque colonne reçoit sa propre valeur de remplissage, et les colonnes non listées dans le dictionnaire restent inchangées.

fillna() modifie-t-il le DataFrame original ?

Non, par défaut fillna() renvoie un nouveau DataFrame et l'original reste inchangé. Pour modifier l'original, utilisez soit l'assignation (df = df.fillna(0)) soit passez inplace=True. L'approche par assignation est recommandée car elle fonctionne avec le chaînage de méthodes et rend le flux de données explicite.

Conclusion

Les valeurs manquantes sont inévitables dans les données du monde réel. La méthode fillna() de pandas vous donne un contrôle précis sur la façon de les gérer :

  • Utilisez le fillna scalaire pour des remplacements simples et uniformes dans tout le DataFrame.
  • Utilisez le fillna avec dictionnaire pour appliquer différentes stratégies de remplissage par colonne -- le pattern le plus courant dans le code de production.
  • Utilisez le forward fill (ffill) et le backward fill (bfill) pour les données ordonnées et les séries temporelles où la propagation des valeurs connues a du sens.
  • Utilisez la moyenne, la médiane ou le mode pour l'imputation statistique des colonnes numériques et catégorielles.
  • Utilisez interpolate() quand les données suivent une tendance naturelle et que vous voulez des valeurs estimées lisses plutôt que des remplissages plats.
  • Utilisez le paramètre limit pour empêcher les méthodes basées sur la propagation de remplir des lacunes excessivement longues.
  • Préférez l'assignation à inplace=True pour un code plus propre et plus lisible.
  • Détectez et auditez toujours les valeurs manquantes avec isna() et notna() avant de choisir une stratégie de remplissage.

Une fois vos valeurs manquantes traitées, des outils comme PyGWalker (opens in a new tab) vous permettent d'explorer interactivement les données nettoyées sans écrire de code de graphiques -- vous aidant à vérifier que votre logique de remplissage a produit des résultats sensés et à passer directement à l'analyse.

📚