Skip to content

pandas: interpolare NaN con interpolate()

Python

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: massimolimit
    • Direzione da interpolare:limit_direction
    • Interpola o estrapola o entrambi:limit_area
    • Operare sul posto:inplace
  • Metodo di interpolazione:method
    • Interpolazione lineare:linear, index, values
    • Usando i valori esistenti:ffill, pad, bfill, backfill
    • Spline di interpolazione:spline
    • Altri
  • 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