¿Cómo reemplazar NaN por valores anteriores o siguientes en pandas DataFrame?

Resuelto zegkljan asked hace 9 años • 10 respuestas

Supongamos que tengo un DataFrame con algunos NaNs:

>>> import pandas as pd
>>> df = pd.DataFrame([[1, 2, 3], [4, None, None], [None, None, 9]])
>>> df
    0   1   2
0   1   2   3
1   4 NaN NaN
2 NaN NaN   9

Lo que debo hacer es reemplazar cada NaNcon el primer NaNvalor que no sea en la misma columna encima. Se supone que la primera fila nunca contendrá un archivo NaN. Entonces para el ejemplo anterior el resultado sería

   0  1  2
0  1  2  3
1  4  2  3
2  4  2  9

Puedo recorrer todo el DataFrame columna por columna, elemento por elemento y establecer los valores directamente, pero ¿existe una manera fácil (óptimamente sin bucles) de lograr esto?

zegkljan avatar Jan 12 '15 22:01 zegkljan
Aceptado

Puede usar el fillnamétodo en el DataFrame y especificar el método como ffill(relleno directo):

>>> df = pd.DataFrame([[1, 2, 3], [4, None, None], [None, None, 9]])
>>> df.fillna(method='ffill')
   0  1  2
0  1  2  3
1  4  2  3
2  4  2  9

Este método...

propagar la última observación válida hacia la siguiente válida

Para ir en sentido contrario, también existe un bfillmétodo.

Este método no modifica el DataFrame in situ; deberá volver a vincular el DataFrame devuelto a una variable o especificar inplace=True:

df.fillna(method='ffill', inplace=True)
Alex Riley avatar Jan 12 '2015 15:01 Alex Riley

La respuesta aceptada es perfecta. Tuve una situación relacionada pero ligeramente diferente en la que tenía que ocupar el puesto de avance pero sólo dentro de los grupos. En caso de que alguien tenga la misma necesidad, sepa que fillna funciona en un objeto DataFrameGroupBy.

>>> example = pd.DataFrame({'number':[0,1,2,nan,4,nan,6,7,8,9],'name':list('aaabbbcccc')})
>>> example
  name  number
0    a     0.0
1    a     1.0
2    a     2.0
3    b     NaN
4    b     4.0
5    b     NaN
6    c     6.0
7    c     7.0
8    c     8.0
9    c     9.0
>>> example.groupby('name')['number'].fillna(method='ffill') # fill in row 5 but not row 3
0    0.0
1    1.0
2    2.0
3    NaN
4    4.0
5    4.0
6    6.0
7    7.0
8    8.0
9    9.0
Name: number, dtype: float64
ErnestScribbler avatar Jun 01 '2017 22:06 ErnestScribbler

Una cosa que noté al probar esta solución es que si tiene N/A al principio o al final de la matriz, fill y bfill no funcionan del todo. Necesitas ambos.

In [224]: df = pd.DataFrame([None, 1, 2, 3, None, 4, 5, 6, None])

In [225]: df.ffill()
Out[225]:
     0
0  NaN
1  1.0
...
7  6.0
8  6.0

In [226]: df.bfill()
Out[226]:
     0
0  1.0
1  1.0
...
7  6.0
8  NaN

In [227]: df.bfill().ffill()
Out[227]:
     0
0  1.0
1  1.0
...
7  6.0
8  6.0
jjs avatar Mar 17 '2017 04:03 jjs

Puedes usar pandas.DataFrame.fillnacon la method='ffill'opción. 'ffill'significa 'relleno directo' y propagará la última observación válida hacia adelante. La alternativa es 'bfill'que funciona de la misma manera, pero al revés.

import pandas as pd

df = pd.DataFrame([[1, 2, 3], [4, None, None], [None, None, 9]])
df = df.fillna(method='ffill')

print(df)
#   0  1  2
#0  1  2  3
#1  4  2  3
#2  4  2  9

También hay una función de sinónimo directo para esto, pandas.DataFrame.ffillpara simplificar las cosas.

Ffisegydd avatar Jan 12 '2015 15:01 Ffisegydd

Versión de una sola columna

  • Complete NAN con el último valor válido
df[column_name].fillna(method='ffill', inplace=True)
  • Complete NAN con el siguiente valor válido
df[column_name].fillna(method='backfill', inplace=True)
SpiralDev avatar Dec 19 '2018 07:12 SpiralDev