Сообщить об ошибке.

Метод .resample() объектов Series/DataFrame в pandas

Передискретизация данных временных рядов

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 - чтобы преобразовать результирующий индекс:

    По умолчанию входное представление сохраняется.

  • on=None - только для DataFrame - столбец, который будет использоваться вместо индекса. Столбец должен быть похож на datetime.

  • level=None - только для MultiIndex - уровень (имя столбца или число уровня), используемый для пересчета. Столбец должен быть похож на datetime.

  • origin='start_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