Jak zamienić NaNs przez poprzedzające wartości w pandas DataFrame?

158

Załóżmy, że mam DataFrame z kilkoma 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

To, co muszę zrobić, to zastąpić każdy NaNpierwszym nie- NaNwartością w tej samej kolumnie powyżej. Zakłada się, że pierwszy wiersz nigdy nie będzie zawierał NaN. Tak więc w poprzednim przykładzie wynik byłby

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

Mogę po prostu zapętlić całą kolumnę DataFrame, element po elemencie i ustawić wartości bezpośrednio, ale czy istnieje łatwy (optymalnie wolny od pętli) sposób osiągnięcia tego?

zegkljan
źródło

Odpowiedzi:

237

Możesz użyć fillnametody w DataFrame i określić metodę jako ffill(wypełnienie do przodu):

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

Ta metoda...

propagate [s] ostatnia ważna obserwacja do przodu do następnej ważnej

Aby pójść w drugą stronę, jest też bfillmetoda.

Ta metoda nie modyfikuje elementu DataFrame inplace - musisz ponownie powiązać zwrócony DataFrame ze zmienną lub określić inplace=True:

df.fillna(method='ffill', inplace=True)
Alex Riley
źródło
Co by się stało, gdyby pusta komórka znajdowała się w indeksie nazw kolumn (np. Kilka kolumn nie miało nazw, ale zawierało dane. Czy istnieje sposób użycia bfill lub ffill do wypełnienia pustej komórki indeksu kolumny komórką w wiersz bezpośrednio pod nim? Na przykład: df = pd.DataFrame ({'col1': [2, 4, 8], 'col2': [2, 0, 0], '': [10, 2, 1]} , index = ['falcon', 'dog', 'spider' ']) Jak mogę użyć bfill lub ffill, aby zmienić nazwę trzeciej kolumny na 10 (która jest wartością wiersza bezpośrednio pod pustą nazwą trzeciej kolumny Dzięki!
GbG
33

Przyjęta odpowiedź jest idealna. Miałem podobną, ale nieco inną sytuację, w której musiałem wypełnić naprzód, ale tylko w grupach. Jeśli ktoś ma taką samą potrzebę, wiedz, że fillna działa na obiekcie 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
źródło
dokładnie to, czego szukałem, ty
Tony,
18

Możesz użyć pandas.DataFrame.fillnaz method='ffill'opcją. 'ffill'oznacza „forward fill” i będzie propagować ostatnią ważną obserwację do przodu. Alternatywą jest to, 'bfill'co działa w ten sam sposób, ale wstecz.

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

pandas.DataFrame.ffillDla ułatwienia istnieje również funkcja bezpośredniego synonimu .

Ffisegydd
źródło
15

Jedną rzeczą, którą zauważyłem podczas wypróbowywania tego rozwiązania, jest to, że jeśli masz N / A na początku lub na końcu tablicy, funkcje ffill i bfill nie działają. Potrzebujesz obu.

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
źródło
Znakomity. Potrzebowałem dokładnie tego do mojego problemu. Wypełnienie zarówno przed, jak i po. Wielkie dzięki.
Prometheus
Świetny. Potrzebuję tego rozwiązania. Dzięki
Junkrat
6

ffill teraz ma swoją własną metodę 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
piRSquared
źródło
5

Tylko jedna wersja kolumnowa

  • Wypełnij NAN ostatnią prawidłową wartością
df[column_name].fillna(method='ffill', inplace=True)
  • Wypełnij NAN kolejną prawidłową wartością
df[column_name].fillna(method='backfill', inplace=True)
SpiralDev
źródło
5

Zgadzam się tylko z ffillmetodą, ale jedną dodatkową informacją jest to, że możesz ograniczyć wypełnienie do przodu za pomocą argumentu słowa kluczowego 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

Teraz z limitargumentem słów kluczowych

>>> 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
Suvo
źródło
1

W moim przypadku mamy szeregi czasowe z różnych urządzeń, ale niektóre urządzenia nie mogły wysłać żadnej wartości w pewnym okresie. Powinniśmy więc utworzyć wartości NA dla każdego urządzenia i okresu, a potem zrobić fillna.

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

Wynik:

        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
Hodza
źródło
0

Możesz użyć fillnado usunięcia lub zastąpienia wartości NaN.

NaN Usuń

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

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

Odniesienia do pand.DataFrame.fillna

Md Jewele Islam
źródło