Pandas & NumPy Academy · Урок

Сдвиги и признаки запаздывания

Создавайте столбцы с запаздыванием и опережением с помощью shift(), вычисляйте изменение между периодами с помощью diff() и рассчитывайте процентное изменение.

Урок 3 из 413 шагов

«Сдвиги и признаки запаздывания» — бесплатный урок Pandas & NumPy Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Pandas & NumPy Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Pandas & NumPy Academy содержит 4 уроков всего.

Почему важны признаки с запаздыванием

В анализе временных рядов значение на предыдущем временном шаге (запаздывание) часто является одним из лучших предикторов текущего значения. Например, вчерашние продажи помогают предсказать сегодняшние. Создание столбцов с запаздыванием позволяет использовать исторические значения как признаки в моделях машинного обучения или при статистическом анализе автокорреляции. Pandas предоставляет shift() для создания таких признаков одним векторизованным вызовом.

shift(): перемещение значений вперёд или назад

Series.shift(n) перемещает все значения вниз на n позиций (положительное n создаёт запаздывание), заполняя первые n строк значением NaN. Передача отрицательного n перемещает значения вверх (создаёт опережение, сдвигая будущие значения назад к текущей строке). Индекс остаётся неизменным — перемещаются только значения.

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)

Создание нескольких столбцов с запаздыванием

Для машинного обучения обычно создают сразу несколько признаков с запаздыванием — сдвиг на 1, на 7 (тот же день на прошлой неделе) и на 30 (тот же день в прошлом месяце). Наиболее понятный способ — создать их в цикле и присвоить каждый новому столбцу. В результирующем DataFrame исходный ряд дополнен всеми его версиями со сдвигом и готов к построению моделей.

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() с freq для сдвига по времени

Вместо сдвига на целое число строк можно сдвигать данные на временное смещение с помощью параметра freq. df.shift(1, freq='ME') сдвигает индекс вперёд на один конец месяца, оставляя значения на месте, но изменяя индекс. Это удобно для выравнивания двух временных рядов, смещённых на фиксированный период, без перестановки значений.

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(): изменение от периода к периоду

Series.diff(n) вычисляет разность между значением и значением, расположенным на n позиций раньше: x[t] - x[t-n]. Это часто используют для вычисления изменений день к дню, различий неделя к неделе или изменений год к году. Первые n строк содержат NaN, поскольку предыдущих значений, из которых можно было бы вычесть текущие, нет.

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(): процентное изменение

Series.pct_change(n) вычисляет относительное процентное изменение: (x[t] - x[t-n]) / x[t-n]. Это необходимо для финансового анализа (дневная доходность), вычисления темпов роста и любых ситуаций, в которых относительное изменение важнее абсолютного. Умножьте результат на 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

Объединение shift() с арифметикой

Вы можете объединять shift() с арифметическими операциями для вычисления производных признаков времени. Например, можно получить отношение сегодняшнего значения к значению прошлой недели, накопительную сумму с фиксированного начала или разность с прошлогодним значением. Все эти операции следуют одному шаблону: сдвиньте исходный ряд и выполните поэлементную арифметику с текущими значениями.

# 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() и cumprod() для накопительных признаков

Series.cumsum() вычисляет текущую сумму от первой строки до каждой строки, а Series.cumprod() — накопительное произведение. Это удобно для накопительной выручки, накопительной доходности (сложного роста) и итогов с начала года. Аргументы не требуются; методы работают с любым числовым Series или столбцом 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)

Признаки с запаздыванием для машинного обучения

При подготовке временных рядов для машинного обучения стандартный этап создания признаков — построение матрицы лаговых признаков. Каждый столбец представляет целевую переменную на отдельном шаге в прошлом. После создания лагов удалите строки с NaN (они появляются в начале из-за отсутствия предыдущих данных) и разделите данные на обучающую и тестовую выборки без перемешивания, сохраняя временной порядок.

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
# ...  ...    ...    ...

Сдвиг внутри групп

Если данные содержат несколько объектов (например, несколько товаров или регионов в одном DataFrame), необходимо вычислять сдвиги отдельно внутри группы каждого объекта. Используйте groupby().shift(), чтобы лаг для первой строки товара A был равен NaN, а не последнему значению товара B. Смешивание групп без этого шага — распространённая и трудно заметная ошибка утечки данных.

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)

Интерпретация знаков pct_change

Положительное значение pct_change() означает, что текущее значение выше предыдущего; отрицательное — что оно ниже. Будьте внимательны к делению на ноль, если предыдущее значение равно нулю: результатом будет NaN или inf в зависимости от знака текущего значения. Используйте replace([float('inf'), float('-inf')], float('nan')), чтобы очистить бесконечные значения после вызова pct_change().

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)

Быстрая проверка

Проверьте, насколько хорошо Вы поняли сдвиги и лаговые признаки из этого урока.

Итоги урока

В этом уроке Вы узнали, что shift(n) создаёт лаговые признаки, сдвигая значения вниз на n позиций; diff(n) вычисляет абсолютное изменение между периодами; pct_change(n) вычисляет относительное процентное изменение; а при работе с несколькими группами необходимо использовать groupby().shift(), чтобы избежать утечки данных. Далее мы извлечём временные признаки с помощью аксессора .dt.

Можно начать бесплатно

Изучай Python с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
30
Уроки
120

Часто задаваемые вопросы

Урок «Сдвиги и признаки запаздывания» бесплатный?

Да — полный текст урока «Сдвиги и признаки запаздывания» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Pandas & NumPy Academy, подпишись на CoddyKit PRO. Курс Pandas & NumPy Academy содержит 4 уроков всего.

Чему я научусь в уроке «Сдвиги и признаки запаздывания»?

Создавайте столбцы с запаздыванием и опережением с помощью shift(), вычисляйте изменение между периодами с помощью diff() и рассчитывайте процентное изменение. Ты практикуешь Pandas & NumPy Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Pandas & NumPy Academy?

Предыдущий опыт не требуется. Pandas & NumPy Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Сдвиги и признаки запаздывания»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Pandas & NumPy Academy?

Да. Каждый урок Pandas & NumPy Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. DatetimeIndex и диапазоны периодов
  2. Ресэмплинг временных рядов
  3. Сдвиги и признаки запаздывания
  4. Извлечение временных признаков
← Назад к Pandas & NumPy Academy