Condizionale Sostituisci Panda


123

Ho un DataFrame e voglio sostituire i valori in una particolare colonna che superano un valore con zero. Avevo pensato che questo fosse un modo per raggiungere questo obiettivo:

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

Se copio il canale in un nuovo frame di dati è semplice:

df2 = df.my_channel 

df2[df2 > 20000] = 0

Questo fa esattamente quello che voglio, ma sembra non funzionare con il canale come parte del DataFrame originale.


Ho trovato quello che penso stavi cercando qui .
feetwet

Risposte:


181

.ixL'indicizzatore funziona bene per la versione panda precedente alla 0.20.0, ma poiché panda 0.20.0, l' .ixindicizzatore è deprecato , quindi dovresti evitare di usarlo. Invece, puoi usare .loco ilocindicizzatori. Puoi risolvere questo problema:

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

Oppure, in una riga,

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

maskti aiuta a selezionare le righe in cui si df.my_channel > 20000trova True, mentre df.loc[mask, column_name] = 0imposta il valore 0 sulle righe selezionate dove si masktrova nella colonna di cui si trova il nome column_name.

Aggiornamento: in questo caso, dovresti usare locperché se lo usi iloc, riceverai un messaggio NotImplementedErrorche ti informa che l'indicizzazione booleana basata su iLocation su un tipo intero non è disponibile .


82

Provare

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

Nota: dalla v0.20.0, ix è stato deprecato a favore di loc/ iloc.


8
Grazie. Ho trovato anche la mia soluzione, che era: df.my_channel [df.my_channel> 20000] = 0
BMichell

2
@BMichell Penso che la tua soluzione potrebbe iniziare a darti avvisi in 0.13, non ho ancora avuto la possibilità di provare
lowtech

yield error: /opt/anaconda3/envs/python35/lib/python3.5/site-packages/ipykernel_launcher.py:1: SettingWithCopyWarning: Un valore sta tentando di essere impostato su una copia di una slice da un DataFrame Vedi le avvertenze in la documentazione: pandas.pydata.org/pandas-docs/stable/… "" "Punto di ingresso per il lancio di un kernel IPython.
Rutger Hofste

@RutgerHofste grazie per averlo detto, ancora un altro argomento non usa mai Python3
lowtech

34

np.where la funzione funziona come segue:

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

Nel tuo caso vorresti:

import numpy as np
df['my_channel'] = np.where(df.my_channel > 20000, 0, df.my_channel)

19

Il motivo per cui il dataframe originale non si aggiorna è perché l' indicizzazione concatenata potrebbe causare la modifica di una copia anziché una visualizzazione del dataframe. I documenti danno questo consiglio:

Quando si impostano i valori in un oggetto panda, è necessario prestare attenzione per evitare quella che viene chiamata indicizzazione concatenata.

Hai alcune alternative: -

loc + Indicizzazione booleana

loc può essere utilizzato per impostare i valori e supporta le maschere booleane:

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

mask + Indicizzazione booleana

Puoi assegnare alla tua serie:

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

Oppure puoi aggiornare la tua serie in posizione:

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

np.where + Indicizzazione booleana

È possibile utilizzare NumPy assegnando la vostra serie originale quando la sua condizione è non soddisfatta; tuttavia, le prime due soluzioni sono più pulite poiché modificano esplicitamente solo i valori specificati.

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

0

Userei la lambdafunzione su Seriesun DataFramecome questo:

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

Non affermo che questo sia un modo efficiente, ma funziona bene.


3
Questo è inefficiente e sconsigliato poiché implica un ciclo a livello di Python in un'operazione per riga.
jpp

Grazie, immagino che possiamo usare locqui, come df.loc[: , 'my_column'] = df['my_column'].map(f). Non so se è veloce come quelli che hai aggiunto sotto.
Ozkan Serttas

2
No, ancora lento perché stai ancora operando per riga piuttosto che per colonna.
jpp

0

Prova questo:

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

o

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.