web-dev-qa-db-fra.com

Pandas création conditionnelle d'une colonne series / dataframe

J'ai un dataframe le long des lignes ci-dessous:

    Type       Set
1    A          Z
2    B          Z           
3    B          X
4    C          Y

Je souhaite ajouter une autre colonne à la structure de données (ou générer une série) de la même longueur que la structure de données (= nombre égal d'enregistrements/lignes), qui définit une couleur verte si Set = 'Z' et 'rouge' si Set = sinon .

Quelle est la meilleure façon de faire cela?

233
user7289

Si vous ne pouvez choisir que parmi deux choix:

_df['color'] = np.where(df['Set']=='Z', 'green', 'red')
_

Par exemple,

_import pandas as pd
import numpy as np

df = pd.DataFrame({'Type':list('ABBC'), 'Set':list('ZZXY')})
df['color'] = np.where(df['Set']=='Z', 'green', 'red')
print(df)
_

les rendements

_  Set Type  color
0   Z    A  green
1   Z    B  green
2   X    B    red
3   Y    C    red
_

Si vous avez plus de deux conditions, utilisez np.select. Par exemple, si vous voulez que color soit

  • yellow quand _(df['Set'] == 'Z') & (df['Type'] == 'A')_
  • sinon blue quand _(df['Set'] == 'Z') & (df['Type'] == 'B')_
  • sinon purple quand _(df['Type'] == 'B')_
  • sinon black,

puis utiliser

_df = pd.DataFrame({'Type':list('ABBC'), 'Set':list('ZZXY')})
conditions = [
    (df['Set'] == 'Z') & (df['Type'] == 'A'),
    (df['Set'] == 'Z') & (df['Type'] == 'B'),
    (df['Type'] == 'B')]
choices = ['yellow', 'blue', 'purple']
df['color'] = np.select(conditions, choices, default='black')
print(df)
_

qui donne

_  Set Type   color
0   Z    A  yellow
1   Z    B    blue
2   X    B  purple
3   Y    C   black
_
548
unutbu

La compréhension de liste est un autre moyen de créer une autre colonne de manière conditionnelle. Si vous utilisez des types d'objet dans les colonnes, comme dans votre exemple, les compréhensions de liste sont généralement plus performantes que la plupart des autres méthodes.

Exemple de compréhension de liste:

df['color'] = ['red' if x == 'Z' else 'green' for x in df['Set']]

% timeit tests:

import pandas as pd
import numpy as np

df = pd.DataFrame({'Type':list('ABBC'), 'Set':list('ZZXY')})
%timeit df['color'] = ['red' if x == 'Z' else 'green' for x in df['Set']]
%timeit df['color'] = np.where(df['Set']=='Z', 'green', 'red')
%timeit df['color'] = df.Set.map( lambda x: 'red' if x == 'Z' else 'green')

1000 loops, best of 3: 239 µs per loop
1000 loops, best of 3: 523 µs per loop
1000 loops, best of 3: 263 µs per loop
93
cheekybastard

Voici encore un autre moyen d'adapter ce chat, en utilisant un dictionnaire pour mapper de nouvelles valeurs sur les clés de la liste:

def map_values(row, values_dict):
    return values_dict[row]

values_dict = {'A': 1, 'B': 2, 'C': 3, 'D': 4}

df = pd.DataFrame({'INDICATOR': ['A', 'B', 'C', 'D'], 'VALUE': [10, 9, 8, 7]})

df['NEW_VALUE'] = df['INDICATOR'].apply(map_values, args = (values_dict,))

A quoi ça ressemble:

df
Out[2]: 
  INDICATOR  VALUE  NEW_VALUE
0         A     10          1
1         B      9          2
2         C      8          3
3         D      7          4

Cette approche peut être très puissante lorsque vous avez plusieurs déclarations de type ifelse- à effectuer (c’est-à-dire de nombreuses valeurs uniques à remplacer).

Et bien sûr, vous pouvez toujours faire ceci:

df['NEW_VALUE'] = df['INDICATOR'].map(values_dict)

Mais cette approche est plus de trois fois plus lente que la apply approche d’en haut, sur ma machine.

Et vous pouvez aussi le faire en utilisant dict.get:

df['NEW_VALUE'] = [values_dict.get(v, None) for v in df['INDICATOR']]
19
blacksite

Une autre manière d’y parvenir est de

df['color'] = df.Set.map( lambda x: 'red' if x == 'Z' else 'green')
16
acharuva

Ce qui suit est plus lent que les approches temporisées ici , mais nous pouvons calculer la colonne supplémentaire en fonction du contenu de plus d'une colonne, et plus de deux valeurs peuvent être calculées pour la colonne supplémentaire.

Exemple simple utilisant uniquement la colonne "Set":

def set_color(row):
    if row["Set"] == "Z":
        return "red"
    else:
        return "green"

df = df.assign(color=df.apply(set_color, axis=1))

print(df)
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B  green
3   Y    C  green

Exemple avec plus de couleurs et plus de colonnes prises en compte:

def set_color(row):
    if row["Set"] == "Z":
        return "red"
    Elif row["Type"] == "C":
        return "blue"
    else:
        return "green"

df = df.assign(color=df.apply(set_color, axis=1))

print(df)
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B  green
3   Y    C   blue

Edit (21/06/2019): Utiliser plydata

Il est également possible d'utiliser plydata pour faire ce genre de choses (cela semble encore plus lent que d'utiliser assign et apply, cependant).

from plydata import define, if_else

Simple if_else:

df = define(df, color=if_else('Set=="Z"', '"red"', '"green"'))

print(df)
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B  green
3   Y    C  green

Nested if_else:

df = define(df, color=if_else(
    'Set=="Z"',
    '"red"',
    if_else('Type=="C"', '"green"', '"blue"')))

print(df)                            
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B   blue
3   Y    C  green
13
bli

Cela a peut-être été possible avec les nouvelles mises à jour de Pandas, mais je pense que ce qui suit est la réponse la plus courte et peut-être la meilleure pour la question, jusqu'à présent. Vous pouvez utiliser une ou plusieurs conditions selon vos besoins.

df=pd.DataFrame(dict(Type='A B B C'.split(), Set='Z Z X Y'.split()))
df['Color'] = "red"
df.loc[(df['Set']=="Z"), 'Color'] = "green"
print(df)

# result: 
  Type Set  Color
0    A   Z  green
1    B   Z  green
2    B   X    red
3    C   Y    red
2
HKRC