python - una - ¿Cómo reemplazar NaNs por valores anteriores en pandas DataFrame?
fillna pandas (8)
Supongamos que tengo un DataFrame con algunos
NaN
s:
>>> 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 necesito hacer es reemplazar cada
NaN
con el primer valor que no sea
NaN
en la misma columna arriba.
Se supone que la primera fila nunca contendrá un
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
Solo puedo recorrer todo el DataFrame columna por columna, elemento por elemento y establecer los valores directamente, pero ¿hay una manera fácil (óptimamente libre de bucles) de lograr esto?
En mi caso, tenemos series temporales de diferentes dispositivos, pero algunos dispositivos no pudieron enviar ningún valor durante algún período. Por lo tanto, deberíamos crear valores de NA para cada dispositivo y período de tiempo, y después de eso debemos completar.
df = pd.DataFrame([["device1", 1, ''first val of device1''], ["device2", 2, ''first val of device2''], ["device3", 3, ''first val of device3'']])
df.pivot(index=1, columns=0, values=2).fillna(method=''ffill'').unstack().reset_index(name=''value'')
Resultado:
0 1 value
0 device1 1 first val of device1
1 device1 2 first val of device1
2 device1 3 first val of device1
3 device2 1 None
4 device2 2 first val of device2
5 device2 3 first val of device2
6 device3 1 None
7 device3 2 None
8 device3 3 first val of device3
La respuesta aceptada es perfecta. Tuve una situación relacionada pero ligeramente diferente en la que tenía que completar hacia adelante, pero solo 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
Puede usar
fillna
con la opción
method=''ffill''
.
''ffill''
significa ''relleno hacia adelante'' 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.ffill
, para simplificar las cosas.
Puede usar el método
fillna
en el DataFrame y especificar el método como
ffill
(relleno de reenvío):
>>> 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 [s] última observación válida hacia adelante a la siguiente válida
Para ir en sentido contrario, también hay un método
bfill
.
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)
Simplemente
ffill
acuerdo con el método
ffill
, pero una información adicional es que puede limitar el relleno hacia adelante con el
limit
argumento de palabra clave.
>>> import pandas as pd
>>> df = pd.DataFrame([[1, 2, 3], [None, None, 6], [None, None, 9]])
>>> df
0 1 2
0 1.0 2.0 3.0
1 NaN NaN 6.0
2 NaN NaN 9.0
>>> df[1].fillna(method=''ffill'', inplace=True)
>>> df
0 1 2
0 1.0 2.0 3.0
1 NaN 2.0 NaN
2 NaN 2.0 9.0
Ahora con argumento de palabra clave
limit
>>> df[0].fillna(method=''ffill'', limit=1, inplace=True)
>>> df
0 1 2
0 1.0 2.0 3
1 1.0 2.0 6
2 NaN 2.0 9
Una cosa que noté al probar esta solución es que si tiene N / A al principio o al final de la matriz, ffill 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
ffill
ahora tiene su propio método
pd.DataFrame.ffill
df.ffill()
0 1 2
0 1.0 2.0 3.0
1 4.0 2.0 3.0
2 4.0 2.0 9.0
Solo una versión de columna
- Rellene NAN con el último valor válido
df[column_name].fillna(method=''ffill'', inplace=True)
- Rellene NAN con el siguiente valor válido
df[column_name].fillna(method=''backfill'', inplace=True)