0Pricing
Pandas & NumPy Academy · Lekcja

Przesuwanie i cechy opóźnione

Twórz kolumny opóźnienia i wyprzedzenia za pomocą shift(), obliczaj zmianę między okresami za pomocą diff() oraz wyznaczaj zmianę procentową.

Przesuwanie i cechy opóźnione to bezpłatna lekcja Pandas & NumPy Academy na CoddyKit. To lekcja 3 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 cechy opóźnione są ważne

W analizie szeregów czasowych wartość z poprzedniego kroku czasowego (opóźnienie) jest często jednym z najlepszych predyktorów bieżącej wartości. Na przykład wczorajsza sprzedaż dostarcza informacji o dzisiejszej sprzedaży. Tworzenie kolumn z opóźnieniami pozwala używać wartości historycznych jako cech w modelach uczenia maszynowego lub w analizie statystycznej autokorelacji. Pandas udostępnia funkcję shift(), która tworzy cechy opóźnione w jednym zwektoryzowanym wywołaniu.

shift(): przesuwanie wartości w przód lub wstecz

Series.shift(n) przesuwa wszystkie wartości w dół o n pozycji (dodatnie n tworzy opóźnienie), wypełniając pierwszych n wierszy wartościami NaN. Przekazanie ujemnego n przesuwa wartości w górę (tworzy wyprzedzenie, przesuwając przyszłe wartości do bieżącego wiersza). Indeks pozostaje bez zmian — przesuwane są tylko wartości.

import pandas as pd
import numpy as np

df = pd.DataFrame(
    {'sales': [100, 120, 115, 130, 140, 125]},
    index=pd.date_range('2024-01-01', periods=6, freq='D')
)

# Lag-1: yesterday's sales
df['sales_lag1'] = df['sales'].shift(1)
# Lead-1: tomorrow's sales (shifted up)
df['sales_lead1'] = df['sales'].shift(-1)
print(df)

Tworzenie wielu kolumn opóźnień

Zwykle tworzy się jednocześnie kilka cech opóźnionych na potrzeby uczenia maszynowego — lag-1, lag-7 (ten sam dzień w poprzednim tygodniu) oraz lag-30 (ten sam dzień w poprzednim miesiącu). Najczytelniej jest utworzyć je w pętli i przypisać każdą do nowej kolumny. Wynikowy DataFrame zawiera oryginalny szereg oraz wszystkie jego wersje z opóźnieniem, gotowe do modelowania.

for lag in [1, 2, 3, 7]:
    df[f'sales_lag{lag}'] = df['sales'].shift(lag)

print(df.columns.tolist())
# ['sales', 'sales_lag1', 'sales_lag2', 'sales_lag3', 'sales_lag7']

# Drop rows with NaN from the largest lag period
df_model = df.dropna()
print('Ready for modelling, shape:', df_model.shape)

shift() z freq do przesuwania opartego na czasie

Zamiast przesuwać dane o całkowitą liczbę wierszy, można przesuwać je o przesunięcie czasowe za pomocą parametru freq. df.shift(1, freq='ME') przesuwa indeks do przodu o jeden koniec miesiąca, pozostawiając wartości na miejscu, ale zmieniając indeks. Jest to przydatne podczas dopasowywania dwóch szeregów czasowych przesuniętych względem siebie o stały okres, bez zmieniania kolejności wartości.

monthly = pd.Series(
    [100, 120, 115],
    index=pd.date_range('2024-01-31', periods=3, freq='ME')
)

# Shift the index forward by 1 month (values stay, index moves)
shifted_index = monthly.shift(1, freq='ME')
print('Original index:', monthly.index.tolist())
print('Shifted index: ', shifted_index.index.tolist())
# Original: [2024-01-31, 2024-02-29, 2024-03-31]
# Shifted:  [2024-02-29, 2024-03-31, 2024-04-30]

diff(): zmiana względem poprzedniego okresu

Series.diff(n) oblicza różnicę między wartością a wartością znajdującą się n pozycji wcześniej: x[t] - x[t-n]. Funkcja jest często używana do obliczania zmian dzień do dnia, różnic tydzień do tygodnia lub zmian rok do roku. Pierwsze n wierszy zawiera NaN, ponieważ nie ma wcześniejszych wartości, od których można by odjąć bieżące.

df['sales_diff1'] = df['sales'].diff(1)  # day-over-day change
df['sales_diff7'] = df['sales'].diff(7)  # week-over-week change

print(df[['sales', 'sales_diff1']].head())
#             sales  sales_diff1
# 2024-01-01    100          NaN
# 2024-01-02    120         20.0  <- +20 from yesterday
# 2024-01-03    115         -5.0  <- -5 from yesterday

pct_change(): zmiana procentowa

Series.pct_change(n) oblicza względną zmianę procentową: (x[t] - x[t-n]) / x[t-n]. Jest to niezbędne w analizie finansowej (stopy zwrotu dzienne), obliczaniu tempa wzrostu oraz w każdej sytuacji, w której zmiana bezwzględna jest mniej miarodajna niż zmiana względna. Aby uzyskać punkty procentowe, należy pomnożyć wynik przez 100.

df['pct_chg'] = df['sales'].pct_change().round(3)
df['pct_chg_7d'] = df['sales'].pct_change(7).round(3)

print(df[['sales', 'pct_chg']].head())
#             sales  pct_chg
# 2024-01-01    100      NaN
# 2024-01-02    120    0.200  <- 20% increase
# 2024-01-03    115   -0.042  <- 4.2% decrease

Łączenie shift() z operacjami arytmetycznymi

Można łączyć shift() z operacjami arytmetycznymi, aby obliczać pochodne cechy czasowe. Przykłady to stosunek dzisiejszej wartości do wartości z poprzedniego tygodnia, suma skumulowana od ustalonego początku lub różnica względem wartości sprzed roku. Wszystkie te przypadki opierają się na tym samym schemacie: należy przesunąć oryginalny szereg i wykonać operacje arytmetyczne element po elemencie na bieżących wartościach.

# Week-over-week growth index (today / last week)
df['wow_ratio'] = (df['sales'] / df['sales'].shift(7)).round(3)

# Deviation from 3-day lag
df['dev_3d'] = df['sales'] - df['sales'].shift(3)

# Is today higher than yesterday? (boolean feature)
df['higher_than_yesterday'] = df['sales'] > df['sales'].shift(1)

print(df[['sales', 'wow_ratio', 'higher_than_yesterday']].head())

cumsum() i cumprod() do tworzenia cech skumulowanych

Series.cumsum() oblicza sumę narastającą od pierwszego wiersza do bieżącego wiersza, natomiast Series.cumprod() oblicza iloczyn skumulowany. Funkcje te są przydatne do obliczania skumulowanych przychodów, skumulowanych stóp zwrotu (wzrostu składanego) oraz sum od początku roku. Nie wymagają argumentów i działają na dowolnym szeregu liczbowym Series lub kolumnie DataFrame.

# Cumulative sales (year-to-date total)
df['ytd_sales'] = df['sales'].cumsum()

# Cumulative product: compound growth factor
returns = pd.Series([0.02, -0.01, 0.03, 0.01, -0.005])
df_ret = pd.DataFrame({'daily_return': returns})
df_ret['compound'] = (1 + df_ret['daily_return']).cumprod() - 1
print(df_ret)

Cechy opóźnione na potrzeby uczenia maszynowego

Podczas przygotowywania danych szeregów czasowych do uczenia maszynowego standardowym etapem inżynierii cech jest utworzenie macierzy cech opóźnionych. Każda kolumna reprezentuje zmienną docelową z innego wcześniejszego kroku czasowego. Po utworzeniu opóźnień należy usunąć wiersze zawierające NaN (pojawiające się na początku z powodu braku wcześniejszych danych) oraz podzielić dane na zbiory treningowy i testowy bez tasowania, zachowując kolejność czasową.

def make_lag_matrix(series, n_lags):
    df_lags = pd.DataFrame({'y': series})
    for lag in range(1, n_lags + 1):
        df_lags[f'lag_{lag}'] = series.shift(lag)
    return df_lags.dropna()

lag_df = make_lag_matrix(df['sales'], n_lags=3)
print(lag_df)
# y    lag_1  lag_2  lag_3
# ...  ...    ...    ...

Przesuwanie w obrębie grup

Gdy dane zawierają wiele obiektów (np. wiele produktów lub regionów w jednym DataFrame), przesunięcia należy obliczać osobno w obrębie grup każdego obiektu. Należy użyć groupby().shift(), aby mieć pewność, że opóźnienie w pierwszym wierszu produktu A ma wartość NaN, a nie ostatnią wartość produktu B. Łączenie grup bez wykonania tego kroku jest częstym i trudnym do wykrycia błędem prowadzącym do wycieku danych.

df_multi = pd.DataFrame({
    'product': ['A', 'A', 'A', 'B', 'B', 'B'],
    'sales': [100, 120, 115, 200, 210, 195]
})

# WRONG: lag crosses product boundary
df_multi['lag_wrong'] = df_multi['sales'].shift(1)

# CORRECT: lag within each product
df_multi['lag_correct'] = df_multi.groupby('product')['sales'].shift(1)
print(df_multi)

Interpretowanie znaków wartości pct_change

Dodatnia wartość pct_change() oznacza, że bieżąca wartość jest wyższa od poprzedniej; wartość ujemna oznacza, że jest niższa. Należy uważać na dzielenie przez zero, gdy poprzednia wartość wynosi zero — wynik to NaN lub inf, zależnie od znaku bieżącej wartości. Aby wyczyścić nieskończone wartości po użyciu pct_change(), należy zastosować replace([float('inf'), float('-inf')], float('nan')).

import numpy as np

s = pd.Series([0, 100, 50, 200])
chg = s.pct_change()
print(chg)
# 0      NaN  <- no prior value
# 1      inf  <- 0 -> 100 (division by zero)
# 2    -0.5   <- 100 -> 50 (-50%)
# 3     3.0   <- 50 -> 200 (+300%)

# Clean infinite values
chg_clean = chg.replace([float('inf'), float('-inf')], float('nan'))
print(chg_clean)

Szybkie sprawdzenie

Sprawdź swoją wiedzę na temat przesuwania i cech opóźnionych z tego modułu.

Podsumowanie lekcji

W tej lekcji nauczyli się Państwo, że: shift(n) tworzy cechy opóźnione, przesuwając wartości w dół o n pozycji; diff(n) oblicza bezwzględną zmianę względem poprzedniego okresu; pct_change(n) oblicza względną zmianę procentową; a podczas pracy z wieloma grupami należy używać groupby().shift(), aby uniknąć wycieku danych. W następnym kroku wyodrębnimy cechy czasowe za pomocą akcesora .dt.

Często zadawane pytania

Czy lekcja „Przesuwanie i cechy opóźnione” jest bezpłatna?

Tak — pełny tekst „Przesuwanie i cechy opóźnione” 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 „Przesuwanie i cechy opóźnione”?

Twórz kolumny opóźnienia i wyprzedzenia za pomocą shift(), obliczaj zmianę między okresami za pomocą diff() oraz wyznaczaj zmianę procentową. Ć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 3 z 4.

Ile czasu zajmuje lekcja „Przesuwanie i cechy opóźnione”?

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

  1. DatetimeIndex i zakresy okresów
  2. Resampling szeregów czasowych
  3. Przesuwanie i cechy opóźnione
  4. Wyodrębnianie cech czasowych
← Powrót do Pandas & NumPy Academy