Функциональность временных рядов/дат
pandas содержит обширные возможности и функции для работы с временными рядами данных для всех областей. Используя типы данных NumPy datetime64 и timedelta64, pandas объединил большое количество функций из других библиотек Python, таких как scikits.timeseries, а также создал огромное количество новых функций для обработки временных рядов.
Например, pandas поддерживает:
Парсинг информации о временных рядах из различных источников и форматов
In [1]: import datetime
In [2]: dti = pd.to_datetime(
...: ["1/1/2018", np.datetime64("2018-01-01"), datetime.datetime(2018, 1, 1)]
...: )
...:
In [3]: dti
Out[3]: DatetimeIndex(['2018-01-01', '2018-01-01', '2018-01-01'], dtype='datetime64[ns]', freq=None)
Генерация последовательностей дат и временных интервалов с фиксированной частотой
In [4]: dti = pd.date_range("2018-01-01", periods=3, freq="H")
In [5]: dti
Out[5]:
DatetimeIndex(['2018-01-01 00:00:00', '2018-01-01 01:00:00',
'2018-01-01 02:00:00'],
dtype='datetime64[ns]', freq='H')
Обработка и преобразование дат и времени с информацией о часовом поясе
In [6]: dti = dti.tz_localize("UTC")
In [7]: dti
Out[7]:
DatetimeIndex(['2018-01-01 00:00:00+00:00', '2018-01-01 01:00:00+00:00',
'2018-01-01 02:00:00+00:00'],
dtype='datetime64[ns, UTC]', freq='H')
In [8]: dti.tz_convert("US/Pacific")
Out[8]:
DatetimeIndex(['2017-12-31 16:00:00-08:00', '2017-12-31 17:00:00-08:00',
'2017-12-31 18:00:00-08:00'],
dtype='datetime64[ns, US/Pacific]', freq='H')
Ресемплирование или преобразование временного ряда в определённую частоту
In [9]: idx = pd.date_range("2018-01-01", periods=5, freq="H")
In [10]: ts = pd.Series(range(len(idx)), index=idx)
In [11]: ts
Out[11]:
2018-01-01 00:00:00 0
2018-01-01 01:00:00 1
2018-01-01 02:00:00 2
2018-01-01 03:00:00 3
2018-01-01 04:00:00 4
Freq: H, dtype: int64
In [12]: ts.resample("2H").mean()
Out[12]:
2018-01-01 00:00:00 0.5
2018-01-01 02:00:00 2.5
2018-01-01 04:00:00 4.0
Freq: 2H, dtype: float64
Выполнение арифметических операций с датами и временем с абсолютными или относительными приращениями времени
In [13]: friday = pd.Timestamp("2018-01-05")
In [14]: friday.day_name()
Out[14]: 'Friday'
# Add 1 day
In [15]: saturday = friday + pd.Timedelta("1 day")
In [16]: saturday.day_name()
Out[16]: 'Saturday'
# Add 1 business day (Friday --> Monday)
In [17]: monday = friday + pd.offsets.BDay()
In [18]: monday.day_name()
Out[18]: 'Monday'
pandas предоставляет относительно компактный и самодостаточный набор инструментов для выполнения вышеперечисленных задач и многих других.
Обзор
pandas охватывает 4 общих понятия, относящиеся ко времени:
Даты и время: Конкретная дата и время с поддержкой часового пояса. Аналогично
datetime.datetimeиз стандартной библиотеки.Временные интервалы: Абсолютная продолжительность времени. Аналогично
datetime.timedeltaиз стандартной библиотеки.Временные отрезки: Продолжительность времени, определённая точкой во времени и её соответствующей частотой.
Смещения дат: Относительная продолжительность времени, учитывающая календарные арифметические операции. Аналогично
dateutil.relativedelta.relativedeltaиз пакетаdateutil.
Концепция | Скалярный класс | Массивноый класс | Тип данных pandas | Основной метод создания |
|---|---|---|---|---|
Даты и время |
|
|
|
|
Временные интервалы |
|
|
|
|
Временные отрезки |
|
|
|
|
Смещения дат |
|
|
|
|
Для данных временных рядов принято представлять временную составляющую в индексе Series или DataFrame, чтобы манипуляции можно было выполнять по отношению к временной составляющей.
In [19]: pd.Series(range(3), index=pd.date_range("2000", freq="D", periods=3))
Out[19]:
2000-01-01 0
2000-01-02 1
2000-01-03 2
Freq: D, dtype: int64
Однако, Series и DataFrame также могут напрямую поддерживать временную составляющую как сами данные.
In [20]: pd.Series(pd.date_range("2000", freq="D", periods=3))
Out[20]:
0 2000-01-01
1 2000-01-02
2 2000-01-03
dtype: datetime64[ns]
Series и DataFrame имеют расширенную поддержку типов данных и функциональность для datetime, timedelta и Period данных при передаче в эти конструкторы. DateOffset данные, однако, будут храниться как данные object.
In [21]: pd.Series(pd.period_range("1/1/2011", freq="M", periods=3))
Out[21]:
0 2011-01
1 2011-02
2 2011-03
dtype: period[M]
In [22]: pd.Series([pd.DateOffset(1), pd.DateOffset(2)])
Out[22]:
0 <DateOffset>
1 <2 * DateOffsets>
dtype: object
In [23]: pd.Series(pd.date_range("1/1/2011", freq="M", periods=3))
Out[23]:
0 2011-01-31
1 2011-02-28
2 2011-03-31
dtype: datetime64[ns]
Наконец, pandas представляет нулевые даты, временные интервалы и временные отрезки как NaT, что полезно для представления пропущенных или нулевых значений дат и имеет аналогичное поведение, как np.nan для данных с плавающей точкой.
In [24]: pd.Timestamp(pd.NaT)
Out[24]: NaT
In [25]: pd.Timedelta(pd.NaT)
Out[25]: NaT
In [26]: pd.Period(pd.NaT)
Out[26]: NaT
# Equality acts as np.nan would
In [27]: pd.NaT == pd.NaT
Out[27]: False
Маркировка времени против временных отрезков
Данные с метками времени — это самый базовый тип данных временных рядов, который связывает значения с моментами времени. Для объектов pandas это означает использование моментов времени.
In [28]: pd.Timestamp(datetime.datetime(2012, 5, 1))
Out[28]: Timestamp('2012-05-01 00:00:00')
In [29]: pd.Timestamp("2012-05-01")
Out[29]: Timestamp('2012-05-01 00:00:00')
In [30]: pd.Timestamp(2012, 5, 1)
Out[30]: Timestamp('2012-05-01 00:00:00')
Однако во многих случаях более естественно связывать такие вещи, как переменные изменения, с временным отрезком вместо этого. Отрезок, представленный Period, может быть указан явно или определён из формата строки даты и времени.
Например:
In [31]: pd.Period("2011-01")
Out[31]: Period('2011-01', 'M')
In [32]: pd.Period("2012-05", freq="D")
Out[32]: Period('2012-05-01', 'D')
Timestamp и Period могут служить индексом. Списки Timestamp и Period автоматически преобразуются в DatetimeIndex и PeriodIndex соответственно.
In [33]: dates = [
....: pd.Timestamp("2012-05-01"),
....: pd.Timestamp("2012-05-02"),
....: pd.Timestamp("2012-05-03"),
....: ]
....:
In [34]: ts = pd.Series(np.random.randn(3), dates)
In [35]: type(ts.index)
Out[35]: pandas.core.indexes.datetimes.DatetimeIndex
In [36]: ts.index
Out[36]: DatetimeIndex(['2012-05-01', '2012-05-02', '2012-05-03'], dtype='datetime64[ns]', freq=None)
In [37]: ts
Out[37]:
2012-05-01 0.469112
2012-05-02 -0.282863
2012-05-03 -1.509059
dtype: float64
In [38]: periods = [pd.Period("2012-01"), pd.Period("2012-02"), pd.Period("2012-03")]
In [39]: ts = pd.Series(np.random.randn(3), periods)
In [40]: type(ts.index)
Out[40]: pandas.core.indexes.period.PeriodIndex
In [41]: ts.index
Out[41]: PeriodIndex(['2012-01', '2012-02', '2012-03'], dtype='period[M]')
In [42]: ts
Out[42]:
2012-01 -1.135632
2012-02 1.212112
2012-03 -0.173215
Freq: M, dtype: float64
pandas позволяет захватить оба представления и преобразовывать между ними. Под капотом pandas представляет метки времени с помощью экземпляров Timestamp а последовательности меток времени с помощью экземпляров DatetimeIndex. Для обычных временных отрезков pandas использует объекты Period для скалярных значений и PeriodIndex для последовательностей отрезков. Лучшая поддержка нерегулярных интервалов с произвольными начальными и конечными точками будет доступна в будущих выпусках.
Преобразование в метки времени
Для преобразования Series или подобного объекта списков объектов дат, например, строк, эпох или их смеси, можно использовать функцию to_datetime. При передаче Series, она возвращает Series (с тем же индексом), а список-подобный объект преобразуется в DatetimeIndex:
In [43]: pd.to_datetime(pd.Series(["Jul 31, 2009", "2010-01-10", None]))
Out[43]:
0 2009-07-31
1 2010-01-10
2 NaT
dtype: datetime64[ns]
In [44]: pd.to_datetime(["2005/11/23", "2010.12.31"])
Out[44]: DatetimeIndex(['2005-11-23', '2010-12-31'], dtype='datetime64[ns]', freq=None)
Если вы используете даты, которые начинаются с дня (т. е. в европейском стиле), можно передать флаг dayfirst:
In [45]: pd.to_datetime(["04-01-2012 10:00"], dayfirst=True)
Out[45]: DatetimeIndex(['2012-01-04 10:00:00'], dtype='datetime64[ns]', freq=None)
In [46]: pd.to_datetime(["14-01-2012", "01-14-2012"], dayfirst=True)
Out[46]: DatetimeIndex(['2012-01-14', '2012-01-14'], dtype='datetime64[ns]', freq=None)
Предупреждение
Как видно из примера выше, dayfirst не является строгой. Если дату невозможно разобрать, предполагая, что день идёт первым, она будет обработана так, как если бы dayfirst было False, а в случае разбора строк дат с разделителями (например, 31-12-2012) также будет выведено предупреждение.
Если вы передадите единственную строку функции to_datetime, она вернёт единственную Timestamp. Timestamp также может принимать строковый ввод, но не принимает параметры разбора строк, такие как dayfirst или format, поэтому используйте to_datetime если они необходимы.
In [47]: pd.to_datetime("2010/11/12")
Out[47]: Timestamp('2010-11-12 00:00:00')
In [48]: pd.Timestamp("2010/11/12")
Out[48]: Timestamp('2010-11-12 00:00:00')
Вы также можете напрямую использовать конструктор DatetimeIndex:
In [49]: pd.DatetimeIndex(["2018-01-01", "2018-01-03", "2018-01-05"])
Out[49]: DatetimeIndex(['2018-01-01', '2018-01-03', '2018-01-05'], dtype='datetime64[ns]', freq=None)
Строка ‘infer’ может быть передана для установки частоты индекса как выведенной частоты при создании:
In [50]: pd.DatetimeIndex(["2018-01-01", "2018-01-03", "2018-01-05"], freq="infer")
Out[50]: DatetimeIndex(['2018-01-01', '2018-01-03', '2018-01-05'], dtype='datetime64[ns]', freq='2D')
Передача аргумента format
В дополнение к требуемой строке даты и времени, можно передать аргумент format для обеспечения конкретного разбора. Это также потенциально может значительно ускорить преобразование.
In [51]: pd.to_datetime("2010/11/12", format="%Y/%m/%d")
Out[51]: Timestamp('2010-11-12 00:00:00')
In [52]: pd.to_datetime("12-11-2010 00:00", format="%d-%m-%Y %H:%M")
Out[52]: Timestamp('2010-11-12 00:00:00')
Для получения дополнительной информации о доступных вариантах при указании параметра format, см. документацию Python по datetime.
Сборка datetime из нескольких столбцов DataFrame
Вы также можете передать DataFrame целых или строковых столбцов для сборки в Series Timestamps.
In [53]: df = pd.DataFrame(
....: {"year": [2015, 2016], "month": [2, 3], "day": [4, 5], "hour": [2, 3]}
....: )
....:
In [54]: pd.to_datetime(df)
Out[54]:
0 2015-02-04 02:00:00
1 2016-03-05 03:00:00
dtype: datetime64[ns]
Вы можете передать только те столбцы, которые вам нужны для сборки.
In [55]: pd.to_datetime(df[["year", "month", "day"]])
Out[55]:
0 2015-02-04
1 2016-03-05
dtype: datetime64[ns]
pd.to_datetime ищет стандартные обозначения компонента datetime в именах столбцов, включая:
обязательные:
year,month,dayнеобязательные:
hour,minute,second,millisecond,microsecond,nanosecond
Недействительные данные
По умолчанию, errors='raise', при невозможности разбора возникает исключение:
In [2]: pd.to_datetime(['2009/07/31', 'asd'], errors='raise')
ValueError: Unknown string format
Передайте errors='ignore' для возврата исходного ввода при невозможности разбора:
In [56]: pd.to_datetime(["2009/07/31", "asd"], errors="ignore")
Out[56]: Index(['2009/07/31', 'asd'], dtype='object')
Передайте errors='coerce' для преобразования неразбираемых данных в NaT (не время):
In [57]: pd.to_datetime(["2009/07/31", "asd"], errors="coerce")
Out[57]: DatetimeIndex(['2009-07-31', 'NaT'], dtype='datetime64[ns]', freq=None)
Маркеры времени эпохи
pandas поддерживает преобразование целых или чисел с плавающей запятой эпохи в Timestamp и DatetimeIndex. Единицей измерения по умолчанию являются наносекунды, так как именно так хранятся внутренние объекты Timestamp. Однако эпохи часто хранятся в другой единице unit, которую можно указать. Они вычисляются от начальной точки, заданной параметром origin.
In [58]: pd.to_datetime(
....: [1349720105, 1349806505, 1349892905, 1349979305, 1350065705], unit="s"
....: )
....:
Out[58]:
DatetimeIndex(['2012-10-08 18:15:05', '2012-10-09 18:15:05',
'2012-10-10 18:15:05', '2012-10-11 18:15:05',
'2012-10-12 18:15:05'],
dtype='datetime64[ns]', freq=None)
In [59]: pd.to_datetime(
....: [1349720105100, 1349720105200, 1349720105300, 1349720105400, 1349720105500],
....: unit="ms",
....: )
....:
Out[59]:
DatetimeIndex(['2012-10-08 18:15:05.100000', '2012-10-08 18:15:05.200000',
'2012-10-08 18:15:05.300000', '2012-10-08 18:15:05.400000',
'2012-10-08 18:15:05.500000'],
dtype='datetime64[ns]', freq=None)
Примечание
Параметр unit не использует те же строки, что и параметр format , о котором говорилось выше. Доступные единицы перечислены в документации к pandas.to_datetime().
Изменено в версии 1.0.0.
Создание Timestamp или DatetimeIndex с временной меткой эпохи с указанным аргументом tz вызовет ошибку ValueError. Если у вас есть эпохи в локальном времени в другой временной зоне, вы можете считать эпохи как временные метки без учета временной зоны, а затем локализовать их в соответствующей временной зоне:
In [60]: pd.Timestamp(1262347200000000000).tz_localize("US/Pacific")
Out[60]: Timestamp('2010-01-01 12:00:00-0800', tz='US/Pacific')
In [61]: pd.DatetimeIndex([1262347200000000000]).tz_localize("US/Pacific")
Out[61]: DatetimeIndex(['2010-01-01 12:00:00-08:00'], dtype='datetime64[ns, US/Pacific]', freq=None)
Примечание
Временные метки эпохи будут округляться до ближайшей наносекунды.
Предупреждение
Преобразование чисел с плавающей запятой эпохи может привести к неточным и неожиданным результатам. Python с плавающей запятой имеют около 15 десятичных знаков точности. Округление при преобразовании из числа с плавающей запятой в высокую точность Timestamp неизбежно. Единственный способ достижения точности — использование типов фиксированной ширины (например, int64).
In [62]: pd.to_datetime([1490195805.433, 1490195805.433502912], unit="s")
Out[62]: DatetimeIndex(['2017-03-22 15:16:45.433000088', '2017-03-22 15:16:45.433502913'], dtype='datetime64[ns]', freq=None)
In [63]: pd.to_datetime(1490195805433502912, unit="ns")
Out[63]: Timestamp('2017-03-22 15:16:45.433502912')
См. также
Из меток времени в эпоху
Чтобы выполнить обратную операцию, а именно преобразовать Timestamp в ‘unix’ эпоху:
In [64]: stamps = pd.date_range("2012-10-08 18:15:05", periods=4, freq="D")
In [65]: stamps
Out[65]:
DatetimeIndex(['2012-10-08 18:15:05', '2012-10-09 18:15:05',
'2012-10-10 18:15:05', '2012-10-11 18:15:05'],
dtype='datetime64[ns]', freq='D')
Мы вычитаем эпоху (полночь 1 января 1970 года по UTC) и затем целочисленно делим на «единицу» (1 секунду).
In [66]: (stamps - pd.Timestamp("1970-01-01")) // pd.Timedelta("1s")
Out[66]: Int64Index([1349720105, 1349806505, 1349892905, 1349979305], dtype='int64')
Использование параметра origin
Используя параметр origin , можно указать альтернативную начальную точку для создания DatetimeIndex . Например, чтобы использовать 1960-01-01 в качестве даты начала:
In [67]: pd.to_datetime([1, 2, 3], unit="D", origin=pd.Timestamp("1960-01-01"))
Out[67]: DatetimeIndex(['1960-01-02', '1960-01-03', '1960-01-04'], dtype='datetime64[ns]', freq=None)
По умолчанию установлено значение origin='unix', которое по умолчанию является 1970-01-01 00:00:00. Часто называется ‘unix epoch’ или POSIX time.
In [68]: pd.to_datetime([1, 2, 3], unit="D")
Out[68]: DatetimeIndex(['1970-01-02', '1970-01-03', '1970-01-04'], dtype='datetime64[ns]', freq=None)
Генерация диапазонов меток времени
Для генерации индекса с метками времени можно использовать конструкторы DatetimeIndex или Index и передать список объектов datetime:
In [69]: dates = [
....: datetime.datetime(2012, 5, 1),
....: datetime.datetime(2012, 5, 2),
....: datetime.datetime(2012, 5, 3),
....: ]
....:
# Note the frequency information
In [70]: index = pd.DatetimeIndex(dates)
In [71]: index
Out[71]: DatetimeIndex(['2012-05-01', '2012-05-02', '2012-05-03'], dtype='datetime64[ns]', freq=None)
# Automatically converted to DatetimeIndex
In [72]: index = pd.Index(dates)
In [73]: index
Out[73]: DatetimeIndex(['2012-05-01', '2012-05-02', '2012-05-03'], dtype='datetime64[ns]', freq=None)
На практике это становится очень громоздким, потому что нам часто нужен очень длинный индекс с большим количеством меток времени. Если нам нужны метки времени с регулярной частотой, мы можем использовать функции date_range() и bdate_range() для создания DatetimeIndex . По умолчанию частота для date_range — это календарный день, а по умолчанию для bdate_range — рабочий день:
In [74]: start = datetime.datetime(2011, 1, 1)
In [75]: end = datetime.datetime(2012, 1, 1)
In [76]: index = pd.date_range(start, end)
In [77]: index
Out[77]:
DatetimeIndex(['2011-01-01', '2011-01-02', '2011-01-03', '2011-01-04',
'2011-01-05', '2011-01-06', '2011-01-07', '2011-01-08',
'2011-01-09', '2011-01-10',
...
'2011-12-23', '2011-12-24', '2011-12-25', '2011-12-26',
'2011-12-27', '2011-12-28', '2011-12-29', '2011-12-30',
'2011-12-31', '2012-01-01'],
dtype='datetime64[ns]', length=366, freq='D')
In [78]: index = pd.bdate_range(start, end)
In [79]: index
Out[79]:
DatetimeIndex(['2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06',
'2011-01-07', '2011-01-10', '2011-01-11', '2011-01-12',
'2011-01-13', '2011-01-14',
...
'2011-12-19', '2011-12-20', '2011-12-21', '2011-12-22',
'2011-12-23', '2011-12-26', '2011-12-27', '2011-12-28',
'2011-12-29', '2011-12-30'],
dtype='datetime64[ns]', length=260, freq='B')
Функции-удобства, такие как date_range и bdate_range , могут использовать различные псевдонимы частоты:
In [80]: pd.date_range(start, periods=1000, freq="M")
Out[80]:
DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31', '2011-04-30',
'2011-05-31', '2011-06-30', '2011-07-31', '2011-08-31',
'2011-09-30', '2011-10-31',
...
'2093-07-31', '2093-08-31', '2093-09-30', '2093-10-31',
'2093-11-30', '2093-12-31', '2094-01-31', '2094-02-28',
'2094-03-31', '2094-04-30'],
dtype='datetime64[ns]', length=1000, freq='M')
In [81]: pd.bdate_range(start, periods=250, freq="BQS")
Out[81]:
DatetimeIndex(['2011-01-03', '2011-04-01', '2011-07-01', '2011-10-03',
'2012-01-02', '2012-04-02', '2012-07-02', '2012-10-01',
'2013-01-01', '2013-04-01',
...
'2071-01-01', '2071-04-01', '2071-07-01', '2071-10-01',
'2072-01-01', '2072-04-01', '2072-07-01', '2072-10-03',
'2073-01-02', '2073-04-03'],
dtype='datetime64[ns]', length=250, freq='BQS-JAN')
date_range и bdate_range облегчают генерацию диапазона дат с использованием различных сочетаний параметров, таких как start, end, periods, и freq . Начальная и конечная даты строго включаются, поэтому даты вне указанных не будут сгенерированы:
In [82]: pd.date_range(start, end, freq="BM")
Out[82]:
DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31', '2011-04-29',
'2011-05-31', '2011-06-30', '2011-07-29', '2011-08-31',
'2011-09-30', '2011-10-31', '2011-11-30', '2011-12-30'],
dtype='datetime64[ns]', freq='BM')
In [83]: pd.date_range(start, end, freq="W")
Out[83]:
DatetimeIndex(['2011-01-02', '2011-01-09', '2011-01-16', '2011-01-23',
'2011-01-30', '2011-02-06', '2011-02-13', '2011-02-20',
'2011-02-27', '2011-03-06', '2011-03-13', '2011-03-20',
'2011-03-27', '2011-04-03', '2011-04-10', '2011-04-17',
'2011-04-24', '2011-05-01', '2011-05-08', '2011-05-15',
'2011-05-22', '2011-05-29', '2011-06-05', '2011-06-12',
'2011-06-19', '2011-06-26', '2011-07-03', '2011-07-10',
'2011-07-17', '2011-07-24', '2011-07-31', '2011-08-07',
'2011-08-14', '2011-08-21', '2011-08-28', '2011-09-04',
'2011-09-11', '2011-09-18', '2011-09-25', '2011-10-02',
'2011-10-09', '2011-10-16', '2011-10-23', '2011-10-30',
'2011-11-06', '2011-11-13', '2011-11-20', '2011-11-27',
'2011-12-04', '2011-12-11', '2011-12-18', '2011-12-25',
'2012-01-01'],
dtype='datetime64[ns]', freq='W-SUN')
In [84]: pd.bdate_range(end=end, periods=20)
Out[84]:
DatetimeIndex(['2011-12-05', '2011-12-06', '2011-12-07', '2011-12-08',
'2011-12-09', '2011-12-12', '2011-12-13', '2011-12-14',
'2011-12-15', '2011-12-16', '2011-12-19', '2011-12-20',
'2011-12-21', '2011-12-22', '2011-12-23', '2011-12-26',
'2011-12-27', '2011-12-28', '2011-12-29', '2011-12-30'],
dtype='datetime64[ns]', freq='B')
In [85]: pd.bdate_range(start=start, periods=20)
Out[85]:
DatetimeIndex(['2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06',
'2011-01-07', '2011-01-10', '2011-01-11', '2011-01-12',
'2011-01-13', '2011-01-14', '2011-01-17', '2011-01-18',
'2011-01-19', '2011-01-20', '2011-01-21', '2011-01-24',
'2011-01-25', '2011-01-26', '2011-01-27', '2011-01-28'],
dtype='datetime64[ns]', freq='B')
Указание start, end, и periods сгенерирует диапазон равномерно распределённых дат от start до end включительно с количеством элементов periods в результирующем DatetimeIndex:
In [86]: pd.date_range("2018-01-01", "2018-01-05", periods=5)
Out[86]:
DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04',
'2018-01-05'],
dtype='datetime64[ns]', freq=None)
In [87]: pd.date_range("2018-01-01", "2018-01-05", periods=10)
Out[87]:
DatetimeIndex(['2018-01-01 00:00:00', '2018-01-01 10:40:00',
'2018-01-01 21:20:00', '2018-01-02 08:00:00',
'2018-01-02 18:40:00', '2018-01-03 05:20:00',
'2018-01-03 16:00:00', '2018-01-04 02:40:00',
'2018-01-04 13:20:00', '2018-01-05 00:00:00'],
dtype='datetime64[ns]', freq=None)
Пользовательские диапазоны частоты
bdate_range также может генерировать диапазон дат с пользовательской частотой, используя параметры weekmask и holidays. Эти параметры будут использованы только в случае передачи пользовательской строки частоты.
In [88]: weekmask = "Mon Wed Fri"
In [89]: holidays = [datetime.datetime(2011, 1, 5), datetime.datetime(2011, 3, 14)]
In [90]: pd.bdate_range(start, end, freq="C", weekmask=weekmask, holidays=holidays)
Out[90]:
DatetimeIndex(['2011-01-03', '2011-01-07', '2011-01-10', '2011-01-12',
'2011-01-14', '2011-01-17', '2011-01-19', '2011-01-21',
'2011-01-24', '2011-01-26',
...
'2011-12-09', '2011-12-12', '2011-12-14', '2011-12-16',
'2011-12-19', '2011-12-21', '2011-12-23', '2011-12-26',
'2011-12-28', '2011-12-30'],
dtype='datetime64[ns]', length=154, freq='C')
In [91]: pd.bdate_range(start, end, freq="CBMS", weekmask=weekmask)
Out[91]:
DatetimeIndex(['2011-01-03', '2011-02-02', '2011-03-02', '2011-04-01',
'2011-05-02', '2011-06-01', '2011-07-01', '2011-08-01',
'2011-09-02', '2011-10-03', '2011-11-02', '2011-12-02'],
dtype='datetime64[ns]', freq='CBMS')
См. также
Ограничения по отметкам времени
Так как pandas представляет отметки времени с разрешением в наносекунды, временной интервал, который можно представить с помощью 64-битного целого числа, ограничен примерно 584 годами:
In [92]: pd.Timestamp.min
Out[92]: Timestamp('1677-09-21 00:12:43.145224193')
In [93]: pd.Timestamp.max
Out[93]: Timestamp('2262-04-11 23:47:16.854775807')
Индексирование
Одно из основных применений DatetimeIndex — это индекс для объектов pandas. Класс DatetimeIndex содержит множество оптимизаций, связанных с временными рядами:
Большой диапазон дат для различных смещений предварительно вычисляется и кэшируется под капотом, чтобы генерация последующих диапазонов дат была очень быстрой (нужно только взять срез).
Быстрое смещение с использованием метода
shiftдля объектов pandas.Объединение перекрывающихся объектов
DatetimeIndexс той же частотой очень быстро (важно для быстрого выравнивания данных).Быстрый доступ к полям дат через свойства, такие как
year,month, и т. д.Функции регуляризации, такие как
snap, и очень быстрая логикаasof.
Объекты DatetimeIndex имеют всю основную функциональность обычных объектов Index, а также множество расширенных методов, специфичных для временных рядов, для удобной обработки частоты.
См. также
Примечание
Хотя pandas не требует, чтобы индекс дат был отсортирован, некоторые из этих методов могут иметь неожиданное или неправильное поведение, если даты не отсортированы.
DatetimeIndex может использоваться как обычный индекс и предлагает всю свою интеллектуальную функциональность, такую как выбор, срезы и т. д.
In [94]: rng = pd.date_range(start, end, freq="BM")
In [95]: ts = pd.Series(np.random.randn(len(rng)), index=rng)
In [96]: ts.index
Out[96]:
DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31', '2011-04-29',
'2011-05-31', '2011-06-30', '2011-07-29', '2011-08-31',
'2011-09-30', '2011-10-31', '2011-11-30', '2011-12-30'],
dtype='datetime64[ns]', freq='BM')
In [97]: ts[:5].index
Out[97]:
DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31', '2011-04-29',
'2011-05-31'],
dtype='datetime64[ns]', freq='BM')
In [98]: ts[::2].index
Out[98]:
DatetimeIndex(['2011-01-31', '2011-03-31', '2011-05-31', '2011-07-29',
'2011-09-30', '2011-11-30'],
dtype='datetime64[ns]', freq='2BM')
Индексирование по части строки
Даты и строки, которые можно преобразовать в отметки времени, могут передаваться в качестве параметров индексирования:
In [99]: ts["1/31/2011"]
Out[99]: 0.11920871129693428
In [100]: ts[datetime.datetime(2011, 12, 25):]
Out[100]:
2011-12-30 0.56702
Freq: BM, dtype: float64
In [101]: ts["10/31/2011":"12/31/2011"]
Out[101]:
2011-10-31 0.271860
2011-11-30 -0.424972
2011-12-30 0.567020
Freq: BM, dtype: float64
Для удобства доступа к более длинным временным рядам можно также передать год или год и месяц в виде строк:
In [102]: ts["2011"]
Out[102]:
2011-01-31 0.119209
2011-02-28 -1.044236
2011-03-31 -0.861849
2011-04-29 -2.104569
2011-05-31 -0.494929
2011-06-30 1.071804
2011-07-29 0.721555
2011-08-31 -0.706771
2011-09-30 -1.039575
2011-10-31 0.271860
2011-11-30 -0.424972
2011-12-30 0.567020
Freq: BM, dtype: float64
In [103]: ts["2011-6"]
Out[103]:
2011-06-30 1.071804
Freq: BM, dtype: float64
Этот тип среза будет работать с DataFrame с DatetimeIndex также. Поскольку частичное строковое выделение является формой среза по меткам, конечные точки будут включены. Это будет включать соответствие времени в указанную дату:
Предупреждение
Индексирование DataFrame строк с одной строкой с помощью getitem (например, frame[dtstring]) устарело начиная с pandas 1.2.0 (в связи с неоднозначностью, является ли это индексированием строк или выбором столбца) и будет удалено в будущей версии. Эквивалент с .loc (например, frame.loc[dtstring]) по-прежнему поддерживается.
In [104]: dft = pd.DataFrame(
.....: np.random.randn(100000, 1),
.....: columns=["A"],
.....: index=pd.date_range("20130101", periods=100000, freq="T"),
.....: )
.....:
In [105]: dft
Out[105]:
A
2013-01-01 00:00:00 0.276232
2013-01-01 00:01:00 -1.087401
2013-01-01 00:02:00 -0.673690
2013-01-01 00:03:00 0.113648
2013-01-01 00:04:00 -1.478427
... ...
2013-03-11 10:35:00 -0.747967
2013-03-11 10:36:00 -0.034523
2013-03-11 10:37:00 -0.201754
2013-03-11 10:38:00 -1.509067
2013-03-11 10:39:00 -1.693043
[100000 rows x 1 columns]
In [106]: dft.loc["2013"]
Out[106]:
A
2013-01-01 00:00:00 0.276232
2013-01-01 00:01:00 -1.087401
2013-01-01 00:02:00 -0.673690
2013-01-01 00:03:00 0.113648
2013-01-01 00:04:00 -1.478427
... ...
2013-03-11 10:35:00 -0.747967
2013-03-11 10:36:00 -0.034523
2013-03-11 10:37:00 -0.201754
2013-03-11 10:38:00 -1.509067
2013-03-11 10:39:00 -1.693043
[100000 rows x 1 columns]
Это начинается с первого времени в месяце и включает последнюю дату и время для месяца:
In [107]: dft["2013-1":"2013-2"]
Out[107]:
A
2013-01-01 00:00:00 0.276232
2013-01-01 00:01:00 -1.087401
2013-01-01 00:02:00 -0.673690
2013-01-01 00:03:00 0.113648
2013-01-01 00:04:00 -1.478427
... ...
2013-02-28 23:55:00 0.850929
2013-02-28 23:56:00 0.976712
2013-02-28 23:57:00 -2.693884
2013-02-28 23:58:00 -1.575535
2013-02-28 23:59:00 -1.573517
[84960 rows x 1 columns]
Это указывает конечное время , которое включает все времена в последний день:
In [108]: dft["2013-1":"2013-2-28"]
Out[108]:
A
2013-01-01 00:00:00 0.276232
2013-01-01 00:01:00 -1.087401
2013-01-01 00:02:00 -0.673690
2013-01-01 00:03:00 0.113648
2013-01-01 00:04:00 -1.478427
... ...
2013-02-28 23:55:00 0.850929
2013-02-28 23:56:00 0.976712
2013-02-28 23:57:00 -2.693884
2013-02-28 23:58:00 -1.575535
2013-02-28 23:59:00 -1.573517
[84960 rows x 1 columns]
Это указывает точное конечное время (и не то же самое, что выше):
In [109]: dft["2013-1":"2013-2-28 00:00:00"]
Out[109]:
A
2013-01-01 00:00:00 0.276232
2013-01-01 00:01:00 -1.087401
2013-01-01 00:02:00 -0.673690
2013-01-01 00:03:00 0.113648
2013-01-01 00:04:00 -1.478427
... ...
2013-02-27 23:56:00 1.197749
2013-02-27 23:57:00 0.720521
2013-02-27 23:58:00 -0.072718
2013-02-27 23:59:00 -0.681192
2013-02-28 00:00:00 -0.557501
[83521 rows x 1 columns]
Мы останавливаемся на включённой конечной точке, так как она является частью индекса:
In [110]: dft["2013-1-15":"2013-1-15 12:30:00"]
Out[110]:
A
2013-01-15 00:00:00 -0.984810
2013-01-15 00:01:00 0.941451
2013-01-15 00:02:00 1.559365
2013-01-15 00:03:00 1.034374
2013-01-15 00:04:00 -1.480656
... ...
2013-01-15 12:26:00 0.371454
2013-01-15 12:27:00 -0.930806
2013-01-15 12:28:00 -0.069177
2013-01-15 12:29:00 0.066510
2013-01-15 12:30:00 -0.003945
[751 rows x 1 columns]
Индексирование по части строки DatetimeIndex также работает с DataFrame с MultiIndex:
In [111]: dft2 = pd.DataFrame(
.....: np.random.randn(20, 1),
.....: columns=["A"],
.....: index=pd.MultiIndex.from_product(
.....: [pd.date_range("20130101", periods=10, freq="12H"), ["a", "b"]]
.....: ),
.....: )
.....:
In [112]: dft2
Out[112]:
A
2013-01-01 00:00:00 a -0.298694
b 0.823553
2013-01-01 12:00:00 a 0.943285
b -1.479399
2013-01-02 00:00:00 a -1.643342
... ...
2013-01-04 12:00:00 b 0.069036
2013-01-05 00:00:00 a 0.122297
b 1.422060
2013-01-05 12:00:00 a 0.370079
b 1.016331
[20 rows x 1 columns]
In [113]: dft2.loc["2013-01-05"]
Out[113]:
A
2013-01-05 00:00:00 a 0.122297
b 1.422060
2013-01-05 12:00:00 a 0.370079
b 1.016331
In [114]: idx = pd.IndexSlice
In [115]: dft2 = dft2.swaplevel(0, 1).sort_index()
In [116]: dft2.loc[idx[:, "2013-01-05"], :]
Out[116]:
A
a 2013-01-05 00:00:00 0.122297
2013-01-05 12:00:00 0.370079
b 2013-01-05 00:00:00 1.422060
2013-01-05 12:00:00 1.016331
Новое в версии 0.25.0.
Срезы с индексированием по строкам также учитывают смещение UTC.
In [117]: df = pd.DataFrame([0], index=pd.DatetimeIndex(["2019-01-01"], tz="US/Pacific"))
In [118]: df
Out[118]:
0
2019-01-01 00:00:00-08:00 0
In [119]: df["2019-01-01 12:00:00+04:00":"2019-01-01 13:00:00+04:00"]
Out[119]:
0
2019-01-01 00:00:00-08:00 0
Срез против точного совпадения
Та же строка, используемая в качестве параметра индексирования, может обрабатываться либо как срез, либо как точное совпадение в зависимости от разрешения индекса. Если строка менее точна, чем индекс, она будет обрабатываться как срез, в противном случае как точное совпадение.
Рассмотрим объект Series с минутным разрешением индекса:
In [120]: series_minute = pd.Series(
.....: [1, 2, 3],
.....: pd.DatetimeIndex(
.....: ["2011-12-31 23:59:00", "2012-01-01 00:00:00", "2012-01-01 00:02:00"]
.....: ),
.....: )
.....:
In [121]: series_minute.index.resolution
Out[121]: 'minute'
Строка отметки времени, менее точная, чем минута, дает объект Series.
In [122]: series_minute["2011-12-31 23"]
Out[122]:
2011-12-31 23:59:00 1
dtype: int64
Строка отметки времени с минутным разрешением (или более точная) дает скаляр вместо этого, т. е. она не преобразуется в срез.
In [123]: series_minute["2011-12-31 23:59"]
Out[123]: 1
In [124]: series_minute["2011-12-31 23:59:00"]
Out[124]: 1
Если разрешение индекса — секунда, то отметка времени с точностью до минуты дает Series.
In [125]: series_second = pd.Series(
.....: [1, 2, 3],
.....: pd.DatetimeIndex(
.....: ["2011-12-31 23:59:59", "2012-01-01 00:00:00", "2012-01-01 00:00:01"]
.....: ),
.....: )
.....:
In [126]: series_second.index.resolution
Out[126]: 'second'
In [127]: series_second["2011-12-31 23:59"]
Out[127]:
2011-12-31 23:59:59 1
dtype: int64
Если строка отметки времени обрабатывается как срез, она может использоваться для индексирования DataFrame с .loc[] также.
In [128]: dft_minute = pd.DataFrame(
.....: {"a": [1, 2, 3], "b": [4, 5, 6]}, index=series_minute.index
.....: )
.....:
In [129]: dft_minute.loc["2011-12-31 23"]
Out[129]:
a b
2011-12-31 23:59:00 1 4
Предупреждение
Однако, если строка обрабатывается как точное совпадение, выбор в DataFrame’s [] будет по столбцам, а не по строкам, см. Основы индексирования. Например, dft_minute['2011-12-31 23:59'] вызовет KeyError, так как '2012-12-31 23:59' имеет такое же разрешение, что и индекс, и нет столбца с таким именем:
Чтобы всегда иметь однозначный выбор, независимо от того, обрабатывается ли строка как срез или как отдельное выделение, используйте .loc.
In [130]: dft_minute.loc["2011-12-31 23:59"]
Out[130]:
a 1
b 4
Name: 2011-12-31 23:59:00, dtype: int64
Также обратите внимание, что разрешение DatetimeIndex не может быть менее точным, чем день.
In [131]: series_monthly = pd.Series(
.....: [1, 2, 3], pd.DatetimeIndex(["2011-12", "2012-01", "2012-02"])
.....: )
.....:
In [132]: series_monthly.index.resolution
Out[132]: 'day'
In [133]: series_monthly["2011-12"] # returns Series
Out[133]:
2011-12-01 1
dtype: int64
Точное индексирование
Как обсуждалось в предыдущем разделе, индексирование DatetimeIndex с частичной строкой зависит от «точности» периода, другими словами, насколько специфичен интервал по отношению к разрешению индекса. В отличие от этого, индексирование с объектами Timestamp или datetime является точным, потому что объекты имеют точное значение. Они также следуют семантике включения обеих конечных точек.
Эти объекты Timestamp и datetime имеют точное hours, minutes, и seconds, даже если они не были явно указаны (они 0).
In [134]: dft[datetime.datetime(2013, 1, 1): datetime.datetime(2013, 2, 28)]
Out[134]:
A
2013-01-01 00:00:00 0.276232
2013-01-01 00:01:00 -1.087401
2013-01-01 00:02:00 -0.673690
2013-01-01 00:03:00 0.113648
2013-01-01 00:04:00 -1.478427
... ...
2013-02-27 23:56:00 1.197749
2013-02-27 23:57:00 0.720521
2013-02-27 23:58:00 -0.072718
2013-02-27 23:59:00 -0.681192
2013-02-28 00:00:00 -0.557501
[83521 rows x 1 columns]
Без значений по умолчанию.
In [135]: dft[
.....: datetime.datetime(2013, 1, 1, 10, 12, 0): datetime.datetime(
.....: 2013, 2, 28, 10, 12, 0
.....: )
.....: ]
.....:
Out[135]:
A
2013-01-01 10:12:00 0.565375
2013-01-01 10:13:00 0.068184
2013-01-01 10:14:00 0.788871
2013-01-01 10:15:00 -0.280343
2013-01-01 10:16:00 0.931536
... ...
2013-02-28 10:08:00 0.148098
2013-02-28 10:09:00 -0.388138
2013-02-28 10:10:00 0.139348
2013-02-28 10:11:00 0.085288
2013-02-28 10:12:00 0.950146
[83521 rows x 1 columns]
Обрезка и индексирование с помощью произвольных значений
Предоставлена удобная функция truncate(), аналогичная срезу. Обратите внимание, что truncate предполагает значение 0 для любого неопределённого компонента даты в DatetimeIndex в отличие от среза, который возвращает любые частично совпадающие даты:
In [136]: rng2 = pd.date_range("2011-01-01", "2012-01-01", freq="W")
In [137]: ts2 = pd.Series(np.random.randn(len(rng2)), index=rng2)
In [138]: ts2.truncate(before="2011-11", after="2011-12")
Out[138]:
2011-11-06 0.437823
2011-11-13 -0.293083
2011-11-20 -0.059881
2011-11-27 1.252450
Freq: W-SUN, dtype: float64
In [139]: ts2["2011-11":"2011-12"]
Out[139]:
2011-11-06 0.437823
2011-11-13 -0.293083
2011-11-20 -0.059881
2011-11-27 1.252450
2011-12-04 0.046611
2011-12-11 0.059478
2011-12-18 -0.286539
2011-12-25 0.841669
Freq: W-SUN, dtype: float64
Даже сложное индексирование с произвольными значениями, нарушающее регулярность частоты DatetimeIndex , приведет к DatetimeIndex, хотя частота будет потеряна:
In [140]: ts2[[0, 2, 6]].index
Out[140]: DatetimeIndex(['2011-01-02', '2011-01-16', '2011-02-13'], dtype='datetime64[ns]', freq=None)
Компоненты времени и даты
Существует несколько свойств времени/даты, к которым можно получить доступ из Timestamp или коллекции временных меток, таких как DatetimeIndex.
Свойство | Описание |
|---|---|
year | Год даты и времени |
month | Месяц даты и времени |
day | День даты и времени |
hour | Час даты и времени |
minute | Минуты даты и времени |
second | Секунды даты и времени |
microsecond | Микросекунды даты и времени |
nanosecond | Наносекунды даты и времени |
date | Возвращает datetime.date (не содержит информацию о часовом поясе) |
time | Возвращает datetime.time (не содержит информацию о часовом поясе) |
timetz | Возвращает datetime.time как местное время с информацией о часовом поясе |
dayofyear | Порядковый номер дня в году |
day_of_year | Порядковый номер дня в году |
weekofyear | Порядковый номер недели в году |
week | Порядковый номер недели в году |
dayofweek | Номер дня недели (понедельник=0, воскресенье=6) |
day_of_week | Номер дня недели (понедельник=0, воскресенье=6) |
weekday | Номер дня недели (понедельник=0, воскресенье=6) |
quarter | Квартал года: Янв-Мар = 1, Апр-Июн = 2 и т.д. |
days_in_month | Количество дней в месяце даты и времени |
is_month_start | Логическое значение, указывающее, является ли это первым днем месяца (определяется частотой) |
is_month_end | Логическое значение, указывающее, является ли это последним днем месяца (определяется частотой) |
is_quarter_start | Логическое значение, указывающее, является ли это первым днем квартала (определяется частотой) |
is_quarter_end | Логическое значение, указывающее, является ли это последним днем квартала (определяется частотой) |
is_year_start | Логическое значение, указывающее, является ли это первым днем года (определяется частотой) |
is_year_end | Логическое значение, указывающее, является ли это последним днем года (определяется частотой) |
is_leap_year | Логическое значение, указывающее, принадлежит ли дата високосному году |
Кроме того, если у вас есть Series со значениями datetimelike, то вы можете получить доступ к этим свойствам с помощью .dt accessor, как подробно описано в разделе о accessor .dt.
Введено в версии 1.1.0.
Вы можете получить год, неделю и день компоненты ISO года из стандарта ISO 8601:
In [141]: idx = pd.date_range(start="2019-12-29", freq="D", periods=4)
In [142]: idx.isocalendar()
Out[142]:
year week day
2019-12-29 2019 52 7
2019-12-30 2020 1 1
2019-12-31 2020 1 2
2020-01-01 2020 1 3
In [143]: idx.to_series().dt.isocalendar()
Out[143]:
year week day
2019-12-29 2019 52 7
2019-12-30 2020 1 1
2019-12-31 2020 1 2
2020-01-01 2020 1 3
Объекты DateOffset
В предыдущих примерах использовались строки частоты (например, 'D'), чтобы указать частоту, которая определяла:
как даты и время в
DatetimeIndexбыли размещены при использованииdate_range()частоту
PeriodилиPeriodIndex
Эти строки частоты соответствуют объекту DateOffset и его подклассам. DateOffset похож на Timedelta, который представляет длительность времени, но следует особым правилам календарной продолжительности. Например, Timedelta день всегда увеличивает datetimes на 24 часа, а DateOffset день увеличивает datetimes до того же времени следующего дня, независимо от того, представляет ли день 23, 24 или 25 часов из-за перехода на летнее время. Однако все подклассы DateOffset которые составляют час или меньше (Hour, Minute, Second, Milli, Micro, Nano ) ведут себя как Timedelta и учитывают абсолютное время.
Базовый DateOffset действует аналогично dateutil.relativedelta (документация relativedelta), который сдвигает дату и время на соответствующую календарную продолжительность, указанную. Арифметический оператор (+) может использоваться для выполнения сдвига.
# This particular day contains a day light savings time transition
In [144]: ts = pd.Timestamp("2016-10-30 00:00:00", tz="Europe/Helsinki")
# Respects absolute time
In [145]: ts + pd.Timedelta(days=1)
Out[145]: Timestamp('2016-10-30 23:00:00+0200', tz='Europe/Helsinki')
# Respects calendar time
In [146]: ts + pd.DateOffset(days=1)
Out[146]: Timestamp('2016-10-31 00:00:00+0200', tz='Europe/Helsinki')
In [147]: friday = pd.Timestamp("2018-01-05")
In [148]: friday.day_name()
Out[148]: 'Friday'
# Add 2 business days (Friday --> Tuesday)
In [149]: two_business_days = 2 * pd.offsets.BDay()
In [150]: friday + two_business_days
Out[150]: Timestamp('2018-01-09 00:00:00')
In [151]: (friday + two_business_days).day_name()
Out[151]: 'Tuesday'
У большинства DateOffsets есть связанные строки частот или псевдонимы сдвига, которые можно передавать в freq ключевые аргументы. Доступные сдвиги дат и соответствующие строки частот приведены ниже:
Сдвиг даты | Строка частоты | Описание |
|---|
None | Общий класс смещения, по умолчанию абсолютные 24 часа | |
|
| рабочий день (будний день) |
| настраиваемый рабочий день | |
| одна неделя, необязательно привязанная к дню недели | |
| x-й день y-й недели каждого месяца | |
| x-й день последней недели каждого месяца | |
| конец календарного месяца | |
| начало календарного месяца | |
| конец рабочего месяца | |
| начало рабочего месяца | |
| конец настраиваемого рабочего месяца | |
| начало настраиваемого рабочего месяца | |
| 15-е число (или другой день_месяца) и конец календарного месяца | |
| 15-е число (или другой день_месяца) и начало календарного месяца | |
| конец календарного квартала | |
| начало календарного квартала | |
| конец рабочего квартала | |
| начало рабочего квартала | |
| квартал торгового года (с 52-53 неделями) | |
| конец календарного года | |
| начало календарного года | |
| конец рабочего года | |
| начало рабочего года | |
| год торгового года (с 52-53 неделями) | |
None | Пасха | |
| рабочий час | |
| настраиваемый рабочий час | |
| один абсолютный день | |
| один час | |
| одна минута | |
| одна секунда | |
| одна миллисекунда |
| одна микросекунда | |
| одна наносекунда |
DateOffsets также имеют методы rollforward() и rollback() для смещения даты вперёд или назад соответственно до действительной даты смещения относительно смещения. Например, бизнес-смещения будут переносить даты, которые выпадают на выходные (суббота и воскресенье) на понедельник, так как бизнес-смещения действуют в будние дни.
In [152]: ts = pd.Timestamp("2018-01-06 00:00:00")
In [153]: ts.day_name()
Out[153]: 'Saturday'
# BusinessHour's valid offset dates are Monday through Friday
In [154]: offset = pd.offsets.BusinessHour(start="09:00")
# Bring the date to the closest offset date (Monday)
In [155]: offset.rollforward(ts)
Out[155]: Timestamp('2018-01-08 09:00:00')
# Date is brought to the closest offset date first and then the hour is added
In [156]: ts + offset
Out[156]: Timestamp('2018-01-08 10:00:00')
Эти операции по умолчанию сохраняют информацию о времени (часах, минутах и т. д.). Для сброса времени до полуночи используйте normalize() перед или после применения операции (в зависимости от того, хотите ли вы включить информацию о времени в операцию).
In [157]: ts = pd.Timestamp("2014-01-01 09:00")
In [158]: day = pd.offsets.Day()
In [159]: day + ts
Out[159]: Timestamp('2014-01-02 09:00:00')
In [160]: (day + ts).normalize()
Out[160]: Timestamp('2014-01-02 00:00:00')
In [161]: ts = pd.Timestamp("2014-01-01 22:00")
In [162]: hour = pd.offsets.Hour()
In [163]: hour + ts
Out[163]: Timestamp('2014-01-01 23:00:00')
In [164]: (hour + ts).normalize()
Out[164]: Timestamp('2014-01-01 00:00:00')
In [165]: (hour + pd.Timestamp("2014-01-01 23:30")).normalize()
Out[165]: Timestamp('2014-01-02 00:00:00')
Параметрические смещения
Некоторые смещения могут быть «параметризованы» при создании, что приводит к различным поведениям. Например, смещение Week для генерации еженедельных данных принимает параметр weekday, что приводит к тому, что сгенерированные даты всегда выпадают на определённый день недели:
In [166]: d = datetime.datetime(2008, 8, 18, 9, 0)
In [167]: d
Out[167]: datetime.datetime(2008, 8, 18, 9, 0)
In [168]: d + pd.offsets.Week()
Out[168]: Timestamp('2008-08-25 09:00:00')
In [169]: d + pd.offsets.Week(weekday=4)
Out[169]: Timestamp('2008-08-22 09:00:00')
In [170]: (d + pd.offsets.Week(weekday=4)).weekday()
Out[170]: 4
In [171]: d - pd.offsets.Week()
Out[171]: Timestamp('2008-08-11 09:00:00')
Вариант normalize будет эффективен для сложения и вычитания.
In [172]: d + pd.offsets.Week(normalize=True)
Out[172]: Timestamp('2008-08-25 00:00:00')
In [173]: d - pd.offsets.Week(normalize=True)
Out[173]: Timestamp('2008-08-11 00:00:00')
Другой пример — параметризация YearEnd с указанным конечным месяцем:
In [174]: d + pd.offsets.YearEnd()
Out[174]: Timestamp('2008-12-31 09:00:00')
In [175]: d + pd.offsets.YearEnd(month=6)
Out[175]: Timestamp('2009-06-30 09:00:00')
Использование смещений с Series / DatetimeIndex
Смещения могут быть использованы с Series или DatetimeIndex для применения смещения к каждому элементу.
In [176]: rng = pd.date_range("2012-01-01", "2012-01-03")
In [177]: s = pd.Series(rng)
In [178]: rng
Out[178]: DatetimeIndex(['2012-01-01', '2012-01-02', '2012-01-03'], dtype='datetime64[ns]', freq='D')
In [179]: rng + pd.DateOffset(months=2)
Out[179]: DatetimeIndex(['2012-03-01', '2012-03-02', '2012-03-03'], dtype='datetime64[ns]', freq=None)
In [180]: s + pd.DateOffset(months=2)
Out[180]:
0 2012-03-01
1 2012-03-02
2 2012-03-03
dtype: datetime64[ns]
In [181]: s - pd.DateOffset(months=2)
Out[181]:
0 2011-11-01
1 2011-11-02
2 2011-11-03
dtype: datetime64[ns]
Если класс смещения непосредственно отображается на Timedelta (Day, Hour, Minute, Second, Micro, Milli, Nano), он может быть использован точно так же, как Timedelta — см. Раздел Timedelta для получения дополнительных примеров.
In [182]: s - pd.offsets.Day(2)
Out[182]:
0 2011-12-30
1 2011-12-31
2 2012-01-01
dtype: datetime64[ns]
In [183]: td = s - pd.Series(pd.date_range("2011-12-29", "2011-12-31"))
In [184]: td
Out[184]:
0 3 days
1 3 days
2 3 days
dtype: timedelta64[ns]
In [185]: td + pd.offsets.Minute(15)
Out[185]:
0 3 days 00:15:00
1 3 days 00:15:00
2 3 days 00:15:00
dtype: timedelta64[ns]
Обратите внимание, что некоторые смещения (например, BQuarterEnd) не имеют векторизованной реализации. Их всё равно можно использовать, но они могут рассчитываться значительно медленнее и покажут PerformanceWarning
In [186]: rng + pd.offsets.BQuarterEnd()
Out[186]: DatetimeIndex(['2012-03-30', '2012-03-30', '2012-03-30'], dtype='datetime64[ns]', freq=None)
Настраиваемые рабочие дни
Класс CDay или CustomBusinessDay предоставляет параметризованный класс BusinessDay, который может использоваться для создания настраиваемых календарей рабочих дней, учитывающих местные праздники и местные выходные.
В качестве интересного примера рассмотрим Египет, где выходными днями являются пятница и суббота.
In [187]: weekmask_egypt = "Sun Mon Tue Wed Thu"
# They also observe International Workers' Day so let's
# add that for a couple of years
In [188]: holidays = [
.....: "2012-05-01",
.....: datetime.datetime(2013, 5, 1),
.....: np.datetime64("2014-05-01"),
.....: ]
.....:
In [189]: bday_egypt = pd.offsets.CustomBusinessDay(
.....: holidays=holidays,
.....: weekmask=weekmask_egypt,
.....: )
.....:
In [190]: dt = datetime.datetime(2013, 4, 30)
In [191]: dt + 2 * bday_egypt
Out[191]: Timestamp('2013-05-05 00:00:00')
Давайте сопоставим с именами дней недели:
In [192]: dts = pd.date_range(dt, periods=5, freq=bday_egypt)
In [193]: pd.Series(dts.weekday, dts).map(pd.Series("Mon Tue Wed Thu Fri Sat Sun".split()))
Out[193]:
2013-04-30 Tue
2013-05-02 Thu
2013-05-05 Sun
2013-05-06 Mon
2013-05-07 Tue
Freq: C, dtype: object
Календари праздников могут быть использованы для предоставления списка праздников. См. раздел календаря праздников для получения дополнительной информации.
In [194]: from pandas.tseries.holiday import USFederalHolidayCalendar
In [195]: bday_us = pd.offsets.CustomBusinessDay(calendar=USFederalHolidayCalendar())
# Friday before MLK Day
In [196]: dt = datetime.datetime(2014, 1, 17)
# Tuesday after MLK Day (Monday is skipped because it's a holiday)
In [197]: dt + bday_us
Out[197]: Timestamp('2014-01-21 00:00:00')
Ежемесячные смещения, учитывающие определённый календарь праздников, можно определить стандартным образом.
In [198]: bmth_us = pd.offsets.CustomBusinessMonthBegin(calendar=USFederalHolidayCalendar())
# Skip new years
In [199]: dt = datetime.datetime(2013, 12, 17)
In [200]: dt + bmth_us
Out[200]: Timestamp('2014-01-02 00:00:00')
# Define date index with custom offset
In [201]: pd.date_range(start="20100101", end="20120101", freq=bmth_us)
Out[201]:
DatetimeIndex(['2010-01-04', '2010-02-01', '2010-03-01', '2010-04-01',
'2010-05-03', '2010-06-01', '2010-07-01', '2010-08-02',
'2010-09-01', '2010-10-01', '2010-11-01', '2010-12-01',
'2011-01-03', '2011-02-01', '2011-03-01', '2011-04-01',
'2011-05-02', '2011-06-01', '2011-07-01', '2011-08-01',
'2011-09-01', '2011-10-03', '2011-11-01', '2011-12-01'],
dtype='datetime64[ns]', freq='CBMS')
Примечание
Строка частоты ‘C’ используется для обозначения того, что используется смещение CustomBusinessDay DateOffset. Важно отметить, что так как CustomBusinessDay является параметризованным типом, экземпляры CustomBusinessDay могут отличаться, и это не может быть обнаружено из строки частоты ‘C’. Поэтому пользователь должен убедиться, что строка частоты ‘C’ используется последовательно в приложении пользователя.
Рабочее время
Класс BusinessHour предоставляет представление рабочего времени по BusinessDay, позволяя использовать определённые начальные и конечные времена.
По умолчанию BusinessHour использует рабочее время с 9:00 до 17:00. Добавление BusinessHour увеличит Timestamp на часовую частоту. Если целевое Timestamp находится вне рабочего времени, перейдите к следующему рабочему часу, а затем увеличьте его. Если результат превышает конец рабочего времени, оставшиеся часы добавляются к следующему рабочему дню.
In [202]: bh = pd.offsets.BusinessHour()
In [203]: bh
Out[203]: <BusinessHour: BH=09:00-17:00>
# 2014-08-01 is Friday
In [204]: pd.Timestamp("2014-08-01 10:00").weekday()
Out[204]: 4
In [205]: pd.Timestamp("2014-08-01 10:00") + bh
Out[205]: Timestamp('2014-08-01 11:00:00')
# Below example is the same as: pd.Timestamp('2014-08-01 09:00') + bh
In [206]: pd.Timestamp("2014-08-01 08:00") + bh
Out[206]: Timestamp('2014-08-01 10:00:00')
# If the results is on the end time, move to the next business day
In [207]: pd.Timestamp("2014-08-01 16:00") + bh
Out[207]: Timestamp('2014-08-04 09:00:00')
# Remainings are added to the next day
In [208]: pd.Timestamp("2014-08-01 16:30") + bh
Out[208]: Timestamp('2014-08-04 09:30:00')
# Adding 2 business hours
In [209]: pd.Timestamp("2014-08-01 10:00") + pd.offsets.BusinessHour(2)
Out[209]: Timestamp('2014-08-01 12:00:00')
# Subtracting 3 business hours
In [210]: pd.Timestamp("2014-08-01 10:00") + pd.offsets.BusinessHour(-3)
Out[210]: Timestamp('2014-07-31 15:00:00')
Вы также можете указать start и end время с помощью ключевых слов. Аргумент должен быть str с hour:minute представлением или экземпляром datetime.time. Указание секунд, микросекунд и наносекунд как рабочего времени приводит к ValueError.
In [211]: bh = pd.offsets.BusinessHour(start="11:00", end=datetime.time(20, 0))
In [212]: bh
Out[212]: <BusinessHour: BH=11:00-20:00>
In [213]: pd.Timestamp("2014-08-01 13:00") + bh
Out[213]: Timestamp('2014-08-01 14:00:00')
In [214]: pd.Timestamp("2014-08-01 09:00") + bh
Out[214]: Timestamp('2014-08-01 12:00:00')
In [215]: pd.Timestamp("2014-08-01 18:00") + bh
Out[215]: Timestamp('2014-08-01 19:00:00')
Передача start времени после end представляет собой рабочее время полуночи. В этом случае рабочее время превышает полночь и перекрывается со следующим днём. Действительные рабочие часы отличаются тем, начались ли они с действительного BusinessDay.
In [216]: bh = pd.offsets.BusinessHour(start="17:00", end="09:00")
In [217]: bh
Out[217]: <BusinessHour: BH=17:00-09:00>
In [218]: pd.Timestamp("2014-08-01 17:00") + bh
Out[218]: Timestamp('2014-08-01 18:00:00')
In [219]: pd.Timestamp("2014-08-01 23:00") + bh
Out[219]: Timestamp('2014-08-02 00:00:00')
# Although 2014-08-02 is Saturday,
# it is valid because it starts from 08-01 (Friday).
In [220]: pd.Timestamp("2014-08-02 04:00") + bh
Out[220]: Timestamp('2014-08-02 05:00:00')
# Although 2014-08-04 is Monday,
# it is out of business hours because it starts from 08-03 (Sunday).
In [221]: pd.Timestamp("2014-08-04 04:00") + bh
Out[221]: Timestamp('2014-08-04 18:00:00')
Применение BusinessHour.rollforward и rollback к рабочему времени приводит к началу следующего рабочего часа или концу предыдущего дня. В отличие от других смещений, BusinessHour.rollforward может давать разные результаты, чем apply по определению.
Это происходит потому, что конец рабочего времени одного дня равен началу рабочего времени следующего дня. Например, при стандартном рабочем времени (9:00 — 17:00) нет промежутка (0 минут) между 2014-08-01 17:00 и 2014-08-04 09:00.
# This adjusts a Timestamp to business hour edge
In [222]: pd.offsets.BusinessHour().rollback(pd.Timestamp("2014-08-02 15:00"))
Out[222]: Timestamp('2014-08-01 17:00:00')
In [223]: pd.offsets.BusinessHour().rollforward(pd.Timestamp("2014-08-02 15:00"))
Out[223]: Timestamp('2014-08-04 09:00:00')
# It is the same as BusinessHour() + pd.Timestamp('2014-08-01 17:00').
# And it is the same as BusinessHour() + pd.Timestamp('2014-08-04 09:00')
In [224]: pd.offsets.BusinessHour() + pd.Timestamp("2014-08-02 15:00")
Out[224]: Timestamp('2014-08-04 10:00:00')
# BusinessDay results (for reference)
In [225]: pd.offsets.BusinessHour().rollforward(pd.Timestamp("2014-08-02"))
Out[225]: Timestamp('2014-08-04 09:00:00')
# It is the same as BusinessDay() + pd.Timestamp('2014-08-01')
# The result is the same as rollworward because BusinessDay never overlap.
In [226]: pd.offsets.BusinessHour() + pd.Timestamp("2014-08-02")
Out[226]: Timestamp('2014-08-04 10:00:00')
BusinessHour рассматривает субботу и воскресенье как выходные. Для использования произвольных выходных дней можно использовать смещение CustomBusinessHour, как объяснено в следующем подраздле.
Настраиваемое рабочее время
CustomBusinessHour — это сочетание BusinessHour и CustomBusinessDay, которое позволяет указать произвольные праздники. CustomBusinessHour работает так же, как BusinessHour за исключением пропуска указанных настраиваемых праздников.
In [227]: from pandas.tseries.holiday import USFederalHolidayCalendar
In [228]: bhour_us = pd.offsets.CustomBusinessHour(calendar=USFederalHolidayCalendar())
# Friday before MLK Day
In [229]: dt = datetime.datetime(2014, 1, 17, 15)
In [230]: dt + bhour_us
Out[230]: Timestamp('2014-01-17 16:00:00')
# Tuesday after MLK Day (Monday is skipped because it's a holiday)
In [231]: dt + bhour_us * 2
Out[231]: Timestamp('2014-01-21 09:00:00')
Вы можете использовать ключевые аргументы, поддерживаемые как BusinessHour, так и CustomBusinessDay.
In [232]: bhour_mon = pd.offsets.CustomBusinessHour(start="10:00", weekmask="Tue Wed Thu Fri")
# Monday is skipped because it's a holiday, business hour starts from 10:00
In [233]: dt + bhour_mon * 2
Out[233]: Timestamp('2014-01-21 10:00:00')
Псевдонимы сдвигов
Для полезных общих частот временных рядов задано несколько строковых псевдонимов. Мы будем называть их псевдонимами сдвигов.
Псевдоним | Описание |
|---|---|
B | частота рабочих дней |
C | частота пользовательских рабочих дней |
D | частота календарных дней |
W | частота недель |
M | частота конца месяца |
SM | частота полумесяца (15-е число и конец месяца) |
BM | частота конца рабочего месяца |
CBM | частота пользовательского конца рабочего месяца |
MS | частота начала месяца |
SMS | частота начала полумесяца (1-е и 15-е числа) |
BMS | частота начала рабочего месяца |
CBMS | частота пользовательского начала рабочего месяца |
Q | частота конца квартала |
BQ | частота конца квартала (рабочие дни) |
QS | частота начала квартала |
BQS | частота начала квартала (рабочие дни) |
A, Y | частота конца года |
BA, BY | частота конца рабочего года |
AS, YS | частота начала года |
BAS, BYS | частота начала рабочего года |
BH | частота рабочего часа |
H | частота часов |
T, min | частота минут |
S | частота секунд |
L, ms | миллисекунды |
U, us | микросекунды |
N | наносекунды |
Примечание
При использовании вышеперечисленных псевдонимов сдвигов следует учитывать, что такие функции, как
date_range(),bdate_range(), будут возвращать только отметки времени, находящиеся в интервале, определенномstart_dateиend_date. Еслиstart_dateне соответствует частоте, возвращаемые отметки времени будут начинаться с ближайшей допустимой отметки времени, то же самое относится кend_date, возвращаемые отметки времени будут заканчиваться на предыдущей допустимой отметке времени.
Например, для сдвига MS, если start_date не является первым числом месяца, возвращаемые отметки времени будут начинаться с первого числа следующего месяца. Если end_date не является первым числом месяца, последней возвращаемой отметкой времени будет первое число соответствующего месяца.
In [234]: dates_lst_1 = pd.date_range("2020-01-06", "2020-04-03", freq="MS")
In [235]: dates_lst_1
Out[235]: DatetimeIndex(['2020-02-01', '2020-03-01', '2020-04-01'], dtype='datetime64[ns]', freq='MS')
In [236]: dates_lst_2 = pd.date_range("2020-01-01", "2020-04-01", freq="MS")
In [237]: dates_lst_2
Out[237]: DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01', '2020-04-01'], dtype='datetime64[ns]', freq='MS')
Из примера видно, что date_range() и bdate_range() вернут только допустимые отметки времени между start_date и end_date. Если эти отметки времени не являются допустимыми для заданной частоты, то будет произведен сдвиг к следующему значению для start_date (соответственно к предыдущему для end_date).
Объединение псевдонимов
Как мы видели ранее, псевдоним и экземпляр сдвига взаимозаменяемы в большинстве функций:
In [238]: pd.date_range(start, periods=5, freq="B")
Out[238]:
DatetimeIndex(['2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06',
'2011-01-07'],
dtype='datetime64[ns]', freq='B')
In [239]: pd.date_range(start, periods=5, freq=pd.offsets.BDay())
Out[239]:
DatetimeIndex(['2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06',
'2011-01-07'],
dtype='datetime64[ns]', freq='B')
Вы можете объединять сдвиги дней и внутридневных сдвигов:
In [240]: pd.date_range(start, periods=10, freq="2h20min")
Out[240]:
DatetimeIndex(['2011-01-01 00:00:00', '2011-01-01 02:20:00',
'2011-01-01 04:40:00', '2011-01-01 07:00:00',
'2011-01-01 09:20:00', '2011-01-01 11:40:00',
'2011-01-01 14:00:00', '2011-01-01 16:20:00',
'2011-01-01 18:40:00', '2011-01-01 21:00:00'],
dtype='datetime64[ns]', freq='140T')
In [241]: pd.date_range(start, periods=10, freq="1D10U")
Out[241]:
DatetimeIndex([ '2011-01-01 00:00:00', '2011-01-02 00:00:00.000010',
'2011-01-03 00:00:00.000020', '2011-01-04 00:00:00.000030',
'2011-01-05 00:00:00.000040', '2011-01-06 00:00:00.000050',
'2011-01-07 00:00:00.000060', '2011-01-08 00:00:00.000070',
'2011-01-09 00:00:00.000080', '2011-01-10 00:00:00.000090'],
dtype='datetime64[ns]', freq='86400000010U')
Привязанные сдвиги
Для некоторых частот можно указать суффикс привязки:
Псевдоним | Описание |
|---|---|
W-SUN | частота недель (воскресенья). То же, что и ‘W’ |
W-MON | частота недель (понедельники) |
W-TUE | частота недель (вторники) |
W-WED | частота недель (среды) |
W-THU | частота недель (четверги) |
W-FRI | частота недель (пятницы) |
W-SAT | частота недель (субботы) |
(B)Q(S)-DEC | частота кварталов, конец года в декабре. То же, что и ‘Q’ |
(B)Q(S)-JAN | частота кварталов, конец года в январе |
(B)Q(S)-FEB | частота кварталов, конец года в феврале |
Они могут быть использованы в качестве аргументов в date_range, bdate_range, конструкторах для DatetimeIndex, а также в различных других функциях, связанных со временными рядами в pandas.
Семантика привязанных сдвигов
Для тех сдвигов, которые привязаны к началу или концу определенной частоты (MonthEnd, MonthBegin, WeekEnd, и т.д.), следующие правила применяются к сдвигам вперед и назад.
Когда n не равно 0, если заданная дата не находится на точке привязки, она привязана к ближайшей точке привязки, и перемещена на |n|-1 дополнительных шагов вперед или назад.
In [242]: pd.Timestamp("2014-01-02") + pd.offsets.MonthBegin(n=1)
Out[242]: Timestamp('2014-02-01 00:00:00')
In [243]: pd.Timestamp("2014-01-02") + pd.offsets.MonthEnd(n=1)
Out[243]: Timestamp('2014-01-31 00:00:00')
In [244]: pd.Timestamp("2014-01-02") - pd.offsets.MonthBegin(n=1)
Out[244]: Timestamp('2014-01-01 00:00:00')
In [245]: pd.Timestamp("2014-01-02") - pd.offsets.MonthEnd(n=1)
Out[245]: Timestamp('2013-12-31 00:00:00')
In [246]: pd.Timestamp("2014-01-02") + pd.offsets.MonthBegin(n=4)
Out[246]: Timestamp('2014-05-01 00:00:00')
In [247]: pd.Timestamp("2014-01-02") - pd.offsets.MonthBegin(n=4)
Out[247]: Timestamp('2013-10-01 00:00:00')
Если заданная дата находится на точке привязки, она перемещается на |n| позиций вперед или назад.
In [248]: pd.Timestamp("2014-01-01") + pd.offsets.MonthBegin(n=1)
Out[248]: Timestamp('2014-02-01 00:00:00')
In [249]: pd.Timestamp("2014-01-31") + pd.offsets.MonthEnd(n=1)
Out[249]: Timestamp('2014-02-28 00:00:00')
In [250]: pd.Timestamp("2014-01-01") - pd.offsets.MonthBegin(n=1)
Out[250]: Timestamp('2013-12-01 00:00:00')
In [251]: pd.Timestamp("2014-01-31") - pd.offsets.MonthEnd(n=1)
Out[251]: Timestamp('2013-12-31 00:00:00')
In [252]: pd.Timestamp("2014-01-01") + pd.offsets.MonthBegin(n=4)
Out[252]: Timestamp('2014-05-01 00:00:00')
In [253]: pd.Timestamp("2014-01-31") - pd.offsets.MonthBegin(n=4)
Out[253]: Timestamp('2013-10-01 00:00:00')
В случае, когда n=0, дата не перемещается, если она находится на точке привязки, в противном случае она переносится вперед к следующей точке привязки.
In [254]: pd.Timestamp("2014-01-02") + pd.offsets.MonthBegin(n=0)
Out[254]: Timestamp('2014-02-01 00:00:00')
In [255]: pd.Timestamp("2014-01-02") + pd.offsets.MonthEnd(n=0)
Out[255]: Timestamp('2014-01-31 00:00:00')
In [256]: pd.Timestamp("2014-01-01") + pd.offsets.MonthBegin(n=0)
Out[256]: Timestamp('2014-01-01 00:00:00')
In [257]: pd.Timestamp("2014-01-31") + pd.offsets.MonthEnd(n=0)
Out[257]: Timestamp('2014-01-31 00:00:00')
Праздники / календари праздников
Праздники и календари предоставляют простой способ определения правил праздников, которые будут использоваться с CustomBusinessDay или в других анализах, требующих предварительно определенного набора праздников. Класс AbstractHolidayCalendar предоставляет все необходимые методы для возврата списка праздников, и только rules необходимо определить в конкретном классе календаря праздников. Кроме того, атрибуты класса start_date и end_date определяют диапазон дат, для которого генерируются праздники. Их следует переопределить в классе AbstractHolidayCalendar, чтобы диапазон применялся ко всем подклассам календаря. USFederalHolidayCalendar — единственный существующий календарь и служит главным образом примером для разработки других календарей.
Для праздников, которые происходят в фиксированные даты (например, День памяти США или 4 июля), правило соблюдения определяет, когда этот праздник отмечается, если он выпадает на выходной день или какой-либо другой неопределяемый день. Определенные правила соблюдения:
Правило | Описание |
|---|---|
nearest_workday | переместить субботу на пятницу, а воскресенье на понедельник |
sunday_to_monday | переместить воскресенье на следующий понедельник |
next_monday_or_tuesday | переместить субботу на понедельник, а воскресенье/понедельник на вторник |
previous_friday | переместить субботу и воскресенье на предыдущую пятницу |
next_monday | переместить субботу и воскресенье на следующий понедельник |
Пример того, как определяются праздники и календари праздников:
In [258]: from pandas.tseries.holiday import (
.....: Holiday,
.....: USMemorialDay,
.....: AbstractHolidayCalendar,
.....: nearest_workday,
.....: MO,
.....: )
.....:
In [259]: class ExampleCalendar(AbstractHolidayCalendar):
.....: rules = [
.....: USMemorialDay,
.....: Holiday("July 4th", month=7, day=4, observance=nearest_workday),
.....: Holiday(
.....: "Columbus Day",
.....: month=10,
.....: day=1,
.....: offset=pd.DateOffset(weekday=MO(2)),
.....: ),
.....: ]
.....:
In [260]: cal = ExampleCalendar()
In [261]: cal.holidays(datetime.datetime(2012, 1, 1), datetime.datetime(2012, 12, 31))
Out[261]: DatetimeIndex(['2012-05-28', '2012-07-04', '2012-10-08'], dtype='datetime64[ns]', freq=None)
- подсказка
-
weekday=MO(2) эквивалентно 2 * Week(weekday=2)
Используя этот календарь, создание индекса или арифметические вычисления смещения пропускают выходные и праздничные дни (т.е. День памяти/4 июля). Например, ниже определяется пользовательское смещение рабочего дня, используя ExampleCalendar. Как и любое другое смещение, его можно использовать для создания DatetimeIndex или добавления к объектам datetime или Timestamp.
In [262]: pd.date_range(
.....: start="7/1/2012", end="7/10/2012", freq=pd.offsets.CDay(calendar=cal)
.....: ).to_pydatetime()
.....:
Out[262]:
array([datetime.datetime(2012, 7, 2, 0, 0),
datetime.datetime(2012, 7, 3, 0, 0),
datetime.datetime(2012, 7, 5, 0, 0),
datetime.datetime(2012, 7, 6, 0, 0),
datetime.datetime(2012, 7, 9, 0, 0),
datetime.datetime(2012, 7, 10, 0, 0)], dtype=object)
In [263]: offset = pd.offsets.CustomBusinessDay(calendar=cal)
In [264]: datetime.datetime(2012, 5, 25) + offset
Out[264]: Timestamp('2012-05-29 00:00:00')
In [265]: datetime.datetime(2012, 7, 3) + offset
Out[265]: Timestamp('2012-07-05 00:00:00')
In [266]: datetime.datetime(2012, 7, 3) + 2 * offset
Out[266]: Timestamp('2012-07-06 00:00:00')
In [267]: datetime.datetime(2012, 7, 6) + offset
Out[267]: Timestamp('2012-07-09 00:00:00')
Диапазоны определяются атрибутами класса start_date и end_date класса AbstractHolidayCalendar. Значения по умолчанию показаны ниже.
In [268]: AbstractHolidayCalendar.start_date
Out[268]: Timestamp('1970-01-01 00:00:00')
In [269]: AbstractHolidayCalendar.end_date
Out[269]: Timestamp('2200-12-31 00:00:00')
Эти даты можно переопределить, задав атрибуты как datetime/Timestamp/строку.
In [270]: AbstractHolidayCalendar.start_date = datetime.datetime(2012, 1, 1)
In [271]: AbstractHolidayCalendar.end_date = datetime.datetime(2012, 12, 31)
In [272]: cal.holidays()
Out[272]: DatetimeIndex(['2012-05-28', '2012-07-04', '2012-10-08'], dtype='datetime64[ns]', freq=None)
Каждый класс календаря доступен по имени с помощью функции get_calendar, которая возвращает экземпляр класса праздника. Любой импортированный класс календаря автоматически будет доступен с помощью этой функции. Кроме того, HolidayCalendarFactory предоставляет удобный интерфейс для создания календарей, которые являются комбинацией календарей или календарей с дополнительными правилами.
In [273]: from pandas.tseries.holiday import get_calendar, HolidayCalendarFactory, USLaborDay
In [274]: cal = get_calendar("ExampleCalendar")
In [275]: cal.rules
Out[275]:
[Holiday: Memorial Day (month=5, day=31, offset=<DateOffset: weekday=MO(-1)>),
Holiday: July 4th (month=7, day=4, observance=<function nearest_workday at 0x7f27b75b8790>),
Holiday: Columbus Day (month=10, day=1, offset=<DateOffset: weekday=MO(+2)>)]
In [276]: new_cal = HolidayCalendarFactory("NewExampleCalendar", cal, USLaborDay)
In [277]: new_cal.rules
Out[277]:
[Holiday: Labor Day (month=9, day=1, offset=<DateOffset: weekday=MO(+1)>),
Holiday: Memorial Day (month=5, day=31, offset=<DateOffset: weekday=MO(-1)>),
Holiday: July 4th (month=7, day=4, observance=<function nearest_workday at 0x7f27b75b8790>),
Holiday: Columbus Day (month=10, day=1, offset=<DateOffset: weekday=MO(+2)>)]
Ресемплирование
pandas имеет простую, мощную и эффективную функциональность для выполнения операций ресемплирования при изменении частоты (например, преобразование данных с частотой в секунду в данные с частотой 5 минут). Это очень распространено, но не ограничивается, финансовыми приложениями.
resample() это группировка по времени, за которой следует метод уменьшения для каждой из групп. Посмотрите примеры в примерах поваренной книги для некоторых продвинутых стратегий.
Метод resample() можно использовать непосредственно из объектов DataFrameGroupBy, см. документацию по группировке.
Основы
In [289]: rng = pd.date_range("1/1/2012", periods=100, freq="S")
In [290]: ts = pd.Series(np.random.randint(0, 500, len(rng)), index=rng)
In [291]: ts.resample("5Min").sum()
Out[291]:
2012-01-01 25103
Freq: 5T, dtype: int64
Функция resample очень гибкая и позволяет указать множество различных параметров для управления преобразованием частоты и операцией ресемплирования.
Любая функция, доступная через диспетчеризацию, доступна в качестве метода возвращаемого объекта, включая sum, mean, std, sem, max, min, median, first, last, ohlc:
In [292]: ts.resample("5Min").mean()
Out[292]:
2012-01-01 251.03
Freq: 5T, dtype: float64
In [293]: ts.resample("5Min").ohlc()
Out[293]:
open high low close
2012-01-01 308 460 9 205
In [294]: ts.resample("5Min").max()
Out[294]:
2012-01-01 460
Freq: 5T, dtype: int64
Для уменьшения выборки closed можно установить на ‘left’ или ‘right’, чтобы указать, какой конец интервала закрыт:
In [295]: ts.resample("5Min", closed="right").mean()
Out[295]:
2011-12-31 23:55:00 308.000000
2012-01-01 00:00:00 250.454545
Freq: 5T, dtype: float64
In [296]: ts.resample("5Min", closed="left").mean()
Out[296]:
2012-01-01 251.03
Freq: 5T, dtype: float64
Параметры, такие как label, используются для управления результирующими метками. label указывает, помечена ли результирующая метка началом или концом интервала.
In [297]: ts.resample("5Min").mean() # by default label='left'
Out[297]:
2012-01-01 251.03
Freq: 5T, dtype: float64
In [298]: ts.resample("5Min", label="left").mean()
Out[298]:
2012-01-01 251.03
Freq: 5T, dtype: float64
Предупреждение
Значения по умолчанию для label и closed равны ‘left’ для всех сдвигов частоты, кроме ‘M’, ‘A’, ‘Q’, ‘BM’, ‘BA’, ‘BQ’ и ‘W’, у которых значение по умолчанию равно ‘right’.
Это может непроизвольно привести к «предвосхищению», где значение для более позднего момента времени подтягивается к предыдущему времени, как в следующем примере с частотой BusinessDay:
In [299]: s = pd.date_range("2000-01-01", "2000-01-05").to_series()
In [300]: s.iloc[2] = pd.NaT
In [301]: s.dt.day_name()
Out[301]:
2000-01-01 Saturday
2000-01-02 Sunday
2000-01-03 NaN
2000-01-04 Tuesday
2000-01-05 Wednesday
Freq: D, dtype: object
# default: label='left', closed='left'
In [302]: s.resample("B").last().dt.day_name()
Out[302]:
1999-12-31 Sunday
2000-01-03 NaN
2000-01-04 Tuesday
2000-01-05 Wednesday
Freq: B, dtype: object
Обратите внимание, как значение для воскресенья было подтянуто к предыдущей пятнице. Чтобы получить поведение, при котором значение для воскресенья перемещается на понедельник, используйте вместо этого
In [303]: s.resample("B", label="right", closed="right").last().dt.day_name()
Out[303]:
2000-01-03 Sunday
2000-01-04 Tuesday
2000-01-05 Wednesday
Freq: B, dtype: object
Параметр axis может быть установлен в 0 или 1 и позволяет выполнить ресемплирование указанной оси для DataFrame.
kind может быть установлен на ‘timestamp’ или ‘period’, чтобы преобразовать результирующий индекс в представления временных меток и временных интервалов. По умолчанию resample сохраняет исходное представление.
convention может быть установлен на ‘start’ или ‘end’ при ресемплировании данных периода (подробности ниже). Он указывает, как периоды с низкой частотой преобразуются в периоды с высокой частотой.
Увеличение выборки
Для увеличения выборки вы можете указать способ увеличения выборки и параметр limit для интерполяции пробелов, которые создаются:
# from secondly to every 250 milliseconds
In [304]: ts[:2].resample("250L").asfreq()
Out[304]:
2012-01-01 00:00:00.000 308.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 204.0
Freq: 250L, dtype: float64
In [305]: ts[:2].resample("250L").ffill()
Out[305]:
2012-01-01 00:00:00.000 308
2012-01-01 00:00:00.250 308
2012-01-01 00:00:00.500 308
2012-01-01 00:00:00.750 308
2012-01-01 00:00:01.000 204
Freq: 250L, dtype: int64
In [306]: ts[:2].resample("250L").ffill(limit=2)
Out[306]:
2012-01-01 00:00:00.000 308.0
2012-01-01 00:00:00.250 308.0
2012-01-01 00:00:00.500 308.0
2012-01-01 00:00:00.750 NaN
2012-01-01 00:00:01.000 204.0
Freq: 250L, dtype: float64
Ресемплирование разреженных данных
Разреженные временные ряды — это временные ряды, в которых у вас значительно меньше точек по отношению к объёму времени, для которого вы хотите выполнить ресемплирование. Небрежное увеличение выборки разреженного ряда может потенциально генерировать множество промежуточных значений. Если вы не хотите использовать метод заполнения этих значений, например, если fill_method равно None, то промежуточные значения будут заполнены NaN.
Поскольку resample представляет собой группировку по времени, следующий подход является эффективным методом ресемплирования только тех групп, которые не состоят полностью из NaN.
In [307]: rng = pd.date_range("2014-1-1", periods=100, freq="D") + pd.Timedelta("1s")
In [308]: ts = pd.Series(range(100), index=rng)
Если мы хотим ресемплировать весь диапазон ряда:
In [309]: ts.resample("3T").sum()
Out[309]:
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: 3T, Length: 47521, dtype: int64
Вместо этого мы можем ресемплировать только те группы, в которых у нас есть точки, как показано ниже:
In [310]: from functools import partial
In [311]: from pandas.tseries.frequencies import to_offset
In [312]: def round(t, freq):
.....: freq = to_offset(freq)
.....: return pd.Timestamp((t.value // freq.delta.value) * freq.delta.value)
.....:
In [313]: ts.groupby(partial(round, freq="3T")).sum()
Out[313]:
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
Length: 100, dtype: int64
Агрегирование
Аналогично интерфейсу агрегирования, интерфейсу группировки и интерфейсу окна, Resampler можно выборочно ресемплировать.
При ресемплировании DataFrame, по умолчанию будет применяться одна и та же функция ко всем столбцам.
In [314]: df = pd.DataFrame(
.....: np.random.randn(1000, 3),
.....: index=pd.date_range("1/1/2012", freq="S", periods=1000),
.....: columns=["A", "B", "C"],
.....: )
.....:
In [315]: r = df.resample("3T")
In [316]: r.mean()
Out[316]:
A B C
2012-01-01 00:00:00 -0.033823 -0.121514 -0.081447
2012-01-01 00:03:00 0.056909 0.146731 -0.024320
2012-01-01 00:06:00 -0.058837 0.047046 -0.052021
2012-01-01 00:09:00 0.063123 -0.026158 -0.066533
2012-01-01 00:12:00 0.186340 -0.003144 0.074752
2012-01-01 00:15:00 -0.085954 -0.016287 -0.050046
Мы можем выбрать определенный столбец или столбцы с помощью стандартного доступа по индексу.
In [317]: r["A"].mean()
Out[317]:
2012-01-01 00:00:00 -0.033823
2012-01-01 00:03:00 0.056909
2012-01-01 00:06:00 -0.058837
2012-01-01 00:09:00 0.063123
2012-01-01 00:12:00 0.186340
2012-01-01 00:15:00 -0.085954
Freq: 3T, Name: A, dtype: float64
In [318]: r[["A", "B"]].mean()
Out[318]:
A B
2012-01-01 00:00:00 -0.033823 -0.121514
2012-01-01 00:03:00 0.056909 0.146731
2012-01-01 00:06:00 -0.058837 0.047046
2012-01-01 00:09:00 0.063123 -0.026158
2012-01-01 00:12:00 0.186340 -0.003144
2012-01-01 00:15:00 -0.085954 -0.016287
Вы можете передать список или словарь функций для выполнения агрегирования, получив на выходе DataFrame:
In [319]: r["A"].agg([np.sum, np.mean, np.std])
Out[319]:
sum mean std
2012-01-01 00:00:00 -6.088060 -0.033823 1.043263
2012-01-01 00:03:00 10.243678 0.056909 1.058534
2012-01-01 00:06:00 -10.590584 -0.058837 0.949264
2012-01-01 00:09:00 11.362228 0.063123 1.028096
2012-01-01 00:12:00 33.541257 0.186340 0.884586
2012-01-01 00:15:00 -8.595393 -0.085954 1.035476
Для ресемплированного DataFrame, вы можете передать список функций для применения к каждому столбцу, что даст агрегированный результат с иерархическим индексом:
In [320]: r.agg([np.sum, np.mean])
Out[320]:
A ... C
sum mean ... sum mean
2012-01-01 00:00:00 -6.088060 -0.033823 ... -14.660515 -0.081447
2012-01-01 00:03:00 10.243678 0.056909 ... -4.377642 -0.024320
2012-01-01 00:06:00 -10.590584 -0.058837 ... -9.363825 -0.052021
2012-01-01 00:09:00 11.362228 0.063123 ... -11.975895 -0.066533
2012-01-01 00:12:00 33.541257 0.186340 ... 13.455299 0.074752
2012-01-01 00:15:00 -8.595393 -0.085954 ... -5.004580 -0.050046
[6 rows x 6 columns]
Передав словарь в aggregate, вы можете применить разные функции агрегирования к столбцам DataFrame:
In [321]: r.agg({"A": np.sum, "B": lambda x: np.std(x, ddof=1)})
Out[321]:
A B
2012-01-01 00:00:00 -6.088060 1.001294
2012-01-01 00:03:00 10.243678 1.074597
2012-01-01 00:06:00 -10.590584 0.987309
2012-01-01 00:09:00 11.362228 0.944953
2012-01-01 00:12:00 33.541257 1.095025
2012-01-01 00:15:00 -8.595393 1.035312
Имена функций также могут быть строками. Для того, чтобы строка была действительной, она должна быть реализована в ресемплированном объекте:
In [322]: r.agg({"A": "sum", "B": "std"})
Out[322]:
A B
2012-01-01 00:00:00 -6.088060 1.001294
2012-01-01 00:03:00 10.243678 1.074597
2012-01-01 00:06:00 -10.590584 0.987309
2012-01-01 00:09:00 11.362228 0.944953
2012-01-01 00:12:00 33.541257 1.095025
2012-01-01 00:15:00 -8.595393 1.035312
Кроме того, вы также можете указать несколько функций агрегирования для каждого столбца отдельно.
In [323]: r.agg({"A": ["sum", "std"], "B": ["mean", "std"]})
Out[323]:
A B
sum std mean std
2012-01-01 00:00:00 -6.088060 1.043263 -0.121514 1.001294
2012-01-01 00:03:00 10.243678 1.058534 0.146731 1.074597
2012-01-01 00:06:00 -10.590584 0.949264 0.047046 0.987309
2012-01-01 00:09:00 11.362228 1.028096 -0.026158 0.944953
2012-01-01 00:12:00 33.541257 0.884586 -0.003144 1.095025
2012-01-01 00:15:00 -8.595393 1.035476 -0.016287 1.035312
Если у DataFrame нет индекса datetimelike, но вы хотите выполнить ресемплирование по столбцу datetimelike в кадре, то его можно передать в ключевое слово on.
In [324]: 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"],
.....: ),
.....: )
.....:
In [325]: df
Out[325]:
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
In [326]: df.resample("M", on="date")[["a"]].sum()
Out[326]:
a
date
2015-01-31 6
2015-02-28 4
Аналогично, если вы хотите выполнить ресемплирование по уровню datetimelike в MultiIndex, его имя или местоположение можно передать в ключевое слово level.
In [327]: df.resample("M", level="d")[["a"]].sum()
Out[327]:
a
d
2015-01-31 6
2015-02-28 4
Итерация по группам
Имея на руках объект Resampler , итерация по сгруппированным данным очень естественна и аналогична itertools.groupby():
In [328]: 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",
.....: ]
.....: ),
.....: )
.....:
In [329]: resampled = small.resample("H")
In [330]: 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
См. Итерация по группам или Resampler.__iter__ для более подробной информации.
Используйте origin или offset для изменения начала интервалов
В версии 1.1.0.
Интервалы группировки корректируются на основе начала дня точки отсчета временного ряда. Это хорошо работает с частотами, кратными дню (например, 30D ) или делящими день на равные части (например, 90s или 1min). Это может создавать несоответствия с некоторыми частотами, которые не соответствуют этим критериям. Чтобы изменить это поведение, вы можете указать фиксированную метку времени с аргументом origin.
Например:
In [331]: start, end = "2000-10-01 23:30:00", "2000-10-02 00:30:00"
In [332]: middle = "2000-10-02 00:00:00"
In [333]: rng = pd.date_range(start, end, freq="7min")
In [334]: ts = pd.Series(np.arange(len(rng)) * 3, index=rng)
In [335]: ts
Out[335]:
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
Здесь мы видим, что при использовании origin с его значением по умолчанию ('start_day') результат после '2000-10-02 00:00:00' не идентичен в зависимости от начала временного ряда:
In [336]: ts.resample("17min", origin="start_day").sum()
Out[336]:
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
In [337]: ts[middle:end].resample("17min", origin="start_day").sum()
Out[337]:
2000-10-02 00:00:00 33
2000-10-02 00:17:00 45
Freq: 17T, dtype: int64
Здесь мы видим, что при установке origin на 'epoch', результат после '2000-10-02 00:00:00' идентичен вне зависимости от начала временного ряда:
In [338]: ts.resample("17min", origin="epoch").sum()
Out[338]:
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
In [339]: ts[middle:end].resample("17min", origin="epoch").sum()
Out[339]:
2000-10-01 23:52:00 15
2000-10-02 00:09:00 39
2000-10-02 00:26:00 24
Freq: 17T, dtype: int64
При необходимости вы можете использовать пользовательскую метку времени для origin:
In [340]: ts.resample("17min", origin="2001-01-01").sum()
Out[340]:
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
In [341]: ts[middle:end].resample("17min", origin=pd.Timestamp("2001-01-01")).sum()
Out[341]:
2000-10-02 00:04:00 54
2000-10-02 00:21:00 24
Freq: 17T, dtype: int64
При необходимости вы можете просто скорректировать интервалы с помощью пользовательского Timedelta, который будет добавлен к значению по умолчанию origin. Эти два примера эквивалентны для этого временного ряда:
In [342]: ts.resample("17min", origin="start").sum()
Out[342]:
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
In [343]: ts.resample("17min", offset="23h30min").sum()
Out[343]:
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
Обратите внимание на использование 'start' для origin в последнем примере. В этом случае origin будет установлено на первое значение временного ряда.
Обратное ресемплирование
Новое в версии 1.3.0.
Вместо изменения начала бинов, иногда нам нужно исправить конец бинов, чтобы выполнить обратное ресемплирование с заданным freq. Обратное ресемплирование устанавливает closed в 'right' по умолчанию, так как последнее значение должно рассматриваться как граничная точка последнего бина.
Мы можем установить origin в 'end'. Значение для определенного индекса Timestamp представляет собой результат ресемплирования от текущего Timestamp минус freq до текущего Timestamp с правым включением.
In [344]: ts.resample('17min', origin='end').sum()
Out[344]:
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
Кроме того, в отличие от параметра 'start_day', поддерживается параметр end_day. Это установит начало в полдень потолка самого большого Timestamp.
In [345]: ts.resample('17min', origin='end_day').sum()
Out[345]:
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
Вышеупомянутый результат использует 2000-10-02 00:29:00 в качестве правой границы последнего бина, поскольку это используется в последующих вычислениях.
In [346]: ceil_mid = rng.max().ceil('D')
In [347]: freq = pd.offsets.Minute(17)
In [348]: bin_res = ceil_mid - freq * ((ceil_mid - rng.max()) // freq)
In [349]: bin_res
Out[349]: Timestamp('2000-10-02 00:29:00')
Представление временного интервала
Регулярные интервалы времени представлены объектами Period в pandas, а последовательности объектов Period собираются в PeriodIndex, которые можно создать с помощью удобной функции period_range.
Период
Period представляет собой временной интервал (например, день, месяц, квартал и т. д.). Вы можете указать интервал через ключевое слово freq с помощью псевдонима частоты, как показано ниже. Так как freq представляет собой интервал Period, он не может быть отрицательным, как «-3D».
In [350]: pd.Period("2012", freq="A-DEC")
Out[350]: Period('2012', 'A-DEC')
In [351]: pd.Period("2012-1-1", freq="D")
Out[351]: Period('2012-01-01', 'D')
In [352]: pd.Period("2012-1-1 19:00", freq="H")
Out[352]: Period('2012-01-01 19:00', 'H')
In [353]: pd.Period("2012-1-1 19:00", freq="5H")
Out[353]: Period('2012-01-01 19:00', '5H')
Добавление и вычитание целых чисел из периодов сдвигает период на его собственную частоту. Арифметические операции не разрешены между Period с разными freq (интервалами).
In [354]: p = pd.Period("2012", freq="A-DEC")
In [355]: p + 1
Out[355]: Period('2013', 'A-DEC')
In [356]: p - 3
Out[356]: Period('2009', 'A-DEC')
In [357]: p = pd.Period("2012-01", freq="2M")
In [358]: p + 2
Out[358]: Period('2012-05', '2M')
In [359]: p - 1
Out[359]: Period('2011-11', '2M')
In [360]: p == pd.Period("2012-01", freq="3M")
Out[360]: False
Если частота Period суточная или выше (D, H, T, S, L, U, N), offsets и подобные объекты могут быть добавлены, если результат может иметь ту же частоту. В противном случае будет поднято исключение ValueError.
In [361]: p = pd.Period("2014-07-01 09:00", freq="H")
In [362]: p + pd.offsets.Hour(2)
Out[362]: Period('2014-07-01 11:00', 'H')
In [363]: p + datetime.timedelta(minutes=120)
Out[363]: Period('2014-07-01 11:00', 'H')
In [364]: p + np.timedelta64(7200, "s")
Out[364]: Period('2014-07-01 11:00', 'H')
In [1]: p + pd.offsets.Minute(5)
Traceback
...
ValueError: Input has different freq from Period(freq=H)
Если у Period другие частоты, можно добавлять только одинаковые offsets. В противном случае будет поднято исключение ValueError.
In [365]: p = pd.Period("2014-07", freq="M")
In [366]: p + pd.offsets.MonthEnd(3)
Out[366]: Period('2014-10', 'M')
In [1]: p + pd.offsets.MonthBegin(3)
Traceback
...
ValueError: Input has different freq from Period(freq=M)
Вычитание экземпляров Period с одинаковой частотой вернет количество единиц частоты между ними:
In [367]: pd.Period("2012", freq="A-DEC") - pd.Period("2002", freq="A-DEC")
Out[367]: <10 * YearEnds: month=12>
PeriodIndex и period_range
Последовательности Period объектов можно собрать в PeriodIndex, который можно создать с помощью удобной функции period_range:
In [368]: prng = pd.period_range("1/1/2011", "1/1/2012", freq="M")
In [369]: prng
Out[369]:
PeriodIndex(['2011-01', '2011-02', '2011-03', '2011-04', '2011-05', '2011-06',
'2011-07', '2011-08', '2011-09', '2011-10', '2011-11', '2011-12',
'2012-01'],
dtype='period[M]')
Конструктор PeriodIndex также можно использовать непосредственно:
In [370]: pd.PeriodIndex(["2011-1", "2011-2", "2011-3"], freq="M")
Out[370]: PeriodIndex(['2011-01', '2011-02', '2011-03'], dtype='period[M]')
Передача умноженной частоты выводит последовательность Period, которая имеет умноженный интервал.
In [371]: pd.period_range(start="2014-01", freq="3M", periods=4)
Out[371]: PeriodIndex(['2014-01', '2014-04', '2014-07', '2014-10'], dtype='period[3M]')
Если start или end являются объектами Period, они будут использованы в качестве опорных конечных точек для PeriodIndex с частотой, соответствующей частоте конструктора PeriodIndex.
In [372]: pd.period_range(
.....: start=pd.Period("2017Q1", freq="Q"), end=pd.Period("2017Q2", freq="Q"), freq="M"
.....: )
.....:
Out[372]: PeriodIndex(['2017-03', '2017-04', '2017-05', '2017-06'], dtype='period[M]')
Так же, как и DatetimeIndex, PeriodIndex также может использоваться для индексации объектов pandas:
In [373]: ps = pd.Series(np.random.randn(len(prng)), prng)
In [374]: ps
Out[374]:
2011-01 -2.916901
2011-02 0.514474
2011-03 1.346470
2011-04 0.816397
2011-05 2.258648
2011-06 0.494789
2011-07 0.301239
2011-08 0.464776
2011-09 -1.393581
2011-10 0.056780
2011-11 0.197035
2011-12 2.261385
2012-01 -0.329583
Freq: M, dtype: float64
PeriodIndex поддерживает сложение и вычитание по тем же правилам, что и Period.
In [375]: idx = pd.period_range("2014-07-01 09:00", periods=5, freq="H")
In [376]: idx
Out[376]:
PeriodIndex(['2014-07-01 09:00', '2014-07-01 10:00', '2014-07-01 11:00',
'2014-07-01 12:00', '2014-07-01 13:00'],
dtype='period[H]')
In [377]: idx + pd.offsets.Hour(2)
Out[377]:
PeriodIndex(['2014-07-01 11:00', '2014-07-01 12:00', '2014-07-01 13:00',
'2014-07-01 14:00', '2014-07-01 15:00'],
dtype='period[H]')
In [378]: idx = pd.period_range("2014-07", periods=5, freq="M")
In [379]: idx
Out[379]: PeriodIndex(['2014-07', '2014-08', '2014-09', '2014-10', '2014-11'], dtype='period[M]')
In [380]: idx + pd.offsets.MonthEnd(3)
Out[380]: PeriodIndex(['2014-10', '2014-11', '2014-12', '2015-01', '2015-02'], dtype='period[M]')
PeriodIndex имеет собственный тип данных под названием period, см. Типы данных Period.
Типы данных Period
PeriodIndex имеет пользовательский тип данных period. Это тип данных расширения pandas, аналогичный типу данных с учетом часового пояса (datetime64[ns, tz]).
Тип данных period содержит атрибут freq и представлен period[freq] такими как period[D] или period[M], используя строки частот.
In [381]: pi = pd.period_range("2016-01-01", periods=3, freq="M")
In [382]: pi
Out[382]: PeriodIndex(['2016-01', '2016-02', '2016-03'], dtype='period[M]')
In [383]: pi.dtype
Out[383]: period[M]
Тип данных period может использоваться в .astype(...). Он позволяет изменить частоту freq объекта PeriodIndex как в .asfreq() и преобразовать DatetimeIndex в PeriodIndex как в to_period():
# change monthly freq to daily freq
In [384]: pi.astype("period[D]")
Out[384]: PeriodIndex(['2016-01-31', '2016-02-29', '2016-03-31'], dtype='period[D]')
# convert to DatetimeIndex
In [385]: pi.astype("datetime64[ns]")
Out[385]: DatetimeIndex(['2016-01-01', '2016-02-01', '2016-03-01'], dtype='datetime64[ns]', freq='MS')
# convert to PeriodIndex
In [386]: dti = pd.date_range("2011-01-01", freq="M", periods=3)
In [387]: dti
Out[387]: DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31'], dtype='datetime64[ns]', freq='M')
In [388]: dti.astype("period[M]")
Out[388]: PeriodIndex(['2011-01', '2011-02', '2011-03'], dtype='period[M]')
Частичная строковая индексация PeriodIndex
PeriodIndex теперь поддерживает частичное строковое срезы с немонотонными индексами.
Новое в версии 1.1.0.
Вы можете передать даты и строки в Series и DataFrame с PeriodIndex, так же как и в DatetimeIndex. Подробности см. в разделе Частичная строковая индексация DatetimeIndex.
In [389]: ps["2011-01"]
Out[389]: -2.9169013294054507
In [390]: ps[datetime.datetime(2011, 12, 25):]
Out[390]:
2011-12 2.261385
2012-01 -0.329583
Freq: M, dtype: float64
In [391]: ps["10/31/2011":"12/31/2011"]
Out[391]:
2011-10 0.056780
2011-11 0.197035
2011-12 2.261385
Freq: M, dtype: float64
Передача строки, представляющей частоту ниже, чем PeriodIndex возвращает данные частичного среза.
In [392]: ps["2011"]
Out[392]:
2011-01 -2.916901
2011-02 0.514474
2011-03 1.346470
2011-04 0.816397
2011-05 2.258648
2011-06 0.494789
2011-07 0.301239
2011-08 0.464776
2011-09 -1.393581
2011-10 0.056780
2011-11 0.197035
2011-12 2.261385
Freq: M, dtype: float64
In [393]: dfp = pd.DataFrame(
.....: np.random.randn(600, 1),
.....: columns=["A"],
.....: index=pd.period_range("2013-01-01 9:00", periods=600, freq="T"),
.....: )
.....:
In [394]: dfp
Out[394]:
A
2013-01-01 09:00 -0.538468
2013-01-01 09:01 -1.365819
2013-01-01 09:02 -0.969051
2013-01-01 09:03 -0.331152
2013-01-01 09:04 -0.245334
... ...
2013-01-01 18:55 0.522460
2013-01-01 18:56 0.118710
2013-01-01 18:57 0.167517
2013-01-01 18:58 0.922883
2013-01-01 18:59 1.721104
[600 rows x 1 columns]
In [395]: dfp.loc["2013-01-01 10H"]
Out[395]:
A
2013-01-01 10:00 -0.308975
2013-01-01 10:01 0.542520
2013-01-01 10:02 1.061068
2013-01-01 10:03 0.754005
2013-01-01 10:04 0.352933
... ...
2013-01-01 10:55 -0.865621
2013-01-01 10:56 -1.167818
2013-01-01 10:57 -2.081748
2013-01-01 10:58 -0.527146
2013-01-01 10:59 0.802298
[60 rows x 1 columns]
Как и в случае с DatetimeIndex, конечные точки будут включены в результат. Приведенный ниже пример вырезает данные с 10:00 до 11:59.
In [396]: dfp["2013-01-01 10H":"2013-01-01 11H"]
Out[396]:
A
2013-01-01 10:00 -0.308975
2013-01-01 10:01 0.542520
2013-01-01 10:02 1.061068
2013-01-01 10:03 0.754005
2013-01-01 10:04 0.352933
... ...
2013-01-01 11:55 -0.590204
2013-01-01 11:56 1.539990
2013-01-01 11:57 -1.224826
2013-01-01 11:58 0.578798
2013-01-01 11:59 -0.685496
[120 rows x 1 columns]
Преобразование частоты и ресемплирование с PeriodIndex
Частоту Period и PeriodIndex можно преобразовать с помощью метода asfreq. Начнем с финансового года 2011, заканчивающегося в декабре:
In [397]: p = pd.Period("2011", freq="A-DEC")
In [398]: p
Out[398]: Period('2011', 'A-DEC')
Мы можем преобразовать его в ежемесячную частоту. Используя параметр how, мы можем указать, нужно ли возвращать начальный или конечный месяц:
In [399]: p.asfreq("M", how="start")
Out[399]: Period('2011-01', 'M')
In [400]: p.asfreq("M", how="end")
Out[400]: Period('2011-12', 'M')
Для удобства предоставляются сокращения «s» и «e»:
In [401]: p.asfreq("M", "s")
Out[401]: Period('2011-01', 'M')
In [402]: p.asfreq("M", "e")
Out[402]: Period('2011-12', 'M')
Преобразование в «супер-период» (например, ежегодная частота — это супер-период квартальной частоты) автоматически возвращает супер-период, содержащий входной период:
In [403]: p = pd.Period("2011-12", freq="M")
In [404]: p.asfreq("A-NOV")
Out[404]: Period('2012', 'A-NOV')
Обратите внимание, что так как мы перешли к годовой частоте, заканчивающейся в ноябре, ежемесячный период декабря 2011 года фактически находится в периоде 2012 A-НОЯ.
Преобразования периодов с закрепленными частотами особенно полезны при работе с различными квартальными данными, распространенными в экономике, бизнесе и других областях. Многие организации определяют кварталы относительно месяца, в котором начинается и заканчивается их финансовый год. Таким образом, первый квартал 2011 года мог начаться в 2010 году или в течение нескольких месяцев 2011 года. С помощью закрепленных частот pandas работает со всеми квартальными частотами Q-JAN до Q-DEC.
Q-DEC определяет регулярные календарные кварталы:
In [405]: p = pd.Period("2012Q1", freq="Q-DEC")
In [406]: p.asfreq("D", "s")
Out[406]: Period('2012-01-01', 'D')
In [407]: p.asfreq("D", "e")
Out[407]: Period('2012-03-31', 'D')
Q-MAR определяет окончание финансового года в марте:
In [408]: p = pd.Period("2011Q4", freq="Q-MAR")
In [409]: p.asfreq("D", "s")
Out[409]: Period('2011-01-01', 'D')
In [410]: p.asfreq("D", "e")
Out[410]: Period('2011-03-31', 'D')
Преобразование между представлениями
Данные со временем отметки можно преобразовать в данные PeriodIndex с помощью to_period и наоборот с помощью to_timestamp:
In [411]: rng = pd.date_range("1/1/2012", periods=5, freq="M")
In [412]: ts = pd.Series(np.random.randn(len(rng)), index=rng)
In [413]: ts
Out[413]:
2012-01-31 1.931253
2012-02-29 -0.184594
2012-03-31 0.249656
2012-04-30 -0.978151
2012-05-31 -0.873389
Freq: M, dtype: float64
In [414]: ps = ts.to_period()
In [415]: ps
Out[415]:
2012-01 1.931253
2012-02 -0.184594
2012-03 0.249656
2012-04 -0.978151
2012-05 -0.873389
Freq: M, dtype: float64
In [416]: ps.to_timestamp()
Out[416]:
2012-01-01 1.931253
2012-02-01 -0.184594
2012-03-01 0.249656
2012-04-01 -0.978151
2012-05-01 -0.873389
Freq: MS, dtype: float64
Помните, что «s» и «e» могут использоваться для возвращения временных меток в начале или конце периода:
In [417]: ps.to_timestamp("D", how="s")
Out[417]:
2012-01-01 1.931253
2012-02-01 -0.184594
2012-03-01 0.249656
2012-04-01 -0.978151
2012-05-01 -0.873389
Freq: MS, dtype: float64
Преобразование между периодом и отметкой времени позволяет использовать некоторые удобные арифметические функции. В следующем примере мы преобразуем квартальную частоту с окончанием года в ноябре в 9:00 последнего дня месяца, следующего за концом квартала:
In [418]: prng = pd.period_range("1990Q1", "2000Q4", freq="Q-NOV")
In [419]: ts = pd.Series(np.random.randn(len(prng)), prng)
In [420]: ts.index = (prng.asfreq("M", "e") + 1).asfreq("H", "s") + 9
In [421]: ts.head()
Out[421]:
1990-03-01 09:00 -0.109291
1990-06-01 09:00 -0.637235
1990-09-01 09:00 -1.735925
1990-12-01 09:00 2.096946
1991-03-01 09:00 -1.039926
Freq: H, dtype: float64
Представление диапазонов за пределами границ
Если у вас есть данные, которые находятся за пределами Timestamp границ, см. Ограничения по меткам времени, тогда вы можете использовать PeriodIndex и/или Series от Periods для выполнения вычислений.
In [422]: span = pd.period_range("1215-01-01", "1381-01-01", freq="D")
In [423]: span
Out[423]:
PeriodIndex(['1215-01-01', '1215-01-02', '1215-01-03', '1215-01-04',
'1215-01-05', '1215-01-06', '1215-01-07', '1215-01-08',
'1215-01-09', '1215-01-10',
...
'1380-12-23', '1380-12-24', '1380-12-25', '1380-12-26',
'1380-12-27', '1380-12-28', '1380-12-29', '1380-12-30',
'1380-12-31', '1381-01-01'],
dtype='period[D]', length=60632)
Для преобразования из представления YYYYMMDD, основанного на int64.
In [424]: s = pd.Series([20121231, 20141130, 99991231])
In [425]: s
Out[425]:
0 20121231
1 20141130
2 99991231
dtype: int64
In [426]: def conv(x):
.....: return pd.Period(year=x // 10000, month=x // 100 % 100, day=x % 100, freq="D")
.....:
In [427]: s.apply(conv)
Out[427]:
0 2012-12-31
1 2014-11-30
2 9999-12-31
dtype: period[D]
In [428]: s.apply(conv)[2]
Out[428]: Period('9999-12-31', 'D')
Их легко можно преобразовать в PeriodIndex.
In [429]: span = pd.PeriodIndex(s.apply(conv))
In [430]: span
Out[430]: PeriodIndex(['2012-12-31', '2014-11-30', '9999-12-31'], dtype='period[D]')
Обработка часовых поясов
pandas предоставляет широкую поддержку работы со временными метками в разных часовых поясах, используя библиотеки pytz и dateutil или объекты datetime.timezone из стандартной библиотеки.
Работа с часовыми поясами
По умолчанию, объекты pandas не учитывают часовой пояс:
In [431]: rng = pd.date_range("3/6/2012 00:00", periods=15, freq="D")
In [432]: rng.tz is None
Out[432]: True
Чтобы локализовать эти даты в часовом поясе (назначить конкретный часовой пояс к дате без указания часового пояса), вы можете использовать метод tz_localize или ключевой аргумент tz в date_range(), Timestamp или DatetimeIndex. Вы можете передать объекты часового пояса pytz или dateutil или строки из базы данных часовых поясов Olson. Строки часовых поясов Olson по умолчанию возвращают объекты часового пояса pytz. Чтобы вернуть объекты часового пояса dateutil, добавьте dateutil/ перед строкой.
В
pytzвы можете найти список распространённых (и менее распространённых) часовых поясов, используяfrom pytz import common_timezones, all_timezones.dateutilиспользует часовые пояса ОС, поэтому нет фиксированного списка. Для распространённых поясов имена совпадают с именамиpytz.
In [433]: import dateutil
# pytz
In [434]: rng_pytz = pd.date_range("3/6/2012 00:00", periods=3, freq="D", tz="Europe/London")
In [435]: rng_pytz.tz
Out[435]: <DstTzInfo 'Europe/London' LMT-1 day, 23:59:00 STD>
# dateutil
In [436]: rng_dateutil = pd.date_range("3/6/2012 00:00", periods=3, freq="D")
In [437]: rng_dateutil = rng_dateutil.tz_localize("dateutil/Europe/London")
In [438]: rng_dateutil.tz
Out[438]: tzfile('/usr/share/zoneinfo/Europe/London')
# dateutil - utc special case
In [439]: rng_utc = pd.date_range(
.....: "3/6/2012 00:00",
.....: periods=3,
.....: freq="D",
.....: tz=dateutil.tz.tzutc(),
.....: )
.....:
In [440]: rng_utc.tz
Out[440]: tzutc()
Новое в версии 0.25.0.
# datetime.timezone
In [441]: rng_utc = pd.date_range(
.....: "3/6/2012 00:00",
.....: periods=3,
.....: freq="D",
.....: tz=datetime.timezone.utc,
.....: )
.....:
In [442]: rng_utc.tz
Out[442]: datetime.timezone.utc
Обратите внимание, что часовой пояс UTC является особым случаем в dateutil и должен быть явно создан как экземпляр dateutil.tz.tzutc. Вы также можете явно создать другие объекты часовых поясов.
In [443]: import pytz
# pytz
In [444]: tz_pytz = pytz.timezone("Europe/London")
In [445]: rng_pytz = pd.date_range("3/6/2012 00:00", periods=3, freq="D")
In [446]: rng_pytz = rng_pytz.tz_localize(tz_pytz)
In [447]: rng_pytz.tz == tz_pytz
Out[447]: True
# dateutil
In [448]: tz_dateutil = dateutil.tz.gettz("Europe/London")
In [449]: rng_dateutil = pd.date_range("3/6/2012 00:00", periods=3, freq="D", tz=tz_dateutil)
In [450]: rng_dateutil.tz == tz_dateutil
Out[450]: True
Чтобы преобразовать объект pandas, учитывающий часовой пояс, из одного часового пояса в другой, вы можете использовать метод tz_convert.
In [451]: rng_pytz.tz_convert("US/Eastern")
Out[451]:
DatetimeIndex(['2012-03-05 19:00:00-05:00', '2012-03-06 19:00:00-05:00',
'2012-03-07 19:00:00-05:00'],
dtype='datetime64[ns, US/Eastern]', freq=None)
Примечание
При использовании часовых поясов pytz, DatetimeIndex будет создавать другой объект часового пояса, чем Timestamp для одного и того же входного значения часового пояса. DatetimeIndex может содержать коллекцию объектов Timestamp, которые могут иметь разные смещения UTC и не могут быть лаконично представлены одним экземпляром часового пояса pytz, в то время как один Timestamp представляет одну точку во времени со специфичным смещением UTC.
In [452]: dti = pd.date_range("2019-01-01", periods=3, freq="D", tz="US/Pacific")
In [453]: dti.tz
Out[453]: <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>
In [454]: ts = pd.Timestamp("2019-01-01", tz="US/Pacific")
In [455]: ts.tz
Out[455]: <DstTzInfo 'US/Pacific' PST-1 day, 16:00:00 STD>
Предупреждение
Будьте осторожны при преобразованиях между библиотеками. Для некоторых часовых поясов pytz и dateutil имеют разные определения часового пояса. Это больше проблема для необычных часовых поясов, чем для стандартных поясов, например, US/Eastern.
Предупреждение
Помните, что определение часового пояса в разных версиях библиотек часовых поясов может не считаться равным. Это может вызвать проблемы при работе со хранимыми данными, которые локализованы с использованием одной версии и обрабатываются с помощью другой версии. См. здесь о том, как справиться с такой ситуацией.
Предупреждение
Для часовых поясов pytz неверно передавать объект часового пояса напрямую в конструктор datetime.datetime (например, datetime.datetime(2011, 1, 1, tzinfo=pytz.timezone('US/Eastern'))). Вместо этого необходимо локализовать дату и время с использованием метода localize на объекте часового пояса pytz.
Предупреждение
Помните, что для дат в будущем корректное преобразование между часовыми поясами (и UTC) не гарантируется никакой библиотекой часовых поясов, потому что смещение часового пояса от UTC может быть изменено соответствующим правительством.
Предупреждение
Если вы используете даты после 2038-01-18, из-за текущих недостатков в базовых библиотеках, вызванных проблемой 2038 года, корректировки летнего времени (DST) для дат, учитывающих часовой пояс, не будут применяться. Когда и если базовые библиотеки будут исправлены, переходы летнего времени будут применяться.
Например, для двух дат, которые находятся в британском летнем времени (и, следовательно, обычно составляют GMT+1), оба следующих утверждения являются истинными:
In [456]: d_2037 = "2037-03-31T010101"
In [457]: d_2038 = "2038-03-31T010101"
In [458]: DST = "Europe/London"
In [459]: assert pd.Timestamp(d_2037, tz=DST) != pd.Timestamp(d_2037, tz="GMT")
In [460]: assert pd.Timestamp(d_2038, tz=DST) == pd.Timestamp(d_2038, tz="GMT")
Внутри, все временные метки хранятся в UTC. Значения из объекта DatetimeIndex или Timestamp, учитывающего часовой пояс, будут иметь свои поля (день, час, минута и т. д.) локализованные в часовом поясе. Однако временные метки с одинаковым значением UTC по-прежнему считаются равными, даже если они находятся в разных часовых поясах:
In [461]: rng_eastern = rng_utc.tz_convert("US/Eastern")
In [462]: rng_berlin = rng_utc.tz_convert("Europe/Berlin")
In [463]: rng_eastern[2]
Out[463]: Timestamp('2012-03-07 19:00:00-0500', tz='US/Eastern', freq='D')
In [464]: rng_berlin[2]
Out[464]: Timestamp('2012-03-08 01:00:00+0100', tz='Europe/Berlin', freq='D')
In [465]: rng_eastern[2] == rng_berlin[2]
Out[465]: True
Операции между Series в разных часовых поясах приведут к UTC Series, выравнивая данные по временным меткам UTC:
In [466]: ts_utc = pd.Series(range(3), pd.date_range("20130101", periods=3, tz="UTC"))
In [467]: eastern = ts_utc.tz_convert("US/Eastern")
In [468]: berlin = ts_utc.tz_convert("Europe/Berlin")
In [469]: result = eastern + berlin
In [470]: result
Out[470]:
2013-01-01 00:00:00+00:00 0
2013-01-02 00:00:00+00:00 2
2013-01-03 00:00:00+00:00 4
Freq: D, dtype: int64
In [471]: result.index
Out[471]:
DatetimeIndex(['2013-01-01 00:00:00+00:00', '2013-01-02 00:00:00+00:00',
'2013-01-03 00:00:00+00:00'],
dtype='datetime64[ns, UTC]', freq='D')
Чтобы удалить информацию о часовом поясе, используйте tz_localize(None) или tz_convert(None). tz_localize(None) удалит часовой пояс, получив локальное представление времени. tz_convert(None) удалит часовой пояс после преобразования в UTC время.
In [472]: didx = pd.date_range(start="2014-08-01 09:00", freq="H", periods=3, tz="US/Eastern")
In [473]: didx
Out[473]:
DatetimeIndex(['2014-08-01 09:00:00-04:00', '2014-08-01 10:00:00-04:00',
'2014-08-01 11:00:00-04:00'],
dtype='datetime64[ns, US/Eastern]', freq='H')
In [474]: didx.tz_localize(None)
Out[474]:
DatetimeIndex(['2014-08-01 09:00:00', '2014-08-01 10:00:00',
'2014-08-01 11:00:00'],
dtype='datetime64[ns]', freq=None)
In [475]: didx.tz_convert(None)
Out[475]:
DatetimeIndex(['2014-08-01 13:00:00', '2014-08-01 14:00:00',
'2014-08-01 15:00:00'],
dtype='datetime64[ns]', freq='H')
# tz_convert(None) is identical to tz_convert('UTC').tz_localize(None)
In [476]: didx.tz_convert("UTC").tz_localize(None)
Out[476]:
DatetimeIndex(['2014-08-01 13:00:00', '2014-08-01 14:00:00',
'2014-08-01 15:00:00'],
dtype='datetime64[ns]', freq=None)
Складывание
Новое в версии 1.1.0.
Для неоднозначных моментов времени pandas поддерживает явное указание ключевого аргумента fold. Из-за перехода на летнее время, одно время на часах может возникнуть дважды при переходе от летнего к зимнему времени; fold описывает, соответствует ли datetime-подобный первый (0) или второй раз (1), когда время на часах попадает в неоднозначное время. Fold поддерживается только для построения из наивных datetime.datetime (см. документацию по datetime для получения подробной информации) или из Timestamp или для построения из компонентов (см. ниже). Поддерживаются только часовые пояса dateutil (см. документацию dateutil для методов dateutil которые обрабатывают неоднозначные даты и время) так как часовые пояса pytz не поддерживают fold (см. документацию pytz для получения подробной информации о том, как pytz обрабатывает неоднозначные даты и время). Для локализации неоднозначной даты и времени с pytz, пожалуйста, используйте Timestamp.tz_localize(). В общем случае, мы рекомендуем полагаться на Timestamp.tz_localize() при локализации неоднозначных дат и времени, если вам нужен прямой контроль над тем, как они обрабатываются.
In [477]: pd.Timestamp(
.....: datetime.datetime(2019, 10, 27, 1, 30, 0, 0),
.....: tz="dateutil/Europe/London",
.....: fold=0,
.....: )
.....:
Out[477]: Timestamp('2019-10-27 01:30:00+0100', tz='dateutil//usr/share/zoneinfo/Europe/London')
In [478]: pd.Timestamp(
.....: year=2019,
.....: month=10,
.....: day=27,
.....: hour=1,
.....: minute=30,
.....: tz="dateutil/Europe/London",
.....: fold=1,
.....: )
.....:
Out[478]: Timestamp('2019-10-27 01:30:00+0000', tz='dateutil//usr/share/zoneinfo/Europe/London')
Неоднозначные моменты времени при локализации
tz_localize может не суметь определить смещение UTC временной метки, потому что летнее время (DST) в местном часовом поясе приводит к тому, что некоторые моменты времени происходят дважды в течение одного дня («часы отстают»). Доступны следующие варианты:
'raise': Вызываетpytz.AmbiguousTimeError(по умолчанию).'infer': Попытка определить правильное смещение, основываясь на монотонности временных меток.'NaT': Заменяет неоднозначные моменты времени наNaT.bool:Trueпредставляет время летнего времени (DST),Falseпредставляет время вне летнего времени (DST). Поддерживается массив-подобное значениеboolдля последовательности моментов времени.
In [479]: rng_hourly = pd.DatetimeIndex(
.....: ["11/06/2011 00:00", "11/06/2011 01:00", "11/06/2011 01:00", "11/06/2011 02:00"]
.....: )
.....:
Это завершится ошибкой, так как есть неоднозначные моменты времени ('11/06/2011 01:00').
In [2]: rng_hourly.tz_localize('US/Eastern')
AmbiguousTimeError: Cannot infer dst time from Timestamp('2011-11-06 01:00:00'), try using the 'ambiguous' argument
Обработайте эти неоднозначные моменты времени, указав следующее.
In [480]: rng_hourly.tz_localize("US/Eastern", ambiguous="infer")
Out[480]:
DatetimeIndex(['2011-11-06 00:00:00-04:00', '2011-11-06 01:00:00-04:00',
'2011-11-06 01:00:00-05:00', '2011-11-06 02:00:00-05:00'],
dtype='datetime64[ns, US/Eastern]', freq=None)
In [481]: rng_hourly.tz_localize("US/Eastern", ambiguous="NaT")
Out[481]:
DatetimeIndex(['2011-11-06 00:00:00-04:00', 'NaT', 'NaT',
'2011-11-06 02:00:00-05:00'],
dtype='datetime64[ns, US/Eastern]', freq=None)
In [482]: rng_hourly.tz_localize("US/Eastern", ambiguous=[True, True, False, False])
Out[482]:
DatetimeIndex(['2011-11-06 00:00:00-04:00', '2011-11-06 01:00:00-04:00',
'2011-11-06 01:00:00-05:00', '2011-11-06 02:00:00-05:00'],
dtype='datetime64[ns, US/Eastern]', freq=None)
Несуществующие времена при локализации
Переход на летнее время может также сместить местное время вперед на 1 час, создавая несуществующие местные времена («часы переводятся вперед»). Поведение локализации временного ряда с несуществующими временами можно контролировать с помощью аргумента nonexistent. Доступны следующие варианты:
'raise': Вызываетpytz.NonExistentTimeError(поведение по умолчанию)'NaT': Заменяет несуществующие времена наNaT'shift_forward': Перемещает несуществующие времена вперед к ближайшему реальному времени'shift_backward': Перемещает несуществующие времена назад к ближайшему реальному времениобъект timedelta: Перемещает несуществующие времена на продолжительность timedelta
In [483]: dti = pd.date_range(start="2015-03-29 02:30:00", periods=3, freq="H")
# 2:30 is a nonexistent time
Локализация несуществующих времен по умолчанию вызовет ошибку.
In [2]: dti.tz_localize('Europe/Warsaw')
NonExistentTimeError: 2015-03-29 02:30:00
Преобразуйте несуществующие времена в NaT или сместите эти времена.
In [484]: dti
Out[484]:
DatetimeIndex(['2015-03-29 02:30:00', '2015-03-29 03:30:00',
'2015-03-29 04:30:00'],
dtype='datetime64[ns]', freq='H')
In [485]: dti.tz_localize("Europe/Warsaw", nonexistent="shift_forward")
Out[485]:
DatetimeIndex(['2015-03-29 03:00:00+02:00', '2015-03-29 03:30:00+02:00',
'2015-03-29 04:30:00+02:00'],
dtype='datetime64[ns, Europe/Warsaw]', freq=None)
In [486]: dti.tz_localize("Europe/Warsaw", nonexistent="shift_backward")
Out[486]:
DatetimeIndex(['2015-03-29 01:59:59.999999999+01:00',
'2015-03-29 03:30:00+02:00',
'2015-03-29 04:30:00+02:00'],
dtype='datetime64[ns, Europe/Warsaw]', freq=None)
In [487]: dti.tz_localize("Europe/Warsaw", nonexistent=pd.Timedelta(1, unit="H"))
Out[487]:
DatetimeIndex(['2015-03-29 03:30:00+02:00', '2015-03-29 03:30:00+02:00',
'2015-03-29 04:30:00+02:00'],
dtype='datetime64[ns, Europe/Warsaw]', freq=None)
In [488]: dti.tz_localize("Europe/Warsaw", nonexistent="NaT")
Out[488]:
DatetimeIndex(['NaT', '2015-03-29 03:30:00+02:00',
'2015-03-29 04:30:00+02:00'],
dtype='datetime64[ns, Europe/Warsaw]', freq=None)
Операции с временными зонами Series
A Series со значениями naive временной зоны представлена с типом datetime64[ns].
In [489]: s_naive = pd.Series(pd.date_range("20130101", periods=3))
In [490]: s_naive
Out[490]:
0 2013-01-01
1 2013-01-02
2 2013-01-03
dtype: datetime64[ns]
A Series со значениями aware временной зоны представлена с типом datetime64[ns, tz], где tz — временная зона
In [491]: s_aware = pd.Series(pd.date_range("20130101", periods=3, tz="US/Eastern"))
In [492]: s_aware
Out[492]:
0 2013-01-01 00:00:00-05:00
1 2013-01-02 00:00:00-05:00
2 2013-01-03 00:00:00-05:00
dtype: datetime64[ns, US/Eastern]
Обе эти Series информация о временной зоне может быть обработана с помощью аксессора .dt, см. раздел аксессоров dt.
Например, чтобы локализовать и преобразовать метку naive во временную зону aware.
In [493]: s_naive.dt.tz_localize("UTC").dt.tz_convert("US/Eastern")
Out[493]:
0 2012-12-31 19:00:00-05:00
1 2013-01-01 19:00:00-05:00
2 2013-01-02 19:00:00-05:00
dtype: datetime64[ns, US/Eastern]
Информация о временной зоне также может быть обработана с помощью метода astype. Этот метод может преобразовывать между различными типами данных с временными зонами aware.
# convert to a new time zone
In [494]: s_aware.astype("datetime64[ns, CET]")
Out[494]:
0 2013-01-01 06:00:00+01:00
1 2013-01-02 06:00:00+01:00
2 2013-01-03 06:00:00+01:00
dtype: datetime64[ns, CET]
Примечание
Использование Series.to_numpy() на Series, возвращает массив NumPy данных. NumPy в настоящее время не поддерживает временные зоны (хотя он и выводит в локальной временной зоне!), поэтому для данных с временной зоной aware возвращается массив объектов Timestamp:
In [495]: s_naive.to_numpy()
Out[495]:
array(['2013-01-01T00:00:00.000000000', '2013-01-02T00:00:00.000000000',
'2013-01-03T00:00:00.000000000'], dtype='datetime64[ns]')
In [496]: s_aware.to_numpy()
Out[496]:
array([Timestamp('2013-01-01 00:00:00-0500', tz='US/Eastern'),
Timestamp('2013-01-02 00:00:00-0500', tz='US/Eastern'),
Timestamp('2013-01-03 00:00:00-0500', tz='US/Eastern')],
dtype=object)
Преобразованием в массив объектов Timestamp сохраняется информация о временной зоне. Например, при преобразовании обратно в Series:
In [497]: pd.Series(s_aware.to_numpy())
Out[497]:
0 2013-01-01 00:00:00-05:00
1 2013-01-02 00:00:00-05:00
2 2013-01-03 00:00:00-05:00
dtype: datetime64[ns, US/Eastern]
Однако, если вам нужен фактический массив NumPy datetime64[ns] (со значениями, преобразованными в UTC), а не массив объектов, вы можете указать аргумент dtype:
In [498]: s_aware.to_numpy(dtype="datetime64[ns]")
Out[498]:
array(['2013-01-01T05:00:00.000000000', '2013-01-02T05:00:00.000000000',
'2013-01-03T05:00:00.000000000'], dtype='datetime64[ns]')
© 2008–2022, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
Licensed under the 3-clause BSD License.
https://pandas.pydata.org/pandas-docs/version/1.5.0/user_guide/timeseries.html