187 votes

Modifier une valeur en fonction d'une autre valeur dans pandas

J'essaie de reproduire mon code Stata en Python, et on m'a orienté vers Pandas. Cependant, j'ai du mal à comprendre comment traiter les données.

Supposons que je veuille passer en revue toutes les valeurs de l'en-tête de la colonne "ID" ; si l'ID correspond à un nombre spécifique, je veux modifier les deux valeurs correspondantes FirstName (nom) et LastName (prénom).

Dans Stata, cela ressemble à ceci :

replace FirstName = "Matt" if ID==103
replace LastName =  "Jones" if ID==103

Ainsi, toutes les valeurs de FirstName correspondant à des valeurs d'ID == 103 sont remplacées par Matt.

Dans Pandas, j'essaie quelque chose comme ceci

df = read_csv("test.csv")
for i in df['ID']:
    if i ==103:
          ...

Je ne sais pas trop où aller à partir de maintenant. Vous avez des idées ?

314voto

EMS Points 9249

Une option consiste à utiliser les fonctions de découpage et d'indexation de Python pour évaluer logiquement les endroits où votre condition s'applique et écraser les données à cet endroit.

En supposant que vous puissiez charger vos données directement dans le fichier pandas avec pandas.read_csv le code suivant pourrait vous être utile.

import pandas
df = pandas.read_csv("test.csv")
df.loc[df.ID == 103, 'FirstName'] = "Matt"
df.loc[df.ID == 103, 'LastName'] = "Jones"

Comme indiqué dans les commentaires, vous pouvez également procéder à l'affectation des deux colonnes en une seule fois :

df.loc[df.ID == 103, ['FirstName', 'LastName']] = 'Matt', 'Jones'

Notez que vous aurez besoin de pandas version 0.11 ou plus récente pour utiliser la fonction loc pour les opérations d'affectation par écrasement. En effet, pour les anciennes versions comme la 0.8 (malgré ce que peuvent dire les détracteurs de l'assignation chaînée), l'assignation chaînée est la bonne c'est pourquoi il est utile de la connaître, même si elle doit être évitée dans les versions plus modernes de pandas.


Une autre façon de procéder consiste à utiliser ce que l'on appelle l'affectation en chaîne. Le comportement de cette méthode est moins stable et elle n'est donc pas considérée comme la meilleure solution (elle est explicitement déconseillé dans la documentation), mais il est utile de le savoir :

import pandas
df = pandas.read_csv("test.csv")
df['FirstName'][df.ID == 103] = "Matt"
df['LastName'][df.ID == 103] = "Jones"

53voto

Rutger Kassies Points 7713

Vous pouvez utiliser map Il peut mapper des valeurs à partir d'un dicton ou même d'une fonction personnalisée.

Supposons qu'il s'agisse de votre df :

    ID First_Name Last_Name
0  103          a         b
1  104          c         d

Créer les dictionnaires :

fnames = {103: "Matt", 104: "Mr"}
lnames = {103: "Jones", 104: "X"}

Et carte :

df['First_Name'] = df['ID'].map(fnames)
df['Last_Name'] = df['ID'].map(lnames)

Le résultat sera le suivant :

    ID First_Name Last_Name
0  103       Matt     Jones
1  104         Mr         X

Ou utiliser une fonction personnalisée :

names = {103: ("Matt", "Jones"), 104: ("Mr", "X")}
df['First_Name'] = df['ID'].map(lambda x: names[x][0])

35voto

ccpizza Points 2653

La question initiale porte sur un cas d'utilisation spécifique et restreint. Pour ceux qui ont besoin de réponses plus générales, voici quelques exemples :

Création d'une nouvelle colonne à partir de données provenant d'autres colonnes

Etant donné le cadre de données ci-dessous :

import pandas as pd
import numpy as np

df = pd.DataFrame([['dog', 'hound', 5],
                   ['cat', 'ragdoll', 1]],
                  columns=['animal', 'type', 'age'])

In[1]:
Out[1]:
  animal     type  age
----------------------
0    dog    hound    5
1    cat  ragdoll    1

Ci-dessous, nous ajoutons un nouveau description comme une concaténation d'autres colonnes en utilisant l'option + qui est remplacée par une opération en série. Le formatage fantaisiste des chaînes de caractères, les chaînes f, etc. ne fonctionneront pas ici, puisque la fonction + s'applique aux scalaires et non aux valeurs "primitives" :

df['description'] = 'A ' + df.age.astype(str) + ' years old ' \
                    + df.type + ' ' + df.animal

In [2]: df
Out[2]:
  animal     type  age                description
-------------------------------------------------
0    dog    hound    5    A 5 years old hound dog
1    cat  ragdoll    1  A 1 years old ragdoll cat

Nous obtenons 1 years pour le chat (au lieu de 1 year ) que nous allons corriger ci-dessous à l'aide de conditionnels.

Modifier une colonne existante avec des conditionnelles

Ici, nous remplaçons l'original animal avec des valeurs provenant d'autres colonnes, et en utilisant np.where pour définir une sous-chaîne conditionnelle basée sur la valeur de age :

# append 's' to 'age' if it's greater than 1
df.animal = df.animal + ", " + df.type + ", " + \
    df.age.astype(str) + " year" + np.where(df.age > 1, 's', '')

In [3]: df
Out[3]:
                 animal     type  age
-------------------------------------
0   dog, hound, 5 years    hound    5
1  cat, ragdoll, 1 year  ragdoll    1

Modifier plusieurs colonnes avec des conditionnelles

Une approche plus souple consiste à appeler .apply() sur l'ensemble d'un cadre de données plutôt que sur une seule colonne :

def transform_row(r):
    r.animal = 'wild ' + r.type
    r.type = r.animal + ' creature'
    r.age = "{} year{}".format(r.age, r.age > 1 and 's' or '')
    return r

df.apply(transform_row, axis=1)

In[4]:
Out[4]:
         animal            type      age
----------------------------------------
0    wild hound    dog creature  5 years
1  wild ragdoll    cat creature   1 year

Dans le code ci-dessus, l'élément transform_row(r) prend un Series représentant une ligne donnée (indiquée par axis=1 la valeur par défaut de axis=0 fournira une Series pour chaque colonne). Cela simplifie le traitement puisque vous pouvez accéder aux valeurs "primitives" réelles de la ligne en utilisant les noms des colonnes et que vous avez la visibilité des autres cellules dans la ligne/colonne donnée.

15voto

Bill Bell Points 1357

Cette question est peut-être encore assez souvent posée pour qu'il vaille la peine d'ajouter un complément à la réponse de M. Kassies. La réponse de M. Kassies dict peut être sous-classée afin qu'une valeur par défaut soit renvoyée pour les clés "manquantes". Ce mécanisme fonctionne bien pour pandas. Mais voir ci-dessous.

Il est ainsi possible d'éviter les principales erreurs.

>>> import pandas as pd
>>> data = { 'ID': [ 101, 201, 301, 401 ] }
>>> df = pd.DataFrame(data)
>>> class SurnameMap(dict):
...     def __missing__(self, key):
...         return ''
...     
>>> surnamemap = SurnameMap()
>>> surnamemap[101] = 'Mohanty'
>>> surnamemap[301] = 'Drake'
>>> df['Surname'] = df['ID'].apply(lambda x: surnamemap[x])
>>> df
    ID  Surname
0  101  Mohanty
1  201         
2  301    Drake
3  401         

La même chose peut être faite plus simplement de la manière suivante. L'utilisation de l'argument "default" pour la fonction get d'un objet dict, il n'est pas nécessaire de sous-classer un dict.

>>> import pandas as pd
>>> data = { 'ID': [ 101, 201, 301, 401 ] }
>>> df = pd.DataFrame(data)
>>> surnamemap = {}
>>> surnamemap[101] = 'Mohanty'
>>> surnamemap[301] = 'Drake'
>>> df['Surname'] = df['ID'].apply(lambda x: surnamemap.get(x, ''))
>>> df
    ID  Surname
0  101  Mohanty
1  201         
2  301    Drake
3  401

9voto

mpriya Points 331
df['FirstName']=df['ID'].apply(lambda x: 'Matt' if x==103 else '')
df['LastName']=df['ID'].apply(lambda x: 'Jones' if x==103 else '')

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