Como substituir os NaNs por valores anteriores no pandas DataFrame?


140

Suponha que eu tenha um DataFrame com alguns 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

O que eu preciso fazer é substituir todos os itens NaNcom o primeiro não NaNvalor na mesma coluna acima. Supõe-se que a primeira linha nunca conterá a NaN. Portanto, no exemplo anterior, o resultado seria

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

Posso apenas percorrer todo o DataFrame, coluna por coluna, elemento por elemento e definir os valores diretamente, mas existe uma maneira fácil (idealmente sem loop) de conseguir isso?

Respostas:


213

Você pode usar o fillnamétodo no DataFrame e especificar o método como ffill(preenchimento para frente):

>>> 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 observação válida para a próxima válida

Para seguir o caminho oposto, há também um bfillmétodo.

Esse método não modifica o DataFrame no local - você precisará religar o DataFrame retornado a uma variável ou especificar inplace=True:

df.fillna(method='ffill', inplace=True)

31

A resposta aceita é perfeita. Eu tive uma situação relacionada, mas um pouco diferente, na qual tive que preencher adiante, mas apenas dentro de grupos. Caso alguém tenha a mesma necessidade, saiba que fillna funciona em um 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

exatamente o que eu estava procurando, ty
Tony

18

Você pode usar pandas.DataFrame.fillnacom a method='ffill'opção 'ffill'significa 'preenchimento para frente' e propagará a última observação válida para frente. A alternativa é a 'bfill'que funciona da mesma maneira, mas ao contrário.

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

Há também uma função de sinônimo direto para isso pandas.DataFrame.ffill, para simplificar as coisas.


14

Uma coisa que notei ao tentar esta solução é que, se você tiver N / A no início ou no final da matriz, o preenchimento e o preenchimento não funcionam. Você precisa dos dois.

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

Brilhante. Eu precisava exatamente disso para o meu problema. Preenchimento antes e depois. Muito obrigado.
Prometheus

Ótimo. Eu preciso dessa solução. Obrigado
Junkrat 9/03/19


5

Apenas uma versão da coluna

  • Preencha NAN com o último valor válido
df[column_name].fillna(method='ffill', inplace=True)
  • Preencha NAN com o próximo valor válido
df[column_name].fillna(method='backfill', inplace=True)

5

Apenas concordando com o ffillmétodo, mas uma informação extra é que você pode limitar o preenchimento direto com o argumento de palavra-chave limit.

>>> 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
1  NaN  NaN   6
2  NaN  NaN   9

>>> df[1].fillna(method='ffill', inplace=True)
>>> df
     0    1    2
0  1.0  2.0    3
1  NaN  2.0    6
2  NaN  2.0    9

Agora com limitargumento de palavra - chave

>>> 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

1

No meu caso, temos séries temporais de dispositivos diferentes, mas alguns dispositivos não puderam enviar nenhum valor durante algum período. Portanto, devemos criar valores de NA para cada dispositivo e período de tempo e, depois disso, preencher.

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

0

Você pode usar fillnapara remover ou substituir valores de NaN.

NaN Remover

import pandas as pd

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

df.fillna(method='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

NaN Replace

df.fillna(0) # 0 means What Value you want to replace 
     0    1    2
0  1.0  2.0  3.0
1  4.0  0.0  0.0
2  0.0  0.0  9.0

Referência pandas.DataFrame.fillna

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.