552 votes

Comment appliquer une fonction à deux colonnes d'un cadre de données Pandas ?

Supposons que j'ai un df qui a des colonnes de 'ID', 'col_1', 'col_2' . Et je définis une fonction :

f = lambda x, y : my_function_expression .

Maintenant, je veux appliquer le f à df Les deux colonnes de l'article 'col_1', 'col_2' pour calculer par éléments une nouvelle colonne 'col_3' , un peu comme :

df['col_3'] = df[['col_1','col_2']].apply(f)  
# Pandas gives : TypeError: ('<lambda>() takes exactly 2 arguments (1 given)'

Comment faire ?

** Ajouter un échantillon détaillé comme ci-dessous ***

import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
mylist = ['a','b','c','d','e','f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

#df['col_3'] = df[['col_1','col_2']].apply(get_sublist,axis=1)
# expect above to output df as below 

  ID  col_1  col_2            col_3
0  1      0      1       ['a', 'b']
1  2      2      4  ['c', 'd', 'e']
2  3      3      5  ['d', 'e', 'f']

4 votes

pouvez-vous appliquer f directement aux colonnes : df['col_3'] = f(df['col_1'],df['col_2'])

1 votes

Il serait utile de savoir ce que f fait

2 votes

non, df['col_3'] = f(df['col_1'],df['col_2']) ne fonctionne pas. Car f n'accepte que les entrées scalaires, pas les entrées vectorielles. OK, vous pouvez supposer que f = lambda x,y : x+y . (bien sûr, mon f réel n'est pas aussi simple, sinon je peux directement df['col_3'] = df['col_1'] + df['col_2'] )

437voto

Aman Points 3440

Voici un exemple utilisant apply sur le cadre de données, que j'appelle avec axis = 1 .

Notez que la différence est qu'au lieu d'essayer de passer deux valeurs à la fonction f Dans ce cas, réécrivez la fonction pour accepter un objet Série pandas, puis indexez la Série pour obtenir les valeurs nécessaires.

In [49]: df
Out[49]: 
          0         1
0  1.000000  0.000000
1 -0.494375  0.570994
2  1.000000  0.000000
3  1.876360 -0.229738
4  1.000000  0.000000

In [50]: def f(x):    
   ....:  return x[0] + x[1]  
   ....:  

In [51]: df.apply(f, axis=1) #passes a Series object, row-wise
Out[51]: 
0    1.000000
1    0.076619
2    1.000000
3    1.646622
4    1.000000

En fonction de votre cas d'utilisation, il est parfois utile de créer un module pandas group et ensuite utiliser apply sur le groupe.

0 votes

Oui, j'ai essayé d'utiliser apply, mais je ne trouve pas d'expression syntaxique valide. Et si chaque ligne de df est unique, faut-il encore utiliser groupby ?

0 votes

J'ai ajouté un exemple à ma réponse, j'espère que cela correspond à ce que vous recherchez. Si non, veuillez fournir un exemple de fonction plus spécifique puisque sum est résolu avec succès par l'une des méthodes proposées jusqu'à présent.

0 votes

Je fournis un exemple détaillé dans la question. Comment utiliser la fonction 'apply' de Pandas pour créer 'col_3' ?

409voto

ajrwhite Points 627

Il existe une méthode propre, en une ligne, pour effectuer cette opération dans Pandas :

df['col_3'] = df.apply(lambda x: f(x.col_1, x.col_2), axis=1)

Cela permet f est une fonction définie par l'utilisateur avec des valeurs d'entrée multiples, et utilise des noms de colonnes (sûrs) plutôt que des indices numériques (non sûrs) pour accéder aux colonnes.

Exemple avec données (basé sur la question initiale) :

import pandas as pd

df = pd.DataFrame({'ID':['1', '2', '3'], 'col_1': [0, 2, 3], 'col_2':[1, 4, 5]})
mylist = ['a', 'b', 'c', 'd', 'e', 'f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

df['col_3'] = df.apply(lambda x: get_sublist(x.col_1, x.col_2), axis=1)

Sortie de print(df) :

  ID  col_1  col_2      col_3
0  1      0      1     [a, b]
1  2      2      4  [c, d, e]
2  3      3      5  [d, e, f]

Si les noms de vos colonnes contiennent des espaces ou partagent un nom avec un attribut de cadre de données existant, vous pouvez les indexer avec des crochets :

df['col_3'] = df.apply(lambda x: f(x['col 1'], x['col 2']), axis=1)

2 votes

Remarque, si vous utilisez axis=1 et votre colonne s'appelle name il ne renverra pas réellement les données de votre colonne mais le index . Similaire à l'obtention du name dans un groupby() . J'ai résolu ce problème en renommant ma colonne.

7 votes

C'EST ÇA ! Je n'avais pas réalisé que l'on pouvait insérer des fonctions définies par l'utilisateur avec plusieurs paramètres d'entrée dans des lambdas. Il est important de noter (je pense) que vous utilisez DF.apply() plutôt que Series.apply(). Cela vous permet d'indexer le df en utilisant les deux colonnes que vous voulez, et de passer la colonne entière dans la fonction, mais parce que vous utilisez apply(), il applique la fonction d'une manière élémentaire dans la colonne entière. C'est génial ! Merci de l'avoir publié !

3 votes

ENFIN ! Tu as sauvé ma journée !

134voto

sjm Points 1299

La solution est simple :

df['col_3'] = df[['col_1','col_2']].apply(lambda x: f(*x), axis=1)

1 votes

En quoi cette réponse est-elle différente de l'approche de la question : df['col_3'] = df[['col_1','col_2']].apply(f) juste pour confirmer, l'approche de la question n'a pas fonctionné parce que le posteur n'a pas spécifié cet axis=1, le défaut est axis = 0 ?

1 votes

Cette réponse est comparable à celle de @Anman, mais un peu plus fine. Il construit une fonction anonyme qui prend un itérable, et le dépaquette avant de le passer à la fonction f.

1 votes

Cette méthode est deux fois plus rapide dans mon cas, avec 100k rangs (par rapport à df.apply(lambda x: f(x.col_1, x.col_2), axis=1) )

44voto

Une question intéressante ! Ma réponse est la suivante :

import pandas as pd

def sublst(row):
    return lst[row['J1']:row['J2']]

df = pd.DataFrame({'ID':['1','2','3'], 'J1': [0,2,3], 'J2':[1,4,5]})
print df
lst = ['a','b','c','d','e','f']

df['J3'] = df.apply(sublst,axis=1)
print df

Sortie :

  ID  J1  J2
0  1   0   1
1  2   2   4
2  3   3   5
  ID  J1  J2      J3
0  1   0   1     [a]
1  2   2   4  [c, d]
2  3   3   5  [d, e]

J'ai changé le nom de la colonne en ID,J1,J2,J3 pour m'assurer que ID < J1 < J2 < J3, afin que la colonne s'affiche dans la bonne séquence.

Une version plus brève :

import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'J1': [0,2,3], 'J2':[1,4,5]})
print df
lst = ['a','b','c','d','e','f']

df['J3'] = df.apply(lambda row:lst[row['J1']:row['J2']],axis=1)
print df

27voto

JoeCondron Points 4533

La méthode que vous recherchez est Series.combine. Cependant, il semble qu'il faille faire attention aux types de données. Dans votre exemple, vous pourriez (comme je l'ai fait en testant la réponse) appeler naïvement

df['col_3'] = df.col_1.combine(df.col_2, func=get_sublist)

Cependant, cela provoque l'erreur suivante :

ValueError: setting an array element with a sequence.

Mon hypothèse la plus probable est qu'il semble s'attendre à ce que le résultat soit du même type que la série qui appelle la méthode (df.col_1 ici). Cependant, la méthode suivante fonctionne :

df['col_3'] = df.col_1.astype(object).combine(df.col_2, func=get_sublist)

df

   ID   col_1   col_2   col_3
0   1   0   1   [a, b]
1   2   2   4   [c, d, e]
2   3   3   5   [d, e, f]

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