Временные ряды/Функциональность дат
Библиотека pandas зарекомендовала себя как очень успешный инструмент для работы с временными рядами, особенно в области финансового анализа данных. Используя типы данных NumPy datetime64 и timedelta64, мы объединили большое количество функций из других библиотек Python, таких как scikits.timeseries, а также создали огромное количество новых функций для работы с временными рядами.
При работе с временными рядами мы часто стремимся к:
- генерации последовательностей дат и временных интервалов с фиксированной частотой
- приведению или преобразованию временных рядов к определённой частоте
- вычислению «относительных» дат на основе различных нестандартных временных интервалов (например, 5 рабочих дней до последнего рабочего дня года) или «сдвигу» дат вперёд или назад
pandas предоставляет относительно компактный и самодостаточный набор инструментов для выполнения вышеперечисленных задач.
Создайте диапазон дат:
# 72 hours starting with midnight Jan 1st, 2011
In [1]: rng = pd.date_range('1/1/2011', periods=72, freq='H')
In [2]: rng[:5]
Out[2]:
DatetimeIndex(['2011-01-01 00:00:00', '2011-01-01 01:00:00',
'2011-01-01 02:00:00', '2011-01-01 03:00:00',
'2011-01-01 04:00:00'],
dtype='datetime64[ns]', freq='H')
Индексирование объектов pandas с датами:
In [3]: ts = pd.Series(np.random.randn(len(rng)), index=rng) In [4]: ts.head() Out[4]: 2011-01-01 00:00:00 0.469112 2011-01-01 01:00:00 -0.282863 2011-01-01 02:00:00 -1.509059 2011-01-01 03:00:00 -1.135632 2011-01-01 04:00:00 1.212112 Freq: H, dtype: float64
Изменение частоты и заполнение пропусков:
# to 45 minute frequency and forward fill
In [5]: converted = ts.asfreq('45Min', method='pad')
In [6]: converted.head()
Out[6]:
2011-01-01 00:00:00 0.469112
2011-01-01 00:45:00 0.469112
2011-01-01 01:30:00 -0.282863
2011-01-01 02:15:00 -1.509059
2011-01-01 03:00:00 -1.135632
Freq: 45T, dtype: float64
Перевыборка:
# Daily means
In [7]: ts.resample('D').mean()
Out[7]:
2011-01-01 -0.319569
2011-01-02 -0.337703
2011-01-03 0.117258
Freq: D, dtype: float64
Обзор
В следующей таблице показаны типы классов, связанных со временем, которые может обрабатывать pandas, и как их создавать.
| Класс | Примечания | Как создать |
|---|---|---|
Timestamp | Представляет собой отдельную метку времени |
to_datetime, Timestamp
|
DatetimeIndex | Индекс Timestamp
|
to_datetime, date_range, DatetimeIndex
|
Period | Представляет собой отдельный временной интервал | Period |
PeriodIndex | Индекс Period
|
period_range, PeriodIndex
|
Метки времени против временных интервалов
Данные с метками времени — это наиболее базовый тип данных временных рядов, которые связывают значения с моментами времени. Для объектов pandas это означает использование моментов времени.
In [8]: pd.Timestamp(datetime(2012, 5, 1))
Out[8]: Timestamp('2012-05-01 00:00:00')
In [9]: pd.Timestamp('2012-05-01')
Out[9]: Timestamp('2012-05-01 00:00:00')
Однако во многих случаях более естественно связывать такие вещи, как переменные изменения, с временным интервалом вместо этого. Интервал, представленный Period, может быть указан явно или определён из формата строки даты и времени.
Например:
In [10]: pd.Period('2011-01')
Out[10]: Period('2011-01', 'M')
In [11]: pd.Period('2012-05', freq='D')
Out[11]: Period('2012-05-01', 'D')
Timestamp и Period могут быть индексом. Списка Timestamp и Period автоматически преобразуются в DatetimeIndex и PeriodIndex соответственно.
In [12]: dates = [pd.Timestamp('2012-05-01'), pd.Timestamp('2012-05-02'), pd.Timestamp('2012-05-03')]
In [13]: ts = pd.Series(np.random.randn(3), dates)
In [14]: type(ts.index)
Out[14]: pandas.tseries.index.DatetimeIndex
In [15]: ts.index
Out[15]: DatetimeIndex(['2012-05-01', '2012-05-02', '2012-05-03'], dtype='datetime64[ns]', freq=None)
In [16]: ts
Out[16]:
2012-05-01 -0.410001
2012-05-02 -0.078638
2012-05-03 0.545952
dtype: float64
In [17]: periods = [pd.Period('2012-01'), pd.Period('2012-02'), pd.Period('2012-03')]
In [18]: ts = pd.Series(np.random.randn(3), periods)
In [19]: type(ts.index)
Out[19]: pandas.tseries.period.PeriodIndex
In [20]: ts.index
Out[20]: PeriodIndex(['2012-01', '2012-02', '2012-03'], dtype='int64', freq='M')
In [21]: ts
Out[21]:
2012-01 -1.219217
2012-02 -1.226825
2012-03 0.769804
Freq: M, dtype: float64
pandas позволяет захватить оба представления и преобразовать их между собой. Под капотом pandas представляет метки времени с помощью экземпляров Timestamp , а последовательности меток времени с помощью экземпляров DatetimeIndex. Для регулярных временных интервалов pandas использует объекты Period для скалярных значений и PeriodIndex для последовательностей интервалов. Лучшая поддержка нерегулярных интервалов с произвольными начальными и конечными точками будет реализована в будущих выпусках.
Преобразование в метки времени
Чтобы преобразовать объект Series или список подобных объектов, содержащих объекты типа дата, например, строки, эпохи или их комбинацию, можно использовать функцию to_datetime. Если в качестве аргумента передана Series, то возвращается Series (с тем же индексом), в то время как список-подобный объект преобразуется в DatetimeIndex:
In [22]: pd.to_datetime(pd.Series(['Jul 31, 2009', '2010-01-10', None])) Out[22]: 0 2009-07-31 1 2010-01-10 2 NaT dtype: datetime64[ns] In [23]: pd.to_datetime(['2005/11/23', '2010.12.31']) Out[23]: DatetimeIndex(['2005-11-23', '2010-12-31'], dtype='datetime64[ns]', freq=None)
Если вы используете даты, в которых день идёт первым (т.е. европейский стиль), вы можете передать флаг dayfirst.
In [24]: pd.to_datetime(['04-01-2012 10:00'], dayfirst=True) Out[24]: DatetimeIndex(['2012-01-04 10:00:00'], dtype='datetime64[ns]', freq=None) In [25]: pd.to_datetime(['14-01-2012', '01-14-2012'], dayfirst=True) Out[25]: DatetimeIndex(['2012-01-14', '2012-01-14'], dtype='datetime64[ns]', freq=None)
Предупреждение
Как видно из приведённого выше примера, dayfirst не является жёстким, поэтому если дату нельзя разобрать, предполагая, что день идёт первым, она будет разобрана так, как будто dayfirst было равно False.
Примечание
Указание аргумента format может значительно ускорить преобразование, а в версиях, более поздних чем 0.13.0, явное указание строкового формата ' %Y%m%d' даёт ещё более быстрый путь.
Если вы передадите одну строку в to_datetime, она вернёт одну Timestamp. Кроме того, Timestamp может принимать строковый ввод. Обратите внимание, что Timestamp не принимает варианты парсинга строк, такие как dayfirst или format, используйте to_datetime, если они требуются.
In [26]: pd.to_datetime('2010/11/12')
Out[26]: Timestamp('2010-11-12 00:00:00')
In [27]: pd.Timestamp('2010/11/12')
Out[27]: Timestamp('2010-11-12 00:00:00')
Новое в версии 0.18.1.
Вы также можете передать DataFrame целочисленных или строковых столбцов для сборки в Series из Timestamps.
In [28]: df = pd.DataFrame({'year': [2015, 2016],
....: 'month': [2, 3],
....: 'day': [4, 5],
....: 'hour': [2, 3]})
....:
In [29]: pd.to_datetime(df)
Out[29]:
0 2015-02-04 02:00:00
1 2016-03-05 03:00:00
dtype: datetime64[ns]
Вы можете передать только те столбцы, которые вам нужны для сборки.
In [30]: pd.to_datetime(df[['year', 'month', 'day']]) Out[30]: 0 2015-02-04 1 2016-03-05 dtype: datetime64[ns]
pd.to_datetime ищет стандартные обозначения компонента даты и времени в именах столбцов, включая:
- обязательно:
year,month,day - необязательно:
hour,minute,second,millisecond,microsecond,nanosecond
Недействительные данные
Примечание
В версии 0.17.0 значение по умолчанию для to_datetime теперь errors='raise', а не errors='ignore'. Это означает, что некорректный парсинг вызовет ошибку, а не вернёт исходный ввод, как в предыдущих версиях.
Передайте errors='coerce' для преобразования недействительных данных в NaT (не время):
Вызвать ошибку при невозможности анализа, это значение по умолчанию
In [2]: pd.to_datetime(['2009/07/31', 'asd'], errors='raise') ValueError: Unknown string format
Вернуть исходный ввод при невозможности анализа
In [4]: pd.to_datetime(['2009/07/31', 'asd'], errors='ignore') Out[4]: array(['2009/07/31', 'asd'], dtype=object)
Вернуть NaT для ввода при невозможности анализа
In [6]: pd.to_datetime(['2009/07/31', 'asd'], errors='coerce') Out[6]: DatetimeIndex(['2009-07-31', 'NaT'], dtype='datetime64[ns]', freq=None)
Маркеры эпохи
Также можно преобразовать целые или вещественные числа эпохи. Единица измерения по умолчанию для них — наносекунды (поскольку именно так хранятся Timestamp). Однако часто эпохи хранятся в другой unit, которая может быть указана:
Типичные единицы измерения хранящихся эпох
In [31]: pd.to_datetime([1349720105, 1349806505, 1349892905,
....: 1349979305, 1350065705], unit='s')
....:
Out[31]:
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 [32]: pd.to_datetime([1349720105100, 1349720105200, 1349720105300,
....: 1349720105400, 1349720105500 ], unit='ms')
....:
Out[32]:
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)
Эти значения работают, но результаты могут быть неожиданными.
In [33]: pd.to_datetime([1]) Out[33]: DatetimeIndex(['1970-01-01 00:00:00.000000001'], dtype='datetime64[ns]', freq=None) In [34]: pd.to_datetime([1, 3.14], unit='s') Out[34]: DatetimeIndex(['1970-01-01 00:00:01', '1970-01-01 00:00:03'], dtype='datetime64[ns]', freq=None)
Примечание
Временные метки эпохи будут округляться до ближайшей наносекунды.
Генерация диапазонов меток времени
Для генерации индекса с метками времени можно использовать конструктор DatetimeIndex или Index и передать список объектов datetime:
In [35]: dates = [datetime(2012, 5, 1), datetime(2012, 5, 2), datetime(2012, 5, 3)] # Note the frequency information In [36]: index = pd.DatetimeIndex(dates) In [37]: index Out[37]: DatetimeIndex(['2012-05-01', '2012-05-02', '2012-05-03'], dtype='datetime64[ns]', freq=None) # Automatically converted to DatetimeIndex In [38]: index = pd.Index(dates) In [39]: index Out[39]: DatetimeIndex(['2012-05-01', '2012-05-02', '2012-05-03'], dtype='datetime64[ns]', freq=None)
Практически это становится очень неудобно, потому что нам часто нужен очень длинный индекс с большим количеством меток времени. Если нам нужны метки времени с регулярной частотой, мы можем использовать функции pandas date_range и bdate_range для создания индексов меток времени.
In [40]: index = pd.date_range('2000-1-1', periods=1000, freq='M')
In [41]: index
Out[41]:
DatetimeIndex(['2000-01-31', '2000-02-29', '2000-03-31', '2000-04-30',
'2000-05-31', '2000-06-30', '2000-07-31', '2000-08-31',
'2000-09-30', '2000-10-31',
...
'2082-07-31', '2082-08-31', '2082-09-30', '2082-10-31',
'2082-11-30', '2082-12-31', '2083-01-31', '2083-02-28',
'2083-03-31', '2083-04-30'],
dtype='datetime64[ns]', length=1000, freq='M')
In [42]: index = pd.bdate_range('2012-1-1', periods=250)
In [43]: index
Out[43]:
DatetimeIndex(['2012-01-02', '2012-01-03', '2012-01-04', '2012-01-05',
'2012-01-06', '2012-01-09', '2012-01-10', '2012-01-11',
'2012-01-12', '2012-01-13',
...
'2012-12-03', '2012-12-04', '2012-12-05', '2012-12-06',
'2012-12-07', '2012-12-10', '2012-12-11', '2012-12-12',
'2012-12-13', '2012-12-14'],
dtype='datetime64[ns]', length=250, freq='B')
Функции-удобства, такие как date_range и bdate_range используют различные псевдонимы частоты. Значение частоты по умолчанию для date_range — календарный день, а по умолчанию для bdate_range — рабочий день
In [44]: start = datetime(2011, 1, 1)
In [45]: end = datetime(2012, 1, 1)
In [46]: rng = pd.date_range(start, end)
In [47]: rng
Out[47]:
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 [48]: rng = pd.bdate_range(start, end)
In [49]: rng
Out[49]:
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 позволяют легко сгенерировать диапазон дат с использованием различных комбинаций параметров, таких как start, end, periods, и freq.
In [50]: pd.date_range(start, end, freq='BM')
Out[50]:
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 [51]: pd.date_range(start, end, freq='W')
Out[51]:
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 [52]: pd.bdate_range(end=end, periods=20)
Out[52]:
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 [53]: pd.bdate_range(start=start, periods=20)
Out[53]:
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')
Дата начала и окончания строго включительно. Поэтому он не будет генерировать даты, выходящие за эти даты, если они указаны.
Ограничения меток времени
Поскольку pandas представляет метки времени с разрешением в наносекунды, временной интервал, который можно представить с помощью 64-битного целого числа, ограничен примерно 584 годами:
In [54]: pd.Timestamp.min
Out[54]: Timestamp('1677-09-22 00:12:43.145225')
In [55]: pd.Timestamp.max
Out[55]: Timestamp('2262-04-11 23:47:16.854775807')
См. здесь, чтобы узнать, как представить данные, выходящие за эти пределы.
DatetimeIndex
Одно из основных применений DatetimeIndex — в качестве индекса для объектов pandas. Класс DatetimeIndex содержит множество оптимизаций, связанных с временными рядами:
- Большой диапазон дат для различных смещений предварительно вычисляется и кешируется под капотом, что делает генерацию последующих диапазонов дат очень быстрой (достаточно взять срез)
- Быстрое сдвижение с помощью метода
shiftиtshiftдля объектов pandas - Объединение перекрывающихся объектов DatetimeIndex с одной и той же частотой очень быстро (важно для быстрого выравнивания данных)
- Быстрый доступ к полям даты через свойства, такие как
year,month, и т.д. - Функции регулярности, такие как
snapи очень быстрая логикаasof
Объекты DatetimeIndex имеют все основные функции обычных объектов Index и множество расширенных методов, специфичных для временных рядов, для удобной обработки частоты.
См. также
Примечание
Хотя pandas не заставляет вас иметь отсортированный индекс дат, некоторые из этих методов могут иметь неожиданное или неправильное поведение, если даты не отсортированы. Поэтому будьте внимательны.
DatetimeIndex может использоваться как обычный индекс и предлагает все его интеллектуальные функции, такие как выбор, срезы и т.д.
In [56]: rng = pd.date_range(start, end, freq='BM')
In [57]: ts = pd.Series(np.random.randn(len(rng)), index=rng)
In [58]: ts.index
Out[58]:
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 [59]: ts[:5].index
Out[59]:
DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31', '2011-04-29',
'2011-05-31'],
dtype='datetime64[ns]', freq='BM')
In [60]: ts[::2].index
Out[60]:
DatetimeIndex(['2011-01-31', '2011-03-31', '2011-05-31', '2011-07-29',
'2011-09-30', '2011-11-30'],
dtype='datetime64[ns]', freq='2BM')
Частичная строковая индексация DatetimeIndex
Вы можете передать даты и строки, которые преобразуются в даты, в качестве параметров индексирования:
In [61]: ts['1/31/2011'] Out[61]: -1.2812473076599531 In [62]: ts[datetime(2011, 12, 25):] Out[62]: 2011-12-30 0.687738 Freq: BM, dtype: float64 In [63]: ts['10/31/2011':'12/31/2011'] Out[63]: 2011-10-31 0.149748 2011-11-30 -0.732339 2011-12-30 0.687738 Freq: BM, dtype: float64
Для удобства доступа к более длинным временным рядам вы также можете передавать год или год и месяц в виде строк:
In [64]: ts['2011'] Out[64]: 2011-01-31 -1.281247 2011-02-28 -0.727707 2011-03-31 -0.121306 2011-04-29 -0.097883 2011-05-31 0.695775 2011-06-30 0.341734 2011-07-29 0.959726 2011-08-31 -1.110336 2011-09-30 -0.619976 2011-10-31 0.149748 2011-11-30 -0.732339 2011-12-30 0.687738 Freq: BM, dtype: float64 In [65]: ts['2011-6'] Out[65]: 2011-06-30 0.341734 Freq: BM, dtype: float64
Этот тип среза будет работать и с DataFrame, имеющим DateTimeIndex в качестве индекса. Так как частичный строковый выбор является формой маркированного среза, конечные точки будут включены. Это будет включать совпадение времени по включённой дате. Вот пример:
In [66]: dft = pd.DataFrame(randn(100000,1),
....: columns=['A'],
....: index=pd.date_range('20130101',periods=100000,freq='T'))
....:
In [67]: dft
Out[67]:
A
2013-01-01 00:00:00 0.176444
2013-01-01 00:01:00 0.403310
2013-01-01 00:02:00 -0.154951
2013-01-01 00:03:00 0.301624
2013-01-01 00:04:00 -2.179861
2013-01-01 00:05:00 -1.369849
2013-01-01 00:06:00 -0.954208
... ...
2013-03-11 10:33:00 -0.293083
2013-03-11 10:34:00 -0.059881
2013-03-11 10:35:00 1.252450
2013-03-11 10:36:00 0.046611
2013-03-11 10:37:00 0.059478
2013-03-11 10:38:00 -0.286539
2013-03-11 10:39:00 0.841669
[100000 rows x 1 columns]
In [68]: dft['2013']
Out[68]:
A
2013-01-01 00:00:00 0.176444
2013-01-01 00:01:00 0.403310
2013-01-01 00:02:00 -0.154951
2013-01-01 00:03:00 0.301624
2013-01-01 00:04:00 -2.179861
2013-01-01 00:05:00 -1.369849
2013-01-01 00:06:00 -0.954208
... ...
2013-03-11 10:33:00 -0.293083
2013-03-11 10:34:00 -0.059881
2013-03-11 10:35:00 1.252450
2013-03-11 10:36:00 0.046611
2013-03-11 10:37:00 0.059478
2013-03-11 10:38:00 -0.286539
2013-03-11 10:39:00 0.841669
[100000 rows x 1 columns]
Это начинается с самого первого момента времени в месяце и включает последнюю дату и время для месяца
In [69]: dft['2013-1':'2013-2']
Out[69]:
A
2013-01-01 00:00:00 0.176444
2013-01-01 00:01:00 0.403310
2013-01-01 00:02:00 -0.154951
2013-01-01 00:03:00 0.301624
2013-01-01 00:04:00 -2.179861
2013-01-01 00:05:00 -1.369849
2013-01-01 00:06:00 -0.954208
... ...
2013-02-28 23:53:00 0.103114
2013-02-28 23:54:00 -1.303422
2013-02-28 23:55:00 0.451943
2013-02-28 23:56:00 0.220534
2013-02-28 23:57:00 -1.624220
2013-02-28 23:58:00 0.093915
2013-02-28 23:59:00 -1.087454
[84960 rows x 1 columns]
Это задаёт конечное время, включая все моменты времени в последний день
In [70]: dft['2013-1':'2013-2-28']
Out[70]:
A
2013-01-01 00:00:00 0.176444
2013-01-01 00:01:00 0.403310
2013-01-01 00:02:00 -0.154951
2013-01-01 00:03:00 0.301624
2013-01-01 00:04:00 -2.179861
2013-01-01 00:05:00 -1.369849
2013-01-01 00:06:00 -0.954208
... ...
2013-02-28 23:53:00 0.103114
2013-02-28 23:54:00 -1.303422
2013-02-28 23:55:00 0.451943
2013-02-28 23:56:00 0.220534
2013-02-28 23:57:00 -1.624220
2013-02-28 23:58:00 0.093915
2013-02-28 23:59:00 -1.087454
[84960 rows x 1 columns]
Это задаёт точное конечное время (и не то же самое, что и выше)
In [71]: dft['2013-1':'2013-2-28 00:00:00']
Out[71]:
A
2013-01-01 00:00:00 0.176444
2013-01-01 00:01:00 0.403310
2013-01-01 00:02:00 -0.154951
2013-01-01 00:03:00 0.301624
2013-01-01 00:04:00 -2.179861
2013-01-01 00:05:00 -1.369849
2013-01-01 00:06:00 -0.954208
... ...
2013-02-27 23:54:00 0.897051
2013-02-27 23:55:00 -0.309230
2013-02-27 23:56:00 1.944713
2013-02-27 23:57:00 0.369265
2013-02-27 23:58:00 0.053071
2013-02-27 23:59:00 -0.019734
2013-02-28 00:00:00 1.388189
[83521 rows x 1 columns]
Мы останавливаемся на включённой конечной точке, так как она является частью индекса
In [72]: dft['2013-1-15':'2013-1-15 12:30:00']
Out[72]:
A
2013-01-15 00:00:00 0.501288
2013-01-15 00:01:00 -0.605198
2013-01-15 00:02:00 0.215146
2013-01-15 00:03:00 0.924732
2013-01-15 00:04:00 -2.228519
2013-01-15 00:05:00 1.517331
2013-01-15 00:06:00 -1.188774
... ...
2013-01-15 12:24:00 1.358314
2013-01-15 12:25:00 -0.737727
2013-01-15 12:26:00 1.838323
2013-01-15 12:27:00 -0.774090
2013-01-15 12:28:00 0.622261
2013-01-15 12:29:00 -0.631649
2013-01-15 12:30:00 0.193284
[751 rows x 1 columns]
Предупреждение
Следующий выбор вызовет KeyError; в противном случае эта методология выбора была бы несовместима с другими методами выбора в pandas (поскольку это не срез, и он не решается в него)
dft['2013-1-15 12:30:00']
Чтобы выбрать одну строку, используйте .loc
In [73]: dft.loc['2013-1-15 12:30:00'] Out[73]: A 0.193284 Name: 2013-01-15 12:30:00, dtype: float64
Новое в версии 0.18.0.
Частичная строковая индексация DatetimeIndex также работает с DataFrame, имеющим MultiIndex. Например:
In [74]: 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 [75]: dft2
Out[75]:
A
2013-01-01 00:00:00 a -0.659574
b 1.494522
2013-01-01 12:00:00 a -0.778425
b -0.253355
2013-01-02 00:00:00 a -2.816159
b -1.210929
2013-01-02 12:00:00 a 0.144669
... ...
2013-01-04 00:00:00 b -1.624463
2013-01-04 12:00:00 a 0.056912
b 0.149867
2013-01-05 00:00:00 a -1.256173
b 2.324544
2013-01-05 12:00:00 a -1.067396
b -0.660996
[20 rows x 1 columns]
In [76]: dft2.loc['2013-01-05']
Out[76]:
A
2013-01-05 00:00:00 a -1.256173
b 2.324544
2013-01-05 12:00:00 a -1.067396
b -0.660996
In [77]: idx = pd.IndexSlice
In [78]: dft2 = dft2.swaplevel(0, 1).sort_index()
In [79]: dft2.loc[idx[:, '2013-01-05'], :]
Out[79]:
A
a 2013-01-05 00:00:00 -1.256173
2013-01-05 12:00:00 -1.067396
b 2013-01-05 00:00:00 2.324544
2013-01-05 12:00:00 -0.660996
Индексирование дат и времени
Индексирование DateTimeIndex с помощью частичной строки зависит от «точности» периода, то есть от того, насколько специфичен интервал по отношению к частоте индекса. В отличие от этого, индексирование с помощью объектов datetime является точным, потому что объекты имеют точное значение. Они также следуют семантике включения обоих концов.
Эти datetime объекты являются специфичными hours, minutes, и seconds, даже если они не были явно указаны (они являются 0).
In [80]: dft[datetime(2013, 1, 1):datetime(2013,2,28)]
Out[80]:
A
2013-01-01 00:00:00 0.176444
2013-01-01 00:01:00 0.403310
2013-01-01 00:02:00 -0.154951
2013-01-01 00:03:00 0.301624
2013-01-01 00:04:00 -2.179861
2013-01-01 00:05:00 -1.369849
2013-01-01 00:06:00 -0.954208
... ...
2013-02-27 23:54:00 0.897051
2013-02-27 23:55:00 -0.309230
2013-02-27 23:56:00 1.944713
2013-02-27 23:57:00 0.369265
2013-02-27 23:58:00 0.053071
2013-02-27 23:59:00 -0.019734
2013-02-28 00:00:00 1.388189
[83521 rows x 1 columns]
Без значений по умолчанию.
In [81]: dft[datetime(2013, 1, 1, 10, 12, 0):datetime(2013, 2, 28, 10, 12, 0)]
Out[81]:
A
2013-01-01 10:12:00 -0.246733
2013-01-01 10:13:00 -1.429225
2013-01-01 10:14:00 -1.265339
2013-01-01 10:15:00 0.710986
2013-01-01 10:16:00 -0.818200
2013-01-01 10:17:00 0.543542
2013-01-01 10:18:00 1.577713
... ...
2013-02-28 10:06:00 0.311249
2013-02-28 10:07:00 2.366080
2013-02-28 10:08:00 -0.490372
2013-02-28 10:09:00 0.373340
2013-02-28 10:10:00 0.638442
2013-02-28 10:11:00 1.330135
2013-02-28 10:12:00 -0.945450
[83521 rows x 1 columns]
Усечение и Fancy Индексирование
Предоставляется функция truncate удобства, эквивалентная срезу:
In [82]: ts.truncate(before='10/31/2011', after='12/31/2011') Out[82]: 2011-10-31 0.149748 2011-11-30 -0.732339 2011-12-30 0.687738 Freq: BM, dtype: float64
Даже сложное fancy индексирование, нарушающее регулярность частоты DatetimeIndex, приведет к результату DatetimeIndex (но частота теряется):
In [83]: ts[[0, 2, 6]].index Out[83]: DatetimeIndex(['2011-01-31', '2011-03-31', '2011-07-29'], dtype='datetime64[ns]', freq=None)
Компоненты времени/даты
Существует несколько свойств времени/даты, которые можно получить из Timestamp или из набора временных меток, таких как DateTimeIndex.
| Свойство | Описание |
|---|---|
| year | Год значения datetime |
| month | Месяц значения datetime |
| day | День значения datetime |
| hour | Час значения datetime |
| minute | Минуты значения datetime |
| second | Секунды значения datetime |
| microsecond | Микросекунды значения datetime |
| nanosecond | Наносекунды значения datetime |
| date | Возвращает datetime.date |
| time | Возвращает datetime.time |
| dayofyear | Порядковый день года |
| weekofyear | Порядковый номер недели года |
| week | Порядковый номер недели года |
| dayofweek | Номер дня недели (понедельник=0, воскресенье=6) |
| weekday | Номер дня недели (понедельник=0, воскресенье=6) |
| weekday_name | Название дня недели (например, пятница) |
| quarter | Квартал даты: Янв-Мар = 1, Апр-Июн = 2 и т.д. |
| days_in_month | Количество дней в месяце значения datetime |
| is_month_start | Логическое значение, указывающее, является ли это первым днем месяца (определяется частотой) |
| is_month_end | Логическое значение, указывающее, является ли это последним днем месяца (определяется частотой) |
| is_quarter_start | Логическое значение, указывающее, является ли это первым днем квартала (определяется частотой) |
| is_quarter_end | Логическое значение, указывающее, является ли это последним днем квартала (определяется частотой) |
| is_year_start | Логическое значение, указывающее, является ли это первым днем года (определяется частотой) |
| is_year_end | Логическое значение, указывающее, является ли это последним днем года (определяется частотой) |
Кроме того, если у вас есть Series со значениями datetimelike, то вы можете получить доступ к этим свойствам через аксессор .dt, см. документацию.
Объекты DateOffset
В предыдущих примерах мы создавали объекты DatetimeIndex с различными частотами, передавая строки частоты, такие как ‘M’, ‘W’ и ‘BM’, в ключевое слово freq. Под капотом эти строки частоты переводятся в экземпляр DateOffset pandas, который представляет собой регулярный приращение частоты. Специфическая логика смещения, такая как «месяц», «рабочий день» или «один час», представлена в его различных подклассах.
| Имя класса | Описание |
|---|---|
| DateOffset | Общий класс смещения, по умолчанию равен 1 календарному дню |
| BDay | рабочий день (день недели) |
| CDay | специальный рабочий день (экспериментальный) |
| Week | одна неделя, необязательно привязанная к дню недели |
| WeekOfMonth | x-й день y-й недели каждого месяца |
| LastWeekOfMonth | x-й день последней недели каждого месяца |
| MonthEnd | конец календарного месяца |
| MonthBegin | начало календарного месяца |
| BMonthEnd | конец рабочего месяца |
| BMonthBegin | начало рабочего месяца |
| CBMonthEnd | конец специального рабочего месяца |
| CBMonthBegin | начало специального рабочего месяца |
| QuarterEnd | конец календарного квартала |
| QuarterBegin | начало календарного квартала |
| BQuarterEnd | конец рабочего квартала |
| BQuarterBegin | начало рабочего квартала |
| FY5253Quarter | розничный (т.е. 52-53 недель) квартал |
| YearEnd | конец календарного года |
| YearBegin | начало календарного года |
| BYearEnd | конец рабочего года |
| BYearBegin | начало рабочего года |
| FY5253 | розничный (т.е. 52-53 недель) год |
| BusinessHour | рабочий час |
| CustomBusinessHour | специальный рабочий час |
| Hour | один час |
| Minute | одна минута |
| Second | одна секунда |
| Milli | одна миллисекунда |
| Micro | одна микросекунда |
| Nano | одна наносекунда |
Основная функция DateOffset принимает те же аргументы, что и dateutil.relativedelta, которая работает так:
In [84]: d = datetime(2008, 8, 18, 9, 0) In [85]: d + relativedelta(months=4, days=5) Out[85]: datetime.datetime(2008, 12, 23, 9, 0)
Мы могли бы сделать то же самое с DateOffset:
In [86]: from pandas.tseries.offsets import *
In [87]: d + DateOffset(months=4, days=5)
Out[87]: Timestamp('2008-12-23 09:00:00')
Ключевые особенности объекта DateOffset:
- его можно добавлять/вычитать к/из объекта datetime, чтобы получить сдвинутую дату
- его можно умножать на целое число (положительное или отрицательное), чтобы приращение применялось несколько раз
- он имеет методы
rollforwardиrollbackдля перемещения даты вперед или назад к следующей или предыдущей «дате смещения»
Подклассы DateOffset определяют функцию apply, которая диктует логику пользовательского приращения даты, такую как добавление рабочих дней:
class BDay(DateOffset):
"""DateOffset increments between business days"""
def apply(self, other):
...
In [88]: d - 5 * BDay()
Out[88]: Timestamp('2008-08-11 09:00:00')
In [89]: d + BMonthEnd()
Out[89]: Timestamp('2008-08-29 09:00:00')
Методы rollforward и rollback делают ровно то, чего вы ожидаете:
In [90]: d
Out[90]: datetime.datetime(2008, 8, 18, 9, 0)
In [91]: offset = BMonthEnd()
In [92]: offset.rollforward(d)
Out[92]: Timestamp('2008-08-29 09:00:00')
In [93]: offset.rollback(d)
Out[93]: Timestamp('2008-07-31 09:00:00')
Безусловно, стоит изучить модуль pandas.tseries.offsets и различные строки документации для классов.
Эти операции (apply, rollforward и rollback) сохраняют информацию о времени (часах, минутах и т. д.) по умолчанию. Чтобы сбросить время, используйте ключевое слово normalize=True при создании экземпляра смещения. Если normalize=True, результат нормализуется после применения функции.
In [94]: day = Day()
In [95]: day.apply(pd.Timestamp('2014-01-01 09:00'))
Out[95]: Timestamp('2014-01-02 09:00:00')
In [96]: day = Day(normalize=True)
In [97]: day.apply(pd.Timestamp('2014-01-01 09:00'))
Out[97]: Timestamp('2014-01-02 00:00:00')
In [98]: hour = Hour()
In [99]: hour.apply(pd.Timestamp('2014-01-01 22:00'))
Out[99]: Timestamp('2014-01-01 23:00:00')
In [100]: hour = Hour(normalize=True)
In [101]: hour.apply(pd.Timestamp('2014-01-01 22:00'))
Out[101]: Timestamp('2014-01-01 00:00:00')
In [102]: hour.apply(pd.Timestamp('2014-01-01 23:00'))
Out[102]: Timestamp('2014-01-02 00:00:00')
Параметрические смещения
Некоторые смещения могут быть «параметризованы» при создании, что приводит к различным результатам. Например, смещение Week для генерации еженедельных данных принимает параметр weekday, что приводит к тому, что генерируемые даты всегда лежат в определенный день недели:
In [103]: d
Out[103]: datetime.datetime(2008, 8, 18, 9, 0)
In [104]: d + Week()
Out[104]: Timestamp('2008-08-25 09:00:00')
In [105]: d + Week(weekday=4)
Out[105]: Timestamp('2008-08-22 09:00:00')
In [106]: (d + Week(weekday=4)).weekday()
Out[106]: 4
In [107]: d - Week()
Out[107]: Timestamp('2008-08-11 09:00:00')
Вариант normalize будет эффективным для сложения и вычитания.
In [108]: d + Week(normalize=True)
Out[108]: Timestamp('2008-08-25 00:00:00')
In [109]: d - Week(normalize=True)
Out[109]: Timestamp('2008-08-11 00:00:00')
Другой пример — параметризация YearEnd со специфическим конечным месяцем:
In [110]: d + YearEnd()
Out[110]: Timestamp('2008-12-31 09:00:00')
In [111]: d + YearEnd(month=6)
Out[111]: Timestamp('2009-06-30 09:00:00')
Использование смещений с Series / DatetimeIndex
Смещения могут использоваться как с Series, так и с DatetimeIndex, чтобы применить смещение к каждому элементу.
In [112]: rng = pd.date_range('2012-01-01', '2012-01-03')
In [113]: s = pd.Series(rng)
In [114]: rng
Out[114]: DatetimeIndex(['2012-01-01', '2012-01-02', '2012-01-03'], dtype='datetime64[ns]', freq='D')
In [115]: rng + DateOffset(months=2)
Out[115]: DatetimeIndex(['2012-03-01', '2012-03-02', '2012-03-03'], dtype='datetime64[ns]', freq='D')
In [116]: s + DateOffset(months=2)
Out[116]:
0 2012-03-01
1 2012-03-02
2 2012-03-03
dtype: datetime64[ns]
In [117]: s - DateOffset(months=2)
Out[117]:
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 [118]: s - Day(2)
Out[118]:
0 2011-12-30
1 2011-12-31
2 2012-01-01
dtype: datetime64[ns]
In [119]: td = s - pd.Series(pd.date_range('2011-12-29', '2011-12-31'))
In [120]: td
Out[120]:
0 3 days
1 3 days
2 3 days
dtype: timedelta64[ns]
In [121]: td + Minute(15)
Out[121]:
0 3 days 00:15:00
1 3 days 00:15:00
2 3 days 00:15:00
dtype: timedelta64[ns]
Обратите внимание, что некоторые смещения (например, BQuarterEnd) не имеют векторизованной реализации. Их можно использовать, но они могут работать значительно медленнее и генерировать PerformanceWarning
In [122]: rng + BQuarterEnd() Out[122]: DatetimeIndex(['2012-03-30', '2012-03-30', '2012-03-30'], dtype='datetime64[ns]', freq=None)
Специальные рабочие дни (экспериментально)
Класс CDay или CustomBusinessDay предоставляет параметризованный класс BusinessDay, который может использоваться для создания настраиваемых календарей рабочих дней, учитывающих местные выходные дни и праздники.
В качестве интересного примера рассмотрим Египет, где выходными днями являются пятница и суббота.
In [123]: from pandas.tseries.offsets import CustomBusinessDay
In [124]: weekmask_egypt = 'Sun Mon Tue Wed Thu'
# They also observe International Workers' Day so let's
# add that for a couple of years
In [125]: holidays = ['2012-05-01', datetime(2013, 5, 1), np.datetime64('2014-05-01')]
In [126]: bday_egypt = CustomBusinessDay(holidays=holidays, weekmask=weekmask_egypt)
In [127]: dt = datetime(2013, 4, 30)
In [128]: dt + 2 * bday_egypt
Out[128]: Timestamp('2013-05-05 00:00:00')
Давайте отобразим на имена дней недели
In [129]: dts = pd.date_range(dt, periods=5, freq=bday_egypt)
In [130]: pd.Series(dts.weekday, dts).map(pd.Series('Mon Tue Wed Thu Fri Sat Sun'.split()))
Out[130]:
2013-04-30 Tue
2013-05-02 Thu
2013-05-05 Sun
2013-05-06 Mon
2013-05-07 Tue
Freq: C, dtype: object
С версии 0.14 календари праздников могут использоваться для предоставления списка праздников. Подробнее см. раздел календарь праздников.
In [131]: from pandas.tseries.holiday import USFederalHolidayCalendar
In [132]: bday_us = CustomBusinessDay(calendar=USFederalHolidayCalendar())
# Friday before MLK Day
In [133]: dt = datetime(2014, 1, 17)
# Tuesday after MLK Day (Monday is skipped because it's a holiday)
In [134]: dt + bday_us
Out[134]: Timestamp('2014-01-21 00:00:00')
Месячные смещения, учитывающие определенный календарь праздников, можно определить обычным способом.
In [135]: from pandas.tseries.offsets import CustomBusinessMonthBegin
In [136]: bmth_us = CustomBusinessMonthBegin(calendar=USFederalHolidayCalendar())
# Skip new years
In [137]: dt = datetime(2013, 12, 17)
In [138]: dt + bmth_us
Out[138]: Timestamp('2014-01-02 00:00:00')
# Define date index with custom offset
In [139]: pd.DatetimeIndex(start='20100101',end='20120101',freq=bmth_us)
Out[139]:
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 [140]: bh = BusinessHour()
In [141]: bh
Out[141]: <BusinessHour: BH=09:00-17:00>
# 2014-08-01 is Friday
In [142]: pd.Timestamp('2014-08-01 10:00').weekday()
Out[142]: 4
In [143]: pd.Timestamp('2014-08-01 10:00') + bh
Out[143]: Timestamp('2014-08-01 11:00:00')
# Below example is the same as: pd.Timestamp('2014-08-01 09:00') + bh
In [144]: pd.Timestamp('2014-08-01 08:00') + bh
Out[144]: Timestamp('2014-08-01 10:00:00')
# If the results is on the end time, move to the next business day
In [145]: pd.Timestamp('2014-08-01 16:00') + bh
Out[145]: Timestamp('2014-08-04 09:00:00')
# Remainings are added to the next day
In [146]: pd.Timestamp('2014-08-01 16:30') + bh
Out[146]: Timestamp('2014-08-04 09:30:00')
# Adding 2 business hours
In [147]: pd.Timestamp('2014-08-01 10:00') + BusinessHour(2)
Out[147]: Timestamp('2014-08-01 12:00:00')
# Subtracting 3 business hours
In [148]: pd.Timestamp('2014-08-01 10:00') + BusinessHour(-3)
Out[148]: Timestamp('2014-07-31 15:00:00')
Также можно указать start и end время с помощью ключевых слов. Аргумент должен быть str, имеющим представление hour:minute или экземпляром datetime.time. Указание секунд, микросекунд и наносекунд как рабочего часа приводит к ValueError.
In [149]: bh = BusinessHour(start='11:00', end=time(20, 0))
In [150]: bh
Out[150]: <BusinessHour: BH=11:00-20:00>
In [151]: pd.Timestamp('2014-08-01 13:00') + bh
Out[151]: Timestamp('2014-08-01 14:00:00')
In [152]: pd.Timestamp('2014-08-01 09:00') + bh
Out[152]: Timestamp('2014-08-01 12:00:00')
In [153]: pd.Timestamp('2014-08-01 18:00') + bh
Out[153]: Timestamp('2014-08-01 19:00:00')
Передача start времени позже end представляет собой полночь рабочего часа. В этом случае рабочий час превышает полночь и переходит на следующий день. Действительные рабочие часы определяются тем, начались ли они с действительного BusinessDay.
In [154]: bh = BusinessHour(start='17:00', end='09:00')
In [155]: bh
Out[155]: <BusinessHour: BH=17:00-09:00>
In [156]: pd.Timestamp('2014-08-01 17:00') + bh
Out[156]: Timestamp('2014-08-01 18:00:00')
In [157]: pd.Timestamp('2014-08-01 23:00') + bh
Out[157]: Timestamp('2014-08-02 00:00:00')
# Although 2014-08-02 is Satuaday,
# it is valid because it starts from 08-01 (Friday).
In [158]: pd.Timestamp('2014-08-02 04:00') + bh
Out[158]: 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 [159]: pd.Timestamp('2014-08-04 04:00') + bh
Out[159]: 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 [160]: BusinessHour().rollback(pd.Timestamp('2014-08-02 15:00'))
Out[160]: Timestamp('2014-08-01 17:00:00')
In [161]: BusinessHour().rollforward(pd.Timestamp('2014-08-02 15:00'))
Out[161]: Timestamp('2014-08-04 09:00:00')
# It is the same as BusinessHour().apply(pd.Timestamp('2014-08-01 17:00')).
# And it is the same as BusinessHour().apply(pd.Timestamp('2014-08-04 09:00'))
In [162]: BusinessHour().apply(pd.Timestamp('2014-08-02 15:00'))
Out[162]: Timestamp('2014-08-04 10:00:00')
# BusinessDay results (for reference)
In [163]: BusinessHour().rollforward(pd.Timestamp('2014-08-02'))
Out[163]: Timestamp('2014-08-04 09:00:00')
# It is the same as BusinessDay().apply(pd.Timestamp('2014-08-01'))
# The result is the same as rollworward because BusinessDay never overlap.
In [164]: BusinessHour().apply(pd.Timestamp('2014-08-02'))
Out[164]: Timestamp('2014-08-04 10:00:00')
BusinessHour считает субботу и воскресенье праздничными днями. Для использования произвольных праздничных дней, вы можете использовать CustomBusinessHour смещение, см. Настраиваемое рабочее время:
Настраиваемое рабочее время
Новое в версии 0.18.1.
CustomBusinessHour — это смесь BusinessHour и CustomBusinessDay, которая позволяет вам указывать произвольные праздничные дни. CustomBusinessHour работает так же, как и BusinessHour, за исключением того, что он пропускает указанные пользовательские праздничные дни.
In [165]: from pandas.tseries.holiday import USFederalHolidayCalendar
In [166]: bhour_us = CustomBusinessHour(calendar=USFederalHolidayCalendar())
# Friday before MLK Day
In [167]: dt = datetime(2014, 1, 17, 15)
In [168]: dt + bhour_us
Out[168]: Timestamp('2014-01-17 16:00:00')
# Tuesday after MLK Day (Monday is skipped because it's a holiday)
In [169]: dt + bhour_us * 2
Out[169]: Timestamp('2014-01-21 09:00:00')
Вы можете использовать ключевые аргументы, поддерживаемые как BusinessHour, так и CustomBusinessDay.
In [170]: bhour_mon = CustomBusinessHour(start='10:00', weekmask='Tue Wed Thu Fri')
# Monday is skipped because it's a holiday, business hour starts from 10:00
In [171]: dt + bhour_mon * 2
Out[171]: Timestamp('2014-01-21 10:00:00')
Псевдонимы смещений
Несколько строковых псевдонимов заданы для полезных общих частот временных рядов. Мы будем называть эти псевдонимы псевдонимами смещений (ранее назывались правилами времени до версии 0.8.0).
| Псевдоним | Описание |
|---|---|
| B | частота рабочих дней |
| C | частота пользовательских рабочих дней (экспериментальная) |
| D | частота календарных дней |
| W | недельная частота |
| M | частота конца месяца |
| BM | частота конца рабочего месяца |
| CBM | частота пользовательского конца рабочего месяца |
| MS | частота начала месяца |
| BMS | частота начала рабочего месяца |
| CBMS | частота пользовательского начала рабочего месяца |
| Q | частота конца квартала |
| BQ | частота конца квартала рабочих дней |
| QS | частота начала квартала |
| BQS | частота начала квартала рабочих дней |
| A | частота конца года |
| BA | частота конца года рабочих дней |
| AS | частота начала года |
| BAS | частота начала года рабочих дней |
| BH | частота рабочих часов |
| H | частота часов |
| T, min | частота минут |
| S | частота секунд |
| L, ms | миллисекунды |
| U, us | микросекунды |
| N | наносекунды |
Сочетание псевдонимов
Как мы видели ранее, псевдоним и экземпляр смещения взаимозаменяемы в большинстве функций:
In [172]: pd.date_range(start, periods=5, freq='B')
Out[172]:
DatetimeIndex(['2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06',
'2011-01-07'],
dtype='datetime64[ns]', freq='B')
In [173]: pd.date_range(start, periods=5, freq=BDay())
Out[173]:
DatetimeIndex(['2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06',
'2011-01-07'],
dtype='datetime64[ns]', freq='B')
Вы можете объединить смещения дня и внутридневные смещения:
In [174]: pd.date_range(start, periods=10, freq='2h20min')
Out[174]:
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 [175]: pd.date_range(start, periods=10, freq='1D10U')
Out[175]:
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 | квартальная частота, конец года в январе |
Эти значения могут быть использованы в качестве аргументов для date_range, bdate_range, конструкторов для DatetimeIndex, а также различных других функций, связанных с временными рядами в pandas.
Семантика смещений с привязкой
Для тех смещений, которые привязаны к началу или концу определенной частоты (MonthEnd, MonthBegin, WeekEnd, и т. д.) применяются следующие правила для перехода вперед и назад.
Когда n не равно 0, если заданная дата не находится на точке привязки, она устанавливается на следующую (предыдущую) точку привязки и перемещается на |n|-1 дополнительных шагов вперед или назад.
In [176]: pd.Timestamp('2014-01-02') + MonthBegin(n=1)
Out[176]: Timestamp('2014-02-01 00:00:00')
In [177]: pd.Timestamp('2014-01-02') + MonthEnd(n=1)
Out[177]: Timestamp('2014-01-31 00:00:00')
In [178]: pd.Timestamp('2014-01-02') - MonthBegin(n=1)
Out[178]: Timestamp('2014-01-01 00:00:00')
In [179]: pd.Timestamp('2014-01-02') - MonthEnd(n=1)
Out[179]: Timestamp('2013-12-31 00:00:00')
In [180]: pd.Timestamp('2014-01-02') + MonthBegin(n=4)
Out[180]: Timestamp('2014-05-01 00:00:00')
In [181]: pd.Timestamp('2014-01-02') - MonthBegin(n=4)
Out[181]: Timestamp('2013-10-01 00:00:00')
Если заданная дата находится на точке привязки, она перемещается на |n| точек вперед или назад.
In [182]: pd.Timestamp('2014-01-01') + MonthBegin(n=1)
Out[182]: Timestamp('2014-02-01 00:00:00')
In [183]: pd.Timestamp('2014-01-31') + MonthEnd(n=1)
Out[183]: Timestamp('2014-02-28 00:00:00')
In [184]: pd.Timestamp('2014-01-01') - MonthBegin(n=1)
Out[184]: Timestamp('2013-12-01 00:00:00')
In [185]: pd.Timestamp('2014-01-31') - MonthEnd(n=1)
Out[185]: Timestamp('2013-12-31 00:00:00')
In [186]: pd.Timestamp('2014-01-01') + MonthBegin(n=4)
Out[186]: Timestamp('2014-05-01 00:00:00')
In [187]: pd.Timestamp('2014-01-31') - MonthBegin(n=4)
Out[187]: Timestamp('2013-10-01 00:00:00')
В случае, когда n=0, дата не перемещается, если она находится на точке привязки, в противном случае она переносится на следующую точку привязки.
In [188]: pd.Timestamp('2014-01-02') + MonthBegin(n=0)
Out[188]: Timestamp('2014-02-01 00:00:00')
In [189]: pd.Timestamp('2014-01-02') + MonthEnd(n=0)
Out[189]: Timestamp('2014-01-31 00:00:00')
In [190]: pd.Timestamp('2014-01-01') + MonthBegin(n=0)
Out[190]: Timestamp('2014-01-01 00:00:00')
In [191]: pd.Timestamp('2014-01-31') + MonthEnd(n=0)
Out[191]: Timestamp('2014-01-31 00:00:00')
Устаревшие псевдонимы
Обратите внимание, что до версии 0.8.0 правила времени имели немного другой вид. Они устарели в версии 0.17.0 и будут удалены в будущих версиях.
| Устаревшее правило времени | Псевдоним смещения |
|---|---|
| WEEKDAY | B |
Как видите, устаревшие квартальные и годовые частоты являются кварталами рабочих дней и окончанием года рабочих дней. Обратите также внимание на устаревшее правило времени для миллисекунд ms по сравнению с новым псевдонимом смещения для начала месяца MS. Это означает, что разбор псевдонимов смещений чувствителен к регистру.
Праздники / Календари праздников
Праздники и календари предоставляют простой способ определения правил праздников, которые можно использовать с CustomBusinessDay или в других анализах, требующих предварительно определенного набора праздничных дней. Класс AbstractHolidayCalendar предоставляет все необходимые методы для возвращения списка праздничных дней, и только rules необходимо определить в конкретном классе календаря праздников. Кроме того, атрибуты класса start_date и end_date определяют диапазон дат, для которых генерируются праздничные дни. Эти атрибуты должны быть переопределены в классе AbstractHolidayCalendar для применения диапазона ко всем подклассам календаря. USFederalHolidayCalendar — единственный существующий календарь и служит в первую очередь примером для разработки других календарей.
Для праздников, которые происходят в определенные даты (например, День памяти США или 4 июля), правило соблюдения определяет, когда этот праздник отмечается, если он выпадает на выходной или какой-либо другой нерабочий день. Определенные правила соблюдения:
| Правило | Описание |
|---|---|
| nearest_workday | перемещение субботы на пятницу, а воскресенья на понедельник |
Пример того, как определяются праздники и календари праздников:
In [192]: from pandas.tseries.holiday import Holiday, USMemorialDay,\
.....: AbstractHolidayCalendar, nearest_workday, MO
.....:
In [193]: class ExampleCalendar(AbstractHolidayCalendar):
.....: rules = [
.....: USMemorialDay,
.....: Holiday('July 4th', month=7, day=4, observance=nearest_workday),
.....: Holiday('Columbus Day', month=10, day=1,
.....: offset=DateOffset(weekday=MO(2))), #same as 2*Week(weekday=2)
.....: ]
.....:
In [194]: cal = ExampleCalendar()
In [195]: cal.holidays(datetime(2012, 1, 1), datetime(2012, 12, 31))
Out[195]: DatetimeIndex(['2012-05-28', '2012-07-04', '2012-10-08'], dtype='datetime64[ns]', freq=None)
Используя этот календарь, создание индекса или выполнение арифметических операций со смещениями пропускают выходные и праздничные дни (т. е. День памяти/4 июля). Например, ниже определено пользовательское смещение рабочего дня, используя ExampleCalendar. Как и любое другое смещение, оно может быть использовано для создания DatetimeIndex или добавлено к объектам datetime или Timestamp.
In [196]: from pandas.tseries.offsets import CDay
In [197]: pd.DatetimeIndex(start='7/1/2012', end='7/10/2012',
.....: freq=CDay(calendar=cal)).to_pydatetime()
.....:
Out[197]:
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 [198]: offset = CustomBusinessDay(calendar=cal)
In [199]: datetime(2012, 5, 25) + offset
Out[199]: Timestamp('2012-05-29 00:00:00')
In [200]: datetime(2012, 7, 3) + offset
Out[200]: Timestamp('2012-07-05 00:00:00')
In [201]: datetime(2012, 7, 3) + 2 * offset
Out[201]: Timestamp('2012-07-06 00:00:00')
In [202]: datetime(2012, 7, 6) + offset
Out[202]: Timestamp('2012-07-09 00:00:00')
Диапазоны определяются атрибутами класса start_date и end_date класса AbstractHolidayCalendar. Значения по умолчанию приведены ниже.
In [203]: AbstractHolidayCalendar.start_date
Out[203]: Timestamp('1970-01-01 00:00:00')
In [204]: AbstractHolidayCalendar.end_date
Out[204]: Timestamp('2030-12-31 00:00:00')
Эти даты могут быть переопределены путем задания атрибутов в виде datetime/Timestamp/строки.
In [205]: AbstractHolidayCalendar.start_date = datetime(2012, 1, 1) In [206]: AbstractHolidayCalendar.end_date = datetime(2012, 12, 31) In [207]: cal.holidays() Out[207]: DatetimeIndex(['2012-05-28', '2012-07-04', '2012-10-08'], dtype='datetime64[ns]', freq=None)
Каждый класс календаря доступен по имени с помощью функции get_calendar, которая возвращает экземпляр класса праздника. Любой импортированный класс календаря автоматически станет доступным с помощью этой функции. Также HolidayCalendarFactory предоставляет удобный интерфейс для создания календарей, которые являются комбинацией календарей или календарей с дополнительными правилами.
In [208]: from pandas.tseries.holiday import get_calendar, HolidayCalendarFactory,\
.....: USLaborDay
.....:
In [209]: cal = get_calendar('ExampleCalendar')
In [210]: cal.rules
Out[210]:
[Holiday: MemorialDay (month=5, day=31, offset=<DateOffset: kwds={'weekday': MO(-1)}>),
Holiday: July 4th (month=7, day=4, observance=<function nearest_workday at 0x12f657a28>),
Holiday: Columbus Day (month=10, day=1, offset=<DateOffset: kwds={'weekday': MO(+2)}>)]
In [211]: new_cal = HolidayCalendarFactory('NewExampleCalendar', cal, USLaborDay)
In [212]: new_cal.rules
Out[212]:
[Holiday: Labor Day (month=9, day=1, offset=<DateOffset: kwds={'weekday': MO(+1)}>),
Holiday: Columbus Day (month=10, day=1, offset=<DateOffset: kwds={'weekday': MO(+2)}>),
Holiday: July 4th (month=7, day=4, observance=<function nearest_workday at 0x12f657a28>),
Holiday: MemorialDay (month=5, day=31, offset=<DateOffset: kwds={'weekday': MO(-1)}>)]
Методы экземпляров, связанные с временными рядами
Перемещение / отставание
Иногда требуется перемещение или отставание значений во временном ряду назад и вперед во времени. Метод для этого — shift, который доступен для всех объектов pandas.
In [213]: ts = ts[:5] In [214]: ts.shift(1) Out[214]: 2011-01-31 NaN 2011-02-28 -1.281247 2011-03-31 -0.727707 2011-04-29 -0.121306 2011-05-31 -0.097883 Freq: BM, dtype: float64
Метод shift принимает аргумент freq, который может принимать класс DateOffset или другой подобный объект timedelta или также псевдоним смещения смещения:
In [215]: ts.shift(5, freq=datetools.bday) Out[215]: 2011-02-07 -1.281247 2011-03-07 -0.727707 2011-04-07 -0.121306 2011-05-06 -0.097883 2011-06-07 0.695775 dtype: float64 In [216]: ts.shift(5, freq='BM') Out[216]: 2011-06-30 -1.281247 2011-07-29 -0.727707 2011-08-31 -0.121306 2011-09-30 -0.097883 2011-10-31 0.695775 Freq: BM, dtype: float64
Вместо изменения выравнивания данных и индекса, объекты DataFrame и Series также имеют удобный метод tshift, который изменяет все даты в индексе на указанное количество смещений:
In [217]: ts.tshift(5, freq='D') Out[217]: 2011-02-05 -1.281247 2011-03-05 -0.727707 2011-04-05 -0.121306 2011-05-04 -0.097883 2011-06-05 0.695775 dtype: float64
Обратите внимание, что с tshift, ведущая запись больше не NaN, так как данные не перестраиваются.
Преобразование частоты
Основная функция для изменения частот — функция asfreq. Для DatetimeIndex, это в основном просто тонкий, но удобный обертка вокруг reindex, который генерирует date_range и вызывает reindex.
In [218]: dr = pd.date_range('1/1/2010', periods=3, freq=3 * datetools.bday)
In [219]: ts = pd.Series(randn(3), index=dr)
In [220]: ts
Out[220]:
2010-01-01 0.532005
2010-01-06 0.544874
2010-01-11 -1.001788
Freq: 3B, dtype: float64
In [221]: ts.asfreq(BDay())
Out[221]:
2010-01-01 0.532005
2010-01-04 NaN
2010-01-05 NaN
2010-01-06 0.544874
2010-01-07 NaN
2010-01-08 NaN
2010-01-11 -1.001788
Freq: B, dtype: float64
asfreq предоставляет дополнительное удобство, позволяющее указать метод интерполяции для любых пробелов, которые могут появиться после преобразования частоты.
In [222]: ts.asfreq(BDay(), method='pad') Out[222]: 2010-01-01 0.532005 2010-01-04 0.532005 2010-01-05 0.532005 2010-01-06 0.544874 2010-01-07 0.544874 2010-01-08 0.544874 2010-01-11 -1.001788 Freq: B, dtype: float64
Заполнение вперёд / назад
Связанные с asfreq и reindex функция fillna, описанная в разделе пропущенных данных.
Преобразование в Python datetime
DatetimeIndex можно преобразовать в массив объектов Python native datetime.datetime, используя метод to_pydatetime.
Ресемплирование временных рядов
Предупреждение
Интерфейс к .resample изменился в версии 0.18.0, чтобы стать более похожим на groupby, а значит, более гибким. См. документацию whatsnew для сравнения с предыдущими версиями.
Pandas имеет простую, мощную и эффективную функциональность для выполнения операций ресемплирования при преобразовании частоты (например, преобразование данных в секунды в данные с 5-минутной частотой). Это очень распространено, но не только в финансовых приложениях.
resample — это основанная на времени группировка, за которой следует метод сокращения для каждой из её групп.
См. некоторые примеры кулинарной книги для некоторых продвинутых стратегий.
In [223]: rng = pd.date_range('1/1/2012', periods=100, freq='S')
In [224]: ts = pd.Series(np.random.randint(0, 500, len(rng)), index=rng)
In [225]: ts.resample('5Min').sum()
Out[225]:
2012-01-01 24390
Freq: 5T, dtype: int64
Функция resample очень гибкая и позволяет указать множество различных параметров для управления преобразованием частоты и операцией ресемплирования.
Параметр how может быть именем функции или функцией массива numpy, которая принимает массив и производит агрегированные значения:
In [226]: ts.resample('5Min').mean()
Out[226]:
2012-01-01 243.9
Freq: 5T, dtype: float64
In [227]: ts.resample('5Min').ohlc()
Out[227]:
open high low close
2012-01-01 161 495 1 245
In [228]: ts.resample('5Min').max()
Out[228]:
2012-01-01 495
Freq: 5T, dtype: int64
Любая функция, доступная через диспетчеризацию, может быть передана параметру how по имени, включая sum, mean, std, sem, max, min, median, first, last, ohlc.
Для уменьшения частоты closed может быть установлено на ‘left’ или ‘right’ для указания того, какой конец интервала закрыт:
In [229]: ts.resample('5Min', closed='right').mean()
Out[229]:
2011-12-31 23:55:00 161.000000
2012-01-01 00:00:00 244.737374
Freq: 5T, dtype: float64
In [230]: ts.resample('5Min', closed='left').mean()
Out[230]:
2012-01-01 243.9
Freq: 5T, dtype: float64
Параметры, такие как label и loffset, используются для управления результатом метками. label определяет, помечена ли результат началом или концом интервала. loffset производит корректировку времени по меткам вывода.
In [231]: ts.resample('5Min').mean() # by default label='right'
Out[231]:
2012-01-01 243.9
Freq: 5T, dtype: float64
In [232]: ts.resample('5Min', label='left').mean()
Out[232]:
2012-01-01 243.9
Freq: 5T, dtype: float64
In [233]: ts.resample('5Min', label='left', loffset='1s').mean()
Out[233]:
2012-01-01 00:00:01 243.9
dtype: float64
Параметр axis может быть установлен в 0 или 1 и позволяет пересэмплировать указанную ось для DataFrame.
kind может быть установлено на ‘timestamp’ или ‘period’ для преобразования результирующего индекса в представление временных меток и интервалов времени. По умолчанию resample сохраняет входное представление.
convention может быть установлено на ‘start’ или ‘end’ при ресемплировании данных периода (подробнее ниже). Это определяет, как периоды с низкой частотой преобразуются в периоды с высокой частотой.
Увеличение частоты
Для увеличения частоты вы можете указать способ увеличения и параметр limit для интерполяции пробелов, которые создаются:
# from secondly to every 250 milliseconds
In [234]: ts[:2].resample('250L').asfreq()
Out[234]:
2012-01-01 00:00:00.000 161.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 199.0
Freq: 250L, dtype: float64
In [235]: ts[:2].resample('250L').ffill()
Out[235]:
2012-01-01 00:00:00.000 161
2012-01-01 00:00:00.250 161
2012-01-01 00:00:00.500 161
2012-01-01 00:00:00.750 161
2012-01-01 00:00:01.000 199
Freq: 250L, dtype: int64
In [236]: ts[:2].resample('250L').ffill(limit=2)
Out[236]:
2012-01-01 00:00:00.000 161.0
2012-01-01 00:00:00.250 161.0
2012-01-01 00:00:00.500 161.0
2012-01-01 00:00:00.750 NaN
2012-01-01 00:00:01.000 199.0
Freq: 250L, dtype: float64
Ресемплирование разреженных временных рядов
Разреженные временные ряды — это временные ряды, в которых у вас намного меньше точек по сравнению с объемом времени, который вы хотите ресемплировать. Непосредственное увеличение частоты разреженного ряда может потенциально сгенерировать множество промежуточных значений. Когда вы не хотите использовать метод для заполнения этих значений, например, fill_method — это None, тогда промежуточные значения будут заполнены NaN.
Поскольку resample — это группировка по времени, следующий метод эффективен для ресемплирования только тех групп, которые не все NaN
In [237]: rng = pd.date_range('2014-1-1', periods=100, freq='D') + pd.Timedelta('1s')
In [238]: ts = pd.Series(range(100), index=rng)
Если мы хотим ресемплировать до полного диапазона ряда
In [239]: ts.resample('3T').sum()
Out[239]:
2014-01-01 00:00:00 0.0
2014-01-01 00:03:00 NaN
2014-01-01 00:06:00 NaN
2014-01-01 00:09:00 NaN
2014-01-01 00:12:00 NaN
2014-01-01 00:15:00 NaN
2014-01-01 00:18:00 NaN
...
2014-04-09 23:42:00 NaN
2014-04-09 23:45:00 NaN
2014-04-09 23:48:00 NaN
2014-04-09 23:51:00 NaN
2014-04-09 23:54:00 NaN
2014-04-09 23:57:00 NaN
2014-04-10 00:00:00 99.0
Freq: 3T, dtype: float64
Мы можем вместо этого ресемплировать только те группы, где у нас есть точки, как показано ниже:
In [240]: from functools import partial
In [241]: from pandas.tseries.frequencies import to_offset
In [242]: def round(t, freq):
.....: freq = to_offset(freq)
.....: return pd.Timestamp((t.value // freq.delta.value) * freq.delta.value)
.....:
In [243]: ts.groupby(partial(round, freq='3T')).sum()
Out[243]:
2014-01-01 0
2014-01-02 1
2014-01-03 2
2014-01-04 3
2014-01-05 4
2014-01-06 5
2014-01-07 6
..
2014-04-04 93
2014-04-05 94
2014-04-06 95
2014-04-07 96
2014-04-08 97
2014-04-09 98
2014-04-10 99
dtype: int64
Агрегирование
Аналогично агрегатам groupby и функциям окна, Resampler можно выборочно ресемплировать.
При ресемплировании DataFrame, по умолчанию будет применяться одна и та же функция ко всем столбцам.
In [244]: df = pd.DataFrame(np.random.randn(1000, 3),
.....: index=pd.date_range('1/1/2012', freq='S', periods=1000),
.....: columns=['A', 'B', 'C'])
.....:
In [245]: r = df.resample('3T')
In [246]: r.mean()
Out[246]:
A B C
2012-01-01 00:00:00 -0.220339 0.034854 -0.073757
2012-01-01 00:03:00 0.037070 0.040013 0.053754
2012-01-01 00:06:00 -0.041597 -0.144562 -0.007614
2012-01-01 00:09:00 0.043127 -0.076432 -0.032570
2012-01-01 00:12:00 -0.027609 0.054618 0.056878
2012-01-01 00:15:00 -0.014181 0.043958 0.077734
Мы можем выбрать определенный столбец или столбцы с помощью стандартного getitem.
In [247]: r['A'].mean()
Out[247]:
2012-01-01 00:00:00 -0.220339
2012-01-01 00:03:00 0.037070
2012-01-01 00:06:00 -0.041597
2012-01-01 00:09:00 0.043127
2012-01-01 00:12:00 -0.027609
2012-01-01 00:15:00 -0.014181
Freq: 3T, Name: A, dtype: float64
In [248]: r[['A','B']].mean()
Out[248]:
A B
2012-01-01 00:00:00 -0.220339 0.034854
2012-01-01 00:03:00 0.037070 0.040013
2012-01-01 00:06:00 -0.041597 -0.144562
2012-01-01 00:09:00 0.043127 -0.076432
2012-01-01 00:12:00 -0.027609 0.054618
2012-01-01 00:15:00 -0.014181 0.043958
Вы можете передать список или словарь функций для агрегирования, выводя DataFrame:
In [249]: r['A'].agg([np.sum, np.mean, np.std])
Out[249]:
sum mean std
2012-01-01 00:00:00 -39.660974 -0.220339 1.033912
2012-01-01 00:03:00 6.672559 0.037070 0.971503
2012-01-01 00:06:00 -7.487453 -0.041597 1.018418
2012-01-01 00:09:00 7.762901 0.043127 1.025842
2012-01-01 00:12:00 -4.969624 -0.027609 0.961649
2012-01-01 00:15:00 -1.418119 -0.014181 0.978847
Если передается словарь, ключи будут использоваться для именования столбцов. В противном случае используется имя функции (хранящееся в объекте функции).
In [250]: r['A'].agg({'result1' : np.sum,
.....: 'result2' : np.mean})
.....:
Out[250]:
result2 result1
2012-01-01 00:00:00 -0.220339 -39.660974
2012-01-01 00:03:00 0.037070 6.672559
2012-01-01 00:06:00 -0.041597 -7.487453
2012-01-01 00:09:00 0.043127 7.762901
2012-01-01 00:12:00 -0.027609 -4.969624
2012-01-01 00:15:00 -0.014181 -1.418119
В ресемплированном DataFrame вы можете передать список функций, которые нужно применить к каждому столбцу, что приводит к агрегированному результату с иерархическим индексом:
In [251]: r.agg([np.sum, np.mean])
Out[251]:
A B C \
sum mean sum mean sum
2012-01-01 00:00:00 -39.660974 -0.220339 6.273786 0.034854 -13.276324
2012-01-01 00:03:00 6.672559 0.037070 7.202361 0.040013 9.675632
2012-01-01 00:06:00 -7.487453 -0.041597 -26.021155 -0.144562 -1.370600
2012-01-01 00:09:00 7.762901 0.043127 -13.757837 -0.076432 -5.862640
2012-01-01 00:12:00 -4.969624 -0.027609 9.831208 0.054618 10.237970
2012-01-01 00:15:00 -1.418119 -0.014181 4.395766 0.043958 7.773442
mean
2012-01-01 00:00:00 -0.073757
2012-01-01 00:03:00 0.053754
2012-01-01 00:06:00 -0.007614
2012-01-01 00:09:00 -0.032570
2012-01-01 00:12:00 0.056878
2012-01-01 00:15:00 0.077734
Передавая словарь в aggregate, вы можете применить разное агрегирование к столбцам DataFrame:
In [252]: r.agg({'A' : np.sum,
.....: 'B' : lambda x: np.std(x, ddof=1)})
.....:
Out[252]:
A B
2012-01-01 00:00:00 -39.660974 1.004756
2012-01-01 00:03:00 6.672559 0.963559
2012-01-01 00:06:00 -7.487453 0.950766
2012-01-01 00:09:00 7.762901 0.949182
2012-01-01 00:12:00 -4.969624 1.093736
2012-01-01 00:15:00 -1.418119 1.028869
Имена функций также могут быть строками. Для того, чтобы строка была действительной, она должна быть реализована в объекте Resampled.
In [253]: r.agg({'A' : 'sum', 'B' : 'std'})
Out[253]:
A B
2012-01-01 00:00:00 -39.660974 1.004756
2012-01-01 00:03:00 6.672559 0.963559
2012-01-01 00:06:00 -7.487453 0.950766
2012-01-01 00:09:00 7.762901 0.949182
2012-01-01 00:12:00 -4.969624 1.093736
2012-01-01 00:15:00 -1.418119 1.028869
Кроме того, вы также можете указать несколько функций агрегирования для каждого столбца отдельно.
In [254]: r.agg({'A' : ['sum','std'], 'B' : ['mean','std'] })
Out[254]:
A B
sum std mean std
2012-01-01 00:00:00 -39.660974 1.033912 0.034854 1.004756
2012-01-01 00:03:00 6.672559 0.971503 0.040013 0.963559
2012-01-01 00:06:00 -7.487453 1.018418 -0.144562 0.950766
2012-01-01 00:09:00 7.762901 1.025842 -0.076432 0.949182
2012-01-01 00:12:00 -4.969624 0.961649 0.054618 1.093736
2012-01-01 00:15:00 -1.418119 0.978847 0.043958 1.028869
Представление временного интервала
Регулярные временные интервалы представлены объектами Period в pandas, а последовательности объектов Period собираются в PeriodIndex, который можно создать с помощью удобной функции period_range.
Период
Period представляет собой временной интервал (например, день, месяц, квартал и т.д.). Вы можете указать интервал через ключевое слово freq с помощью псевдонима частоты, как показано ниже. Поскольку freq представляет собой временной интервал Period, он не может быть отрицательным, как «-3D».
In [255]: pd.Period('2012', freq='A-DEC')
Out[255]: Period('2012', 'A-DEC')
In [256]: pd.Period('2012-1-1', freq='D')
Out[256]: Period('2012-01-01', 'D')
In [257]: pd.Period('2012-1-1 19:00', freq='H')
Out[257]: Period('2012-01-01 19:00', 'H')
In [258]: pd.Period('2012-1-1 19:00', freq='5H')
Out[258]: Period('2012-01-01 19:00', '5H')
Добавление и вычитание целых чисел из периодов сдвигает период на свою собственную частоту. Арифметика не допускается между Period с разными freq (интервалом).
In [259]: p = pd.Period('2012', freq='A-DEC')
In [260]: p + 1
Out[260]: Period('2013', 'A-DEC')
In [261]: p - 3
Out[261]: Period('2009', 'A-DEC')
In [262]: p = pd.Period('2012-01', freq='2M')
In [263]: p + 2
Out[263]: Period('2012-05', '2M')
In [264]: p - 1
Out[264]: Period('2011-11', '2M')
In [265]: p == pd.Period('2012-01', freq='3M')
---------------------------------------------------------------------------
IncompatibleFrequency Traceback (most recent call last)
<ipython-input-265-ff54ce3238f5> in <module>()
----> 1 p == pd.Period('2012-01', freq='3M')
/Users/tom.augspurger/miniconda3/envs/docs/lib/python2.7/site-packages/pandas/pandas/src/period.pyx in pandas._period.Period.__richcmp__ (pandas/src/period.c:12486)()
769 if other.freq != self.freq:
770 msg = _DIFFERENT_FREQ.format(self.freqstr, other.freqstr)
--> 771 raise IncompatibleFrequency(msg)
772 if self.ordinal == tslib.iNaT or other.ordinal == tslib.iNaT:
773 return _nat_scalar_rules[op]
IncompatibleFrequency: Input has different freq=3M from Period(freq=2M)
Если Period частота дневная или выше (D, H, T, S, L, U, N), offsets и timedelta-подобные могут быть добавлены, если результат может иметь ту же частоту. В противном случае будет поднята ошибка ValueError.
In [266]: p = pd.Period('2014-07-01 09:00', freq='H')
In [267]: p + Hour(2)
Out[267]: Period('2014-07-01 11:00', 'H')
In [268]: p + timedelta(minutes=120)
Out[268]: Period('2014-07-01 11:00', 'H')
In [269]: p + np.timedelta64(7200, 's')
Out[269]: Period('2014-07-01 11:00', 'H')
In [1]: p + Minute(5) Traceback ... ValueError: Input has different freq from Period(freq=H)
Если у Period есть другие частоты, можно складывать только с одинаковой offsets. В противном случае будет поднята ошибка ValueError.
In [270]: p = pd.Period('2014-07', freq='M')
In [271]: p + MonthEnd(3)
Out[271]: Period('2014-10', 'M')
In [1]: p + MonthBegin(3) Traceback ... ValueError: Input has different freq from Period(freq=M)
Вычитание объектов Period с одинаковой частотой вернет количество единиц частоты между ними:
In [272]: pd.Period('2012', freq='A-DEC') - pd.Period('2002', freq='A-DEC')
Out[272]: 10
PeriodIndex и period_range
Регулярные последовательности объектов Period можно собрать в PeriodIndex, который можно построить с помощью удобной функции period_range.
In [273]: prng = pd.period_range('1/1/2011', '1/1/2012', freq='M')
In [274]: prng
Out[274]:
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='int64', freq='M')
Конструктор PeriodIndex также можно использовать напрямую:
In [275]: pd.PeriodIndex(['2011-1', '2011-2', '2011-3'], freq='M') Out[275]: PeriodIndex(['2011-01', '2011-02', '2011-03'], dtype='int64', freq='M')
Передача частоты, умноженной на константу, создает последовательность Period, имеющую умноженный интервал.
In [276]: pd.PeriodIndex(start='2014-01', freq='3M', periods=4) Out[276]: PeriodIndex(['2014-01', '2014-04', '2014-07', '2014-10'], dtype='int64', freq='3M')
Точно так же, как и DatetimeIndex, PeriodIndex также может использоваться для индексирования объектов pandas.
In [277]: ps = pd.Series(np.random.randn(len(prng)), prng) In [278]: ps Out[278]: 2011-01 -1.022670 2011-02 1.371155 2011-03 1.035277 2011-04 1.694400 2011-05 -1.659733 2011-06 0.511432 2011-07 0.433176 2011-08 -0.317955 2011-09 -0.517114 2011-10 -0.310466 2011-11 0.543957 2011-12 0.492003 2012-01 0.193420 Freq: M, dtype: float64
PeriodIndex поддерживает сложение и вычитание по тому же правилу, что и Period.
In [279]: idx = pd.period_range('2014-07-01 09:00', periods=5, freq='H')
In [280]: idx
Out[280]:
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='int64', freq='H')
In [281]: idx + Hour(2)
Out[281]:
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='int64', freq='H')
In [282]: idx = pd.period_range('2014-07', periods=5, freq='M')
In [283]: idx
Out[283]: PeriodIndex(['2014-07', '2014-08', '2014-09', '2014-10', '2014-11'], dtype='int64', freq='M')
In [284]: idx + MonthEnd(3)
Out[284]: PeriodIndex(['2014-10', '2014-11', '2014-12', '2015-01', '2015-02'], dtype='int64', freq='M')
Частичное строковое индексирование PeriodIndex
Вы можете передавать даты и строки в Series и DataFrame с помощью PeriodIndex, таким же образом, как и DatetimeIndex. Для получения подробностей см. Частичное строковое индексирование DatetimeIndex.
In [285]: ps['2011-01'] Out[285]: -1.022669594890105 In [286]: ps[datetime(2011, 12, 25):] Out[286]: 2011-12 0.492003 2012-01 0.193420 Freq: M, dtype: float64 In [287]: ps['10/31/2011':'12/31/2011'] Out[287]: 2011-10 -0.310466 2011-11 0.543957 2011-12 0.492003 Freq: M, dtype: float64
Передача строки, представляющей частоту ниже PeriodIndex, возвращает данные, частично нарезенные.
In [288]: ps['2011']
Out[288]:
2011-01 -1.022670
2011-02 1.371155
2011-03 1.035277
2011-04 1.694400
2011-05 -1.659733
2011-06 0.511432
2011-07 0.433176
2011-08 -0.317955
2011-09 -0.517114
2011-10 -0.310466
2011-11 0.543957
2011-12 0.492003
Freq: M, dtype: float64
In [289]: dfp = pd.DataFrame(np.random.randn(600,1),
.....: columns=['A'],
.....: index=pd.period_range('2013-01-01 9:00', periods=600, freq='T'))
.....:
In [290]: dfp
Out[290]:
A
2013-01-01 09:00 0.197720
2013-01-01 09:01 -0.284769
2013-01-01 09:02 0.061491
2013-01-01 09:03 1.630257
2013-01-01 09:04 2.042442
2013-01-01 09:05 -0.804392
2013-01-01 09:06 0.212760
... ...
2013-01-01 18:53 0.150586
2013-01-01 18:54 -0.679569
2013-01-01 18:55 -0.910216
2013-01-01 18:56 -0.413168
2013-01-01 18:57 -0.247752
2013-01-01 18:58 1.590875
2013-01-01 18:59 -2.005294
[600 rows x 1 columns]
In [291]: dfp['2013-01-01 10H']
Out[291]:
A
2013-01-01 10:00 -0.569936
2013-01-01 10:01 -1.179183
2013-01-01 10:02 -0.838602
2013-01-01 10:03 -1.727539
2013-01-01 10:04 1.334027
2013-01-01 10:05 0.417423
2013-01-01 10:06 -0.221189
... ...
2013-01-01 10:53 -0.375925
2013-01-01 10:54 0.212750
2013-01-01 10:55 -0.592417
2013-01-01 10:56 -0.466064
2013-01-01 10:57 -1.715347
2013-01-01 10:58 -0.634913
2013-01-01 10:59 -0.809471
[60 rows x 1 columns]
Как и в DatetimeIndex, конечные точки будут включены в результат. В приведенном ниже примере данные нарезаются с 10:00 до 11:59.
In [292]: dfp['2013-01-01 10H':'2013-01-01 11H']
Out[292]:
A
2013-01-01 10:00 -0.569936
2013-01-01 10:01 -1.179183
2013-01-01 10:02 -0.838602
2013-01-01 10:03 -1.727539
2013-01-01 10:04 1.334027
2013-01-01 10:05 0.417423
2013-01-01 10:06 -0.221189
... ...
2013-01-01 11:53 0.616198
2013-01-01 11:54 2.843156
2013-01-01 11:55 0.572537
2013-01-01 11:56 1.709706
2013-01-01 11:57 -0.205490
2013-01-01 11:58 1.759719
2013-01-01 11:59 -1.181485
[120 rows x 1 columns]
Преобразование частоты и ресемплирование с PeriodIndex
Частоту Period и PeriodIndex можно преобразовать с помощью метода asfreq. Начнем с финансового года 2011, который заканчивается в декабре:
In [293]: p = pd.Period('2011', freq='A-DEC')
In [294]: p
Out[294]: Period('2011', 'A-DEC')
Мы можем преобразовать его в месячную частоту. Используя параметр how, мы можем указать, возвращать ли начальный или конечный месяц:
In [295]: p.asfreq('M', how='start')
Out[295]: Period('2011-01', 'M')
In [296]: p.asfreq('M', how='end')
Out[296]: Period('2011-12', 'M')
Для удобства предоставляются сокращения ‘s’ и ‘e’:
In [297]: p.asfreq('M', 's')
Out[297]: Period('2011-01', 'M')
In [298]: p.asfreq('M', 'e')
Out[298]: Period('2011-12', 'M')
Преобразование в «суперпериод» (например, годовая частота — это суперпериод квартальной частоты) автоматически возвращает суперпериод, который включает входной период:
In [299]: p = pd.Period('2011-12', freq='M')
In [300]: p.asfreq('A-NOV')
Out[300]: Period('2012', 'A-NOV')
Обратите внимание, что так как мы перешли к годовой частоте, которая заканчивается в ноябре, месячный период декабря 2011 года фактически находится в периоде 2012 A-NOV.
Преобразования периодов с закреплёнными частотами особенно полезны при работе с различными квартальными данными, характерными для экономики, бизнеса и других областей. Многие организации определяют кварталы относительно месяца, в котором начинается и заканчивается их финансовый год. Таким образом, первый квартал 2011 года мог начаться в 2010 году или в течение нескольких месяцев 2011 года. С помощью закреплённых частот pandas работает со всеми квартальными частотами Q-JAN до Q-DEC.
Q-DEC определяет регулярные календарные кварталы:
In [301]: p = pd.Period('2012Q1', freq='Q-DEC')
In [302]: p.asfreq('D', 's')
Out[302]: Period('2012-01-01', 'D')
In [303]: p.asfreq('D', 'e')
Out[303]: Period('2012-03-31', 'D')
Q-MAR определяет финансовый год, заканчивающийся в марте:
In [304]: p = pd.Period('2011Q4', freq='Q-MAR')
In [305]: p.asfreq('D', 's')
Out[305]: Period('2011-01-01', 'D')
In [306]: p.asfreq('D', 'e')
Out[306]: Period('2011-03-31', 'D')
Преобразование между представлениями
Данные с метками времени можно преобразовать в данные с PeriodIndex с помощью to_period, а обратно — с помощью to_timestamp:
In [307]: rng = pd.date_range('1/1/2012', periods=5, freq='M')
In [308]: ts = pd.Series(np.random.randn(len(rng)), index=rng)
In [309]: ts
Out[309]:
2012-01-31 2.167674
2012-02-29 -1.505130
2012-03-31 1.005802
2012-04-30 0.481525
2012-05-31 -0.352151
Freq: M, dtype: float64
In [310]: ps = ts.to_period()
In [311]: ps
Out[311]:
2012-01 2.167674
2012-02 -1.505130
2012-03 1.005802
2012-04 0.481525
2012-05 -0.352151
Freq: M, dtype: float64
In [312]: ps.to_timestamp()
Out[312]:
2012-01-01 2.167674
2012-02-01 -1.505130
2012-03-01 1.005802
2012-04-01 0.481525
2012-05-01 -0.352151
Freq: MS, dtype: float64
Помните, что ‘s’ и ‘e’ могут использоваться для возвращения временных меток в начале или конце периода:
In [313]: ps.to_timestamp('D', how='s')
Out[313]:
2012-01-01 2.167674
2012-02-01 -1.505130
2012-03-01 1.005802
2012-04-01 0.481525
2012-05-01 -0.352151
Freq: MS, dtype: float64
Преобразование между периодом и меткой времени позволяет использовать некоторые удобные функции арифметики. В следующем примере мы преобразуем квартальную частоту с годом, заканчивающимся в ноябре, в 9:00 по завершении месяца, следующего за концом квартала:
In [314]: prng = pd.period_range('1990Q1', '2000Q4', freq='Q-NOV')
In [315]: ts = pd.Series(np.random.randn(len(prng)), prng)
In [316]: ts.index = (prng.asfreq('M', 'e') + 1).asfreq('H', 's') + 9
In [317]: ts.head()
Out[317]:
1990-03-01 09:00 -0.608988
1990-06-01 09:00 0.412294
1990-09-01 09:00 -0.715938
1990-12-01 09:00 1.297773
1991-03-01 09:00 -2.260765
Freq: H, dtype: float64
Представление интервалов за пределами диапазона
Если у вас есть данные, которые находятся за пределами Timestamp границ, см. ограничения по меткам времени, то вы можете использовать PeriodIndex и/или Series из Periods для выполнения вычислений.
In [318]: span = pd.period_range('1215-01-01', '1381-01-01', freq='D')
In [319]: span
Out[319]:
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='int64', length=60632, freq='D')
Для преобразования из представления YYYYMMDD, основанного на int64.
In [320]: s = pd.Series([20121231, 20141130, 99991231])
In [321]: s
Out[321]:
0 20121231
1 20141130
2 99991231
dtype: int64
In [322]: def conv(x):
.....: return pd.Period(year = x // 10000, month = x//100 % 100, day = x%100, freq='D')
.....:
In [323]: s.apply(conv)
Out[323]:
0 2012-12-31
1 2014-11-30
2 9999-12-31
dtype: object
In [324]: s.apply(conv)[2]
Out[324]: Period('9999-12-31', 'D')
Эти значения легко преобразуются в PeriodIndex.
In [325]: span = pd.PeriodIndex(s.apply(conv)) In [326]: span Out[326]: PeriodIndex(['2012-12-31', '2014-11-30', '9999-12-31'], dtype='int64', freq='D')
Обработка часовых поясов
Pandas предоставляет широкую поддержку работы со временными отметками в разных часовых поясах с использованием библиотек pytz и dateutil. dateutil поддержка появилась в версии 0.14.1 и в настоящее время поддерживается только для часовых поясов с фиксированным смещением и tzfile. По умолчанию используется библиотека pytz. Поддержка dateutil предоставляется для совместимости с другими приложениями, например, если вы используете dateutil в других пакетах Python.
Работа с часовыми поясами
По умолчанию объекты pandas не учитывают часовой пояс:
In [327]: rng = pd.date_range('3/6/2012 00:00', periods=15, freq='D')
In [328]: rng.tz is None
Out[328]: True
Чтобы указать часовой пояс, вы можете использовать ключевое слово tz для date_range и других функций. Строки часовых поясов библиотеки Dateutil отличаются от часовых поясов pytz тем, что начинаются с dateutil/.
- В
pytzвы можете найти список распространенных (и менее распространенных) часовых поясов, используяfrom pytz import common_timezones, all_timezones. -
dateutilиспользует часовые пояса операционной системы, поэтому фиксированный список недоступен. Для распространённых зон названия совпадают сpytz.
# pytz
In [329]: rng_pytz = pd.date_range('3/6/2012 00:00', periods=10, freq='D',
.....: tz='Europe/London')
.....:
In [330]: rng_pytz.tz
Out[330]: <DstTzInfo 'Europe/London' LMT-1 day, 23:59:00 STD>
# dateutil
In [331]: rng_dateutil = pd.date_range('3/6/2012 00:00', periods=10, freq='D',
.....: tz='dateutil/Europe/London')
.....:
In [332]: rng_dateutil.tz
Out[332]: tzfile('/usr/share/zoneinfo/Europe/London')
# dateutil - utc special case
In [333]: rng_utc = pd.date_range('3/6/2012 00:00', periods=10, freq='D',
.....: tz=dateutil.tz.tzutc())
.....:
In [334]: rng_utc.tz
Out[334]: tzutc()
Обратите внимание, что часовой пояс UTC является особым случаем в dateutil и должен быть явно построен как экземпляр dateutil.tz.tzutc. Вы также можете сначала явно построить другие часовые пояса, что даёт вам больший контроль над используемым часовым поясом:
# pytz
In [335]: tz_pytz = pytz.timezone('Europe/London')
In [336]: rng_pytz = pd.date_range('3/6/2012 00:00', periods=10, freq='D',
.....: tz=tz_pytz)
.....:
In [337]: rng_pytz.tz == tz_pytz
Out[337]: True
# dateutil
In [338]: tz_dateutil = dateutil.tz.gettz('Europe/London')
In [339]: rng_dateutil = pd.date_range('3/6/2012 00:00', periods=10, freq='D',
.....: tz=tz_dateutil)
.....:
In [340]: rng_dateutil.tz == tz_dateutil
Out[340]: True
Временные отметки, как и объект Python datetime.datetime, могут быть либо без часового пояса, либо с часовым поясом. Объекты временных рядов и DatetimeIndex без часового пояса можно локализовать, используя tz_localize:
In [341]: ts = pd.Series(np.random.randn(len(rng)), rng)
In [342]: ts_utc = ts.tz_localize('UTC')
In [343]: ts_utc
Out[343]:
2012-03-06 00:00:00+00:00 0.679135
2012-03-07 00:00:00+00:00 0.345668
2012-03-08 00:00:00+00:00 -1.143903
2012-03-09 00:00:00+00:00 0.487087
2012-03-10 00:00:00+00:00 -1.421073
2012-03-11 00:00:00+00:00 -0.327463
2012-03-12 00:00:00+00:00 0.169899
2012-03-13 00:00:00+00:00 0.867568
2012-03-14 00:00:00+00:00 -0.834122
2012-03-15 00:00:00+00:00 -1.698494
2012-03-16 00:00:00+00:00 0.974717
2012-03-17 00:00:00+00:00 0.966771
2012-03-18 00:00:00+00:00 -0.754168
2012-03-19 00:00:00+00:00 -1.434246
2012-03-20 00:00:00+00:00 0.848935
Freq: D, dtype: float64
Опять же, вы можете сначала явно построить объект часового пояса. Вы можете использовать метод tz_convert для преобразования объектов pandas для конвертации данных с часовым поясом в другой часовой пояс:
In [344]: ts_utc.tz_convert('US/Eastern')
Out[344]:
2012-03-05 19:00:00-05:00 0.679135
2012-03-06 19:00:00-05:00 0.345668
2012-03-07 19:00:00-05:00 -1.143903
2012-03-08 19:00:00-05:00 0.487087
2012-03-09 19:00:00-05:00 -1.421073
2012-03-10 19:00:00-05:00 -0.327463
2012-03-11 20:00:00-04:00 0.169899
2012-03-12 20:00:00-04:00 0.867568
2012-03-13 20:00:00-04:00 -0.834122
2012-03-14 20:00:00-04:00 -1.698494
2012-03-15 20:00:00-04:00 0.974717
2012-03-16 20:00:00-04:00 0.966771
2012-03-17 20:00:00-04:00 -0.754168
2012-03-18 20:00:00-04:00 -1.434246
2012-03-19 20:00:00-04:00 0.848935
Freq: D, dtype: float64
Предупреждение
Будьте осторожны с преобразованиями между библиотеками. Для некоторых зон pytz и dateutil имеют разные определения зоны. Это больше проблема для необычных часовых поясов, чем для «стандартных» зон, таких как US/Eastern.
Предупреждение
Учитывайте, что определение часового пояса в разных версиях библиотек часовых поясов может не считаться одинаковым. Это может вызвать проблемы при работе с сохранёнными данными, которые локализованы с использованием одной версии и обрабатываются с другой версией. См. здесь, как справиться с такой ситуацией.
Предупреждение
Неправильно передавать часовой пояс напрямую в конструктор datetime.datetime (например, datetime.datetime(2011, 1, 1, tz=timezone('US/Eastern')). Вместо этого, необходимо локализовать datetime, используя метод localize для часового пояса.
Внутри, все временные отметки хранятся в UTC. Скалярные значения из DatetimeIndex с часовым поясом будут иметь поля (день, час, минута) локализованные к часовому поясу. Однако, временные отметки с одинаковым значением UTC по-прежнему считаются равными, даже если они находятся в разных часовых поясах:
In [345]: rng_eastern = rng_utc.tz_convert('US/Eastern')
In [346]: rng_berlin = rng_utc.tz_convert('Europe/Berlin')
In [347]: rng_eastern[5]
Out[347]: Timestamp('2012-03-10 19:00:00-0500', tz='US/Eastern', offset='D')
In [348]: rng_berlin[5]
Out[348]: Timestamp('2012-03-11 01:00:00+0100', tz='Europe/Berlin', offset='D')
In [349]: rng_eastern[5] == rng_berlin[5]
Out[349]: True
Как и Series, DataFrame, и DatetimeIndex, Timestamp``s can be converted to other time zones using ``tz_convert:
In [350]: rng_eastern[5]
Out[350]: Timestamp('2012-03-10 19:00:00-0500', tz='US/Eastern', offset='D')
In [351]: rng_berlin[5]
Out[351]: Timestamp('2012-03-11 01:00:00+0100', tz='Europe/Berlin', offset='D')
In [352]: rng_eastern[5].tz_convert('Europe/Berlin')
Out[352]: Timestamp('2012-03-11 01:00:00+0100', tz='Europe/Berlin')
Локализация Timestamp функций так же, как DatetimeIndex и Series:
In [353]: rng[5]
Out[353]: Timestamp('2012-03-11 00:00:00', offset='D')
In [354]: rng[5].tz_localize('Asia/Shanghai')
Out[354]: Timestamp('2012-03-11 00:00:00+0800', tz='Asia/Shanghai')
Операции между Series в разных часовых поясах приведут к Series в UTC, выравнивая данные по временным меткам UTC:
In [355]: eastern = ts_utc.tz_convert('US/Eastern')
In [356]: berlin = ts_utc.tz_convert('Europe/Berlin')
In [357]: result = eastern + berlin
In [358]: result
Out[358]:
2012-03-06 00:00:00+00:00 1.358269
2012-03-07 00:00:00+00:00 0.691336
2012-03-08 00:00:00+00:00 -2.287805
2012-03-09 00:00:00+00:00 0.974174
2012-03-10 00:00:00+00:00 -2.842146
2012-03-11 00:00:00+00:00 -0.654926
2012-03-12 00:00:00+00:00 0.339798
2012-03-13 00:00:00+00:00 1.735136
2012-03-14 00:00:00+00:00 -1.668245
2012-03-15 00:00:00+00:00 -3.396988
2012-03-16 00:00:00+00:00 1.949435
2012-03-17 00:00:00+00:00 1.933541
2012-03-18 00:00:00+00:00 -1.508335
2012-03-19 00:00:00+00:00 -2.868493
2012-03-20 00:00:00+00:00 1.697870
Freq: D, dtype: float64
In [359]: result.index
Out[359]:
DatetimeIndex(['2012-03-06', '2012-03-07', '2012-03-08', '2012-03-09',
'2012-03-10', '2012-03-11', '2012-03-12', '2012-03-13',
'2012-03-14', '2012-03-15', '2012-03-16', '2012-03-17',
'2012-03-18', '2012-03-19', '2012-03-20'],
dtype='datetime64[ns, UTC]', freq='D')
Чтобы удалить часовой пояс из tz-aware DatetimeIndex, используйте tz_localize(None) или tz_convert(None). tz_localize(None) удалит часовой пояс, сохраняя местное время. tz_convert(None) удалит часовой пояс после преобразования в UTC время.
In [360]: didx = pd.DatetimeIndex(start='2014-08-01 09:00', freq='H', periods=10, tz='US/Eastern')
In [361]: didx
Out[361]:
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', '2014-08-01 12:00:00-04:00',
'2014-08-01 13:00:00-04:00', '2014-08-01 14:00:00-04:00',
'2014-08-01 15:00:00-04:00', '2014-08-01 16:00:00-04:00',
'2014-08-01 17:00:00-04:00', '2014-08-01 18:00:00-04:00'],
dtype='datetime64[ns, US/Eastern]', freq='H')
In [362]: didx.tz_localize(None)
Out[362]:
DatetimeIndex(['2014-08-01 09:00:00', '2014-08-01 10:00:00',
'2014-08-01 11:00:00', '2014-08-01 12:00:00',
'2014-08-01 13:00:00', '2014-08-01 14:00:00',
'2014-08-01 15:00:00', '2014-08-01 16:00:00',
'2014-08-01 17:00:00', '2014-08-01 18:00:00'],
dtype='datetime64[ns]', freq='H')
In [363]: didx.tz_convert(None)
Out[363]:
DatetimeIndex(['2014-08-01 13:00:00', '2014-08-01 14:00:00',
'2014-08-01 15:00:00', '2014-08-01 16:00:00',
'2014-08-01 17:00:00', '2014-08-01 18:00:00',
'2014-08-01 19:00:00', '2014-08-01 20:00:00',
'2014-08-01 21:00:00', '2014-08-01 22:00:00'],
dtype='datetime64[ns]', freq='H')
# tz_convert(None) is identical with tz_convert('UTC').tz_localize(None)
In [364]: didx.tz_convert('UCT').tz_localize(None)
Out[364]:
DatetimeIndex(['2014-08-01 13:00:00', '2014-08-01 14:00:00',
'2014-08-01 15:00:00', '2014-08-01 16:00:00',
'2014-08-01 17:00:00', '2014-08-01 18:00:00',
'2014-08-01 19:00:00', '2014-08-01 20:00:00',
'2014-08-01 21:00:00', '2014-08-01 22:00:00'],
dtype='datetime64[ns]', freq='H')
Неоднозначные моменты при локализации
В некоторых случаях, localize не может определить летнее и стандартное время, когда существуют дубликаты. Это часто происходит при чтении файлов или записей баз данных, которые просто дублируют часы. Передача ambiguous='infer' (аргумент infer_dst в предыдущих версиях) в tz_localize попытается определить правильное смещение. Пример сверху не удастся, так как содержит неоднозначные временные отметки, а снизу будет определено правильное смещение.
In [365]: 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 03: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 [366]: rng_hourly_eastern = rng_hourly.tz_localize('US/Eastern', ambiguous='infer')
In [367]: rng_hourly_eastern.tolist()
Out[367]:
[Timestamp('2011-11-06 00:00:00-0400', tz='US/Eastern'),
Timestamp('2011-11-06 01:00:00-0400', tz='US/Eastern'),
Timestamp('2011-11-06 01:00:00-0500', tz='US/Eastern'),
Timestamp('2011-11-06 02:00:00-0500', tz='US/Eastern'),
Timestamp('2011-11-06 03:00:00-0500', tz='US/Eastern')]
Помимо 'infer', поддерживается несколько других аргументов. Передача массивоподобного значения bool или 0/1, где True обозначает летнее время, а False - стандартное время, позволяет различать более одного перехода на летнее время (например, если у вас несколько записей в базе данных, каждая со своим переходом на летнее время). Или передача 'NaT' заполнит временные метки перехода значениями "not-a-time". Эти методы доступны как в конструкторе DatetimeIndex, так и в tz_localize.
In [368]: rng_hourly_dst = np.array([1, 1, 0, 0, 0])
In [369]: rng_hourly.tz_localize('US/Eastern', ambiguous=rng_hourly_dst).tolist()
Out[369]:
[Timestamp('2011-11-06 00:00:00-0400', tz='US/Eastern'),
Timestamp('2011-11-06 01:00:00-0400', tz='US/Eastern'),
Timestamp('2011-11-06 01:00:00-0500', tz='US/Eastern'),
Timestamp('2011-11-06 02:00:00-0500', tz='US/Eastern'),
Timestamp('2011-11-06 03:00:00-0500', tz='US/Eastern')]
In [370]: rng_hourly.tz_localize('US/Eastern', ambiguous='NaT').tolist()
Out[370]:
[Timestamp('2011-11-06 00:00:00-0400', tz='US/Eastern'),
NaT,
NaT,
Timestamp('2011-11-06 02:00:00-0500', tz='US/Eastern'),
Timestamp('2011-11-06 03:00:00-0500', tz='US/Eastern')]
In [371]: didx = pd.DatetimeIndex(start='2014-08-01 09:00', freq='H', periods=10, tz='US/Eastern')
In [372]: didx
Out[372]:
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', '2014-08-01 12:00:00-04:00',
'2014-08-01 13:00:00-04:00', '2014-08-01 14:00:00-04:00',
'2014-08-01 15:00:00-04:00', '2014-08-01 16:00:00-04:00',
'2014-08-01 17:00:00-04:00', '2014-08-01 18:00:00-04:00'],
dtype='datetime64[ns, US/Eastern]', freq='H')
In [373]: didx.tz_localize(None)
Out[373]:
DatetimeIndex(['2014-08-01 09:00:00', '2014-08-01 10:00:00',
'2014-08-01 11:00:00', '2014-08-01 12:00:00',
'2014-08-01 13:00:00', '2014-08-01 14:00:00',
'2014-08-01 15:00:00', '2014-08-01 16:00:00',
'2014-08-01 17:00:00', '2014-08-01 18:00:00'],
dtype='datetime64[ns]', freq='H')
In [374]: didx.tz_convert(None)
Out[374]:
DatetimeIndex(['2014-08-01 13:00:00', '2014-08-01 14:00:00',
'2014-08-01 15:00:00', '2014-08-01 16:00:00',
'2014-08-01 17:00:00', '2014-08-01 18:00:00',
'2014-08-01 19:00:00', '2014-08-01 20:00:00',
'2014-08-01 21:00:00', '2014-08-01 22:00:00'],
dtype='datetime64[ns]', freq='H')
# tz_convert(None) is identical with tz_convert('UTC').tz_localize(None)
In [375]: didx.tz_convert('UCT').tz_localize(None)
Out[375]:
DatetimeIndex(['2014-08-01 13:00:00', '2014-08-01 14:00:00',
'2014-08-01 15:00:00', '2014-08-01 16:00:00',
'2014-08-01 17:00:00', '2014-08-01 18:00:00',
'2014-08-01 19:00:00', '2014-08-01 20:00:00',
'2014-08-01 21:00:00', '2014-08-01 22:00:00'],
dtype='datetime64[ns]', freq='H')
TZ-aware типы данных
Новое в версии 0.17.0.
Series/DatetimeIndex с неявным значением часового пояса представлены типом данных datetime64[ns].
In [376]: s_naive = pd.Series(pd.date_range('20130101',periods=3))
In [377]: s_naive
Out[377]:
0 2013-01-01
1 2013-01-02
2 2013-01-03
dtype: datetime64[ns]
Series/DatetimeIndex с явным значением часового пояса представлены типом данных datetime64[ns, tz].
In [378]: s_aware = pd.Series(pd.date_range('20130101',periods=3,tz='US/Eastern'))
In [379]: s_aware
Out[379]:
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, см. здесь.
Например, чтобы локализовать и преобразовать неявную метку времени в явную с часовым поясом.
In [380]: s_naive.dt.tz_localize('UTC').dt.tz_convert('US/Eastern')
Out[380]:
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(...) явные (и неявные) метки времени с часовым поясом. Эта операция фактически является локализация И преобразование для неявной метки времени, и преобразование для явной.
# localize and convert a naive timezone
In [381]: s_naive.astype('datetime64[ns, US/Eastern]')
Out[381]:
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]
# make an aware tz naive
In [382]: s_aware.astype('datetime64[ns]')
Out[382]:
0 2013-01-01 05:00:00
1 2013-01-02 05:00:00
2 2013-01-03 05:00:00
dtype: datetime64[ns]
# convert to a new timezone
In [383]: s_aware.astype('datetime64[ns, CET]')
Out[383]:
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]
Примечание
Использование аксессора .values для Series, возвращает массив numpy данных. Эти значения преобразуются в UTC, так как numpy в настоящее время не поддерживает часовые пояса (хотя он выводит в местном часовом поясе!).
In [384]: s_naive.values
Out[384]:
array(['2012-12-31T18:00:00.000000000-0600',
'2013-01-01T18:00:00.000000000-0600',
'2013-01-02T18:00:00.000000000-0600'], dtype='datetime64[ns]')
In [385]: s_aware.values
Out[385]:
array(['2012-12-31T23:00:00.000000000-0600',
'2013-01-01T23:00:00.000000000-0600',
'2013-01-02T23:00:00.000000000-0600'], dtype='datetime64[ns]')
Обратите внимание, что после преобразования в массив numpy теряется информация о часовом поясе.
In [386]: pd.Series(s_aware.values) Out[386]: 0 2013-01-01 05:00:00 1 2013-01-02 05:00:00 2 2013-01-03 05:00:00 dtype: datetime64[ns]
Однако, их можно легко преобразовать
In [387]: pd.Series(s_aware.values).dt.tz_localize('UTC').dt.tz_convert('US/Eastern')
Out[387]:
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]
© 2008–2012, 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/0.18.1/timeseries.html