Dlaczego moja funkcja „Zastosuj” Pandy nie działa w przypadku wielu kolumn? [Zamknięte]

239

Mam pewne problemy z funkcją stosowania Pand, gdy używam wielu kolumn z następującą ramką danych

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

i następująca funkcja

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

Kiedy próbuję zastosować tę funkcję do:

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

Pojawia się komunikat o błędzie:

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

Nie rozumiem tej wiadomości, poprawnie zdefiniowałem nazwę.

Byłbym bardzo wdzięczny za wszelką pomoc w tej sprawie

Aktualizacja

Dzięki za pomoc. Naprawdę popełniłem kilka błędów składniowych w kodzie, indeks powinien zostać wstawiony ''. Jednak nadal pojawia się ten sam problem przy użyciu bardziej złożonej funkcji, takiej jak:

def my_test(a):
    cum_diff = 0
    for ix in df.index():
        cum_diff = cum_diff + (a - df['a'][ix])
    return cum_diff 
Andy
źródło
1
Unikaj używania applyjak najwięcej. Jeśli nie jesteś pewien, czy musisz go użyć, prawdopodobnie nie. Polecam rzucić okiem Kiedy powinienem kiedykolwiek chcieć używać pand Apply () w moim kodzie? .
cs95
Chodzi tylko o błędy składniowe odnoszące się do kolumny ramki danych i dlaczego funkcje potrzebują argumentów. Jeśli chodzi o twoje drugie pytanie, funkcja my_test(a)nie wie, co dfjest, ponieważ nie została przekazana jako argument (chyba że dfma być globalny, co byłoby okropną praktyką). Musisz przekazać wszystkie potrzebne wartości jako argumenty (najlepiej w kolejności), w przeciwnym razie skąd funkcja wiedziałaby, skąd dfpochodzi? Poza tym programowanie w przestrzeni nazw zaśmieconej zmiennymi globalnymi jest złą praktyką, nie złapie się takich błędów.
smci

Odpowiedzi:

379

Wygląda na to, że zapomniałeś ''swojego sznurka.

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, moim zdaniem, bardziej elegancki jest następujący sposób:

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

In [54]: df['Value'] = df.apply(my_test2, axis=1)
waitingkuo
źródło
Dzięki, masz rację, zapomniałem „”. Jednak nadal mam ten sam problem z bardziej złożoną funkcją. Byłbym bardzo wdzięczny za twoją pomoc. Dzięki
Andy
5
@ Andy następujące [53-54] pozwalają na stosowanie bardziej złożonych funkcji.
Andy Hayden
@Andy możesz zdefiniować swoją złożoną funkcję, taką jak In [53].
waitingkuo
czy wszystkie stosowane strategie działają tak samo? Jestem nowy w pandach i zawsze uważałem, że stosowanie nieco enigmatycznego jest łatwe, ale twoja strategia w [53-54] jest dla mnie łatwa do zrozumienia (i mam nadzieję, że pamiętam) ... na dużym stole jest tak szybka, jak inna forma zastosowania przedstawione?
whytheq
Dlaczego tworzenie oddzielnej metody jest uważane za bardziej eleganckie - nawet w przypadku małych metod. Robię znaczące projekty w Pythonie od 7 lat, ale prawdopodobnie nigdy nie będę brany pod uwagę z pythonistapowodu niektórych perspektyw, w tym tej.
javadba,
33

Jeśli chcesz tylko obliczyć (kolumna a)% (kolumna b), nie potrzebujesz apply, po prostu zrób to bezpośrednio:

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
źródło
16
Wiem, to tylko przykład pokazania mojego problemu ze stosowaniem funkcji do wielu kolumn
Andy
18

Powiedzmy, że chcemy zastosować funkcję add5 do kolumn „a” i „b” DataFrame df

def add5(x):
    return x+5

df[['a', 'b']].apply(add5)
Mir_Murtaza
źródło
Podczas próby napisania fragmentu kodu pojawia się błąd. TypeError: ('musi być str, nie int', 'wystąpił w indeksie b') czy możesz przyjrzeć się temu.
debaonline4u
Kolumna b twojej ramki danych to kolumna typu łańcuchowego lub typu obiektowego, powinna być kolumną całkowitą, którą należy dodać z liczbą.
Mir_Murtaza
Czy zmiany nie miałyby zastosowania tylko po przypisaniu?
S.aad
11

Wszystkie powyższe sugestie działają, ale jeśli chcesz, aby twoje obliczenia były bardziej wydajne, powinieneś skorzystać z operacji wektorów numpy (jak wskazano tutaj) .

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

Przykład 1: zapętlanie za pomocą pandas.apply():

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

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

Najwolniejszy przebieg trwał 7,49 razy dłużej niż najszybszy. Może to oznaczać, że wynik pośredni jest buforowany. 1000 pętli, najlepiej 3: 481 µs na pętlę

Przykład 2: wektoryzacja za pomocą pandas.apply():

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

Najwolniejszy przebieg trwał 458,85 razy dłużej niż najszybszy. Może to oznaczać, że wynik pośredni jest buforowany. 10000 pętli, najlepiej 3: 70,9 µs na pętlę

Przykład 3: wektoryzacja za pomocą tablic numpy:

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

Najwolniejszy przebieg trwał 7,98 razy dłużej niż najszybszy. Może to oznaczać, że wynik pośredni jest buforowany. 100000 pętli, najlepiej 3: 6,39 µs na pętlę

Wektoryzacja za pomocą tablic numpy poprawiła prędkość o prawie dwa rzędy wielkości.

Blane
źródło
Wyniki zmieniają się jeszcze bardziej dramatycznie dla dużych liczb, np. Zastępując 6 10K, otrzymuję odpowiednio 248 ms, 332 µs, 263 µs. Tak więc oba wektoryzowane rozwiązania są znacznie bliżej siebie, ale niewektoryzowane rozwiązanie jest 1000 razy wolniejsze. (testowany na python-3.7)
Stason
3

Jest to to samo co poprzednie rozwiązanie, ale zdefiniowałem funkcję w samym df.apply:

df['Value'] = df.apply(lambda row: row['a']%row['c'], axis=1)
Shaurya Airi
źródło
2

Podałem porównanie wszystkich trzech omówionych powyżej.

Używanie wartości

% timeit df ['wartość'] = df ['a']. wartości% df ['c']. wartości

139 µs ± 1,91 µs na pętlę (średnia ± odchylenie standardowe z 7 serii, każda 10000 pętli)

Bez wartości

% timeit df ['wartość'] = df ['a']% df ['c'] 

216 µs ± 1,86 µs na pętlę (średnia ± odchylenie standardowe z 7 serii, 1000 pętli każda)

Zastosuj funkcję

% timeit df ['Wartość'] = df.apply (wiersz lambda: wiersz ['a']% wiersz ['c'], oś = 1)

474 µs ± 5.07 µs na pętlę (średnia ± odchylenie standardowe z 7 serii, 1000 pętli każda)

Gursewak Singh
źródło