Warunkowe zamień pandy

123

Mam DataFrame i chcę zamienić wartości w określonej kolumnie, które przekraczają wartość zerem. Myślałem, że jest to sposób na osiągnięcie tego:

df[df.my_channel > 20000].my_channel = 0

Jeśli skopiuję kanał do nowej ramki danych, to proste:

df2 = df.my_channel 

df2[df2 > 20000] = 0

Robi dokładnie to, czego chcę, ale wydaje się, że nie działa z kanałem jako częścią oryginalnej ramki DataFrame.

BMichell
źródło
Znalazłem to, czego myślę, że szukałeś tutaj .
mokre stopy

Odpowiedzi:

181

.ixindeksator działa dobrze dla pand w wersji wcześniejszej niż 0.20.0, ale od pandy 0.20.0 .ixindeksator jest przestarzały , więc należy go unikać. Zamiast tego możesz użyć indeksatorów .loclub iloc. Możesz rozwiązać ten problem poprzez:

mask = df.my_channel > 20000
column_name = 'my_channel'
df.loc[mask, column_name] = 0

Lub w jednej linii

df.loc[df.my_channel > 20000, 'my_channel'] = 0

maskpomaga wybrać wiersze, w których df.my_channel > 20000jest True, podczas gdy df.loc[mask, column_name] = 0ustawia wartość 0 na wybrane wiersze, w których znajdują się maskblokady w kolumnie o nazwie column_name.

Aktualizacja: w tym przypadku należy użyć, locponieważ jeśli go użyjesz iloc, otrzymasz NotImplementedErrorinformację, że indeksowanie boolowskie oparte na iLocation na typie całkowitym nie jest dostępne .

lmiguelvargasf
źródło
81

Próbować

df.loc[df.my_channel > 20000, 'my_channel'] = 0

Uwaga: Ponieważ v0.20.0, ix została zaniechana na korzyść loc/ iloc.

lowtech
źródło
8
Dziękuję Ci. Znalazłem również własne rozwiązanie, które było: df.my_channel [df.my_channel> 20000] = 0
BMichell,
2
@BMichell Myślę, że twoje rozwiązanie może zacząć dawać ostrzeżenia w 0.13, nie miałem jeszcze szansy spróbować
lowtech
błąd wydajności: /opt/anaconda3/envs/python35/lib/python3.5/site-packages/ipykernel_launcher.py:1: SettingWithCopyWarning: Próba ustawienia wartości na kopii wycinka z DataFrame Zapoznaj się z zastrzeżeniami w dokumentacja: pandas.pydata.org/pandas-docs/stable/… "" "Punkt wejścia do uruchomienia jądra IPython.
Rutger Hofste
@RutgerHofste dzięki za wspomnienie o tym, kolejny argument nigdy nie korzysta z Pythona3
lowtech
34

np.where funkcja działa w następujący sposób:

df['X'] = np.where(df['Y']>=50, 'yes', 'no')

W twoim przypadku chciałbyś:

import numpy as np
df['my_channel'] = np.where(df.my_channel > 20000, 0, df.my_channel)
seeiespi
źródło
19

Powodem, dla którego oryginalna ramka danych nie jest aktualizowana, jest to, że indeksowanie łańcuchowe może spowodować zmodyfikowanie kopii zamiast widoku ramki danych. W docs dać tej rady:

Podczas ustawiania wartości w obiekcie pandy należy zachować ostrożność, aby uniknąć tak zwanego indeksowania łańcuchowego.

Masz kilka alternatyw: -

loc + Indeksowanie logiczne

loc może służyć do ustawiania wartości i obsługuje maski boolowskie:

df.loc[df['my_channel'] > 20000, 'my_channel'] = 0

mask + Indeksowanie logiczne

Do swojej serii możesz przypisać:

df['my_channel'] = df['my_channel'].mask(df['my_channel'] > 20000, 0)

Możesz też zaktualizować swoją serię na miejscu:

df['my_channel'].mask(df['my_channel'] > 20000, 0, inplace=True)

np.where + Indeksowanie logiczne

Państwo może używać NumPy przypisując swój oryginalnej serii, gdy warunek nie spełnione; jednak pierwsze dwa rozwiązania są bardziej przejrzyste, ponieważ jawnie zmieniają tylko określone wartości.

df['my_channel'] = np.where(df['my_channel'] > 20000, 0, df['my_channel'])
jpp
źródło
0

Użyłbym lambdafunkcji na Seriestakiej DataFramejak ta:

f = lambda x: 0 if x>100 else 1
df['my_column'] = df['my_column'].map(f)

Nie twierdzę, że jest to skuteczny sposób, ale działa dobrze.

Ozkan Serttas
źródło
3
Jest to nieefektywne i nie jest zalecane, ponieważ obejmuje pętlę na poziomie języka Python w operacji wierszowej.
jpp
Dziękuję, myślę, że możemy loctutaj użyć df.loc[: , 'my_column'] = df['my_column'].map(f). Nie wiem, czy jest szybki jak te, które dodałeś poniżej.
Ozkan Serttas
2
Nie, nadal wolno, ponieważ nadal pracujesz w wierszach, a nie w kolumnach.
jpp
0

Spróbuj tego:

df.my_channel = df.my_channel.where(df.my_channel <= 20000, other= 0)

lub

df.my_channel = df.my_channel.mask(df.my_channel > 20000, other= 0)

R. Shams
źródło