Pandas
обладает простой, мощной и эффективной функциональностью для выполнения операций передискретизации во время преобразования частоты (например, преобразование вторичных данных в 5-минутные интервалы/периоды). Это чрезвычайно распространено при анализе данных.
df = DataFrame.resample(rule, axis=no_default, closed=None, label=None, convention='start', kind=None, on=None, level=None, origin='start_day', offset=None, group_keys=False) s = Series.resample(rule, axis=no_default, closed=None, label=None, convention='start', kind=None, on=None, level=None, origin='start_day', offset=None, group_keys=False)
rule
- строка временной интервал/период (смещение частоты) или объект, представляющий целевое преобразование (DateOffset
, pandas.Timedelta
).
axis=no_default
- что использовать для повышающей или понижающей дискретизации. Устарело с версии 2.0.0: теперь, чтобы использовать столбцы нужно вызвать DataFrame.T.resample(…)
. Используемая строка/столбец должны быть pandas.DatetimeIndex
, pandas.TimedeltaIndex
или pandas.PeriodIndex
.
closed=None
- какая сторона интервала rule
будет включена в сегмент (пример). Принимает значения: ‘right’
и ‘left’
. По умолчанию установлено значение 'left'
для всех интервалов/периодов, за исключением 'M'
, 'A'
, 'Q'
, 'BM'
, 'BA'
, 'BQ'
и 'W'
, которые по умолчанию имеют значение 'right'
.
label=None
- как отметить край периода для включения в сегмент (пример) при его закрытии аргументом closed
. Значение по умолчанию 'left' для всех интервалов/периодов, за исключением 'M'
, 'A'
, 'Q'
, 'BM'
, 'BA'
, 'BQ'
и 'W'
, которые по умолчанию имеют значение 'right'
.
convention='start'
- только для pandas.PeriodIndex
, определяет, следует ли использовать начало или конец периода/интервала (аргумента rule
). Принимает значения: 'start'
(‘s’
) и ‘end’
(‘e’
).
kind=None
- чтобы преобразовать результирующий индекс:
pandas.DatetimeIndex
необходимо установить значение 'timestamp'
, pandas.PeriodIndex
необходимо установить значение 'period'
.По умолчанию входное представление сохраняется.
on=None
- только для DataFrame
- столбец, который будет использоваться вместо индекса. Столбец должен быть похож на datetime
.
level=None
- только для MultiIndex
- уровень (имя столбца или число уровня), используемый для пересчета. Столбец должен быть похож на datetime
.
origin='start_day'
- метка времени, по которой корректируется группировка. Исходный часовой пояс должен совпадать с часовым поясом индекса. Если передается строка, то должна быть одной из следующего:
'epoch'
: начало - 1970-01-01;'start'
: начало - это первое значение интервала/периода;'start_day'
: начало - это первый день в полночь интервала/периода;'end'
: начало - это последнее значение интервала/периода;'end_day'
: начало - это крайняя полночь интервала/периода.Примечание. Вступает в силу только для фиксированных частот, таких как дни, часы и минуты, а не месяцы или кварталы.
offset=None
- смещение timedelta
, добавленное к исходной точке. Принимает pandas.Timedelta
или строку смещения частоты.
group_keys=False
- следует ли включать ключи группы в индекс результата при использовании метода DataFrame.apply()
для объекта с повторной выборкой.
pandas.api.typing.Resampler
DataFrame.resample()
:Методы DataFrame.resample()
и Series.resample()
модуля pandas
передискретизируют данные временных рядов.
Это удобные методы преобразования частот и передискретизации временных рядов. Объект Series
/DataFrame
должен иметь индекс, похожий на дату и время (pandas.DatetimeIndex
, pandas.PeriodIndex
или pandas.TimedeltaIndex
), или вызывающий Series
/DataFrame
должен передать метку Series
/Index
, похожей на datetime
, в ключевой аргумент on
/level
.
О методе
.resample()
можно думать как о группировке по времени, за которым следует метод агрегирования для каждой из его групп. Другими словами - этоGroupBy
основанный на времени. Его можно использовать непосредственно из объектов обычной группировкиDataFrameGroupBy
.
>>> import pandas as pd >>> import numpy as np >>> rng = pd.date_range("1/1/2012", periods=100, freq="s") >>> ts = pd.Series(np.random.randint(0, 500, len(rng)), index=rng) >>> ts.resample("5Min").sum() # 2012-01-01 24937 # Freq: 5min, dtype: int64
Метод .resample()
объектов Series
/DataFrame
является очень гибким и позволяет указать множество различных параметров для управления преобразованием частоты и операцией повторной выборки.
Любой встроенный агрегирующий метод, доступный через GroupBy
, также доступен как метод возвращаемого объекта, включая: .sum()
, .mean()
, .std()
, .sem()
, .max()
, .min()
, .median()
, .first()
, .last()
, .ohlc()
:
>>> ts.resample("5Min").mean() # 2012-01-01 249.37 # Freq: 5min, dtype: float64 >>> ts.resample("5Min").ohlc() # open high low close # 2012-01-01 108 499 0 206 >>> ts.resample("5Min").max() # 2012-01-01 499 # Freq: 5min, dtype: int64
Для понижающей дискретизации, для указания, какой конец интервала является закрытым, значение аргумента closed
может быть установлено в значение ‘left’
или ‘right’
:
>>> ts.resample("5Min", closed="right").mean() # 2011-12-31 23:55:00 108.00000 # 2012-01-01 00:00:00 250.79798 # Freq: 5min, dtype: float64 >>> ts.resample("5Min", closed="left").mean() # 2012-01-01 249.37 # Freq: 5min, dtype: float64
Аргумент label
используются для управления результирующими метками, которые указывают, будет ли результат помечен началом или концом интервала.
# по умолчанию `label='left'` >>> ts.resample("5Min").mean() # 2012-01-01 249.37 # Freq: 5min, dtype: float64 >>> ts.resample("5Min", label="left").mean() # 2012-01-01 249.37 # Freq: 5min, dtype: float64
Предупреждение. Значения по умолчанию для аргументов
label
иclosed
- это'left'
для всех частотных смещений, за исключением'ME'
,'YE'
,'QE'
,'BME'
,'BYE'
,'BQE'
и'W'
, которые по умолчанию имеют значение'right'
.Это может непреднамеренно привести к просмотру вперед, когда значение более позднего времени возвращается к предыдущему времени, как в следующем примере с частотой "рабочие дни":
>>> s = pd.date_range("2000-01-01", "2000-01-05").to_series() >>> s.iloc[2] = pd.NaT >>> s.dt.day_name() # 2000-01-01 Saturday # 2000-01-02 Sunday # 2000-01-03 NaN # 2000-01-04 Tuesday # 2000-01-05 Wednesday # Freq: D, dtype: object # по умолчанию: label='left', closed='left' >>> s.resample("B").last().dt.day_name() # 1999-12-31 Sunday # 2000-01-03 NaN # 2000-01-04 Tuesday # 2000-01-05 Wednesday # Freq: B, dtype: objectОбратите внимание, что значение для воскресенья вернулось к предыдущей пятнице. Чтобы получить поведение, при котором значение для воскресенья переносится на понедельник, нужно использовать:
>>> s.resample("B", label="right", closed="right").last().dt.day_name() # 2000-01-03 Sunday # 2000-01-04 Tuesday # 2000-01-05 Wednesday # 2000-01-06 NaN # Freq: B, dtype: object
Аргумент axis
может быть установлен в 0 или 1, что позволяет выполнить повторную выборку по указанной оси.
Для аргумента kind
можно задать значение 'timestamp'
или 'period'
, для преобразования результирующего индекса в/из представления метки времени и временного интервала. По умолчанию метод .resample()
сохраняет входное представление.
Аргумент convention
может быть установлен в 'start'
или 'end'
при повторной выборке данных периода (подробнее ниже). Оно определяет, как низкочастотные периоды преобразуются в периоды более высокой частоты.
Для повышения дискретизации можно указать способ и аргумент limit
для интерполяции по создаваемым промежуткам:
# повышаем разрешение от секунды # до каждых 250 миллисекунд >>> ts[:2].resample("250ms").asfreq() # 2012-01-01 00:00:00.000 108.0 # 2012-01-01 00:00:00.250 NaN # 2012-01-01 00:00:00.500 NaN # 2012-01-01 00:00:00.750 NaN # 2012-01-01 00:00:01.000 430.0 # Freq: 250ms, dtype: float64 >>> ts[:2].resample("250ms").ffill() # 2012-01-01 00:00:00.000 108 # 2012-01-01 00:00:00.250 108 # 2012-01-01 00:00:00.500 108 # 2012-01-01 00:00:00.750 108 # 2012-01-01 00:00:01.000 430 # Freq: 250ms, dtype: int64 >>> ts[:2].resample("250ms").ffill(limit=2) # 2012-01-01 00:00:00.000 108.0 # 2012-01-01 00:00:00.250 108.0 # 2012-01-01 00:00:00.500 108.0 # 2012-01-01 00:00:00.750 NaN # 2012-01-01 00:00:01.000 430.0 # Freq: 250ms, dtype: float64
Разреженные временные ряды - это те, в которых намного меньше временных точек по сравнению с количеством точек, по котором нужно выполнить повторную выборку. Наивная повышающая выборка разреженного ряда потенциально может генерировать множество промежуточных/пропущенных значений. Если нет необходимости использовать метод для заполнения пропущенных значений, то тогда эти значения будут заполнены NaN
.
Так как повторная выборка - это своего рода GroupBy
основанная на времени, ниже описан метод эффективной повторной выборки только тех групп, у которых не все значения являются NaN
.
>>> rng = pd.date_range("2014-1-1", periods=100, freq="D") + pd.Timedelta("1s") >>> ts = pd.Series(range(100), index=rng)
Если необходимо выполнить повторную выборку для всего диапазона серии:
>>> ts.resample("3min").sum() # 2014-01-01 00:00:00 0 # 2014-01-01 00:03:00 0 # 2014-01-01 00:06:00 0 # 2014-01-01 00:09:00 0 # 2014-01-01 00:12:00 0 # .. # 2014-04-09 23:48:00 0 # 2014-04-09 23:51:00 0 # 2014-04-09 23:54:00 0 # 2014-04-09 23:57:00 0 # 2014-04-10 00:00:00 99 # Freq: 3min, Length: 47521, dtype: int64
Можно выполнить повторную выборку только тех групп, в которых присутствуют точки:
>>> from functools import partial >>> from pandas.tseries.frequencies import to_offset def round(t, freq): freq = to_offset(freq) td = pd.Timedelta(freq) return pd.Timestamp((t.value // td.value) * td.value) >>> ts.groupby(partial(round, freq="3min")).sum() # 2014-01-01 0 # 2014-01-02 1 # 2014-01-03 2 # 2014-01-04 3 # 2014-01-05 4 # .. # 2014-04-06 95 # 2014-04-07 96 # 2014-04-08 97 # 2014-04-09 98 # 2014-04-10 99
Метод .resample()
возвращает экземпляр Resampler
. Так вот, экземпляр Resampler
позволяет сделать передискретизацию выборочно. Другими словами, повторная выборка может быть выборочной.
При повторной выборке DataFrame
, по умолчанию, одна и та же функция будет применяться ко всем столбцам.
df = pd.DataFrame( np.random.randn(1000, 3), index=pd.date_range("1/1/2012", freq="s", periods=1000), columns=["A", "B", "C"], ) >>> r = df.resample("3min") >>> r.mean() # A B C # 2012-01-01 00:00:00 0.022829 0.094448 -0.048866 # 2012-01-01 00:03:00 0.136390 -0.143401 0.007721 # 2012-01-01 00:06:00 -0.032720 0.052944 -0.012141 # 2012-01-01 00:09:00 0.132600 -0.052076 0.011792 # 2012-01-01 00:12:00 -0.029085 0.103086 0.015926 # 2012-01-01 00:15:00 0.020947 0.089426 0.172600
Экземпляр Resampler
позволяет выбрать конкретный столбец/столбцы, используя стандартный "getitem
".
>>> r["A"].mean() # 2012-01-01 00:00:00 0.022829 # 2012-01-01 00:03:00 0.136390 # 2012-01-01 00:06:00 -0.032720 # 2012-01-01 00:09:00 0.132600 # 2012-01-01 00:12:00 -0.029085 # 2012-01-01 00:15:00 0.020947 # Freq: 3min, Name: A, dtype: float64 >>> r[["A", "B"]].mean() # A B # 2012-01-01 00:00:00 0.022829 0.094448 # 2012-01-01 00:03:00 0.136390 -0.143401 # 2012-01-01 00:06:00 -0.032720 0.052944 # 2012-01-01 00:09:00 0.132600 -0.052076 # 2012-01-01 00:12:00 -0.029085 0.103086 # 2012-01-01 00:15:00 0.020947 0.089426
Можно передать список или набор функций для агрегирования, выводя DataFrame
:
>>> r["A"].agg(["sum", "mean", "std"]) # sum mean std # 2012-01-01 00:00:00 4.109197 0.022829 0.981974 # 2012-01-01 00:03:00 24.550261 0.136390 1.033548 # 2012-01-01 00:06:00 -5.889569 -0.032720 1.051676 # 2012-01-01 00:09:00 23.867969 0.132600 1.072202 # 2012-01-01 00:12:00 -5.235257 -0.029085 1.032037 # 2012-01-01 00:15:00 2.094737 0.020947 1.016523
В DataFrame
с повторной выборкой можно передать список функций для применения к каждому столбцу, что дает агрегированный результат с иерархическим индексом:
>>> r.agg(["sum", "mean"]) # A B C # sum mean sum mean sum mean # 2012-01-01 00:00:00 4.109197 0.022829 17.000703 0.094448 -8.795932 -0.048866 # 2012-01-01 00:03:00 24.550261 0.136390 -25.812122 -0.143401 1.389698 0.007721 # 2012-01-01 00:06:00 -5.889569 -0.032720 9.529892 0.052944 -2.185363 -0.012141 # 2012-01-01 00:09:00 23.867969 0.132600 -9.373647 -0.052076 2.122641 0.011792 # 2012-01-01 00:12:00 -5.235257 -0.029085 18.555395 0.103086 2.866605 0.015926 # 2012-01-01 00:15:00 2.094737 0.020947 8.942611 0.089426 17.260037 0.172600
Передавая словарь вида {'nameCollumn': aggFunc, ...}
, можно применить разную агрегацию к разным столбцам DataFrame
:
>>> r.agg({"A": "sum", "B": lambda x: np.std(x, ddof=1)}) # A B # 2012-01-01 00:00:00 4.109197 1.040406 # 2012-01-01 00:03:00 24.550261 1.040631 # 2012-01-01 00:06:00 -5.889569 1.048884 # 2012-01-01 00:09:00 23.867969 0.988539 # 2012-01-01 00:12:00 -5.235257 0.975007 # 2012-01-01 00:15:00 2.094737 1.035515
Имена функций могут быть строками. Чтобы строка была допустимой, она должна быть реализована в объекте с повторной выборкой:
>>> r.agg({"A": "sum", "B": "std"}) # A B # 2012-01-01 00:00:00 4.109197 1.040406 # 2012-01-01 00:03:00 24.550261 1.040631 # 2012-01-01 00:06:00 -5.889569 1.048884 # 2012-01-01 00:09:00 23.867969 0.988539 # 2012-01-01 00:12:00 -5.235257 0.975007 # 2012-01-01 00:15:00 2.094737 1.035515
Кроме того можно указать несколько функций агрегирования для каждого столбца отдельно.
>>> r.agg({"A": ["sum", "std"], "B": ["mean", "std"]}) # A B # sum std mean std # 2012-01-01 00:00:00 4.109197 0.981974 0.094448 1.040406 # 2012-01-01 00:03:00 24.550261 1.033548 -0.143401 1.040631 # 2012-01-01 00:06:00 -5.889569 1.051676 0.052944 1.048884 # 2012-01-01 00:09:00 23.867969 1.072202 -0.052076 0.988539 # 2012-01-01 00:12:00 -5.235257 1.032037 0.103086 0.975007 # 2012-01-01 00:15:00 2.094737 1.016523 0.089426 1.035515
Если DataFrame
не имеет индекса, представленного как datetime
, но есть столбец с данными, похожими на дату/время, то по нему можно выполнить повторную выборку, передав его имя ключевому аргументу on
.
df = pd.DataFrame( {"date": pd.date_range("2015-01-01", freq="W", periods=5), "a": np.arange(5)}, index=pd.MultiIndex.from_arrays( [[1, 2, 3, 4, 5], pd.date_range("2015-01-01", freq="W", periods=5)], names=["v", "d"], ), ) >>> df # date a # v d # 1 2015-01-04 2015-01-04 0 # 2 2015-01-11 2015-01-11 1 # 3 2015-01-18 2015-01-18 2 # 4 2015-01-25 2015-01-25 3 # 5 2015-02-01 2015-02-01 4 >>> df.resample("ME", on="date")[["a"]].sum() # a # date # 2015-01-31 6 # 2015-02-28 4
Аналогично, если нужно выполнить повторную выборку по уровню MultiIndex
, похожему на дату/время, его имя или местоположение можно передать ключевому аргументу level
.
>>> df.resample("ME", level="d")[["a"]].sum() # a # d # 2015-01-31 6 # 2015-02-28 4
С объектом Resampler
, перебор сгруппированных данных является очень естественным и работает аналогично itertools.groupby()
:
small = pd.Series( range(6), index=pd.to_datetime( [ "2017-01-01T00:00:00", "2017-01-01T00:30:00", "2017-01-01T00:31:00", "2017-01-01T01:00:00", "2017-01-01T03:00:00", "2017-01-01T03:05:00", ] ), ) >>> resampled = small.resample("h") >>> for name, group in resampled: ... print("Group: ", name) ... print("-" * 27) ... print(group, end="\n\n") # Group: 2017-01-01 00:00:00 # --------------------------- # 2017-01-01 00:00:00 0 # 2017-01-01 00:30:00 1 # 2017-01-01 00:31:00 2 # dtype: int64 # # Group: 2017-01-01 01:00:00 # --------------------------- # 2017-01-01 01:00:00 3 # dtype: int64 # # Group: 2017-01-01 02:00:00 # --------------------------- # Series([], dtype: int64) # # Group: 2017-01-01 03:00:00 # --------------------------- # 2017-01-01 03:00:00 4 # 2017-01-01 03:05:00 5 # dtype: int64
origin
or offset
для регулировки начала интервалов.Ячейки группировки корректируются на основе начала дня начальной точки временного ряда. Это хорошо работает с частотами, кратными суткам (например, '30D'
) или равномерно разделяющими день (например, '90s'
или '1min'
). НО может привести к несоответствиям с частотами, которые не соответствуют этим критериям. Чтобы изменить такое поведение, можно указать фиксированную временную метку с аргументом origin
.
Например:
>>> start, end = "2000-10-01 23:30:00", "2000-10-02 00:30:00" >>> middle = "2000-10-02 00:00:00" >>> rng = pd.date_range(start, end, freq="7min") >>> ts = pd.Series(np.arange(len(rng)) * 3, index=rng) >>> ts # 2000-10-01 23:30:00 0 # 2000-10-01 23:37:00 3 # 2000-10-01 23:44:00 6 # 2000-10-01 23:51:00 9 # 2000-10-01 23:58:00 12 # 2000-10-02 00:05:00 15 # 2000-10-02 00:12:00 18 # 2000-10-02 00:19:00 21 # 2000-10-02 00:26:00 24 # Freq: 7min, dtype: int64
Ниже видим, что при использовании origin='start_day'
(значение по умолчанию), результат после '2000-10-02 00:00:00' не идентичен в зависимости от начала временного ряда:
>>> ts.resample("17min", origin="start_day").sum() # 2000-10-01 23:14:00 0 # 2000-10-01 23:31:00 9 # 2000-10-01 23:48:00 21 # 2000-10-02 00:05:00 54 # 2000-10-02 00:22:00 24 # Freq: 17min, dtype: int64 >>> ts[middle:end].resample("17min", origin="start_day").sum() # 2000-10-02 00:00:00 33 # 2000-10-02 00:17:00 45 # Freq: 17min, dtype: int64
Смотрим код ниже. При установке значения origin='epoch'
, результат после '2000-10-02 00:00:00' идентичен в зависимости от начала временного ряда:
>>> ts.resample("17min", origin="epoch").sum() # 2000-10-01 23:18:00 0 # 2000-10-01 23:35:00 18 # 2000-10-01 23:52:00 27 # 2000-10-02 00:09:00 39 # 2000-10-02 00:26:00 24 # Freq: 17min, dtype: int64 >>> ts[middle:end].resample("17min", origin="epoch").sum() # 2000-10-01 23:52:00 15 # 2000-10-02 00:09:00 39 # 2000-10-02 00:26:00 24 # Freq: 17min, dtype: int64
При необходимости, для origin
можно использовать пользовательскую временную метку:
>>> ts.resample("17min", origin="2001-01-01").sum() # 2000-10-01 23:30:00 9 # 2000-10-01 23:47:00 21 # 2000-10-02 00:04:00 54 # 2000-10-02 00:21:00 24 # Freq: 17min, dtype: int64 >>> ts[middle:end].resample("17min", origin=pd.Timestamp("2001-01-01")).sum() # 2000-10-02 00:04:00 54 # 2000-10-02 00:21:00 24 # Freq: 17min, dtype: int64
При необходимости, можно просто настроить интервалы со смещением offset
равным pandas.Timedelta
, которое будет добавлено к исходной точке по умолчанию. Два примера ниже - эквивалентны для данного временного ряда:
>>> ts.resample("17min", origin="start").sum() # 2000-10-01 23:30:00 9 # 2000-10-01 23:47:00 21 # 2000-10-02 00:04:00 54 # 2000-10-02 00:21:00 24 # Freq: 17min, dtype: int64 >>> ts.resample("17min", offset="23h30min").sum() # 2000-10-01 23:30:00 9 # 2000-10-01 23:47:00 21 # 2000-10-02 00:04:00 54 # 2000-10-02 00:21:00 24 # Freq: 17min, dtype: int64
Обратите внимание на использование 'start'
для origin
в последнем примере. В этом случае для origin
будет установлено первое значение временного ряда.
Для выполнения обратной передискретизации с заданной частотой freq
, вместо настройки начала интервалов нужно зафиксировать конец интервалов. Обратная повторная выборка по умолчанию устанавливает значение closed='right'
, т.к. последнее значение следует рассматривать как граничную точку для последней ячейки.
Можно установить origin='end'
. Значение для определенного индекса Timestamp
обозначает результат повторной выборки из текущего Timestamp
за вычетом частоты freq
в текущий Timestamp
с правильным закрытием.
>>> ts.resample('17min', origin='end').sum() # 2000-10-01 23:35:00 0 # 2000-10-01 23:52:00 18 # 2000-10-02 00:09:00 27 # 2000-10-02 00:26:00 63 # Freq: 17min, dtype: int64
Кроме того, в отличие от 'start_day'
, поддерживается значение 'end_day'
. Это установит origin
как максимальную полночь самой большой метки времени Timestamp
.
>>> ts.resample('17min', origin='end_day').sum() # 2000-10-01 23:38:00 3 # 2000-10-01 23:55:00 15 # 2000-10-02 00:12:00 45 # 2000-10-02 00:29:00 45 # Freq: 17min, dtype: int64
Приведенный выше результат использует 2000-10-02 00:29:00 в качестве правого края последней ячейки с момента следующего вычисления.
>>> ceil_mid = rng.max().ceil('D') >>> freq = pd.offsets.Minute(17) >>> bin_res = ceil_mid - freq * ((ceil_mid - rng.max()) // freq) >>> bin_res # Timestamp('2000-10-02 00:29:00')
.resample()
объектов Series
/DataFrame
Начнем с создания Series
с 9 временными метками по одной минуте.
>>> index = pd.date_range('1/1/2000', periods=9, freq='T') >>> series = pd.Series(range(9), index=index) >>> series # 2000-01-01 00:00:00 0 # 2000-01-01 00:01:00 1 # 2000-01-01 00:02:00 2 # 2000-01-01 00:03:00 3 # 2000-01-01 00:04:00 4 # 2000-01-01 00:05:00 5 # 2000-01-01 00:06:00 6 # 2000-01-01 00:07:00 7 # 2000-01-01 00:08:00 8 # Freq: T, dtype: int64
Уменьшим выборку Series
до 3-минутных периодов и суммируем значения временных меток, попадающих в период.
>>> series.resample('3T').sum() # 2000-01-01 00:00:00 3 # 2000-01-01 00:03:00 12 # 2000-01-01 00:06:00 21 # Freq: 3T, dtype: int64
Разделим Series
на периоды по 3 минуты (как в примере выше) но отметим каждый интервал как label='right'
. Обратите внимание, что значение временного интервала, используемое в качестве индексной метки, не включается в сегмент, который он маркирует. Например, в исходной серии сегмент 2000-01-01 00:03:00 содержит значение 3, но суммированное значение в сегменте с повторной выборкой с меткой 2000-01-01 00:03:00 не включает 3 (если бы это было так, то суммарное значение было бы 6, а не 3).
>>> series.resample('3T', label='right').sum() # 2000-01-01 00:03:00 3 # 2000-01-01 00:06:00 12 # 2000-01-01 00:09:00 21 # Freq: 3T, dtype: int64
Чтобы включить значение, необходимо закрыть правую часть интервала closed='right'
, как показано в примере ниже. Разделим Series
на периоды по 3 минуты (как в примере выше), но закроем правую часть интервала между периодами.
>>> series.resample('3T', label='right', closed='right').sum() # 2000-01-01 00:00:00 0 # 2000-01-01 00:03:00 6 # 2000-01-01 00:06:00 15 # 2000-01-01 00:09:00 15 # Freq: 3T, dtype: int64
Передискретизация ряда в 30-секундные периоды/сегменты.
>>> series.resample('30S').asfreq()[0:5] # выберем первые 5 строк # 2000-01-01 00:00:00 0.0 # 2000-01-01 00:00:30 NaN # 2000-01-01 00:01:00 1.0 # 2000-01-01 00:01:30 NaN # 2000-01-01 00:02:00 2.0 # Freq: 30S, dtype: float64
Разделим Series
на периоды по 30 секунд и заполним значения NA
/NaN
, распространив последнее допустимое наблюдение (Series.ffill()
).
>>> series.resample('30S').ffill()[0:5] # 2000-01-01 00:00:00 0 # 2000-01-01 00:00:30 0 # 2000-01-01 00:01:00 1 # 2000-01-01 00:01:30 1 # 2000-01-01 00:02:00 2 # Freq: 30S, dtype: int64
Разделим Series
на периоды по 30 секунд и заполним значения NA
/NaN
, распространив следующее допустимое наблюдение (Series.bffill()
).
>>> series.resample('30S').bfill()[0:5] # 2000-01-01 00:00:00 0 # 2000-01-01 00:00:30 1 # 2000-01-01 00:01:00 1 # 2000-01-01 00:01:30 2 # 2000-01-01 00:02:00 2 # Freq: 30S, dtype: int64
Передаем пользовательскую функцию через Series.apply()
def custom_resampler(arraylike): return np.sum(arraylike) + 5 >>> series.resample('3T').apply(custom_resampler) # 2000-01-01 00:00:00 8 # 2000-01-01 00:03:00 17 # 2000-01-01 00:06:00 26 # Freq: 3T, dtype: int64
Для Series
с pandas.PeriodIndex
аргумент convention
может использоваться для управления тем, следует ли использовать начало или конец аргумента rule
(периода)
Пересчет по годам с помощью соглашения convention='start'
. Значения присваиваются первому кварталу периода rule='Q'
.
s = pd.Series([1, 2], index=pd.period_range('2012-01-01', freq='A', periods=2)) >>> s # 2012 1 # 2013 2 # Freq: A-DEC, dtype: int64 >>> s.resample('Q', convention='start').asfreq() # 2012Q1 1.0 # 2012Q2 NaN # 2012Q3 NaN # 2012Q4 NaN # 2013Q1 2.0 # 2013Q2 NaN # 2013Q3 NaN # 2013Q4 NaN # Freq: Q-DEC, dtype: float64
Пересчет кварталов по месяцам, используя convention='end'
. Значения присваиваются последнему месяцу периода rule='M'
.
q = pd.Series([1, 2, 3, 4], index=pd.period_range('2018-01-01', freq='Q', periods=4)) >>> q # 2018Q1 1 # 2018Q2 2 # 2018Q3 3 # 2018Q4 4 # Freq: Q-DEC, dtype: int64 >>> q.resample('M', convention='end').asfreq() # 2018-03 1.0 # 2018-04 NaN # 2018-05 NaN # 2018-06 2.0 # 2018-07 NaN # 2018-08 NaN # 2018-09 3.0 # 2018-10 NaN # 2018-11 NaN # 2018-12 4.0 # Freq: M, dtype: float64
Для объектов DataFrame
аргумент on
можно использовать для указания столбца вместо индекса для пересчета.
d = {'price': [10, 11, 9, 13, 14, 18, 17, 19], 'volume': [50, 60, 40, 100, 50, 100, 40, 50]} df = pd.DataFrame(d) df['week_starting'] = pd.date_range('01/01/2018', periods=8, freq='W') >>> df # price volume week_starting # 0 10 50 2018-01-07 # 1 11 60 2018-01-14 # 2 9 40 2018-01-21 # 3 13 100 2018-01-28 # 4 14 50 2018-02-04 # 5 18 100 2018-02-11 # 6 17 40 2018-02-18 # 7 19 50 2018-02-25 >>> df.resample('M', on='week_starting').mean() # price volume # week_starting # 2018-01-31 10.75 62.5 # 2018-02-28 17.00 60.0
Для DataFrame
с MultiIndex
можно использовать аргумент level
, чтобы указать, на каком уровне необходимо выполнить пересчет.
days = pd.date_range('1/1/2000', periods=4, freq='D') d2 = {'price': [10, 11, 9, 13, 14, 18, 17, 19], 'volume': [50, 60, 40, 100, 50, 100, 40, 50]} df2 = pd.DataFrame(d2, index=pd.MultiIndex.from_product( [days, ['morning', 'afternoon']] ) ) >>> df2 # price volume # 2000-01-01 morning 10 50 # afternoon 11 60 # 2000-01-02 morning 9 40 # afternoon 13 100 # 2000-01-03 morning 14 50 # afternoon 18 100 # 2000-01-04 morning 17 40 # afternoon 19 50 >>> df2.resample('D', level=0).sum() # price volume # 2000-01-01 21 110 # 2000-01-02 22 140 # 2000-01-03 32 150 # 2000-01-04 36 90
Если нужно настроить начало периодов на основе фиксированной временной метки:
>>> start, end = '2000-10-01 23:30:00', '2000-10-02 00:30:00' >>> rng = pd.date_range(start, end, freq='7min') >>> ts = pd.Series(np.arange(len(rng)) * 3, index=rng) >>> ts # 2000-10-01 23:30:00 0 # 2000-10-01 23:37:00 3 # 2000-10-01 23:44:00 6 # 2000-10-01 23:51:00 9 # 2000-10-01 23:58:00 12 # 2000-10-02 00:05:00 15 # 2000-10-02 00:12:00 18 # 2000-10-02 00:19:00 21 # 2000-10-02 00:26:00 24 # Freq: 7T, dtype: int64 >>> ts.resample('17min').sum() # 2000-10-01 23:14:00 0 # 2000-10-01 23:31:00 9 # 2000-10-01 23:48:00 21 # 2000-10-02 00:05:00 54 # 2000-10-02 00:22:00 24 # Freq: 17T, dtype: int64 >>> ts.resample('17min', origin='epoch').sum() # 2000-10-01 23:18:00 0 # 2000-10-01 23:35:00 18 # 2000-10-01 23:52:00 27 # 2000-10-02 00:09:00 39 # 2000-10-02 00:26:00 24 # Freq: 17T, dtype: int64 >>> ts.resample('17min', origin='2000-01-01').sum() # 2000-10-01 23:24:00 3 # 2000-10-01 23:41:00 15 # 2000-10-01 23:58:00 45 # 2000-10-02 00:15:00 45 # Freq: 17T, dtype: int64
Если нужно скорректировать начало ячеек со смещением pandas.Timedelta
, две следующие строки эквивалентны:
>>> ts.resample('17min', origin='start').sum() # 2000-10-01 23:30:00 9 # 2000-10-01 23:47:00 21 # 2000-10-02 00:04:00 54 # 2000-10-02 00:21:00 24 # Freq: 17T, dtype: int64 >>> ts.resample('17min', offset='23h30min').sum() # 2000-10-01 23:30:00 9 # 2000-10-01 23:47:00 21 # 2000-10-02 00:04:00 54 # 2000-10-02 00:21:00 24 # Freq: 17T, dtype: int64
Если необходимо использовать самую позднюю временную метку в качестве конца периода rule
:
>>> ts.resample('17min', origin='end').sum() # 2000-10-01 23:35:00 0 # 2000-10-01 23:52:00 18 # 2000-10-02 00:09:00 27 # 2000-10-02 00:26:00 63 # Freq: 17T, dtype: int64
В origin
в отличие от start_day
, можно использовать end_day
, чтобы принять "максимальную" полночь самой ПОЗДНЕЙ отметкой времени в качестве конца интервала rule
и удалить интервалы, не содержащие данных:
>>> ts.resample('17min', origin='end_day').sum() # 2000-10-01 23:38:00 3 # 2000-10-01 23:55:00 15 # 2000-10-02 00:12:00 45 # 2000-10-02 00:29:00 45 # Freq: 17T, dtype: int64