apply() dla kolumn i wierszy
Użyj DataFrame.apply() z axis=0 i axis=1, aby uruchomić niestandardową funkcję dla każdej kolumny lub każdego wiersza.
apply() dla kolumn i wierszy to bezpłatna lekcja Pandas & NumPy Academy na CoddyKit. To lekcja 1 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.
Kiedy używać apply()
DataFrame.apply() uruchamia niestandardową funkcję języka Python dla każdej kolumny (axis=0) lub każdego wiersza (axis=1). Jest to rozwiązanie ostatniej szansy — wolniejsze od wbudowanych operacji wektoryzowanych — ale niezbędne, gdy obliczenia nie dają się wyrazić za pomocą arytmetyki NumPy, indeksowania logicznego lub wbudowanych funkcji agregujących. Należy go używać, gdy potrzebna jest złożona logika warunkowa, niestandardowe analizowanie napisów albo wieloetapowe obliczenia wykonywane jednocześnie dla pojedynczego wiersza lub kolumny.
import pandas as pd
import numpy as np
df = pd.DataFrame({
'price': [10.5, 25.0, 8.3, 100.0],
'quantity': [3, 1, 5, 2],
'tax_rate': [0.05, 0.10, 0.05, 0.15]
})
print(df)apply() dla kolumn (axis=0)
Przy axis=0 (wartość domyślna) apply() przekazuje funkcji każdą kolumnę jako Series. Funkcja otrzymuje po jednej kolumnie i powinna zwrócić skalar (w przypadku agregacji) albo Series (w przypadku transformacji). Jest to przydatne do zastosowania niestandardowej normalizacji lub czyszczenia w sposób jednolity dla wszystkich kolumn liczbowych w jednym wywołaniu.
# Custom range normalisation (0 to 1) for each column
def min_max_scale(col):
return (col - col.min()) / (col.max() - col.min())
df_scaled = df[['price', 'quantity']].apply(min_max_scale, axis=0)
print(df_scaled)apply() dla wierszy (axis=1)
Przy axis=1 apply() przekazuje funkcji każdy wiersz jako Series. Indeksem wiersza jest nazwa kolumny, dlatego wartości można pobierać po nazwie. Jest to idealne rozwiązanie do obliczeń na poziomie wiersza zależnych od wielu kolumn, takich jak obliczanie całkowitego przychodu po opodatkowaniu, gdy każdy wiersz ma własną stawkę podatku.
def revenue_after_tax(row):
subtotal = row['price'] * row['quantity']
return subtotal * (1 + row['tax_rate'])
df['revenue_after_tax'] = df.apply(revenue_after_tax, axis=1)
print(df)Zwracanie wielu wartości za pomocą apply()
Funkcja przekazana do apply(axis=1) może zwrócić obiekt pd.Series z nazwanymi elementami, który Pandas rozwija do wielu nowych kolumn. Jest to czytelniejsze niż dwukrotne wywołanie apply() w celu utworzenia dwóch nowych kolumn. Alternatywnie funkcja może zwrócić obiekt dict, który Pandas również rozwija do kolumn.
def compute_totals(row):
subtotal = row['price'] * row['quantity']
tax = subtotal * row['tax_rate']
return pd.Series({'subtotal': subtotal, 'tax': tax, 'total': subtotal + tax})
result = df.apply(compute_totals, axis=1)
print(result)apply() do niestandardowej agregacji kolumn
Użyj apply(func, axis=0), aby obliczyć niestandardową statystykę dla każdej kolumny i zwrócić podsumowującą Series. Można na przykład obliczyć współczynnik zmienności (odchylenie standardowe podzielone przez średnią) dla każdej kolumny liczbowej w jednym wywołaniu. Wynikiem jest Series indeksowana nazwami kolumn, co ułatwia szybką diagnostykę poszczególnych kolumn.
def coeff_of_variation(col):
return col.std() / col.mean() if col.mean() != 0 else np.nan
cv = df[['price', 'quantity']].apply(coeff_of_variation, axis=0)
print('Coefficient of variation per column:')
print(cv)Porównanie apply() z alternatywami wektoryzowanymi
Przed użyciem apply() zawsze należy sprawdzić, czy istnieje alternatywa wektoryzowana. W przypadku obliczania przychodu po opodatkowaniu wyrażenie df['price'] * df['quantity'] * (1 + df['tax_rate']) robi to samo co wersja z apply(axis=1), ale działa 10–100 razy szybciej, ponieważ korzysta z pętli NumPy na poziomie C. apply() należy pozostawić dla przypadków, w których logika wektoryzowana byłaby nieczytelnie złożona.
import time
start = time.time()
df['vectorised'] = df['price'] * df['quantity'] * (1 + df['tax_rate'])
print('Vectorised:', time.time() - start, 's')
start = time.time()
df['apply_result'] = df.apply(revenue_after_tax, axis=1)
print('apply():', time.time() - start, 's')apply() z funkcją lambda
W przypadku krótkich, jednowierszowych transformacji należy przekazać funkcję lambda bezpośrednio do apply(), zamiast definiować funkcję nazwaną. Lambdy są zwięzłe przy prostych operacjach, ale należy ich unikać w przypadku złożonej logiki, dla której łatwiejsza do zrozumienia i przetestowania jest nazwana funkcja z komentarzami. Lambd należy używać oszczędnie — nie można ich łatwo testować jednostkowo ani profilować po nazwie.
# Clip revenue between 0 and 200, then round to nearest 10
df['revenue_clean'] = df['revenue_after_tax'].apply(lambda x: round(min(max(x, 0), 200) / 10) * 10)
print(df[['revenue_after_tax', 'revenue_clean']])Przekazywanie dodatkowych argumentów do apply()
Dodatkowe argumenty do funkcji należy przekazywać za pomocą krotki args lub argumentów nazwanych w wywołaniu apply(func, args=(val,), axis=1). Pozwala to uniknąć używania zmiennych globalnych wewnątrz funkcji i ułatwia jej ponowne wykorzystanie z innymi wartościami parametrów. W Pythonie 3.8 lub nowszym można również użyć functools.partial do utworzenia częściowo zastosowanej wersji funkcji.
def discount_price(row, discount_pct, threshold):
if row['quantity'] >= threshold:
return row['price'] * (1 - discount_pct)
return row['price']
df['discounted_price'] = df.apply(
discount_price, axis=1,
args=(0.1, 3) # 10% discount for quantity >= 3
)
print(df[['price', 'quantity', 'discounted_price']])apply() do konwersji typów
Gdy kolumna zawiera różne typy — niektóre wartości są całkowite, inne zmiennoprzecinkowe, a jeszcze inne są napisami — astype() zgłasza błąd. Należy użyć apply(pd.to_numeric, errors='coerce'), aby spróbować konwersji wiersz po wierszu i zwrócić NaN dla wartości, których nie można przekonwertować. Jest to bezpieczny wzorzec dla kolumn, które powinny być liczbowe, ale zawierają sporadyczne wartości nieliczbowe wynikające z błędów wprowadzania danych.
messy = pd.Series(['10', '20.5', 'N/A', '100', '--'])
clean = messy.apply(pd.to_numeric, errors='coerce')
print(clean)Wydajność: apply() a np.vectorize
Gdy funkcję działającą na skalarach trzeba zastosować element po elemencie, np.vectorize(func)(array) jest zazwyczaj szybsze niż Series.apply(func), ponieważ vectorize biblioteki NumPy korzysta z mechanizmu wywołań przez C zamiast narzutu wywołań funkcji Pythona. Jednak np.vectorize nadal działa wolniej niż prawdziwe rozgłaszanie — jest przydatne tylko wtedy, gdy żadna forma rozgłaszania nie oddaje tej logiki.
def classify_size(price):
if price < 15:
return 'small'
elif price < 50:
return 'medium'
return 'large'
# np.vectorize approach
classify_v = np.vectorize(classify_size)
df['size_class'] = classify_v(df['price'].values)
print(df[['price', 'size_class']])Kiedy apply() jest właściwym wyborem
Apply jest właściwym narzędziem, gdy: (1) logika wymaga jednoczesnego rozgałęziania na podstawie wartości wielu kolumn; (2) funkcja wywołuje zewnętrzne API lub bazę danych dla każdego wiersza (co nieuchronnie odbywa się sekwencyjnie); (3) funkcja analizuje złożony napis, taki jak obiekt JSON przechowywany w komórce; lub (4) funkcja zwraca obiekt o zmiennej długości, taki jak lista. We wszystkich pozostałych przypadkach należy preferować operacje wektoryzowane ze względu na szybkość i czytelność.
import json
# Parse a JSON metadata column row by row
df['metadata'] = ['{"color": "red"}', '{"color": "blue"}', '{}', '{"color": "green"}']
df['color'] = df['metadata'].apply(lambda s: json.loads(s).get('color', 'unknown'))
print(df[['metadata', 'color']])Szybkie sprawdzenie
Sprawdź swoją wiedzę na temat koncepcji analizy danych omówionych w tej lekcji.
Podsumowanie lekcji
W tej lekcji nauczyli się Państwo: używać apply(axis=0) do obliczania niestandardowych statystyk na poziomie kolumn, używać apply(axis=1) do obliczeń na poziomie wierszy z wykorzystaniem wielu kolumn oraz rozpoznawać sytuacje, w których ze względu na wydajność lepiej użyć alternatyw wektoryzowanych. Następnie omówimy użycie apply() z GroupBy do złożonych agregacji na poziomie grup.
Często zadawane pytania
Czy lekcja „apply() dla kolumn i wierszy” jest bezpłatna?
Tak — pełny tekst „apply() dla kolumn i wierszy” 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() dla kolumn i wierszy”?
Użyj DataFrame.apply() z axis=0 i axis=1, aby uruchomić niestandardową funkcję dla każdej kolumny lub każdego wiersza. Ć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 1 z 4.
Ile czasu zajmuje lekcja „apply() dla kolumn i wierszy”?
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()