apply() z GroupBy
Przekaż funkcję działającą na wielu wierszach do groupby().apply(), aby obliczać złożone podsumowania grup, których nie da się wyrazić za pomocą agg().
apply() z GroupBy to bezpłatna lekcja Pandas & NumPy Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Pandas & NumPy Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Pandas & NumPy Academy zawiera 4 lekcji w sumie.
Dlaczego GroupBy potrzebuje apply()
Wbudowana metoda agg() obsługuje proste agregacje, takie jak suma, średnia i liczność — zwracając jedną wartość skalarną dla każdej grupy. Niektóre obliczenia na poziomie grup wymagają jednak przeanalizowania całej ramki danych będącej podramką danej grupy, a nie tylko pojedynczej kolumny. groupby().apply(func) przekazuje do funkcji pełną ramkę danych grupy i zbiera wyniki, umożliwiając tworzenie złożonych podsumowań, których nie da się wyrazić za pomocą agg().
import pandas as pd
import numpy as np
df = pd.DataFrame({
'region': ['North', 'North', 'South', 'South', 'North'],
'product': ['A', 'B', 'A', 'A', 'C'],
'revenue': [100, 250, 180, 90, 300]
})
print(df)Zwracanie wartości skalarnej dla każdej grupy
Gdy funkcja przekazana do groupby().apply() zwraca wartość skalarną, wynikiem jest Series indeksowana kluczami grup — identyczna z wynikiem agg(). Ta forma jest przydatna, gdy obliczenie wartości skalarnej wymaga użycia wielu kolumn, na przykład obliczenia stosunku przychodu z najlepiej sprzedającego się produktu do całkowitego przychodu grupy, czego nie da się wyrazić w pojedynczej specyfikacji kolumny agg().
def top_product_share(group):
top = group['revenue'].max()
total = group['revenue'].sum()
return top / total
share = df.groupby('region').apply(top_product_share)
print(share)Zwracanie Series dla każdej grupy
Gdy funkcja zwraca pd.Series, wynik ma MultiIndex: poziom zewnętrzny zawiera klucz grupy, a poziom wewnętrzny — indeks Series. Jest to przydatne do obliczania wielu statystyk dla każdej grupy w jednym wywołaniu apply, co pozwala utworzyć tabelę podsumowania, w której każda grupa ma wiele wierszy z metrykami.
def group_stats(group):
return pd.Series({
'total': group['revenue'].sum(),
'top_product': group.loc[group['revenue'].idxmax(), 'product'],
'n_products': group['product'].nunique()
})
result = df.groupby('region').apply(group_stats)
print(result)Zwracanie DataFrame dla każdej grupy
Gdy funkcja zwraca pd.DataFrame, groupby().apply() łączy wszystkie podramki danych grup pionowo. To najbardziej wszechstronna forma: pozwala filtrować lub przekształcać wiersze w obrębie każdej grupy i zwracać zmodyfikowany podzbiór. Można na przykład zachować tylko 2 najlepiej sprzedające się produkty pod względem przychodu w każdym regionie.
def top2_per_region(group):
return group.nlargest(2, 'revenue')
top2 = df.groupby('region').apply(top2_per_region)
print(top2.reset_index(drop=True))Obliczanie wyników standaryzowanych Z w obrębie grup
Częstym zastosowaniem groupby().apply() jest standaryzacja w obrębie grup. Zamiast normalizować przychód względem wszystkich zamówień, co miesza regiony, należy obliczyć wynik Z przychodu osobno dla każdego regionu. Wynik Z równy 2 w regionie North oznacza „2 odchylenia standardowe powyżej średniej dla regionu North” — jest to bardziej miarodajny punkt odniesienia niż 2 odchylenia standardowe powyżej średniej globalnej.
def within_group_zscore(group):
mean = group['revenue'].mean()
std = group['revenue'].std()
group = group.copy()
group['revenue_z'] = (group['revenue'] - mean) / std
return group
df_z = df.groupby('region').apply(within_group_zscore).reset_index(drop=True)
print(df_z)Niestandardowa agregacja: średnia winsoryzowana
Średnia winsoryzowana ogranicza wpływ wartości skrajnych przed obliczeniem średniej, dzięki czemu jest odporniejsza niż zwykła średnia w przypadku rozkładów skośnych. Tej niestandardowej agregacji nie da się wyrazić za pomocą standardowych funkcji agg(), ale można ją łatwo wykonać za pomocą groupby().apply(): ograniczyć wartości do 5. i 95. percentyla w obrębie każdej grupy, a następnie obliczyć średnią dla tak przekształconych wartości.
def winsorised_mean(group, lower_pct=0.05, upper_pct=0.95):
col = group['revenue']
lo = col.quantile(lower_pct)
hi = col.quantile(upper_pct)
clipped = col.clip(lo, hi)
return clipped.mean()
wins_mean = df.groupby('region').apply(winsorised_mean)
print('Winsorised mean by region:')
print(wins_mean)Niestandardowe okno kroczące dla każdej grupy
Okna kroczące zastosowane do całej ramki danych ignorują granice grup. Jeśli obliczą Państwo 3-wierszową średnią kroczącą dla szeregu czasowego, w którym przeplatają się dane dwóch produktów, okno nieprawidłowo przekroczy granice produktów. Należy zastosować okno kroczące wewnątrz groupby().apply(), aby zapewnić wykonywanie każdego obliczenia wyłącznie w obrębie jego grupy — na przykład obliczyć 3-miesięczną kroczącą średnią przychodu dla każdego regionu.
def group_rolling_mean(group, window=3):
group = group.sort_values('order_date').copy()
group['rolling_revenue'] = group['revenue'].rolling(window, min_periods=1).mean()
return group
# df_ts has order_date column
# df_rolled = df_ts.groupby('region').apply(group_rolling_mean).reset_index(drop=True)
print('Rolling window per group applied inside apply()')Parametr include_groups (Pandas 2.2+)
W Pandas 2.2 i nowszych groupby().apply() zgłasza FutureWarning, jeśli klucze grupowania pojawiają się w ramce danych otrzymywanej przez funkcję. Należy przekazać include_groups=False, aby wykluczyć kolumny grupowania z podramki danych przekazywanej do funkcji. Pozwala to uniknąć zarówno ostrzeżenia, jak i przypadkowych operacji na kolumnach zawierających klucze wewnątrz ciała funkcji.
def revenue_only(group):
# group does not include 'region' column when include_groups=False
return group['revenue'].sum()
# result = df.groupby('region').apply(revenue_only, include_groups=False)
print('include_groups=False avoids FutureWarning in Pandas 2.2+')Łączenie wyników apply() za pomocą reset_index
Gdy groupby().apply() zwraca DataFrame dla każdej grupy, wynik ma MultiIndex, którego zewnętrzny poziom zawiera klucz grupy. Należy użyć reset_index(drop=True), aby spłaszczyć indeks do zwykłego zakresu liczb całkowitych. Alternatywnie można użyć reset_index(level=0), aby przenieść klucz grupy do kolumny i wyraźnie uwzględnić go w wynikach.
result = df.groupby('region').apply(top2_per_region)
print('With MultiIndex:')
print(result.head())
print('\nAfter reset_index:')
print(result.reset_index(drop=True))Kiedy zamiast apply() wybrać transform()
groupby().apply() służy do złożonych obliczeń na poziomie grup, które zwracają dane o innym kształcie niż dane wejściowe. groupby().transform() służy do obliczeń dodających nową kolumnę zgodną z oryginalnym indeksem — na przykład do rozpropagowania średniej grupy z powrotem do każdego wiersza w celu normalizacji. Należy użyć transform, gdy wynik ma mieć taki sam kształt jak oryginalna DataFrame, a apply, gdy kształt wyniku jest inny.
# transform: adds group mean back to each row
df['group_mean_revenue'] = df.groupby('region')['revenue'].transform('mean')
# apply: computes one scalar per group
group_totals = df.groupby('region')['revenue'].apply(sum)
print(df[['region', 'revenue', 'group_mean_revenue']])Wskazówka dotycząca wydajności: unikanie apply() dla prostych agregacji
groupby().apply() jest znacznie wolniejsze niż groupby().agg() w przypadku prostych operacji, ponieważ apply() tworzy pełny obiekt Python dla każdej grupy. Dla sum, średnich i liczności należy zawsze używać agg(). Po apply() należy sięgać w sytuacjach, które rzeczywiście wymagają pełnej ramki danych grupy — przy warunkowej logice obejmującej wiele kolumn, niestandardowych statystykach lub filtrowaniu wewnątrz grup.
# SLOW: apply for simple sum
slow = df.groupby('region').apply(lambda g: g['revenue'].sum())
# FAST: native agg
fast = df.groupby('region')['revenue'].sum()
print('Both produce the same result:')
print(fast.equals(slow))Szybkie sprawdzenie
Sprawdź swoją wiedzę na temat analizy danych z tej lekcji.
Podsumowanie lekcji
W tej lekcji nauczyli się Państwo: zwracać wartości skalarne, Series i DataFrame z użyciem groupby().apply(), obliczać wyniki Z w obrębie grup oraz niestandardowe odporne statystyki i wybierać między apply(), agg() a transform() do operacji na poziomie grup. Następnie omówimy map() i applymap() do operacji na poszczególnych elementach Series i DataFrame.
Często zadawane pytania
Czy lekcja „apply() z GroupBy” jest bezpłatna?
Tak — pełny tekst „apply() z GroupBy” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Pandas & NumPy Academy, przejdź na CoddyKit PRO. Kurs Pandas & NumPy Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „apply() z GroupBy”?
Przekaż funkcję działającą na wielu wierszach do groupby().apply(), aby obliczać złożone podsumowania grup, których nie da się wyrazić za pomocą agg(). Ćwiczysz Pandas & NumPy Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Pandas & NumPy Academy?
Nie wymagamy żadnego doświadczenia. Pandas & NumPy Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.
Ile czasu zajmuje lekcja „apply() z GroupBy”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Pandas & NumPy Academy?
Tak. Każda lekcja Pandas & NumPy Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- apply() dla kolumn i wierszy
- apply() z GroupBy
- map() i applymap() do operacji elementowych
- Łańcuchowe wywoływanie metod z pipe()