Zastąp nieprawidłowe wartości wartością Brak w Pandas DataFrame

80

Czy istnieje metoda zastępowania wartości Nonew Pandas w Pythonie?

Możesz użyć df.replace('pre', 'post')i zastąpić wartość inną, ale nie można tego zrobić, jeśli chcesz zastąpić Nonewartość, co jeśli spróbujesz, otrzymasz dziwny wynik.

Oto przykład:

df = DataFrame(['-',3,2,5,1,-5,-1,'-',9])
df.replace('-', 0)

co zwraca pomyślny wynik.

Ale,

df.replace('-', None)

co zwraca następujący wynik:

0
0   - // this isn't replaced
1   3
2   2
3   5
4   1
5  -5
6  -1
7  -1 // this is changed to `-1`...
8   9

Dlaczego zwraca się tak dziwny wynik?

Ponieważ chcę wlać tę ramkę danych do bazy danych MySQL, nie mogę umieścić NaNwartości w żadnym elemencie w mojej ramce danych i zamiast tego chcę wstawić None. Z pewnością możesz najpierw zmienić '-'na, NaNa następnie przekonwertować NaNna None, ale chcę wiedzieć, dlaczego ramka danych działa w tak straszny sposób.

Testowane na pandas 0.12.0 dev w Pythonie 2.7 i OS X 10.8. Python jest preinstalowaną wersją na OS X i zainstalowałem pandy za pomocą skryptu SciPy Superpack, dla twojej informacji.

Blaszard
źródło
Czy write_framenie parsuje NaNs do nones?
Andy Hayden
Tak. Wystąpił InternalError: (1054, u"Unknown column 'nan' in 'field list'")błąd. Nie wiem o żadnych innych rozwiązaniach poza konwersją NaNdo metody Noneprzed wykonaniem write_frame.
Blaszard
Jakiej wersji pand używasz?
Andy Hayden
Scipy Super Pack daje programistę? Ok, zdecydowanie myślę, że powinieneś poruszyć ten problem na githubie , nie powinien być zbyt trudny do naprawienia.
Andy Hayden
Jeśli czytasz te dane z CSV / Excel , możesz odczytać te wartości jako NaN używając na_valuesargumentu. Więcej informacji w tej odpowiedzi.
cs95

Odpowiedzi:

111

Właściwie w późniejszych wersjach pand da to TypeError:

df.replace('-', None)
TypeError: If "to_replace" and "value" are both None then regex must be a mapping

Możesz to zrobić, przekazując listę lub słownik:

In [11]: df.replace('-', df.replace(['-'], [None]) # or .replace('-', {0: None})
Out[11]:
      0
0  None
1     3
2     2
3     5
4     1
5    -5
6    -1
7  None
8     9

Ale zalecam używanie NaN zamiast None:

In [12]: df.replace('-', np.nan)
Out[12]:
     0
0  NaN
1    3
2    2
3    5
4    1
5   -5
6   -1
7  NaN
8    9
Andy Hayden
źródło
15
Lub po prostu lista, na przykład df.replace(['-'], [None]), albo df.replace({'-': None}), tak myślę. Użycie Nonejako wartownika wyklucza również użycie go jako wartości ..
DSM,
@ user2360798 replace jest w rzeczywistości bardzo bogatą w funkcje (skomplikowaną do odczytu) funkcją, choć (dev) docstring jest naprawdę dobry.
Andy Hayden
4
Nie wiem, czy to oczywiste, ale musiałem przypisać dfsobie z powrotem w stylu:df = df.replace({'?': np.nan})
luckyging3r
3
@AndyHayden df.replace('-', df.replace(['-'], [None])wygląda dziwnie , czy to literówka?
lin_bug
2
@lin_bug Chociaż wygląda na to, że nie działa już w ostatnich wersjach pand. df.where (df! = '-', None) działa
Andy Hayden
17

Preferuję rozwiązanie replacez a dictze względu na jego prostotę i elegancję:

df.replace({'-': None})

Możesz też mieć więcej zamienników:

df.replace({'-': None, 'None': None})

I nawet w przypadku większych zamienników zawsze jest oczywiste i jasne, co zastępuje się tym - co moim zdaniem jest znacznie trudniejsze w przypadku długich list.

Michael Dorner
źródło
1
Warto zauważyć, że częścią tego, dlaczego ta technika działa, jest to, że użycie dicttypu w to_replacepowoduje, że methodparametr nie jest oceniany, a tym samym method='pad'domyślnie nie ma żadnych złych skutków.
bsplosion
15

wherejest prawdopodobnie tym, czego szukasz. Więc

data=data.where(data=='-', None) 

Z dokumentów pandy :

where [zwraca] obiekt o tym samym kształcie co self i którego odpowiednie wpisy pochodzą z self, gdzie cond jest True, a w przeciwnym razie z other).

user2966041
źródło
5
To jest faktycznie niedokładne. data = data.where (data == '-', None) zamieni wszystko, co NIE JEST RÓWNE z '-' na None. Wersja Where Pandy zachowuje wartość pierwszego argumentu (w tym przypadku dane == '-') i zamienia cokolwiek innego na drugi argument (w tym przypadku None). Jest to trochę zagmatwane, ponieważ np. Gdzie jest bardziej wyraźne, ponieważ pyta o warunek w pierwszym argumencie, a następnie o jeśli prawda w drugim argumencie, a następnie if false w trzecim argumencie.
clg4
8

Przed przystąpieniem do tego postu ważne jest, aby zrozumieć różnicę między NaN i None . Jeden to typ zmiennoprzecinkowy, drugi to typ obiektowy. Pandy lepiej nadają się do pracy z typami skalarnymi, ponieważ wiele metod na tych typach można wektoryzować. Pandy próbują obsłużyć konsekwentnie None i NaN, ale NumPy nie może.

Moja sugestia ( i Andy'ego ) to trzymać się NaN.

Ale żeby odpowiedzieć na twoje pytanie ...

pandy> = 0.18: użyj na_values=['-']argumentu zread_csv

Jeśli załadowałeś te dane z CSV / Excel, mam dla Ciebie dobrą wiadomość. Możesz wyłączyć to w katalogu głównym podczas ładowania danych, zamiast pisać poprawkę z kodem jako kolejny krok.

Większość pd.read_*funkcji (takich jak read_csvi read_excel) przyjmuje na_valuesatrybut.

file.csv

A,B
-,1
3,-
2,-
5,3
1,-2
-5,4
-1,-1
-,0
9,0

Teraz, aby przekonwertować -znaki na NaN, zrób,

import pandas as pd
df = pd.read_csv('file.csv', na_values=['-'])
df

     A    B
0  NaN  1.0
1  3.0  NaN
2  2.0  NaN
3  5.0  3.0
4  1.0 -2.0
5 -5.0  4.0
6 -1.0 -1.0
7  NaN  0.0
8  9.0  0.0

I podobnie w przypadku innych funkcji / formatów plików.

PS: W wersji 0.24 + możesz zachować typ całkowity, nawet jeśli twoja kolumna ma NaN (tak, porozmawiaj o tym, że masz ciastko i jesz). Możesz określićdtype='Int32'

df = pd.read_csv('file.csv', na_values=['-'], dtype='Int32')
df

     A    B
0  NaN    1
1    3  NaN
2    2  NaN
3    5    3
4    1   -2
5   -5    4
6   -1   -1
7  NaN    0
8    9    0

df.dtypes

A    Int32
B    Int32
dtype: object

Dtype nie jest konwencjonalnym typem int ... ale raczej typem Nullable Integer. Istnieją inne opcje.


Obsługa danych liczbowych: pd.to_numericzerrors='coerce

Jeśli masz do czynienia z danymi liczbowymi, szybszym rozwiązaniem jest skorzystanie pd.to_numericz errors='coerce'argumentem, który wymusza nieprawidłowe wartości (wartości, które nie mogą być oddane do numerycznej) do Nan.

pd.to_numeric(df['A'], errors='coerce')

0    NaN
1    3.0
2    2.0
3    5.0
4    1.0
5   -5.0
6   -1.0
7    NaN
8    9.0
Name: A, dtype: float64

Aby zachować wartość całkowitą dtype (dopuszcza wartość null), użyj

pd.to_numeric(df['A'], errors='coerce').astype('Int32')

0    NaN
1      3
2      2
3      5
4      1
5     -5
6     -1
7    NaN
8      9
Name: A, dtype: Int32 

Aby wymusić wiele kolumn, użyj apply:

df[['A', 'B']].apply(pd.to_numeric, errors='coerce').astype('Int32')

     A    B
0  NaN    1
1    3  NaN
2    2  NaN
3    5    3
4    1   -2
5   -5    4
6   -1   -1
7  NaN    0
8    9    0

... i przypisz wynik z powrotem po.

Więcej informacji można znaleźć w tej odpowiedzi .

cs95
źródło
3
df = pd.DataFrame(['-',3,2,5,1,-5,-1,'-',9])
df = df.where(df!='-', None)
Shravan kp
źródło
0

Ustawienie wartości null można wykonać za pomocą np.nan:

import numpy as np
df.replace('-', np.nan)

Zaletą jest to, że df.last_valid_index()uznaje je za nieważne.

Freek Wiekmeijer
źródło
0

Korzystanie z zamiany i przypisywanie nowego pliku df:

import pandas as pd
df = pd.DataFrame(['-',3,2,5,1,-5,-1,'-',9])
dfnew = df.replace('-', 0)
print(dfnew)


(venv) D:\assets>py teste2.py
   0
0  0
1  3
2  2
3  5
4  1
5 -5
daniel rocha
źródło
0
df.replace('-', np.nan).astype("object")

Zapewni to możliwość isnull()późniejszego użycia w ramce danych

Keng Chan
źródło
0

W wersji Pandas ≥1.0.0 użyłbym DataFrame.replacelub Series.replace:

df.replace(old_val, pd.NA, inplace=True)

Jest to lepsze z dwóch powodów:

  1. Używa pd.NAzamiast Nonelub np.nan.
  2. Zastępuje wartość w miejscu, co może być bardziej wydajne pod względem pamięci.
Acumenus
źródło