Rozważmy następującą ramkę danych:
A B C D
0 foo one 0.162003 0.087469
1 bar one -1.156319 -1.526272
2 foo two 0.833892 -1.666304
3 bar three -2.026673 -0.322057
4 foo two 0.411452 -0.954371
5 bar two 0.765878 -0.095968
6 foo one -0.654890 0.678091
7 foo three -1.789842 -1.130922
Działają następujące polecenia:
> df.groupby('A').apply(lambda x: (x['C'] - x['D']))
> df.groupby('A').apply(lambda x: (x['C'] - x['D']).mean())
ale żadna z następujących prac:
> df.groupby('A').transform(lambda x: (x['C'] - x['D']))
ValueError: could not broadcast input array from shape (5) into shape (5,3)
> df.groupby('A').transform(lambda x: (x['C'] - x['D']).mean())
TypeError: cannot concatenate a non-NDFrame object
Czemu? Przykład w dokumentacji wydaje się sugerować, że wywołanie transform
grupy pozwala na wykonanie operacji wierszowej:
# Note that the following suggests row-wise operation (x.mean is the column mean)
zscore = lambda x: (x - x.mean()) / x.std()
transformed = ts.groupby(key).transform(zscore)
Innymi słowy, myślałem, że transformacja jest w istocie specyficznym typem zastosowania (takim, które nie agreguje). Gdzie się mylę?
Dla porównania poniżej przedstawiono konstrukcję oryginalnej ramki danych powyżej:
df = pd.DataFrame({'A' : ['foo', 'bar', 'foo', 'bar',
'foo', 'bar', 'foo', 'foo'],
'B' : ['one', 'one', 'two', 'three',
'two', 'two', 'one', 'three'],
'C' : randn(8), 'D' : randn(8)})
transform
musi zwracać liczbę, wiersz lub ten sam kształt co argument. jeśli jest to liczba, liczba zostanie ustawiona na wszystkie elementy w grupie, jeśli jest to wiersz, zostanie nadana do wszystkich wierszy w grupie. W Twoim kodzie funkcja lambda zwraca kolumnę, której nie można rozgłaszać do grupy.zscore
),transform
otrzymujesz funkcję lambda, która zakłada, że każdyx
jest elementem wgroup
, a także zwraca wartość na element w grupie. czego mi brakuje?apply
przechodzi w całym df, aletransform
przekazuje każdą kolumnę indywidualnie jako serię. 2)apply
może zwracać dowolne dane wyjściowe kształtu (skalarne / serie / DataFrame / tablica / lista ...), podczas gdytransform
musi zwracać sekwencję (seria 1D / tablica / lista) o tej samej długości co grupa. Dlatego OPapply()
nie potrzebujetransform()
. To dobre pytanie, ponieważ lekarz nie wyjaśnił jasno obu różnic. (podobne do rozróżnienia międzyapply/map/applymap
lub innymi rzeczami ...)Odpowiedzi:
Dwie główne różnice między
apply
itransform
Istnieją dwie główne różnice między metodami
transform
iapply
grupowaniem.apply
niejawnie przekazuje wszystkie kolumny dla każdej grupy jako DataFrame do funkcji niestandardowej.transform
przekazuje każdą kolumnę dla każdej grupy indywidualnie jako serię do funkcji niestandardowej.apply
może zwrócić wartość skalarną, Series lub DataFrame (lub tablicę numpy lub nawet listę) .transform
musi zwracać sekwencję (jednowymiarową serię, tablicę lub listę) o tej samej długości co grupa .Tak więc
transform
działa tylko na jednej serii naraz iapply
działa na całej ramce DataFrame na raz.Sprawdzanie funkcji niestandardowej
Bardzo pomocne może być sprawdzenie danych wejściowych do funkcji niestandardowej przekazanej do
apply
lubtransform
.Przykłady
Stwórzmy przykładowe dane i sprawdźmy grupy, abyś mógł zobaczyć, o czym mówię:
Stwórzmy prostą funkcję niestandardową, która wypisuje typ niejawnie przekazanego obiektu, a następnie zgłosi błąd, aby można było zatrzymać wykonywanie.
Teraz przekażmy tę funkcję zarówno do metody groupby, jak
apply
itransform
metod, aby zobaczyć, jaki obiekt jest do niej przekazywany:Jak widać, DataFrame jest przekazywana do
inspect
funkcji. Możesz się zastanawiać, dlaczego typ DataFrame został dwukrotnie wydrukowany. Pandy dwukrotnie prowadzi pierwszą grupę. Robi to w celu określenia, czy istnieje szybki sposób zakończenia obliczeń, czy nie. To drobny szczegół, o który nie powinieneś się martwić.Teraz zróbmy to samo z
transform
Przekazano serię - zupełnie inny obiekt Pandy.
W związku z tym
transform
można jednocześnie pracować tylko z jedną serią. To nie da na to, aby działać na dwóch kolumnach w tym samym czasie. Tak więc, jeśli spróbujemy odjąć kolumnęa
zb
wnętrza naszej funkcji niestandardowej, otrzymamy błąd ztransform
. Zobacz poniżej:Otrzymujemy KeyError, gdy pandy próbują znaleźć indeks Serii,
a
który nie istnieje. Możesz wykonać tę operację,apply
ponieważ ma ona całą ramkę DataFrame:Wynik jest serią i jest nieco zagmatwany, ponieważ zachowany jest oryginalny indeks, ale mamy dostęp do wszystkich kolumn.
Wyświetlanie przekazanego obiektu pandy
Jeszcze bardziej może pomóc wyświetlenie całego obiektu pandy w funkcji niestandardowej, dzięki czemu możesz dokładnie zobaczyć, z czym pracujesz. Możesz użyć
print
oświadczeń, które lubię używaćdisplay
funkcji zIPython.display
modułu, aby ramki DataFrames były ładnie wyświetlane w HTML w notatniku jupyter:Zrzut ekranu:
Transformacja musi zwracać jednowymiarową sekwencję o takim samym rozmiarze jak grupa
Inna różnica polega na tym, że
transform
musi zwracać jednowymiarową sekwencję o takim samym rozmiarze jak grupa. W tym konkretnym przypadku każda grupa ma dwa wiersze, więctransform
musi zwrócić sekwencję dwóch wierszy. Jeśli tak nie jest, zgłaszany jest błąd:Komunikat o błędzie tak naprawdę nie opisuje problemu. Musisz zwrócić sekwencję o tej samej długości co grupa. Tak więc działałaby taka funkcja:
Zwracanie pojedynczego obiektu skalarnego działa również w przypadku
transform
Jeśli zwrócisz tylko jeden skalar z funkcji niestandardowej,
transform
użyje go dla każdego z wierszy w grupie:źródło
np
nie jest zdefiniowany. Zakładam, że początkujący byliby wdzięczni za uwzględnienieimport numpy as np
w odpowiedzi.Ponieważ czułem się podobnie zdezorientowany
.transform
operacją vs..apply
, znalazłem kilka odpowiedzi, które rzucają trochę światła na ten problem. Na przykład ta odpowiedź była bardzo pomocna.Jak dotąd moje wyniki są takie, że
.transform
będzie działać (lub poradzić sobie) zSeries
(kolumnami) w oderwaniu od siebie . Oznacza to, że w Twoich dwóch ostatnich rozmowach:Poprosiłeś
.transform
o pobranie wartości z dwóch kolumn i „to” w rzeczywistości nie „widzi” obu w tym samym czasie (że tak powiem).transform
przyjrzy się kolumnom ramki danych jedna po drugiej i zwróci serię (lub grupę serii) „utworzonych” skalarami, które są powtarzanelen(input_column)
razy.Więc ten skalar, który powinien być użyty
.transform
do uzyskania wyniku,Series
jest wynikiem jakiejś funkcji redukcji zastosowanej na wejściuSeries
(i tylko na JEDNEJ serii / kolumnie na raz).Rozważ ten przykład (w swojej ramce danych):
przyniesie:
To dokładnie to samo, co gdybyś używał go tylko na jednej kolumnie naraz:
wydajność:
Zwróć uwagę, że
.apply
w ostatnim przykładzie (df.groupby('A')['C'].apply(zscore)
) działałoby dokładnie w ten sam sposób, ale nie powiedzie się, jeśli spróbujesz użyć jej na ramce danych:daje błąd:
Więc gdzie jeszcze jest
.transform
przydatne? Najprostszym przypadkiem jest próba przypisania wyników funkcji redukcji z powrotem do oryginalnej ramki danych.wydajność:
Próbuje to samo z
.apply
dałobyNaNs
sięsum_C
. Bo.apply
zwróciłby zredukowanySeries
, którego nie wie jak odesłać z powrotem:dający:
Istnieją również przypadki, w których
.transform
jest używany do filtrowania danych:Mam nadzieję, że to doda nieco większej przejrzystości.
źródło
.transform()
może również służyć do uzupełniania brakujących wartości. Zwłaszcza jeśli chcesz rozgłaszać średnią grupy lub statystykę grupową doNaN
wartości w tej grupie. Niestety, dokumentacja pand również nie była dla mnie pomocna..groupby().filter()
robi to samo. Dziękuję za wyjaśnienie.apply()
i.transform()
bardzo mnie wprawia w zakłopotanie.df.groupby().transform()
nie mogę pracować dla podgrupy df, zawsze otrzymuję błąd,ValueError: transform must return a scalar value for each group
ponieważtransform
Mam zamiar użyć bardzo prostego fragmentu, aby zilustrować różnicę:
DataFrame wygląda następująco:
W tej tabeli znajdują się 3 identyfikatory klientów, każdy klient dokonał trzech transakcji i za każdym razem zapłacił 1,2,3 dolara.
Teraz chcę znaleźć minimalną kwotę płatności dokonaną przez każdego klienta. Można to zrobić na dwa sposoby:
Używając
apply
:grouping.min ()
Zwrot wygląda następująco:
Używając
transform
:grouping.transform (min)
Zwrot wygląda następująco:
Obie metody zwracają
Series
obiekt, alelength
pierwsza to 3, alength
druga to 9.Jeśli chcesz odpowiedzieć
What is the minimum price paid by each customer
,apply
metoda jest bardziej odpowiednia do wyboru.Jeśli chcesz odpowiedzieć
What is the difference between the amount paid for each transaction vs the minimum payment
, to chcesz skorzystaćtransform
, ponieważ:Apply
nie działa tutaj tylko dlatego, że zwraca serię o rozmiarze 3, ale oryginalna długość pliku df wynosi 9. Nie można go łatwo zintegrować z powrotem z oryginalnym df.źródło
jest jak
lub
źródło