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.

BMichell
la source
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 .

lmiguelvargasf
la source
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.

lowtech
la source
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)
seeiespi
la source
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'])
jpp
la source
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.

Ozkan Serttas
la source
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)

R. Shams
la source