Pourquoi ma fonction "Appliquer" de Pandas référençant plusieurs colonnes ne fonctionne-t-elle pas? [fermé]

239

J'ai des problèmes avec la fonction d'application Pandas, lorsque j'utilise plusieurs colonnes avec le cadre de données suivant

df = DataFrame ({'a' : np.random.randn(6),
                 'b' : ['foo', 'bar'] * 3,
                 'c' : np.random.randn(6)})

et la fonction suivante

def my_test(a, b):
    return a % b

Lorsque j'essaie d'appliquer cette fonction avec:

df['Value'] = df.apply(lambda row: my_test(row[a], row[c]), axis=1)

Je reçois le message d'erreur:

NameError: ("global name 'a' is not defined", u'occurred at index 0')

Je ne comprends pas ce message, j'ai bien défini le nom.

J'apprécierais grandement toute aide sur cette question

Mettre à jour

Merci de votre aide. J'ai en effet fait quelques erreurs de syntaxe avec le code, l'index devrait être mis ''. Cependant, je reçois toujours le même problème en utilisant une fonction plus complexe telle que:

def my_test(a):
    cum_diff = 0
    for ix in df.index():
        cum_diff = cum_diff + (a - df['a'][ix])
    return cum_diff 
Andy
la source
1
É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
Il s'agit simplement d'erreurs de syntaxe référençant une colonne de trame de données et pourquoi les fonctions ont-elles besoin d'arguments. Quant à votre deuxième question, la fonction my_test(a)ne sait pas ce qu'elle dfest car elle n'a pas été passée en argument (sauf si elle dfest supposée être globale, ce qui serait une pratique terrible). Vous devez passer toutes les valeurs dont vous aurez besoin dans une fonction comme arguments (de préférence dans l'ordre), sinon comment la fonction pourrait-elle savoir d'où dfvient-elle? De plus, c'est une mauvaise pratique de programmer dans un espace de noms jonché de variables globales, vous n'attraperez pas d'erreurs comme celle-ci.
smci

Réponses:

379

Semble que vous avez oublié le ''de votre chaîne.

In [43]: df['Value'] = df.apply(lambda row: my_test(row['a'], row['c']), axis=1)

In [44]: df
Out[44]:
                    a    b         c     Value
          0 -1.674308  foo  0.343801  0.044698
          1 -2.163236  bar -2.046438 -0.116798
          2 -0.199115  foo -0.458050 -0.199115
          3  0.918646  bar -0.007185 -0.001006
          4  1.336830  foo  0.534292  0.268245
          5  0.976844  bar -0.773630 -0.570417

BTW, à mon avis, la manière suivante est plus élégante:

In [53]: def my_test2(row):
....:     return row['a'] % row['c']
....:     

In [54]: df['Value'] = df.apply(my_test2, axis=1)
Waitkuo
la source
Merci, tu as raison j'ai oublié le ''. Cependant, j'ai toujours le même problème avec une fonction plus complexe. J'apprécierais grandement votre aide. Merci
Andy
5
@Andy suivant [53-54] vous permet d'appliquer des fonctions plus complexes.
Andy Hayden
@Andy vous pouvez définir votre fonction complexe comme la méthode In [53].
waitkuo
toutes les stratégies d'application fonctionnent-elles de la même manière? Je suis nouveau aux pandas et j'ai toujours trouvé appliquer un peu énigmatique mais votre stratégie en [53-54] est facile à comprendre pour moi (et j'espère m'en souvenir) ... sur une grande table est-ce aussi rapide que l'autre forme d'application présenté?
whytheq
Pourquoi est-ce que la création d'une méthode distincte est considérée comme plus élégante - même pour les méthodes minuscules. Je fais des projets importants en python depuis 7 ans, mais je ne serai probablement jamais considéré comme étant pythonistadû à certaines perspectives, y compris celle-ci.
javadba
33

Si vous voulez juste calculer (colonne a)% (colonne b), vous n'avez pas besoin apply, faites-le directement:

In [7]: df['a'] % df['c']                                                                                                                                                        
Out[7]: 
0   -1.132022                                                                                                                                                                    
1   -0.939493                                                                                                                                                                    
2    0.201931                                                                                                                                                                    
3    0.511374                                                                                                                                                                    
4   -0.694647                                                                                                                                                                    
5   -0.023486                                                                                                                                                                    
Name: a
herrfz
la source
16
Je sais, c'est juste un exemple pour montrer mon problème dans l'application d'une fonction à plusieurs colonnes
Andy
18

Disons que nous voulons appliquer une fonction add5 aux colonnes 'a' et 'b' de DataFrame df

def add5(x):
    return x+5

df[['a', 'b']].apply(add5)
Mir_Murtaza
la source
J'obtiens l'erreur suivante en essayant votre extrait de code. TypeError: ('doit être str, pas int', 's'est produit à l'index b') pouvez-vous s'il vous plaît examiner cela.
debaonline4u
La colonne b de votre dataframe est une colonne de type chaîne ou de type objet, elle doit être une colonne entière à ajouter avec un nombre.
Mir_Murtaza
Les modifications ne s'appliqueraient-elles qu'après l'affectation?
S.aad
11

Toutes les suggestions ci-dessus fonctionnent, mais si vous voulez que vos calculs soient plus efficaces, vous devriez profiter des opérations vectorielles numpy (comme indiqué ici) .

import pandas as pd
import numpy as np


df = pd.DataFrame ({'a' : np.random.randn(6),
             'b' : ['foo', 'bar'] * 3,
             'c' : np.random.randn(6)})

Exemple 1: boucle avec pandas.apply():

%%timeit
def my_test2(row):
    return row['a'] % row['c']

df['Value'] = df.apply(my_test2, axis=1)

La course la plus lente a pris 7,49 fois plus longtemps que la plus rapide. Cela pourrait signifier qu'un résultat intermédiaire est mis en cache. 1000 boucles, le meilleur de 3: 481 µs par boucle

Exemple 2: vectoriser en utilisant pandas.apply():

%%timeit
df['a'] % df['c']

La course la plus lente a pris 458,85 fois plus longtemps que la plus rapide. Cela pourrait signifier qu'un résultat intermédiaire est mis en cache. 10000 boucles, meilleur de 3: 70,9 µs par boucle

Exemple 3: vectoriser en utilisant des tableaux numpy:

%%timeit
df['a'].values % df['c'].values

La course la plus lente a pris 7,98 fois plus longtemps que la plus rapide. Cela pourrait signifier qu'un résultat intermédiaire est mis en cache. 100000 boucles, meilleur de 3: 6,39 µs par boucle

La vectorisation à l'aide de tableaux numpy a donc amélioré la vitesse de près de deux ordres de grandeur.

Blane
la source
Les résultats changent encore plus radicalement pour les grands nombres, par exemple en remplaçant 6 par 10K, j'obtiens respectivement 248 ms, 332 µs, 263 µs. Les deux solutions vectorisées sont donc beaucoup plus proches l'une de l'autre, mais la solution non vectorisée est 1000 fois plus lente. (testé sur python-3.7)
stason
3

C'est la même chose que la solution précédente mais j'ai défini la fonction dans df.apply lui-même:

df['Value'] = df.apply(lambda row: row['a']%row['c'], axis=1)
shaurya airi
la source
2

J'ai donné la comparaison des trois ci-dessus.

Utiliser des valeurs

% timeit df ['value'] = df ['a']. values% df ['c']. values

139 µs ± 1,91 µs par boucle (moyenne ± écart standard de 7 pistes, 10000 boucles chacune)

Sans valeurs

% timeit df ['value'] = df ['a']% df ['c'] 

216 µs ± 1,86 µs par boucle (écart moyen ± standard de 7 passages, 1000 boucles chacun)

Appliquer la fonction

% timeit df ['Value'] = df.apply (ligne lambda: ligne ['a']% ligne ['c'], axe = 1)

474 µs ± 5,07 µs par boucle (écart moyen ± standard de 7 passages, 1000 boucles chacun)

Gursewak Singh
la source