Cómo llenar NaNs en un marco de datos de Pandas

En este tutorial, aprenda cómo llenar NaN en un Pandas DataFrame usando los métodos de media, mediana, modo, valor constante, relleno hacia atrás y hacia adelante, así como también cómo interpolar valores faltantes con Python y Scipy.

Los valores faltantes son comunes y ocurren debido a un error humano, un error del instrumento, el procesamiento de otro equipo o simplemente por la falta de datos para una determinada observación.

En este Byte, veremos cómo llenar NaNs en un DataFrame, si elige manejar NaNs llenándolos.

En primer lugar, vamos a crear un DataFrame simulado con algunos valores aleatorios eliminados:

1
2
3
4
5
6
import numpy as np
array = np.random.randn(25, 3)
mask = np.random.choice([1, 0], array.shape, p=[.3, .7]).astype(bool)
array[mask] = np.nan

df = pd.DataFrame(array, columns=['Col1', 'Col2', 'Col3'])
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
        Col1      Col2      Col3 
0  -0.671603 -0.792415  0.783922
1   0.207720       NaN  0.996131
2  -0.892115 -1.282333       NaN
3  -0.315598 -2.371529 -1.959646
4        NaN       NaN -0.584636
5   0.314736 -0.692732 -0.303951
6   0.355121       NaN       NaN
7        NaN -1.900148  1.230828
8  -1.795468  0.490953       NaN
9  -0.678491 -0.087815       NaN
10  0.755714  0.550589 -0.702019
11  0.951908 -0.529933  0.344544
12       NaN  0.075340 -0.187669
13       NaN  0.314342 -0.936066
14       NaN  1.293355  0.098964

Grafiquemos, digamos, la tercera columna:

1
plt.plot(df['Col3'])

Cuando se llena con varias técnicas, este gráfico lleno de NaN se puede reemplazar con:

fillna() - Media, mediana, moda

Puede completar estos valores en una nueva columna y asignarlos a la columna que desea completar, o en el lugar usando el argumento inplace. Aquí, extraeremos los valores completos en una nueva columna para facilitar la inspección:

1
2
3
mean = df['Col3'].fillna(df['Col3'].mean(), inplace=False)
median = df['Col3'].fillna(df['Col3'].median(), inplace=False)
mode = df['Col3'].fillna(df['Col3'].mode(), inplace=False)

La mediana, la media y la moda de la columna son -0.187669, -0.110873 y 0.000000 y estos valores se utilizarán para cada NaN respectivamente. Esto se está llenando efectivamente con valores constantes, donde el valor que se ingresa depende de la entrada de la columna.

Primero, llenar con valores medianos da como resultado:

Con valores medios:

Con valores de modo:

fillna() - Valor constante

También puede rellenar con un valor constante en su lugar:

1
constant = df['Col3'].fillna(0, inplace=False

Esto da como resultado que se coloque un valor constante (0) en lugar de cada NaN. 0 está cerca de nuestra mediana y media e igual a la moda, por lo que los valores completos se parecerán mucho a ese método para nuestro conjunto de datos simulado:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
0     0.783922
1     0.996131
2     0.000000
3    -1.959646
4    -0.584636
5    -0.303951
6     0.000000
7     1.230828
8     0.000000
9     0.000000
10   -0.702019
11    0.344544
12   -0.187669
13   -0.936066
14    0.098964

fillna() - Relleno hacia adelante y hacia atrás

En cada fila, puede hacer un relleno hacia adelante o hacia atrás, tomando el valor de la fila anterior o posterior:

1
2
ffill = df['Col3'].fillna(method='ffill')
bfill = df['Col3'].fillna(method='bfill')

Con el relleno hacia adelante, dado que nos falta la fila 2, el valor de la fila 1 se toma para llenar la segunda. Los valores se propagan hacia adelante:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
0     0.783922
1     0.996131
2     0.996131
3    -1.959646
4    -0.584636
5    -0.303951
6    -0.303951
7     1.230828
8     1.230828
9     1.230828
10   -0.702019
11    0.344544
12   -0.187669
13   -0.936066
14    0.098964

Con el llenado hacia atrás, sucede lo contrario. La fila 2 se llena con el valor de la fila 3:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
0     0.783922
1     0.996131
2    -1.959646
3    -1.959646
4    -0.584636
5    -0.303951
6     1.230828
7     1.230828
8    -0.702019
9    -0.702019
10   -0.702019
11    0.344544
12   -0.187669
13   -0.936066
14    0.098964

Sin embargo, si hay más de un ‘NaN’ en una secuencia, estos no funcionarán bien y pueden generar una cascada de NaN más abajo, sesgando los datos y eliminando los valores realmente registrados.

interpolar()

El método interpolate() delega la interpolación de valores al [conjunto de métodos para interpolar valores] de SciPy (https://docs.scipy.org/doc/scipy/reference/interpolate.html). Acepta una amplia variedad de argumentos, incluidos más cercano, cero, slinear, cuadrático, cúbico, spline, baricéntrico, polinomio, krogh, por partes_polinomio, spline, pchip, akima, cubicspline, etc.

La interpolación es mucho más flexible e “inteligente” que simplemente llenar valores con constantes o medias variables como los métodos anteriores.

La interpolación puede completar correctamente una secuencia de una manera que ningún otro método puede hacerlo, como por ejemplo:

1
2
3
4
5
6
7
s = pd.Series([0, 1, np.nan, np.nan, np.nan, 5])
s.fillna(s.mean()).values
# array([0., 1., 2., 2., 2., 5.])
s.fillna(method='ffill').values
# array([0., 1., 1., 1., 1., 5.])
s.interpolate().values
# array([0., 1., 2., 3., 4., 5.])

La interpolación predeterminada es lineal, y asumir que 1...5 es probablemente una secuencia 1, 2, 3, 4, 5 no es descabellado (pero no está garantizado). Tanto el llenado constante como el llenado hacia adelante o hacia atrás fallan miserablemente aquí. En términos generales, la interpolación suele ser un buen amigo cuando se trata de llenar NaN con señales ruidosas o conjuntos de datos corruptos.

Experimentar con tipos de interpolación puede producir mejores resultados.

Aquí hay dos métodos de interpolación (empalme y polinomio requieren un argumento orden):

1
2
nearest = df['Col3'].interpolate(method='nearest')
polynomial = df['Col3'].interpolate(method='polynomial', order=3)

Estos dan como resultado:

Y: