Okna rozszerzające
Obliczaj skumulowane statystyki obejmujące wszystkie wiersze od początku do danego punktu za pomocą expanding().sum().
Okna rozszerzające 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.
Czym jest okno rozszerzające?
Okno rozszerzające obejmuje wszystkie wiersze od samego początku obiektu Series aż do bieżącego wiersza — okno rośnie wraz z każdym nowym wierszem, zamiast zachowywać stały rozmiar. W wierszu 0 zawiera tylko jedną wartość, w wierszu 5 zawiera sześć wartości, a w wierszu 100 zawiera 101 wartości. Okna rozszerzające służą do obliczania skumulowanych statystyk, takich jak suma narastająca, średnia narastająca lub maksimum narastające od początku zbioru danych do bieżącego momentu.
import pandas as pd
import numpy as np
sales = pd.Series(
[100, 150, 120, 200, 180, 160, 220, 190],
index=pd.date_range('2024-01-01', periods=8)
)
# Expanding mean — grows with each row
df = pd.DataFrame({'sales': sales})
df['cumulative_mean'] = df['sales'].expanding().mean()
print(df)Metoda expanding()
Series.expanding(min_periods=1) zwraca obiekt Expanding. Parametr min_periods (domyślnie równy 1) określa minimalną liczbę obserwacji wymaganą do uzyskania pierwszego wyniku innego niż NaN. W przeciwieństwie do rolling(n) okno w expanding() nie ma stałego rozmiaru — zawsze zaczyna się na początku. Można do niego dołączyć dowolną agregację: .sum(), .mean(), .std(), .min(), .max().
import pandas as pd
import numpy as np
np.random.seed(42)
monthly_revenue = pd.Series(
np.random.randint(500, 1500, 12),
index=pd.date_range('2024-01', periods=12, freq='ME'),
name='revenue'
)
df = pd.DataFrame({'revenue': monthly_revenue})
df['cumsum'] = df['revenue'].expanding().sum()
df['cummean'] = df['revenue'].expanding().mean()
df['cummax'] = df['revenue'].expanding().max()
df['cummin'] = df['revenue'].expanding().min()
print(df)Pandas cumsum, cumprod, cummax, cummin
W przypadku najczęściej używanych statystyk skumulowanych Pandas udostępnia bezpośrednie metody dla obiektów Series i DataFrame, bez konieczności używania expanding(): cumsum(), cumprod(), cummax() i cummin(). Są to zoptymalizowane implementacje, które działają nieco szybciej niż ich odpowiedniki z expanding(). Tych skrótów należy używać przy typowych metrykach biznesowych, takich jak przychód od początku roku (cumsum) lub maksymalna cena w całej historii (cummax).
import pandas as pd
import numpy as np
prices = pd.Series(
[100, 95, 110, 105, 120, 115, 130, 125],
index=pd.date_range('2024-01-01', periods=8)
)
df = pd.DataFrame({'price': prices})
df['cummax'] = df['price'].cummax() # all-time high
df['cummin'] = df['price'].cummin() # all-time low
df['cumsum'] = df['price'].cumsum() # cumulative total
df['cumprod'] = (1 + df['price'].pct_change()).cumprod() # growth index
print(df.round(3))Obliczanie wartości od początku roku za pomocą expanding
Klasycznym zastosowaniem okien rozszerzających jest przychód od początku roku (YTD). Dla każdego dnia chcemy obliczyć sumę wszystkich przychodów od 1 stycznia bieżącego roku do tego dnia włącznie. Jest to po prostu cumsum() zastosowane w obrębie każdej grupy lat. Należy użyć groupby(year).cumsum() — suma skumulowana jest resetowana na początku każdego roku kalendarzowego i narasta w jego trakcie.
import pandas as pd
import numpy as np
np.random.seed(0)
dates = pd.date_range('2023-01-01', '2024-06-30', freq='ME')
daily = pd.DataFrame({
'date': dates,
'revenue': np.random.randint(100, 500, len(dates))
})
# Year-to-date cumulative revenue
daily['year'] = daily['date'].dt.year
daily['ytd_revenue'] = daily.groupby('year')['revenue'].cumsum()
daily = daily.drop(columns='year')
print(daily.tail(10).to_string(index=False))Odchylenie standardowe w oknie rozszerzającym
expanding().std() oblicza narastające odchylenie standardowe, wykorzystując wszystkie dane od początku do bieżącego wiersza. Jest to przydatne do monitorowania, czy zmienność metryki rośnie, czy maleje w czasie — na przykład do śledzenia, czy dzienna sprzedaż produktu staje się bardziej, czy mniej przewidywalna wraz z rozwojem firmy. W przeciwieństwie do kroczącego odchylenia standardowego, które odzwierciedla tylko niedawną zmienność, rozszerzające odchylenie standardowe uwzględnia pełne historyczne rozproszenie danych.
import pandas as pd
import numpy as np
np.random.seed(42)
sales = pd.Series(
np.concatenate([
np.random.normal(100, 5, 30), # stable period
np.random.normal(100, 25, 30) # volatile period
]),
index=pd.date_range('2024-01-01', periods=60)
)
df = pd.DataFrame({'sales': sales})
df['expanding_std'] = df['sales'].expanding().std()
df['rolling_30d_std'] = df['sales'].rolling(30).std()
print(df.tail(10).round(2))Porównanie okien kroczących i rozszerzających
Najważniejsza różnica jest następująca: okna kroczące odzwierciedlają niedawne wyniki (na przykład ostatnie n dni) i są niewrażliwe na dane sprzed długiego czasu, natomiast okna rozszerzające uwzględniają całą historię, dlatego powoli zbliżają się do stabilnych wartości. Należy używać okien kroczących, gdy interesują Państwa niedawne trendy (średnia krocząca z 7 dni szybciej reaguje na ostatnie zmiany niż średnia z 90 dni). Okna rozszerzające należy wybierać, gdy potrzebne są statystyki dla całej historii lub metryki YTD, które nie mogą pomijać wcześniejszych danych.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(10)
data = pd.Series(
np.random.randn(60).cumsum() + 50,
index=pd.date_range('2024-01-01', periods=60)
)
df = pd.DataFrame({'value': data})
df['rolling_10'] = data.rolling(10).mean()
df['expanding'] = data.expanding().mean()
df.plot(figsize=(12, 5), title='Rolling 10-day Mean vs. Expanding Mean')
plt.ylabel('Value')
plt.show()Expanding Apply dla niestandardowych statystyk skumulowanych
expanding().apply(func) stosuje niestandardową funkcję do wszystkich danych od początku do bieżącego wiersza, podobnie jak rolling().apply(). Umożliwia to obliczanie skumulowanych statystyk, które nie są dostępne jako wbudowane funkcje — na przykład skumulowanego współczynnika zmienności (std/mean), mediany narastającej lub skumulowanego współczynnika Sharpe’a. Funkcja otrzymuje tablicę NumPy zawierającą wszystkie dotychczas napotkane wartości i musi zwrócić skalar.
import pandas as pd
import numpy as np
np.random.seed(0)
returns = pd.Series(
np.random.normal(0.001, 0.02, 60),
index=pd.date_range('2024-01-01', periods=60)
)
# Cumulative Sharpe ratio (mean/std of returns)
def sharpe(arr):
if len(arr) < 2:
return float('nan')
return arr.mean() / arr.std() * (252 ** 0.5) # annualised
df = pd.DataFrame({'returns': returns})
df['cum_sharpe'] = df['returns'].expanding().apply(sharpe, raw=True)
print(df.tail(8).round(4))Okna rozszerzające i obsługa wartości NaN
Okna rozszerzające domyślnie pomijają wartości NaN — wartość NaN znajdująca się w środku obiektu Series jest ignorowana podczas obliczania skumulowanej sumy lub średniej. Można to sprawdzić za pomocą skipna=True (ustawienia domyślnego w większości agregacji Pandas). W przypadku szeregów czasowych z rzeczywiście brakującymi wartościami (np. brakiem notowań w weekendy) statystyki rozszerzające są obliczane wyłącznie na podstawie niepustych wartości napotkanych do tej pory, co zazwyczaj jest pożądanym zachowaniem.
import pandas as pd
import numpy as np
data = pd.Series([10, np.nan, 20, 30, np.nan, 40, 50])
# cumsum skips NaN by default
df = pd.DataFrame({'value': data})
df['cumsum'] = df['value'].cumsum() # NaN propagates in cumsum!
df['expanding_sum'] = df['value'].expanding().sum() # NaN skipped
print(df)
print('\nNote: cumsum propagates NaN; expanding().sum() skips it.')Okna rozszerzające do obliczania rekordów narastających
Okna rozszerzające idealnie nadają się do obliczania rekordów narastających: najwyższej wartości w całej historii, najlepszego kwartału w historii lub najniższego wskaźnika błędów od momentu uruchomienia. Te KPI wymagają pamiętania wszystkich wartości historycznych — okno kroczące zapomniałoby o starych rekordach. Użycie cummax() lub cummin() zwraca najlepszy (lub najgorszy) wynik narastający w każdym punkcie, co ułatwia śledzenie momentów ustanowienia nowych rekordów.
import pandas as pd
import numpy as np
np.random.seed(5)
sales = pd.Series(
np.random.randint(100, 300, 20),
index=pd.date_range('2024-01-01', periods=20),
name='daily_sales'
)
df = pd.DataFrame({'sales': sales})
df['all_time_max'] = df['sales'].cummax()
df['new_record'] = df['sales'] == df['all_time_max']
print('Days where a new sales record was set:')
print(df[df['new_record']].drop(columns='new_record'))Przykład praktyczny: skumulowane stopy zwrotu
W finansach skumulowane stopy zwrotu pokazują, jak bardzo inwestycja urosła od daty początkowej do każdej kolejnej daty. Wychodząc od dziennych stóp zwrotu wyrażonych procentowo, iloczyn skumulowany wartości (1 + daily_return) daje całkowity współczynnik wzrostu. W tym przypadku iloczyn rozszerzający jest bardziej naturalny niż iloczyn kroczący — chcemy śledzić całkowity wzrost od początku inwestycji, a nie tylko w ciągu ostatnich n dni.
import pandas as pd
import numpy as np
np.random.seed(7)
start_price = 100
daily_returns = pd.Series(
np.random.normal(0.0005, 0.015, 252),
index=pd.date_range('2024-01-01', periods=252)
)
# Cumulative return = product of (1 + r_i)
cum_returns = (1 + daily_returns).cumprod()
portfolio_value = start_price * cum_returns
print(f'Start value: ${start_price:.2f}')
print(f'End value: ${portfolio_value.iloc[-1]:.2f}')
print(f'Total return: {(portfolio_value.iloc[-1]/start_price - 1)*100:.1f}%')Kiedy używać expanding, rolling i cumsum
Wskazówki dotyczące wyboru właściwego podejścia:
- Należy używać
cumsum() / cummax() / cummin()do prostych agregacji skumulowanych — jest to najszybsza opcja. - Należy używać
expanding().mean() / std(), gdy potrzebne są skumulowane średnie lub wariancje narastające. - Należy używać
expanding().apply(custom_func)do złożonych statystyk dla całej historii, które nie są dostępne jako funkcje wbudowane. - Należy używać
rolling(n)zamiast expanding, gdy na bieżącą wartość powinna wpływać tylko niedawna historia.
Szybki test
Sprawdź swoją wiedzę na temat okien rozszerzających z tego modułu.
Podsumowanie modułu
W tym module nauczyłeś się, że: expanding() oblicza skumulowane statystyki w rosnącym oknie zaczynającym się na początku obiektu Series, cumsum/cummax/cummin to zoptymalizowane skróty do typowych operacji skumulowanych, a okna rozszerzające idealnie nadają się do rekordów w całej historii, metryk YTD i skumulowanych stóp zwrotu z inwestycji. Następnie omówimy wykładniczo ważone średnie kroczące (EWMA), które nadają większą wagę nowszym obserwacjom.
Ucz się Python dzięki korepetycjom AI — za darmo
Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.
- Kursy
- 30
- Lekcje
- 120
Często zadawane pytania
Czy lekcja „Okna rozszerzające” jest bezpłatna?
Tak — pełny tekst „Okna rozszerzające” 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 „Okna rozszerzające”?
Obliczaj skumulowane statystyki obejmujące wszystkie wiersze od początku do danego punktu za pomocą expanding().sum(). Ć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 „Okna rozszerzające”?
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.