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 

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)

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

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)

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.


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)

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)

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.