Remplacer les pandas conditionnel


123

J'ai un DataFrame et je souhaite remplacer les valeurs d'une colonne particulière qui dépassent une valeur par zéro. J'avais pensé que c'était un moyen d'y parvenir:

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

Si je copie le canal dans une nouvelle trame de données, c'est simple:

df2 = df.my_channel 

df2[df2 > 20000] = 0

Cela fait exactement ce que je veux, mais semble ne pas fonctionner avec le canal dans le cadre du DataFrame original.


J'ai trouvé ce que je pense que vous cherchiez ici .
Feetwet

Réponses:


181

.ixl'indexeur fonctionne bien pour la version pandas antérieure à 0.20.0, mais depuis pandas 0.20.0, l' .ixindexeur est obsolète , vous devez donc éviter de l'utiliser. Au lieu de cela, vous pouvez utiliser .locou des ilocindexeurs. Vous pouvez résoudre ce problème en:

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

Ou, en une seule ligne,

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

maskvous aide à sélectionner les lignes dans lesquelles df.my_channel > 20000est True, tandis que df.loc[mask, column_name] = 0définit la valeur 0 pour les lignes sélectionnées où se trouve maskdans la colonne dont le nom est column_name.

Mise à jour: dans ce cas, vous devez utiliser loccar si vous utilisez iloc, vous obtiendrez un messageNotImplementedError vous indiquant que l' indexation booléenne basée sur iLocation sur un type entier n'est pas disponible .


82

Essayer

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

Remarque: depuis la v0.20.0, ix est obsolète au profit de loc/ iloc.


8
Je vous remercie. J'ai aussi trouvé ma propre solution, qui était: df.my_channel [df.my_channel> 20000] = 0
BMichell

2
@BMichell Je pense que votre solution pourrait commencer à vous donner des avertissements dans la 0.13, je n'ai pas encore eu l'occasion d'essayer
lowtech

erreur de rendement: /opt/anaconda3/envs/python35/lib/python3.5/site-packages/ipykernel_launcher.py:1: SettingWithCopyWarning: Une valeur essaie d'être définie sur une copie d'une tranche à partir d'un DataFrame Voir les mises en garde dans la documentation: pandas.pydata.org/pandas-docs/stable/… "" "Point d'entrée pour le lancement d'un noyau IPython.
Rutger Hofste

@RutgerHofste merci d'avoir mentionné cela, encore un autre argument n'utilise jamais Python3
lowtech

34

np.where La fonction fonctionne comme suit:

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

Dans votre cas, vous voudriez:

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

19

La raison pour laquelle votre dataframe d'origine ne se met pas à jour est que l' indexation chaînée peut vous amener à modifier une copie plutôt qu'une vue de votre dataframe. La documentation donne ce conseil:

Lors de la définition de valeurs dans un objet pandas, il faut veiller à éviter ce que l'on appelle l'indexation chaînée.

Vous avez quelques alternatives: -

loc + Indexation booléenne

loc peut être utilisé pour définir des valeurs et prend en charge les masques booléens:

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

mask + Indexation booléenne

Vous pouvez attribuer à votre série:

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

Ou vous pouvez mettre à jour votre série sur place:

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

np.where + Indexation booléenne

Vous pouvez utiliser NumPy en attribuant votre série d'origine lorsque votre condition n'est pas satisfaite; cependant, les deux premières solutions sont plus propres car elles ne modifient explicitement que les valeurs spécifiées.

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

0

J'utiliserais la lambdafonction sur Seriesun DataFramecomme ceci:

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

Je ne prétends pas que ce soit un moyen efficace, mais cela fonctionne bien.


3
Ceci est inefficace et déconseillé car cela implique une boucle de niveau Python dans une opération par ligne.
jpp

Merci, je suppose que nous pouvons utiliser locici, comme df.loc[: , 'my_column'] = df['my_column'].map(f). Je ne sais pas si c'est rapide comme ceux que vous avez ajoutés ci-dessous.
Ozkan Serttas

2
Non, toujours lent car vous travaillez toujours par ligne plutôt que par colonne.
jpp

0

Essaye ça:

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

ou

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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.