94 votes

Pandas/Python : Définir la valeur d'une colonne en fonction de la valeur d'une autre colonne

Je dois définir la valeur d'une colonne en fonction de la valeur d'une autre dans un cadre de données Pandas. Voici la logique :

if df['c1'] == 'Value':
    df['c2'] = 10
else:
    df['c2'] = df['c3']

Je ne parviens pas à obtenir ce que je veux, c'est-à-dire créer simplement une colonne avec de nouvelles valeurs (ou modifier la valeur d'une colonne existante : l'un ou l'autre fonctionne pour moi).

Si j'essaie d'exécuter le code ci-dessus ou si je l'écris comme une fonction et que j'utilise la méthode apply, j'obtiens ce qui suit :

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

149voto

sacul Points 29881

une façon de procéder serait d'utiliser l'indexation avec .loc .

Exemple

En l'absence d'un exemple de dataframe, je vais en créer un ici :

import numpy as np
import pandas as pd

df = pd.DataFrame({'c1': list('abcdefg')})
df.loc[5, 'c1'] = 'Value'

>>> df
      c1
0      a
1      b
2      c
3      d
4      e
5  Value
6      g

En supposant que vous vouliez créer une nouvelle colonne c2 équivalent à c1 sauf dans les cas suivants c1 est Value Dans ce cas, vous souhaitez l'attribuer à 10 :

Tout d'abord, vous pouvez créer une nouvelle colonne c2 et le définir comme équivalent à c1 en utilisant l'une des deux lignes suivantes (elles font essentiellement la même chose) :

df = df.assign(c2 = df['c1'])
# OR:
df['c2'] = df['c1']

Ensuite, trouvez tous les indices où c1 est égal à 'Value' en utilisant .loc et attribuez la valeur souhaitée dans c2 à ces indices :

df.loc[df['c1'] == 'Value', 'c2'] = 10

Et vous vous retrouvez avec ça :

>>> df
      c1  c2
0      a   a
1      b   b
2      c   c
3      d   d
4      e   e
5  Value  10
6      g   g

Si, comme vous l'avez suggéré dans votre question, vous voulez peut-être parfois juste remplacer les valeurs de la colonne que vous avez déjà plutôt que de créer une nouvelle colonne, il suffit de sauter la création de la colonne et de faire ce qui suit :

df['c1'].loc[df['c1'] == 'Value'] = 10
# or:
df.loc[df['c1'] == 'Value', 'c1'] = 10

Je vous donne :

>>> df
      c1
0      a
1      b
2      c
3      d
4      e
5     10
6      g

8 votes

La deuxième solution a été déterminante pour moi. Je n'avais pas réalisé qu'on pouvait utiliser .loc comme une instruction WHERE en SQL. C'est logique. Je vous remercie.

0 votes

Comment appliquer 10 à plusieurs colonnes au lieu d'une seule ? Par exemple : df.loc[df['c1'] == 'Value', 'c2', 'c3', 'c4] = 10.

1 votes

Je pense que vous devez placer toutes les colonnes dont vous avez besoin pour mettre à jour la valeur dans une liste, puis faire une boucle dans cette liste et changer le paramètre du nom de la colonne dans celle-ci ?

54voto

DJK Points 4148

vous pouvez utiliser np.where() pour fixer des valeurs sur la base d'une codition

#df
   c1  c2  c3
0   4   2   1
1   8   7   9
2   1   5   8
3   3   3   5
4   3   6   8

Maintenant, changez les valeurs (ou définissez) dans la colonne ['c2'] en fonction de votre état.

df['c2'] = np.where(df.c1 == 8,'X',df.c3)

  c1  c3 c4
0   4   1  1
1   8   9  X
2   1   8  8
3   3   5  5
4   3   8  8

0 votes

Que faire si je veux garder toutes les colonnes originales

1 votes

@mLstudent33, utilisant df['newColName'] = ... si vous utilisez un nom de colonne qui n'est pas dans votre cadre de données pour créer une nouvelle colonne, en supposant que newColName n'existe pas déjà

1 votes

Supérieur à toutes les autres solutions, car (a) plus lisible en raison de la moindre ambiguïté de l'ordre des termes et (b) plus évolutif car il est plus facile de le modifier pour prendre en compte plusieurs colonnes et (c) rapide, pas de code interprété dans un lambda.

30voto

AlexanderHughes Points 137

essayez :

df['c2'] = df['c1'].apply(lambda x: 10 if x == 'Value' else x)

0 votes

Merci @AlexanderHughes. Mon message original comportait une faute de frappe : il y a en fait trois colonnes à prendre en compte, donc cette solution ne fonctionnerait pas.

7 votes

Devrait être df.apply(lambda x: 10 if x['c1'] == 'Value' else x['c3'],axis=1)

9 votes

Cela peut poser des problèmes de performance avec les grands ensembles de données. df.apply() est plus lent.

5voto

Ralf Points 9612

Je suggère de le faire en deux étapes :

# set fixed value to 'c2' where the condition is met
df.loc[df['c1'] == 'Value', 'c2'] = 10

# copy value from 'c3' to 'c2' where the condition is NOT met
df.loc[df['c1'] != 'Value', 'c2'] = df[df['c1'] != 'Value', 'c3']

0 votes

C'est très confus, pouvez-vous montrer quelques tableaux pour clarifier ?

5voto

nimbous Points 51

Vous pouvez utiliser pandas.DataFrame.mask pour ajouter virtuellement autant de conditions que vous le souhaitez :

data = {'a': [1,2,3,4,5], 'b': [6,8,9,10,11]}

d = pd.DataFrame.from_dict(data, orient='columns')
c = {'c1': (2, 'Value1'), 'c2': (3, 'Value2'), 'c3': (5, d['b'])}

d['new'] = np.nan
for value in c.values():
    d['new'].mask(d['a'] == value[0], value[1], inplace=True)

d['new'] = d['new'].fillna('Else')
d

Sortie :

    a   b   new
0   1   6   Else
1   2   8   Value1
2   3   9   Value2
3   4   10  Else
4   5   11  11

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X