Pandas de reemplazo condicional

123

Tengo un DataFrame y quiero reemplazar los valores en una columna en particular que exceden un valor con cero. Pensé que esta era una forma de lograr esto:

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

Si copio el canal en un nuevo marco de datos, es simple:

df2 = df.my_channel 

df2[df2 > 20000] = 0

Esto hace exactamente lo que quiero, pero parece que no funciona con el canal como parte del DataFrame original.

BMichell
fuente
Encontré lo que creo que estabas buscando aquí .
feetwet

Respuestas:

181

.ixEl indexador funciona bien para la versión de pandas anterior a 0.20.0, pero desde pandas 0.20.0, el .ixindexador está en desuso , por lo que debe evitar su uso. En su lugar, puede utilizar .loco ilocindexadores. Puede resolver este problema de la siguiente manera:

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

O, en una línea,

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

maskle ayuda a seleccionar las filas en las que df.my_channel > 20000está True, mientras df.loc[mask, column_name] = 0establece el valor 0 en las filas seleccionadas donde se maskmantiene en la columna cuyo nombre es column_name.

Actualización: en este caso, debe usar locporque si lo usa iloc, obtendrá un NotImplementedErrormensaje que le indicará que la indexación booleana basada en iLocation en un tipo entero no está disponible .

lmiguelvargasf
fuente
81

Tratar

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

Nota: Desde v0.20.0, ix se ha desaprobado a favor de loc/ iloc.

baja tecnología
fuente
8
Gracias. También encontré mi propia solución, que era: df.my_channel [df.my_channel> 20000] = 0
BMichell
2
@BMichell Creo que su solución podría comenzar a darle advertencias en 0.13, no tuve la oportunidad de probarlo todavía
lowtech
error de rendimiento: /opt/anaconda3/envs/python35/lib/python3.5/site-packages/ipykernel_launcher.py:1: SettingWithCopyWarning: se está intentando establecer un valor en una copia de un segmento de un DataFrame Consulte las advertencias en la documentación: pandas.pydata.org/pandas-docs/stable/… "" "Punto de entrada para lanzar un kernel de IPython.
Rutger Hofste
@RutgerHofste gracias por mencionar eso, otro argumento más: nunca use Python3
lowtech
34

np.where La función funciona de la siguiente manera:

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

En tu caso querrías:

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

La razón por la que su marco de datos original no se actualiza es porque la indexación encadenada puede hacer que modifique una copia en lugar de una vista de su marco de datos. Los médicos dan este consejo:

Al establecer valores en un objeto pandas, se debe tener cuidado para evitar lo que se denomina indexación encadenada.

Tiene algunas alternativas: -

loc + Indexación booleana

loc se puede utilizar para establecer valores y admite máscaras booleanas:

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

mask + Indexación booleana

Puedes asignar a tu serie:

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

O puede actualizar su serie en su lugar:

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

np.where + Indexación booleana

Usted puede utilizar NumPy mediante la asignación de su serie original cuando su condición es no satisfecha; sin embargo, las dos primeras soluciones son más limpias ya que cambian explícitamente solo valores especificados.

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

Usaría la lambdafunción en una Seriesde DataFrameestas formas:

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

No afirmo que sea una forma eficaz, pero funciona bien.

Ozkan Serttas
fuente
3
Esto es ineficiente y no se recomienda, ya que implica un bucle de nivel de Python en una operación de fila.
jpp
Gracias, creo que podemos usar locaquí, como df.loc[: , 'my_column'] = df['my_column'].map(f). No sé si es rápido como los que agregaste a continuación.
Ozkan Serttas
2
No, sigue siendo lento ya que todavía está operando en filas en lugar de en columnas.
jpp
0

Prueba esto:

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)

R. Shams
fuente