Pandas: Comment puis-je utiliser la fonction apply () pour une seule colonne?

260

J'ai un cadre de données pandas avec deux colonnes. J'ai besoin de modifier les valeurs de la première colonne sans affecter la seconde et de récupérer l'intégralité du bloc de données avec uniquement les valeurs de la première colonne modifiées. Comment puis-je faire cela en utilisant Appliquer dans les pandas?

Amani
la source
4
Veuillez publier des données d'échantillon d'entrée et la sortie souhaitée.
Fabio Lamanna
Vous ne devriez presque jamais l'utiliser applydans une situation comme celle-ci. Opérez directement sur la colonne.
Ted Petrou
Comme l'a dit Ted Petrou, évitez d'en utiliser applyautant que possible. Si vous n'êtes pas sûr de devoir l'utiliser, ce n'est probablement pas le cas. Je recommande de jeter un œil à Quand devrais-je jamais vouloir utiliser pandas apply () dans mon code? .
cs95
La question n'est pas complètement claire: s'agit-il d'appliquer une fonction à chaque élément d'une colonne ou d'appliquer une fonction à la colonne dans son ensemble (par exemple: inverser la colonne)?
Pierre ALBARÈDE

Réponses:

339

Étant donné un exemple de trame dfde données:

a,b
1,2
2,3
3,4
4,5

ce que vous voulez c'est:

df['a'] = df['a'].apply(lambda x: x + 1)

qui renvoie:

   a  b
0  2  2
1  3  3
2  4  4
3  5  5
Fabio Lamanna
la source
9
applyne devrait jamais être utilisé dans une situation comme celle-ci
Ted Petrou
5
@TedPetrou vous avez parfaitement raison, c'était juste un exemple sur la façon d'appliquer une fonction générale sur une seule colonne, comme l'OP l'a demandé.
Fabio Lamanna
14
Lorsque j'essaie de faire cela, j'obtiens l'avertissement suivant: "Une valeur tente d'être définie sur une copie d'une tranche à partir d'un DataFrame. Essayez plutôt d'utiliser .loc [row_indexer, col_indexer] = value"
dagrun
24
Par curiosité: pourquoi ne pas appliquer dans cette situation? Quelle est la situation exactement?
Oncle Ben Ben
19
@UncleBenBen applyutilise en général une boucle interne sur les lignes qui est beaucoup plus lente que les fonctions vectorisées, comme par exemple df.a = df.a / 2(voir la réponse de Mike Muller).
Fabio Lamanna
66

Pour une seule colonne mieux à utiliser map(), comme ceci:

df = pd.DataFrame([{'a': 15, 'b': 15, 'c': 5}, {'a': 20, 'b': 10, 'c': 7}, {'a': 25, 'b': 30, 'c': 9}])

    a   b  c
0  15  15  5
1  20  10  7
2  25  30  9



df['a'] = df['a'].map(lambda a: a / 2.)

      a   b  c
0   7.5  15  5
1  10.0  10  7
2  12.5  30  9
George Petrov
la source
78
Pourquoi est-ce map()mieux que apply()pour une seule colonne?
ChaimG
2
C'était très utile. Je l'ai utilisé pour extraire les noms de fichiers des chemins stockés dans une colonnedf['file_name'] = df['Path'].map(lambda a: os.path.basename(a))
mmann1123
46
map () est pour Series (c'est-à-dire des colonnes uniques) et fonctionne sur une cellule à la fois, tandis que apply () est pour DataFrame et fonctionne sur une ligne entière à la fois.
jpcgt
3
@jpcgt Cela signifie-t-il que la carte est plus rapide que dans ce cas?
Viragos
@ChaimG je vois que ce système d'exploitation explique bien: stackoverflow.com/a/19798528/571828
象 嘉 道
41

Vous n'avez absolument pas besoin d'une fonction. Vous pouvez travailler directement sur une colonne entière.

Exemples de données:

>>> df = pd.DataFrame({'a': [100, 1000], 'b': [200, 2000], 'c': [300, 3000]})
>>> df

      a     b     c
0   100   200   300
1  1000  2000  3000

La moitié de toutes les valeurs de la colonne a:

>>> df.a = df.a / 2
>>> df

     a     b     c
0   50   200   300
1  500  2000  3000
Mike Müller
la source
Et si je veux diviser chaque élément d'une colonne par "/" et prendre la première partie?
K47
12

Bien que les réponses données soient correctes, elles modifient la trame de données initiale, ce qui n'est pas toujours souhaitable (et, étant donné que l'OP a demandé des exemples "en utilisant apply", il se pourrait qu'ils voulaient une version qui renvoie une nouvelle trame de données, tout comme apply).

Ceci est possible en utilisant assign: il est valable pour assignles colonnes existantes, comme l'indique la documentation (c'est moi qui souligne):

Attribuez de nouvelles colonnes à un DataFrame.

Renvoie un nouvel objet avec toutes les colonnes d'origine en plus de nouvelles. Les colonnes existantes qui sont réaffectées seront remplacées .

En bref:

In [1]: import pandas as pd

In [2]: df = pd.DataFrame([{'a': 15, 'b': 15, 'c': 5}, {'a': 20, 'b': 10, 'c': 7}, {'a': 25, 'b': 30, 'c': 9}])

In [3]: df.assign(a=lambda df: df.a / 2)
Out[3]: 
      a   b  c
0   7.5  15  5
1  10.0  10  7
2  12.5  30  9

In [4]: df
Out[4]: 
    a   b  c
0  15  15  5
1  20  10  7
2  25  30  9

Notez que la fonction transmettra toute la trame de données, pas seulement la colonne que vous souhaitez modifier, vous devrez donc vous assurer de sélectionner la bonne colonne dans votre lambda.

Thibaut Dubernet
la source
9

Si vous êtes vraiment préoccupé par la vitesse d'exécution de votre fonction d'application et que vous avez un énorme ensemble de données sur lequel travailler, vous pouvez utiliser swifter pour accélérer l'exécution, voici un exemple pour swifter sur le cadre de données pandas:

import pandas as pd
import swifter

def fnc(m):
    return m*3+4

df = pd.DataFrame({"m": [1,2,3,4,5,6], "c": [1,1,1,1,1,1], "x":[5,3,6,2,6,1]})

# apply a self created function to a single column in pandas
df["y"] = df.m.swifter.apply(fnc)

Cela permettra à tous vos cœurs de processeur de calculer le résultat, ce qui sera beaucoup plus rapide que les fonctions d'application normales. Essayez de me faire savoir si cela vous est utile.

durjoy
la source
1

Permettez-moi d'essayer un calcul complexe en utilisant datetime et en considérant les null ou les espaces vides. Je réduis 30 ans sur une colonne datetime et utilise la applyméthode ainsi que lambdaet convertis le format datetime. La ligne if x != '' else xprendra soin de tous les espaces vides ou nuls en conséquence.

df['Date'] = df['Date'].fillna('')
df['Date'] = df['Date'].apply(lambda x : ((datetime.datetime.strptime(str(x), '%m/%d/%Y') - datetime.timedelta(days=30*365)).strftime('%Y%m%d')) if x != '' else x)
Harry_pb
la source