
Vous pouvez interpoler les valeurs manquantes (NaN) dans pandas.DataFrame et Series avec interpolate().
Cet article décrit le contenu suivant.
- Utilisation de base d’interpolate()
- Ligne ou colonne :
axis - Nombre maximal de NaN consécutifs à remplir :
limit - Sens d’interpolation :
limit_direction - Interpoler ou extrapoler ou les deux :
limit_area - Opérer sur place :
inplace
- Ligne ou colonne :
- Méthode d’interpolation :
method- Interpolation linéaire:
linear, index, values - Utilisation des valeurs existantes :
ffill, pad, bfill, backfill - Interpolation spline :
spline - Les autres
- Interpolation linéaire:
- Pour les données de séries chronologiques
- Dans le cas où le type de données dtype est un objet (par exemple, une chaîne)
Utilisez dropna() et fillna() pour supprimer les valeurs manquantes NaN ou pour les remplir avec une valeur spécifique. Voir les articles suivants.
Utilisation de base d’interpolate()
Le pandas.DataFrame suivant est utilisé comme exemple.
import pandas as pd
import numpy as np
df = pd.DataFrame({'col1': [0, np.nan, np.nan, 3, 4],
'col2': [np.nan, 1, 2, np.nan, np.nan],
'col3': [4, np.nan, np.nan, 7, 10]})
print(df)
# col1 col2 col3
# 0 0.0 NaN 4.0
# 1 NaN 1.0 NaN
# 2 NaN 2.0 NaN
# 3 3.0 NaN 7.0
# 4 4.0 NaN 10.0
Par défaut, une interpolation linéaire est effectuée pour chaque colonne. La même valeur est répétée pour Nan en bas, et Nan en haut reste inchangé.
print(df.interpolate())
# col1 col2 col3
# 0 0.0 NaN 4.0
# 1 1.0 1.0 5.0
# 2 2.0 2.0 6.0
# 3 3.0 2.0 7.0
# 4 4.0 2.0 10.0
Ligne ou colonne :axis
Si axe=1, l’interpolation est effectuée pour chaque ligne. La même valeur est répétée pour le NaN le plus à droite et le NaN le plus à gauche reste inchangé.
print(df.interpolate(axis=1))
# col1 col2 col3
# 0 0.0 2.0 4.0
# 1 NaN 1.0 1.0
# 2 NaN 2.0 2.0
# 3 3.0 5.0 7.0
# 4 4.0 7.0 10.0
Nombre maximal de NaN consécutifs à remplir :limit
Si les NaN sont consécutifs, vous pouvez spécifier le nombre maximal d’interpolations avec la limite d’argument. La valeur par défaut est Aucun, ce qui signifie que tous les NaN consécutifs sont interpolés.
print(df.interpolate(limit=1))
# col1 col2 col3
# 0 0.0 NaN 4.0
# 1 1.0 1.0 5.0
# 2 NaN 2.0 NaN
# 3 3.0 2.0 7.0
# 4 4.0 NaN 10.0
Sens d’interpolation :limit_direction
La direction à interpoler est spécifiée avec l’argument limit_direction parmi ‘forward’, ‘backward’ ou ‘both’.
print(df.interpolate(limit=1, limit_direction='forward'))
# col1 col2 col3
# 0 0.0 NaN 4.0
# 1 1.0 1.0 5.0
# 2 NaN 2.0 NaN
# 3 3.0 2.0 7.0
# 4 4.0 NaN 10.0
print(df.interpolate(limit=1, limit_direction='backward'))
# col1 col2 col3
# 0 0.0 1.0 4.0
# 1 NaN 1.0 NaN
# 2 2.0 2.0 6.0
# 3 3.0 NaN 7.0
# 4 4.0 NaN 10.0
print(df.interpolate(limit=1, limit_direction='both'))
# col1 col2 col3
# 0 0.0 1.0 4.0
# 1 1.0 1.0 5.0
# 2 2.0 2.0 6.0
# 3 3.0 2.0 7.0
# 4 4.0 NaN 10.0
Comme mentionné ci-dessus, par défaut, les NaN en haut (ou à gauche) sont laissés tels quels, mais si vous définissez limit_direction=’both’, les deux extrémités sont interpolées.
print(df.interpolate(limit_direction='both'))
# col1 col2 col3
# 0 0.0 1.0 4.0
# 1 1.0 1.0 5.0
# 2 2.0 2.0 6.0
# 3 3.0 2.0 7.0
# 4 4.0 2.0 10.0
Vous pouvez spécifier la zone à interpoler avec l’argument limit_area.
- ‘à l’intérieur’ : uniquement une interpolation
- ‘outside’ : seulement extrapolation
- Aucun (par défaut) : Interpolation et extrapolation
print(df.interpolate(limit_area='inside'))
# col1 col2 col3
# 0 0.0 NaN 4.0
# 1 1.0 1.0 5.0
# 2 2.0 2.0 6.0
# 3 3.0 NaN 7.0
# 4 4.0 NaN 10.0
print(df.interpolate(limit_area='outside'))
# col1 col2 col3
# 0 0.0 NaN 4.0
# 1 NaN 1.0 NaN
# 2 NaN 2.0 NaN
# 3 3.0 2.0 7.0
# 4 4.0 2.0 10.0
print(df.interpolate(limit_area='outside', limit_direction='both'))
# col1 col2 col3
# 0 0.0 1.0 4.0
# 1 NaN 1.0 NaN
# 2 NaN 2.0 NaN
# 3 3.0 2.0 7.0
# 4 4.0 2.0 10.0
Notez que le mot « extrapolation » est utilisé par commodité, mais comme vous pouvez le voir dans les résultats ci-dessus, dans l’interpolation linéaire (par défaut), les valeurs extérieures sont des répétitions des valeurs finales et ne sont pas extrapolées linéairement. Dans l’interpolation spline décrite ci-dessous, les valeurs extérieures sont extrapolées plutôt que répétées.
Opérer sur place :inplace
Comme avec de nombreuses autres méthodes, vous pouvez mettre à jour l’objet lui-même avec inplace=True.
df.interpolate(inplace=True)
print(df)
# col1 col2 col3
# 0 0.0 NaN 4.0
# 1 1.0 1.0 5.0
# 2 2.0 2.0 6.0
# 3 3.0 2.0 7.0
# 4 4.0 2.0 10.0
Méthode d’interpolation :method
La méthode d’interpolation est spécifiée par la méthode du premier argument. La valeur par défaut est ‘linear’ (interpolation linéaire).
Interpolation linéaire:linear, index, values
Avec method=’linear’ (par défaut), l’index est ignoré, mais avec method=’index’ ou method=’values’, il est interpolé en utilisant la valeur de l’index.
s = pd.Series([0, np.nan, np.nan, 3],
index=[0, 4, 6, 8])
print(s)
# 0 0.0
# 4 NaN
# 6 NaN
# 8 3.0
# dtype: float64
print(s.interpolate())
# 0 0.0
# 4 1.0
# 6 2.0
# 8 3.0
# dtype: float64
print(s.interpolate('index'))
# 0 0.00
# 4 1.50
# 6 2.25
# 8 3.00
# dtype: float64
Si la colonne d’index est des chaînes, method=’linear’ (par défaut) convient, mais si method=’index’ ou method=’values’, une erreur est générée.
s.index = list('abcd')
print(s)
# a 0.0
# b NaN
# c NaN
# d 3.0
# dtype: float64
print(s.interpolate())
# a 0.0
# b 1.0
# c 2.0
# d 3.0
# dtype: float64
# print(s.interpolate('index'))
# TypeError: Cannot cast array data from dtype('O') to dtype('float64') according to the rule 'safe'
Utilisation des valeurs existantes :ffill, pad, bfill, backfill
Les NaN sont remplis avec la valeur existante précédente si method=’ffill’ ou method=’pad’, ou avec la valeur existante suivante si method=’bfill’ ou method=’backfill’.
s = pd.Series([np.nan, 1, np.nan, 2, np.nan])
print(s)
# 0 NaN
# 1 1.0
# 2 NaN
# 3 2.0
# 4 NaN
# dtype: float64
print(s.interpolate('ffill'))
# 0 NaN
# 1 1.0
# 2 1.0
# 3 2.0
# 4 2.0
# dtype: float64
print(s.interpolate('bfill'))
# 0 1.0
# 1 1.0
# 2 2.0
# 3 2.0
# 4 NaN
# dtype: float64
Il devrait être limit_direction=’forward’ si method= »ffill’, ‘pad’ et limit_direction=’backward’ si method= »bfill’, ‘backfill’.
# s.interpolate('ffill', limit_direction='both')
# ValueError: `limit_direction` must be 'forward' for method `ffill`
# s.interpolate('bfill', limit_direction='both')
# ValueError: `limit_direction` must be 'backward' for method `bfill`
Vous pouvez faire la même chose avec la méthode fillna() avec la méthode argument.
print(s.fillna(method='ffill'))
# 0 NaN
# 1 1.0
# 2 1.0
# 3 2.0
# 4 2.0
# dtype: float64
print(s.fillna(method='bfill'))
# 0 1.0
# 1 1.0
# 2 2.0
# 3 2.0
# 4 NaN
# dtype: float64
Interpolation spline :spline
Si method=’spline’, l’interpolation spline est effectuée. Vous devez spécifier l’ordre des arguments.
s = pd.Series([0, 10, np.nan, np.nan, 4, np.nan],
index=[0, 2, 5, 6, 8, 12])
print(s)
# 0 0.0
# 2 10.0
# 5 NaN
# 6 NaN
# 8 4.0
# 12 NaN
# dtype: float64
print(s.interpolate('spline', order=2))
# 0 0.00
# 2 10.00
# 5 13.75
# 6 12.00
# 8 4.00
# 12 -30.00
# dtype: float64
L’interpolation spline utilise toujours l’index. Si l’indice change, le résultat change également.
s.index = range(6)
print(s)
# 0 0.0
# 1 10.0
# 2 NaN
# 3 NaN
# 4 4.0
# 5 NaN
# dtype: float64
print(s.interpolate('spline', order=2))
# 0 0.0
# 1 10.0
# 2 14.0
# 3 12.0
# 4 4.0
# 5 -10.0
# dtype: float64
Par conséquent, l’interpolation spline nécessite que l’index soit des nombres. S’il s’agit de chaînes, une erreur est générée.
s.index = list('abcdef')
print(s)
# a 0.0
# b 10.0
# c NaN
# d NaN
# e 4.0
# f NaN
# dtype: float64
# print(s.interpolate('spline', order=2))
# ValueError: Index column must be numeric or datetime type when using spline method other than linear.
# Try setting a numeric or datetime index column before interpolating.
Les autres
Il existe d’autres méthodes d’interpolation qui peuvent être spécifiées pour l’argument method :'nearest', 'zero', 'slinear', 'quadratic', 'cubic', 'barycentric', 'krogh', 'polynomial', 'piecewise_polynomial', 'from_derivatives', 'pchip', 'akima'.
Comme mentionné dans la documentation officielle, ce sont des wrappers pour les fonctions SciPy, y compris l’interpolation spline (« spline ») mentionnée ci-dessus.
Dans tous les cas, l’index doit être numérique, comme dans l’interpolation spline.
Pour les données de séries chronologiques
method=’time’ est fourni pour les données de séries chronologiques. Dans le cas de method=’time’, l’interpolation linéaire est effectuée en fonction de la date et de l’heure de la colonne d’index.
df_nan = pd.DataFrame({'value': [1, pd.np.nan, pd.np.nan, pd.np.nan, 31]},
index=pd.to_datetime(['2018-01-01', '2018-01-02', '2018-01-15', '2018-01-20', '2018-01-31']))
print(df_nan)
# value
# 2018-01-01 1.0
# 2018-01-02 NaN
# 2018-01-15 NaN
# 2018-01-20 NaN
# 2018-01-31 31.0
print(df_nan.interpolate())
# value
# 2018-01-01 1.0
# 2018-01-02 8.5
# 2018-01-15 16.0
# 2018-01-20 23.5
# 2018-01-31 31.0
print(df_nan.interpolate('time'))
# value
# 2018-01-01 1.0
# 2018-01-02 2.0
# 2018-01-15 15.0
# 2018-01-20 20.0
# 2018-01-31 31.0
Dans le cas où le type de données dtype est un objet (par exemple, une chaîne)
Par exemple, le type de données dtype d’une colonne contenant un élément de chaîne est object.
s_object = pd.Series(['A', np.nan, 'C'])
print(s_object)
# 0 A
# 1 NaN
# 2 C
# dtype: object
La colonne objet n’est pas interpolée par method=’linear’ (par défaut) ou d’autres méthodes. Il peut être rempli par des méthodes telles que ffill, pad, bfill et backfill, qui utilisent les valeurs existantes.
print(s_object.interpolate())
# 0 A
# 1 NaN
# 2 C
# dtype: object
print(s_object.interpolate('ffill'))
# 0 A
# 1 A
# 2 C
# dtype: object
Il en va de même si l’élément est un nombre mais que le type de données est un objet.
s_object_num = pd.Series([0, np.nan, 2], dtype=object)
print(s_object_num)
# 0 0
# 1 NaN
# 2 2
# dtype: object
print(s_object_num.interpolate())
# 0 0
# 1 NaN
# 2 2
# dtype: object
print(s_object_num.interpolate('ffill'))
# 0 0
# 1 0
# 2 2
# dtype: int64
Si vous le convertissez en float avec astype(), vous pouvez interpoler. Notez qu’il ne peut pas être converti en int s’il contient NaN.
print(s_object_num.astype(float).interpolate())
# 0 0.0
# 1 1.0
# 2 2.0
# dtype: float64
# print(s_object_num.astype(int))
# ValueError: cannot convert float NaN to integer
