Коротко
shift(1)сдвигает значения на строку вниз — в строке оказывается предыдущее значение;shift(-1)— следующее. Освободившиеся места —NaN.diff()— разность с предыдущим (s - s.shift()),pct_change()— относительное изменение (s / s.shift() - 1).periods=k— сравнение со значением k строк назад.shift(1, freq="D")сдвигает даты в индексе, а значения оставляет на месте.- Для нескольких рядов в одной таблице —
groupby(...)["col"].diff(), иначе разности посчитаются между разными рядами.
Синтаксис
s.shift(periods=1, freq=None, fill_value=None)
s.diff(periods=1)
s.pct_change(periods=1)
df.groupby("key")["col"].shift(), .diff(), .pct_change()Параметры
ПараметрЧто делаетПо умолчанию
periodsна сколько строк сдвинуть или с каким значением сравнить; отрицательное — вперёд1freqв shift: сдвинуть индекс-даты на частоту вместо сдвига значенийNonefill_valueв shift: чем заполнить освободившиеся местаNaNПримеры
days = pd.date_range("2024-03-01", periods=6, freq="D")
price = pd.Series([100.0, 102.0, 101.0, 105.0, 105.0, 99.0], index=days, name="price")
trades = pd.DataFrame({ # сделки по двум тикерам вперемешку
"ticker": ["A", "B", "A", "B", "A"],
"price": [10.0, 50.0, 11.0, 48.0, 12.5],
})pd.DataFrame({"price": price, "yesterday": price.shift(1), "tomorrow": price.shift(-1)})Вывод
price yesterday tomorrow 2024-03-01 100.0 NaN 102.0 2024-03-02 102.0 100.0 101.0 2024-03-03 101.0 102.0 105.0 2024-03-04 105.0 101.0 105.0 2024-03-05 105.0 105.0 99.0 2024-03-06 99.0 105.0 NaN
price.diff() # price − вчерашняя ценаВывод
2024-03-01 NaN 2024-03-02 2.0 2024-03-03 -1.0 2024-03-04 4.0 2024-03-05 0.0 2024-03-06 -6.0 Freq: D, Name: price, dtype: float64
(price.pct_change() * 100).round(2) # изменение в процентахВывод
2024-03-01 NaN 2024-03-02 2.00 2024-03-03 -0.98 2024-03-04 3.96 2024-03-05 0.00 2024-03-06 -5.71 Freq: D, Name: price, dtype: float64
price.diff(periods=3) # к значению 3 дня назадВывод
2024-03-01 NaN 2024-03-02 NaN 2024-03-03 NaN 2024-03-04 5.0 2024-03-05 3.0 2024-03-06 -2.0 Freq: D, Name: price, dtype: float64
price.shift(1, freq="D").head(3) # сдвинулись даты, а не значенияВывод
2024-03-02 100.0 2024-03-03 102.0 2024-03-04 101.0 Freq: D, Name: price, dtype: float64
trades.assign(change=trades.groupby("ticker")["price"].diff()) # внутри каждого тикераВывод
ticker price change 0 A 10.0 NaN 1 B 50.0 NaN 2 A 11.0 1.0 3 B 48.0 -2.0 4 A 12.5 1.5
Подводные камни
Разности между разными рядами
Если в одной таблице несколько тикеров, магазинов или клиентов, простой diff вычтет значение одного ряда из другого.
trades.assign(change=trades["price"].diff()) # без группировки — разница между разными тикерамиВывод
ticker price change 0 A 10.0 NaN 1 B 50.0 40.0 2 A 11.0 -39.0 3 B 48.0 37.0 4 A 12.5 -35.5
pct_change и пропуски
s = pd.Series([100.0, None, 110.0])
print(s.pct_change()) # у пропуска и после него — NaN
s.ffill().pct_change() # старое поведение — явный ffill перед расчётомВывод
0 NaN 1 NaN 2 NaN dtype: float64 0 NaN 1 0.0 2 0.1 dtype: float64