
Puoi interpolare i valori mancanti (NaN) in pandas.DataFrame e Series con interpolate().
In questo articolo vengono descritti i seguenti contenuti.
- Utilizzo di base di interpolate()
- Riga o colonna:
axis
- Numero di NaN consecutivi da compilare: massimo
limit
- Direzione da interpolare:
limit_direction
- Interpola o estrapola o entrambi:
limit_area
- Operare sul posto:
inplace
- Riga o colonna:
- Metodo di interpolazione:
method
- Interpolazione lineare:
linear, index, values
- Usando i valori esistenti:
ffill, pad, bfill, backfill
- Spline di interpolazione:
spline
- Altri
- Interpolazione lineare:
- Per dati di serie temporali
- Nel caso in cui il tipo di dati dtype sia oggetto (es. stringa)
Usa dropna() e fillna() per rimuovere i valori mancanti NaN o per riempirli con un valore specifico. Vedi i seguenti articoli.
Utilizzo di base di interpolate()
Il seguente pandas.DataFrame viene utilizzato come esempio.
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
Per configurazione predefinita, l’interpolazione lineare viene eseguita per ciascuna colonna. Lo stesso valore si ripete per Nan in basso e Nan in alto è invariato.
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
Riga o colonna:axis
Se asse=1, l’interpolazione viene eseguita per ogni riga. Lo stesso valore viene utilizzato per il NaN più a destra e il NaN più a sinistra rimane invariato.
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
Numero di NaN consecutivi da compilare: massimolimit
Se i NaN sono consecutivi, è possibile specificare il numero massimo di interpolazioni con il limite di argomenti. Il valore predefinito è Nessuno, il che significa che tutti i NaN consecutivi sono interpolati.
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
Direzione da interpolare:limit_direction
La direzione da interpolare è specificata con l’argomento limit_direction come uno tra ‘avanti’, ‘indietro’ o ‘entrambi’.
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
Come accennato in precedenza, per passare predefinita, i NaN in alto (oa sinistra) vengono lasciati così come sono, ma se si imposta limit_direction=’entrambi’, entrambe le estremità vengono interpolate.
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
È possibile specificare l’area da interpolare con l’argomento limit_area.
- ‘dentro’: interpolazione solista
- ‘esterno’: solo estrapolazione
- Nessuno (predefinito): sia l’interpolazione che l’estrapolazione
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
Si noti che la parola “estrapolazione” è usata per comodità, ma come si può vedere dai risultati precedenti, nell’interpolazione lineare (impostazione predefinita), i valori esterni sono ripetizioni dei valori finali e non sono estrapolati linearmente. Nell’interpolazione spline definita di seguito, i valori esterni vengono estrapolati anziché ripetuti.
Operare sul posto:inplace
Come con molti altri metodi, puoi aggiornare l’oggetto stesso con 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
Metodo di interpolazione:method
Il metodo di interpolazione è specificato dal metodo del primo argomento. Il valore predefinito è ‘lineare’ (interpolazione lineare).
Interpolazione lineare:linear, index, values
Con=’lineare’ (predefinito), l’indice viene ignorato, ma con metodo=’indice’ o metodo=’valori’, viene interpolato utilizzando il valore dell’indice.
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
Se la colonna dell’indice è strings, method=’linear’ (impostazione predefinita) va bene, ma se method=’index’ o method=’values’ viene generato un errore.
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'
Usando i valori esistenti:ffill, pad, bfill, backfill
I NaN vengono riempiti con il valore esistente precedente se method=’fill’ o method=’pad’, o con il valore esistente successivo se method=’bfill’ o 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
Dovrebbe essere limit_direction=’forward’ if method=”fill’, ‘pad’ e limit_direction=’backward’ if 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`
Puoi fare lo stesso con il metodo fillna() con il metodo 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
Spline di interpolazione:spline
Se metodo=’spline’, l’interpolazione spline viene eseguita. È necessario specificare l’ordine degli argomenti.
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’interpolazione spline utilizza sempre l’indice. Se l’indice cambia, cambia anche il risultato.
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
Pertanto, l’interpolazione spline richiede che l’indice sia numerico. Se si tratta di stringhe, viene generato un errore.
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.
Altri
Esistono altri metodi di interpolazione che possono essere specificati per l’argomento del metodo:'nearest', 'zero', 'slinear', 'quadratic', 'cubic', 'barycentric', 'krogh', 'polynomial', 'piecewise_polynomial', 'from_derivatives', 'pchip', 'akima'.
Come menzionato nella documentazione ufficiale, questi sono wrapper per le funzioni SciPy, inclusa l’interpolazione spline (“spline”) menzionata sopra.
In tutti i casi, l’indice deve essere numerico, viene nell’interpolazione spline.
Per dati di serie temporali
metodo=’tempo’ viene fornito per i dati di serie temporali. Nel caso di method=’time’, l’interpolazione lineare viene eseguita in base alla data e all’ora della colonna dell’indice.
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
Nel caso in cui il tipo di dati dtype sia oggetto (es. stringa)
Ad esempio, il tipo di dati dtype di una colonna contenente un elemento stringa è oggetto.
s_object = pd.Series(['A', np.nan, 'C'])
print(s_object)
# 0 A
# 1 NaN
# 2 C
# dtype: object
La colonna dell’oggetto non è interpolata da method=’lineare’ (predefinito) o altri metodi. Può essere riempito con metodi come ffill, pad, bfill e backfill, che utilizza i valori esistenti.
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
Lo stesso vale se l’elemento è un numero ma il tipo di dati è oggetto.
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
Se lo converti in float con astype(), puoi interpolare. Nota che non può essere convertito in int se contiene 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