Warum funktioniert die Funktion "Anwenden" meiner Pandas, die auf mehrere Spalten verweist, nicht? [geschlossen]


239

Ich habe einige Probleme mit der Pandas-Apply-Funktion, wenn mehrere Spalten mit dem folgenden Datenrahmen verwendet werden

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

und die folgende Funktion

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

Wenn ich versuche, diese Funktion anzuwenden mit:

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

Ich erhalte die Fehlermeldung:

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

Ich verstehe diese Nachricht nicht, ich habe den Namen richtig definiert.

Ich würde mich über jede Hilfe zu diesem Thema sehr freuen

Aktualisieren

Danke für Ihre Hilfe. Ich habe in der Tat einige Syntaxfehler mit dem Code gemacht, der Index sollte '' gesetzt werden. Ich bekomme jedoch immer noch das gleiche Problem mit einer komplexeren Funktion wie:

def my_test(a):
    cum_diff = 0
    for ix in df.index():
        cum_diff = cum_diff + (a - df['a'][ix])
    return cum_diff 

1
Vermeiden Sie es, applyso viel wie möglich zu verwenden. Wenn Sie nicht sicher sind, ob Sie es verwenden müssen, tun Sie es wahrscheinlich nicht. Ich empfehle einen Blick auf Wann sollte ich jemals pandas apply () in meinem Code verwenden wollen? .
cs95

Hier geht es nur um Syntaxfehler, die auf eine Datenrahmenspalte verweisen, und warum Funktionen Argumente benötigen. Was Ihre zweite Frage my_test(a)betrifft, weiß die Funktion nicht, was es dfist, da sie nicht als Argument übergeben wurde (es dfsei denn, es handelt sich um eine globale Frage, was eine schreckliche Praxis wäre). Sie müssen alle Werte, die Sie innerhalb einer Funktion benötigen, als Argumente übergeben (vorzugsweise in der Reihenfolge). Wie sonst würde die Funktion sonst wissen, woher sie dfkommt? Es ist auch eine schlechte Praxis, in einem mit globalen Variablen übersäten Namespace zu programmieren. Sie werden solche Fehler nicht abfangen.
smci

Antworten:


379

Scheint, als hättest du ''deine Saite vergessen .

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

Übrigens ist meiner Meinung nach der folgende Weg eleganter:

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

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

Danke, du hast recht, ich habe das '' vergessen. Ich habe jedoch immer noch das gleiche Problem mit einer komplexeren Funktion. Ich würde mich sehr über Ihre Hilfe freuen. Danke
Andy

5
Mit @Andy [53-54] können Sie komplexere Funktionen anwenden.
Andy Hayden

@Andy Sie können Ihre komplexe Funktion wie In [53] definieren.
Warten

Führen alle angewandten Strategien die gleiche Leistung aus? Ich bin neu in Pandas und habe immer festgestellt, dass die Anwendung etwas rätselhaft ist, aber Ihre Strategie in [53-54] ist für mich leicht zu verstehen (und ich erinnere mich hoffentlich) ... auf einem großen Tisch ist es so schnell wie die andere Form der Anwendung vorgestellt?
Whytheq

Warum wird das Erstellen einer separaten Methode als eleganter angesehen - selbst bei winzigen Methoden? Ich mache seit 7 Jahren bedeutende Projekte in Python, werde aber pythonistaaufgrund einiger Perspektiven, einschließlich dieser, wahrscheinlich nie als solche angesehen .
Javadba

33

Wenn Sie nur (Spalte a)% (Spalte b) berechnen möchten, müssen Sie dies nicht applydirekt tun:

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

16
Ich weiß, es ist nur ein Beispiel, um mein Problem beim Anwenden einer Funktion auf mehrere Spalten zu zeigen
Andy

18

Angenommen, wir möchten eine Funktion add5 auf die Spalten 'a' und 'b' von DataFrame df anwenden

def add5(x):
    return x+5

df[['a', 'b']].apply(add5)

Beim Versuch Ihres Code-Snippets wird folgende Fehlermeldung angezeigt. TypeError: ('muss str sein, nicht int', 'ist bei Index b aufgetreten') Können Sie das bitte untersuchen?
debaonline4u

Die Spalte b Ihres Datenrahmens ist eine Zeichenfolgentyp- oder Objekttypspalte. Es sollte sich um eine Ganzzahlspalte handeln, die mit einer Zahl hinzugefügt werden soll.
Mir_Murtaza

Würden die Änderungen nicht erst nach der Zuweisung gelten?
S.aad

11

Alle oben genannten Vorschläge funktionieren, aber wenn Sie möchten, dass Ihre Berechnungen effizienter werden, sollten Sie die Anzahl der Vektoroperationen nutzen (wie hier ausgeführt) .

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)})

Beispiel 1: Schleifen mit pandas.apply():

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

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

Der langsamste Lauf dauerte 7,49-mal länger als der schnellste. Dies könnte bedeuten, dass ein Zwischenergebnis zwischengespeichert wird. 1000 Schleifen, am besten 3: 481 µs pro Schleife

Beispiel 2: Vektorisieren mit pandas.apply():

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

Der langsamste Lauf dauerte 458,85-mal länger als der schnellste. Dies könnte bedeuten, dass ein Zwischenergebnis zwischengespeichert wird. 10000 Schleifen, am besten 3: 70,9 µs pro Schleife

Beispiel 3: Vektorisieren mit Numpy-Arrays:

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

Der langsamste Lauf dauerte 7,98 Mal länger als der schnellste. Dies könnte bedeuten, dass ein Zwischenergebnis zwischengespeichert wird. 100000 Schleifen, am besten 3: 6,39 µs pro Schleife

Das Vektorisieren mit Numpy-Arrays verbesserte die Geschwindigkeit um fast zwei Größenordnungen.


Die Ergebnisse ändern sich bei großen Zahlen noch dramatischer, z. B. wenn ich 6 durch 10K ersetze, erhalte ich 248 ms, 332 µs bzw. 263 µs. Beide vektorisierten Lösungen sind also viel näher beieinander, aber die nicht vektorisierte Lösung ist 1000-mal langsamer. (getestet auf Python-3.7)
Stason

3

Dies ist das gleiche wie bei der vorherigen Lösung, aber ich habe die Funktion in df.apply selbst definiert:

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

2

Ich habe den Vergleich aller drei oben diskutierten gegeben.

Werte verwenden

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

139 µs ± 1,91 µs pro Schleife (Mittelwert ± Standardabweichung von 7 Läufen, jeweils 10000 Schleifen)

Ohne Werte

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

216 µs ± 1,86 µs pro Schleife (Mittelwert ± Standardabweichung von 7 Läufen, jeweils 1000 Schleifen)

Funktion anwenden

% timeit df ['Wert'] = df.apply (Lambda-Zeile: Zeile ['a']% Zeile ['c'], Achse = 1)

474 µs ± 5,07 µs pro Schleife (Mittelwert ± Standardabweichung von 7 Läufen, jeweils 1000 Schleifen)

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.