Временные ряды/функциональность дат
Библиотека 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')
In [10]: pd.Timestamp(2012, 5, 1)
Out[10]: Timestamp('2012-05-01 00:00:00')
Однако во многих случаях более естественно связывать такие вещи, как переменные изменения, с временным интервалом вместо этого. Интервал, представленный Period, может быть указан явно или определен из формата строки даты и времени.
Например:
In [11]: pd.Period('2011-01')
Out[11]: Period('2011-01', 'M')
In [12]: pd.Period('2012-05', freq='D')
Out[12]: Period('2012-05-01', 'D')
Timestamp и Period могут быть индексом. Списки Timestamp и Period автоматически преобразуются в DatetimeIndex и PeriodIndex соответственно.
In [13]: dates = [pd.Timestamp('2012-05-01'), pd.Timestamp('2012-05-02'), pd.Timestamp('2012-05-03')]
In [14]: ts = pd.Series(np.random.randn(3), dates)
In [15]: type(ts.index)
Out[15]: pandas.tseries.index.DatetimeIndex
In [16]: ts.index
Out[16]: DatetimeIndex(['2012-05-01', '2012-05-02', '2012-05-03'], dtype='datetime64[ns]', freq=None)
In [17]: ts
Out[17]:
2012-05-01 -0.410001
2012-05-02 -0.078638
2012-05-03 0.545952
dtype: float64
In [18]: periods = [pd.Period('2012-01'), pd.Period('2012-02'), pd.Period('2012-03')]
In [19]: ts = pd.Series(np.random.randn(3), periods)
In [20]: type(ts.index)
Out[20]: pandas.tseries.period.PeriodIndex
In [21]: ts.index
Out[21]: PeriodIndex(['2012-01', '2012-02', '2012-03'], dtype='period[M]', freq='M')
In [22]: ts
Out[22]:
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 [23]: pd.to_datetime(pd.Series(['Jul 31, 2009', '2010-01-10', None])) Out[23]: 0 2009-07-31 1 2010-01-10 2 NaT dtype: datetime64[ns] In [24]: pd.to_datetime(['2005/11/23', '2010.12.31']) Out[24]: DatetimeIndex(['2005-11-23', '2010-12-31'], dtype='datetime64[ns]', freq=None)
Если вы используете даты, которые начинаются с дня (т. е. европейский стиль), вы можете передать флаг dayfirst.
In [25]: pd.to_datetime(['04-01-2012 10:00'], dayfirst=True) Out[25]: DatetimeIndex(['2012-01-04 10:00:00'], dtype='datetime64[ns]', freq=None) In [26]: pd.to_datetime(['14-01-2012', '01-14-2012'], dayfirst=True) Out[26]: 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 [27]: pd.to_datetime('2010/11/12')
Out[27]: Timestamp('2010-11-12 00:00:00')
In [28]: pd.Timestamp('2010/11/12')
Out[28]: Timestamp('2010-11-12 00:00:00')
Введено в версии 0.18.1.
Вы также можете передать DataFrame целых или строковых столбцов для сборки в Series Timestamps.
In [29]: df = pd.DataFrame({'year': [2015, 2016],
....: 'month': [2, 3],
....: 'day': [4, 5],
....: 'hour': [2, 3]})
....:
In [30]: pd.to_datetime(df)
Out[30]:
0 2015-02-04 02:00:00
1 2016-03-05 03:00:00
dtype: datetime64[ns]
Вы можете передать только необходимые для сборки столбцы.
In [31]: pd.to_datetime(df[['year', 'month', 'day']]) Out[31]: 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)
Маркировки эпохи
Также можно преобразовать целочисленные или плавающие значения временных меток эпохи. Единицей измерения по умолчанию является наносекунда (поскольку именно так хранятся временные метки эпохи). Однако часто эпохи хранятся в других единицах измерения, которые можно указать:
Типичные единицы измерения эпохи
In [32]: pd.to_datetime([1349720105, 1349806505, 1349892905,
....: 1349979305, 1350065705], unit='s')
....:
Out[32]:
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 [33]: pd.to_datetime([1349720105100, 1349720105200, 1349720105300,
....: 1349720105400, 1349720105500 ], unit='ms')
....:
Out[33]:
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 [34]: pd.to_datetime([1]) Out[34]: DatetimeIndex(['1970-01-01 00:00:00.000000001'], dtype='datetime64[ns]', freq=None) In [35]: pd.to_datetime([1, 3.14], unit='s') Out[35]: DatetimeIndex(['1970-01-01 00:00:01', '1970-01-01 00:00:03.140000'], dtype='datetime64[ns]', freq=None)
Примечание
Временные метки эпохи будут округляться до ближайшей наносекунды.
Генерация диапазонов временных меток
Для генерации индекса с временными метками можно использовать конструктор DatetimeIndex или Index и передать список объектов datetime:
In [36]: dates = [datetime(2012, 5, 1), datetime(2012, 5, 2), datetime(2012, 5, 3)] # Note the frequency information In [37]: index = pd.DatetimeIndex(dates) In [38]: index Out[38]: DatetimeIndex(['2012-05-01', '2012-05-02', '2012-05-03'], dtype='datetime64[ns]', freq=None) # Automatically converted to DatetimeIndex In [39]: index = pd.Index(dates) In [40]: index Out[40]: DatetimeIndex(['2012-05-01', '2012-05-02', '2012-05-03'], dtype='datetime64[ns]', freq=None)
На практике это становится очень громоздким, поскольку нам часто нужен очень длинный индекс с большим количеством временных меток. Если нам нужны временные метки с регулярной частотой, мы можем использовать функции pandas date_range и bdate_range для создания индексов временных меток.
In [41]: index = pd.date_range('2000-1-1', periods=1000, freq='M')
In [42]: index
Out[42]:
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 [43]: index = pd.bdate_range('2012-1-1', periods=250)
In [44]: index
Out[44]:
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 [45]: start = datetime(2011, 1, 1)
In [46]: end = datetime(2012, 1, 1)
In [47]: rng = pd.date_range(start, end)
In [48]: rng
Out[48]:
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 [49]: rng = pd.bdate_range(start, end)
In [50]: rng
Out[50]:
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 [51]: pd.date_range(start, end, freq='BM')
Out[51]:
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 [52]: pd.date_range(start, end, freq='W')
Out[52]:
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 [53]: pd.bdate_range(end=end, periods=20)
Out[53]:
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 [54]: pd.bdate_range(start=start, periods=20)
Out[54]:
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 [55]: pd.Timestamp.min
Out[55]: Timestamp('1677-09-21 00:12:43.145225')
In [56]: pd.Timestamp.max
Out[56]: 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 [57]: rng = pd.date_range(start, end, freq='BM')
In [58]: ts = pd.Series(np.random.randn(len(rng)), index=rng)
In [59]: ts.index
Out[59]:
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 [60]: ts[:5].index
Out[60]:
DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31', '2011-04-29',
'2011-05-31'],
dtype='datetime64[ns]', freq='BM')
In [61]: ts[::2].index
Out[61]:
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 [62]: ts['1/31/2011'] Out[62]: -1.2812473076599531 In [63]: ts[datetime(2011, 12, 25):] Out[63]: 2011-12-30 0.687738 Freq: BM, dtype: float64 In [64]: ts['10/31/2011':'12/31/2011'] Out[64]: 2011-10-31 0.149748 2011-11-30 -0.732339 2011-12-30 0.687738 Freq: BM, dtype: float64
Для обеспечения удобства доступа к более длинным временным рядам вы также можете передавать год или год и месяц как строки:
In [65]: ts['2011'] Out[65]: 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 [66]: ts['2011-6'] Out[66]: 2011-06-30 0.341734 Freq: BM, dtype: float64
Этот тип среза будет работать и с DataFrame с DateTimeIndex в качестве индекса. Поскольку частичный выбор по строкам является формой выбора по меткам, конечные точки будут включены. Это включало бы совпадение времен в указанную дату. Вот пример:
In [67]: dft = pd.DataFrame(randn(100000,1),
....: columns=['A'],
....: index=pd.date_range('20130101',periods=100000,freq='T'))
....:
In [68]: dft
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']
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-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 [70]: dft['2013-1':'2013-2']
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']
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-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 [72]: dft['2013-1':'2013-2-28 00:00:00']
Out[72]:
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 [73]: dft['2013-1-15':'2013-1-15 12:30:00']
Out[73]:
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 [74]: dft.loc['2013-1-15 12:30:00'] Out[74]: A 0.193284 Name: 2013-01-15 12:30:00, dtype: float64
Новая в версии 0.18.0.
Частичный строковый индекс DateTimeIndex также работает с DataFrame с MultiIndex. Например:
In [75]: 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 [76]: dft2
Out[76]:
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 [77]: dft2.loc['2013-01-05']
Out[77]:
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 [78]: idx = pd.IndexSlice
In [79]: dft2 = dft2.swaplevel(0, 1).sort_index()
In [80]: dft2.loc[idx[:, '2013-01-05'], :]
Out[80]:
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 [81]: dft[datetime(2013, 1, 1):datetime(2013,2,28)]
Out[81]:
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 [82]: dft[datetime(2013, 1, 1, 10, 12, 0):datetime(2013, 2, 28, 10, 12, 0)]
Out[82]:
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]
Усечение и индексирование с помощью произвольных значений
Предоставлена удобная функция усечения, которая эквивалентна срезу:
In [83]: ts.truncate(before='10/31/2011', after='12/31/2011') Out[83]: 2011-10-31 0.149748 2011-11-30 -0.732339 2011-12-30 0.687738 Freq: BM, dtype: float64
Даже сложное индексирование с произвольными значениями, нарушающее регулярность частоты DatetimeIndex, приведет к DatetimeIndex (но частота теряется):
In [84]: ts[[0, 2, 6]].index Out[84]: DatetimeIndex(['2011-01-31', '2011-03-31', '2011-07-29'], dtype='datetime64[ns]', freq=None)
Компоненты даты и времени
Существует несколько свойств даты и времени, которые можно получить из Timestamp или коллекции временных меток, например, DateTimeIndex.
| Свойство | Описание |
|---|---|
| year | Год даты и времени |
| month | Месяц даты и времени |
| day | День даты и времени |
| hour | Час даты и времени |
| minute | Минуты даты и времени |
| second | Секунды даты и времени |
| microsecond | Микросекунды даты и времени |
| nanosecond | Наносекунды даты и времени |
| date | Возвращает datetime.date (не содержит информации о часовом поясе) |
| time | Возвращает datetime.time (не содержит информации о часовом поясе) |
| dayofyear | Порядковый день года |
| weekofyear | Порядковый номер недели года |
| week | Порядковый номер недели года |
| dayofweek | Номер дня недели, где понедельник=0, воскресенье=6 |
| weekday | Номер дня недели, где понедельник=0, воскресенье=6 |
| weekday_name | Название дня недели (например, пятница) |
| quarter | Квартал даты: Янв=Мар = 1, Апр-Июн = 2 и т. д. |
| days_in_month | Количество дней в месяце даты и времени |
| is_month_start | Логическое значение, указывающее, является ли это первым днем месяца (определяется частотой) |
| is_month_end | Логическое значение, указывающее, является ли это последним днем месяца (определяется частотой) |
| is_quarter_start | Логическое значение, указывающее, является ли это первым днем квартала (определяется частотой) |
| is_quarter_end | Логическое значение, указывающее, является ли это последним днем квартала (определяется частотой) |
| is_year_start | Логическое значение, указывающее, является ли это первым днем года (определяется частотой) |
| is_year_end | Логическое значение, указывающее, является ли это последним днем года (определяется частотой) |
| is_leap_year | Логическое значение, указывающее, принадлежит ли дата високосному году |
Кроме того, если у вас есть Series со значениями datetimelike, то вы можете получить доступ к этим свойствам через .dt аксессор, см. документацию
Объекты DateOffset
В предыдущих примерах мы создавали объекты DatetimeIndex с различными частотами, передавая в freq ключевое слово строки частоты, такие как ‘M’, ‘W’, и ‘BM. Под капотом эти строки частоты переводятся в экземпляр DateOffset pandas, который представляет собой регулярное увеличение частоты. Специфическая логика смещения, такая как «месяц», «рабочий день» или «один час», представлена в различных подклассах.
| Имя класса | Описание |
|---|---|
| DateOffset | Общий класс смещения, по умолчанию равен 1 календарному дню |
| BDay | рабочий день (день недели) |
| CDay | настраиваемый рабочий день (экспериментально) |
| Week | одна неделя, необязательно привязанная к дню недели |
| WeekOfMonth | x-й день y-й недели каждого месяца |
| LastWeekOfMonth | x-й день последней недели каждого месяца |
| MonthEnd | конец календарного месяца |
| MonthBegin | начало календарного месяца |
| BMonthEnd | конец рабочего месяца |
| BMonthBegin | начало рабочего месяца |
| CBMonthEnd | конец настраиваемого рабочего месяца |
| CBMonthBegin | начало настраиваемого рабочего месяца |
| SemiMonthEnd | 15-е число (или другой day_of_month) и конец календарного месяца |
| SemiMonthBegin | 15-е число (или другой day_of_month) и начало календарного месяца |
| 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 [85]: d = datetime(2008, 8, 18, 9, 0) In [86]: d + relativedelta(months=4, days=5) Out[86]: datetime.datetime(2008, 12, 23, 9, 0)
Мы могли бы сделать то же самое с DateOffset:
In [87]: from pandas.tseries.offsets import *
In [88]: d + DateOffset(months=4, days=5)
Out[88]: 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 [89]: d - 5 * BDay()
Out[89]: Timestamp('2008-08-11 09:00:00')
In [90]: d + BMonthEnd()
Out[90]: Timestamp('2008-08-29 09:00:00')
Методы rollforward и rollback делают ровно то, что вы ожидаете:
In [91]: d
Out[91]: datetime.datetime(2008, 8, 18, 9, 0)
In [92]: offset = BMonthEnd()
In [93]: offset.rollforward(d)
Out[93]: Timestamp('2008-08-29 09:00:00')
In [94]: offset.rollback(d)
Out[94]: Timestamp('2008-07-31 09:00:00')
Стоит обязательно изучить модуль pandas.tseries.offsets и различные строки документации для классов.
Эти операции (apply, rollforward и rollback) по умолчанию сохраняют информацию о времени (час, минута и т. д.). Для сброса времени используйте normalize=True ключевое слово при создании экземпляра смещения. Если normalize=True, результат нормализуется после применения функции.
In [95]: day = Day()
In [96]: day.apply(pd.Timestamp('2014-01-01 09:00'))
Out[96]: Timestamp('2014-01-02 09:00:00')
In [97]: day = Day(normalize=True)
In [98]: day.apply(pd.Timestamp('2014-01-01 09:00'))
Out[98]: Timestamp('2014-01-02 00:00:00')
In [99]: hour = Hour()
In [100]: hour.apply(pd.Timestamp('2014-01-01 22:00'))
Out[100]: Timestamp('2014-01-01 23:00:00')
In [101]: hour = Hour(normalize=True)
In [102]: hour.apply(pd.Timestamp('2014-01-01 22:00'))
Out[102]: Timestamp('2014-01-01 00:00:00')
In [103]: hour.apply(pd.Timestamp('2014-01-01 23:00'))
Out[103]: Timestamp('2014-01-02 00:00:00')
Параметрические смещения
Некоторые смещения могут быть «параметризованы» при создании, что приводит к различным результатам. Например, смещение Week для генерации еженедельных данных принимает параметр weekday, который приводит к тому, что сгенерированные даты всегда находятся в определенный день недели:
In [104]: d
Out[104]: datetime.datetime(2008, 8, 18, 9, 0)
In [105]: d + Week()
Out[105]: Timestamp('2008-08-25 09:00:00')
In [106]: d + Week(weekday=4)
Out[106]: Timestamp('2008-08-22 09:00:00')
In [107]: (d + Week(weekday=4)).weekday()
Out[107]: 4
In [108]: d - Week()
Out[108]: Timestamp('2008-08-11 09:00:00')
normalize параметр будет действовать для сложения и вычитания.
In [109]: d + Week(normalize=True)
Out[109]: Timestamp('2008-08-25 00:00:00')
In [110]: d - Week(normalize=True)
Out[110]: Timestamp('2008-08-11 00:00:00')
Другой пример — параметризация YearEnd с конкретным конечным месяцем:
In [111]: d + YearEnd()
Out[111]: Timestamp('2008-12-31 09:00:00')
In [112]: d + YearEnd(month=6)
Out[112]: Timestamp('2009-06-30 09:00:00')
Использование смещений с Series / DatetimeIndex
Смещения могут использоваться как с Series, так и с DatetimeIndex, чтобы применить смещение к каждому элементу.
In [113]: rng = pd.date_range('2012-01-01', '2012-01-03')
In [114]: s = pd.Series(rng)
In [115]: rng
Out[115]: DatetimeIndex(['2012-01-01', '2012-01-02', '2012-01-03'], dtype='datetime64[ns]', freq='D')
In [116]: rng + DateOffset(months=2)
Out[116]: DatetimeIndex(['2012-03-01', '2012-03-02', '2012-03-03'], dtype='datetime64[ns]', freq='D')
In [117]: s + DateOffset(months=2)
Out[117]:
0 2012-03-01
1 2012-03-02
2 2012-03-03
dtype: datetime64[ns]
In [118]: s - DateOffset(months=2)
Out[118]:
0 2011-11-01
1 2011-11-02
2 2011-11-03
dtype: datetime64[ns]
Если класс смещения напрямую соответствует Timedelta (Day, Hour, Minute, Second, Micro, Milli, Nano), его можно использовать точно так же, как Timedelta — см. раздел Временные интервалы для получения дополнительных примеров.
In [119]: s - Day(2)
Out[119]:
0 2011-12-30
1 2011-12-31
2 2012-01-01
dtype: datetime64[ns]
In [120]: td = s - pd.Series(pd.date_range('2011-12-29', '2011-12-31'))
In [121]: td
Out[121]:
0 3 days
1 3 days
2 3 days
dtype: timedelta64[ns]
In [122]: td + Minute(15)
Out[122]:
0 3 days 00:15:00
1 3 days 00:15:00
2 3 days 00:15:00
dtype: timedelta64[ns]
Обратите внимание, что некоторые смещения (например, BQuarterEnd) не имеют векторизованной реализации. Их все равно можно использовать, но они могут рассчитываться значительно медленнее и вызовут PerformanceWarning
In [123]: rng + BQuarterEnd() Out[123]: DatetimeIndex(['2012-03-30', '2012-03-30', '2012-03-30'], dtype='datetime64[ns]', freq=None)
Настраиваемые рабочие дни (Экспериментально)
Класс CDay или CustomBusinessDay предоставляет параметрический BusinessDay класс, который можно использовать для создания настраиваемых календарей рабочих дней, учитывающих местные праздники и местные выходные.
В качестве интересного примера рассмотрим Египет, где выходные — пятница и суббота.
In [124]: from pandas.tseries.offsets import CustomBusinessDay
In [125]: weekmask_egypt = 'Sun Mon Tue Wed Thu'
# They also observe International Workers' Day so let's
# add that for a couple of years
In [126]: holidays = ['2012-05-01', datetime(2013, 5, 1), np.datetime64('2014-05-01')]
In [127]: bday_egypt = CustomBusinessDay(holidays=holidays, weekmask=weekmask_egypt)
In [128]: dt = datetime(2013, 4, 30)
In [129]: dt + 2 * bday_egypt
Out[129]: Timestamp('2013-05-05 00:00:00')
Давайте отобразим дни недели
In [130]: dts = pd.date_range(dt, periods=5, freq=bday_egypt)
In [131]: pd.Series(dts.weekday, dts).map(pd.Series('Mon Tue Wed Thu Fri Sat Sun'.split()))
Out[131]:
2013-04-30 Tue
2013-05-02 Thu
2013-05-05 Sun
2013-05-06 Mon
2013-05-07 Tue
Freq: C, dtype: object
С версии v0.14 календари праздников можно использовать для предоставления списка праздников. Подробнее см. раздел календарь праздников.
In [132]: from pandas.tseries.holiday import USFederalHolidayCalendar
In [133]: bday_us = CustomBusinessDay(calendar=USFederalHolidayCalendar())
# Friday before MLK Day
In [134]: dt = datetime(2014, 1, 17)
# Tuesday after MLK Day (Monday is skipped because it's a holiday)
In [135]: dt + bday_us
Out[135]: Timestamp('2014-01-21 00:00:00')
Месячные смещения, учитывающие определенный календарь праздников, можно определить обычным способом.
In [136]: from pandas.tseries.offsets import CustomBusinessMonthBegin
In [137]: bmth_us = CustomBusinessMonthBegin(calendar=USFederalHolidayCalendar())
# Skip new years
In [138]: dt = datetime(2013, 12, 17)
In [139]: dt + bmth_us
Out[139]: Timestamp('2014-01-02 00:00:00')
# Define date index with custom offset
In [140]: pd.DatetimeIndex(start='20100101',end='20120101',freq=bmth_us)
Out[140]:
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. Важно отметить, что так как CustomBusinessDay является параметризованным типом, экземпляры CustomBusinessDay могут отличаться, и это не обнаруживается из строки частоты ‘C’. Поэтому пользователь должен гарантировать, что строка частоты ‘C’ используется последовательно в приложении пользователя.
Часы работы
Класс BusinessHour предоставляет представление часов работы BusinessDay, позволяя использовать конкретные начальное и конечное время.
По умолчанию, BusinessHour использует часы работы с 9:00 до 17:00. Добавление BusinessHour увеличит Timestamp на час. Если целевое значение Timestamp находится вне часов работы, переместитесь к ближайшим часам работы, а затем увеличьте его. Если результат превышает конечное время работы, остаток добавляется к следующему рабочему дню.
In [141]: bh = BusinessHour()
In [142]: bh
Out[142]: <BusinessHour: BH=09:00-17:00>
# 2014-08-01 is Friday
In [143]: pd.Timestamp('2014-08-01 10:00').weekday()
Out[143]: 4
In [144]: pd.Timestamp('2014-08-01 10:00') + bh
Out[144]: Timestamp('2014-08-01 11:00:00')
# Below example is the same as: pd.Timestamp('2014-08-01 09:00') + bh
In [145]: pd.Timestamp('2014-08-01 08:00') + bh
Out[145]: Timestamp('2014-08-01 10:00:00')
# If the results is on the end time, move to the next business day
In [146]: pd.Timestamp('2014-08-01 16:00') + bh
Out[146]: Timestamp('2014-08-04 09:00:00')
# Remainings are added to the next day
In [147]: pd.Timestamp('2014-08-01 16:30') + bh
Out[147]: Timestamp('2014-08-04 09:30:00')
# Adding 2 business hours
In [148]: pd.Timestamp('2014-08-01 10:00') + BusinessHour(2)
Out[148]: Timestamp('2014-08-01 12:00:00')
# Subtracting 3 business hours
In [149]: pd.Timestamp('2014-08-01 10:00') + BusinessHour(-3)
Out[149]: Timestamp('2014-07-31 15:00:00')
Также вы можете указать время start и end с помощью ключевых слов. Аргумент должен быть str, который имеет представление hour:minute или экземпляр datetime.time. Указание секунд, микросекунд и наносекунд как часов работы приводит к ValueError.
In [150]: bh = BusinessHour(start='11:00', end=time(20, 0))
In [151]: bh
Out[151]: <BusinessHour: BH=11:00-20:00>
In [152]: pd.Timestamp('2014-08-01 13:00') + bh
Out[152]: Timestamp('2014-08-01 14:00:00')
In [153]: pd.Timestamp('2014-08-01 09:00') + bh
Out[153]: Timestamp('2014-08-01 12:00:00')
In [154]: pd.Timestamp('2014-08-01 18:00') + bh
Out[154]: Timestamp('2014-08-01 19:00:00')
Передача времени start позже, чем end представляет полночь в часах работы. В этом случае часы работы превышают полночь и перекрываются со следующим днем. Действительные часы работы определяются тем, начались ли они с действительного BusinessDay.
In [155]: bh = BusinessHour(start='17:00', end='09:00')
In [156]: bh
Out[156]: <BusinessHour: BH=17:00-09:00>
In [157]: pd.Timestamp('2014-08-01 17:00') + bh
Out[157]: Timestamp('2014-08-01 18:00:00')
In [158]: pd.Timestamp('2014-08-01 23:00') + bh
Out[158]: Timestamp('2014-08-02 00:00:00')
# Although 2014-08-02 is Satuaday,
# it is valid because it starts from 08-01 (Friday).
In [159]: pd.Timestamp('2014-08-02 04:00') + bh
Out[159]: 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 [160]: pd.Timestamp('2014-08-04 04:00') + bh
Out[160]: 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 [161]: BusinessHour().rollback(pd.Timestamp('2014-08-02 15:00'))
Out[161]: Timestamp('2014-08-01 17:00:00')
In [162]: BusinessHour().rollforward(pd.Timestamp('2014-08-02 15:00'))
Out[162]: 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 [163]: BusinessHour().apply(pd.Timestamp('2014-08-02 15:00'))
Out[163]: Timestamp('2014-08-04 10:00:00')
# BusinessDay results (for reference)
In [164]: BusinessHour().rollforward(pd.Timestamp('2014-08-02'))
Out[164]: 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 [165]: BusinessHour().apply(pd.Timestamp('2014-08-02'))
Out[165]: Timestamp('2014-08-04 10:00:00')
BusinessHour считает субботу и воскресенье выходными. Чтобы использовать произвольные выходные дни, вы можете использовать смещение CustomBusinessHour, см. Специальные часы работы:
Специальные часы работы
Новое в версии 0.18.1.
CustomBusinessHour представляет собой сочетание BusinessHour и CustomBusinessDay, что позволяет указать произвольные выходные дни. CustomBusinessHour работает так же, как BusinessHour за исключением пропуска указанных специальных выходных.
In [166]: from pandas.tseries.holiday import USFederalHolidayCalendar
In [167]: bhour_us = CustomBusinessHour(calendar=USFederalHolidayCalendar())
# Friday before MLK Day
In [168]: dt = datetime(2014, 1, 17, 15)
In [169]: dt + bhour_us
Out[169]: Timestamp('2014-01-17 16:00:00')
# Tuesday after MLK Day (Monday is skipped because it's a holiday)
In [170]: dt + bhour_us * 2
Out[170]: Timestamp('2014-01-21 09:00:00')
Вы можете использовать ключевые аргументы, поддерживаемые BusinessHour и CustomBusinessDay.
In [171]: 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 [172]: dt + bhour_mon * 2
Out[172]: Timestamp('2014-01-21 10:00:00')
Псевдонимы смещений
Для полезных общих частот временных рядов задано несколько строковых псевдонимов. Мы будем называть эти псевдонимы псевдонимами смещения (ранее назывались правилами времени до версии v0.8.0).
| Псевдоним | Описание |
|---|---|
| B | частота рабочих дней |
| C | частота специальных рабочих дней (экспериментально) |
| D | частота календарных дней |
| W | недельная частота |
| M | частота конца месяца |
| SM | частота конца полумесяца (15-е и конец месяца) |
| BM | частота конца рабочего месяца |
| CBM | частота конца специального рабочего месяца |
| MS | частота начала месяца |
| SMS | частота начала полумесяца (1-е и 15-е) |
| BMS | частота начала рабочего месяца |
| CBMS | частота начала специального рабочего месяца |
| Q | частота конца квартала |
| BQ | частота конца квартала рабочих дней |
| QS | частота начала квартала |
| BQS | частота начала квартала рабочих дней |
| A | частота конца года |
| BA | частота конца года рабочих дней |
| AS | частота начала года |
| BAS | частота начала года рабочих дней |
| BH | частота рабочих часов |
| H | частота в час |
| T, min | частота в минуту |
| S | частота в секунду |
| L, ms | миллисекунды |
| U, us | микросекунды |
| N | наносекунды |
Комбинирование псевдонимов
Как мы видели ранее, псевдоним и экземпляр смещения взаимозаменяемы в большинстве функций:
In [173]: pd.date_range(start, periods=5, freq='B')
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=5, freq=BDay())
Out[174]:
DatetimeIndex(['2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06',
'2011-01-07'],
dtype='datetime64[ns]', freq='B')
Вы можете комбинировать смещения дней и внутридневных смещений:
In [175]: pd.date_range(start, periods=10, freq='2h20min')
Out[175]:
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 [176]: pd.date_range(start, periods=10, freq='1D10U')
Out[176]:
DatetimeIndex([ '2011-01-01 00:00:00', '2011-01-02 00:00:00.000010',
'2011-01-03 00:00:00.000020', '2011-01-04 00:00:00.000030',
'2011-01-05 00:00:00.000040', '2011-01-06 00:00:00.000050',
'2011-01-07 00:00:00.000060', '2011-01-08 00:00:00.000070',
'2011-01-09 00:00:00.000080', '2011-01-10 00:00:00.000090'],
dtype='datetime64[ns]', freq='86400000010U')
Якорные смещения
Для некоторых частот вы можете указать суффикс якорной точки:
| Псевдоним | Описание |
|---|---|
| W-SUN | недельная частота (воскресенья). То же самое, что и ‘W’ |
| W-MON | недельная частота (понедельники) |
| W-TUE | недельная частота (вторники) |
| W-WED | недельная частота (среды) |
| W-THU | недельная частота (четверги) |
| W-FRI | недельная частота (пятницы) |
| W-SAT | недельная частота (субботы) |
| (B)Q(S)-DEC | квартальная частота, конец года в декабре. То же самое, что и ‘Q’ |
| (B)Q(S)-JAN | квартальная частота, конец года в январе |
| (B)Q(S)-FEB | квартальная частота, конец года в феврале |
| (B)Q(S)-MAR | квартальная частота, конец года в марте |
| (B)Q(S)-APR | квартальная частота, конец года в апреле |
| (B)Q(S)-MAY | квартальная частота, конец года в мае |
| (B)Q(S)-JUN | квартальная частота, конец года в июне |
| (B)Q(S)-JUL | квартальная частота, конец года в июле |
| (B)Q(S)-AUG | квартальная частота, конец года в августе |
| (B)Q(S)-SEP | квартальная частота, конец года в сентябре |
| (B)Q(S)-OCT | квартальная частота, конец года в октябре |
| (B)Q(S)-NOV | квартальная частота, конец года в ноябре |
| (B)A(S)-DEC | годовая частота, якорная точка в конце декабря. То же самое, что и ‘A’ |
| (B)A(S)-JAN | годовая частота, якорная точка в конце января |
| (B)A(S)-FEB | годовая частота, якорная точка в конце февраля |
| (B)A(S)-MAR | годовая частота, якорная точка в конце марта |
| (B)A(S)-APR | годовая частота, якорная точка в конце апреля |
| (B)A(S)-MAY | годовая частота, якорная точка в конце мая |
| (B)A(S)-JUN | годовая частота, якорная точка в конце июня |
| (B)A(S)-JUL | годовая частота, якорная точка в конце июля |
| (B)A(S)-AUG | годовая частота, якорная точка в конце августа |
| (B)A(S)-SEP | годовая частота, якорная точка в конце сентября |
| (B)A(S)-OCT | годовая частота, якорная точка в конце октября |
| (B)A(S)-NOV | годовая частота, якорная точка в конце ноября |
Эти значения могут быть использованы в качестве аргументов date_range, bdate_range, конструкторов для DatetimeIndex, а также в различных других функциях временных рядов в pandas.
Семантика якорных смещений
Для смещений, закрепленных к началу или концу определённой частоты (MonthEnd, MonthBegin, WeekEnd, и т. д.), применяются следующие правила для прокрутки вперёд и назад.
Когда n не равно 0, если заданная дата не совпадает с точкой якорной точки, она перемещается к ближайшей точке якорной точки, и затем перемещается на |n|-1 дополнительных шагов вперёд или назад.
In [177]: pd.Timestamp('2014-01-02') + MonthBegin(n=1)
Out[177]: Timestamp('2014-02-01 00:00:00')
In [178]: pd.Timestamp('2014-01-02') + MonthEnd(n=1)
Out[178]: Timestamp('2014-01-31 00:00:00')
In [179]: pd.Timestamp('2014-01-02') - MonthBegin(n=1)
Out[179]: Timestamp('2014-01-01 00:00:00')
In [180]: pd.Timestamp('2014-01-02') - MonthEnd(n=1)
Out[180]: Timestamp('2013-12-31 00:00:00')
In [181]: pd.Timestamp('2014-01-02') + MonthBegin(n=4)
Out[181]: Timestamp('2014-05-01 00:00:00')
In [182]: pd.Timestamp('2014-01-02') - MonthBegin(n=4)
Out[182]: Timestamp('2013-10-01 00:00:00')
Если заданная дата совпадает с точкой якорной точки, она перемещается на |n| точек вперёд или назад.
In [183]: pd.Timestamp('2014-01-01') + MonthBegin(n=1)
Out[183]: Timestamp('2014-02-01 00:00:00')
In [184]: pd.Timestamp('2014-01-31') + MonthEnd(n=1)
Out[184]: Timestamp('2014-02-28 00:00:00')
In [185]: pd.Timestamp('2014-01-01') - MonthBegin(n=1)
Out[185]: Timestamp('2013-12-01 00:00:00')
In [186]: pd.Timestamp('2014-01-31') - MonthEnd(n=1)
Out[186]: Timestamp('2013-12-31 00:00:00')
In [187]: pd.Timestamp('2014-01-01') + MonthBegin(n=4)
Out[187]: Timestamp('2014-05-01 00:00:00')
In [188]: pd.Timestamp('2014-01-31') - MonthBegin(n=4)
Out[188]: Timestamp('2013-10-01 00:00:00')
В случае, когда n=0, дата не перемещается, если она находится на якорной точке, иначе она переносится на следующую якорную точку.
In [189]: pd.Timestamp('2014-01-02') + MonthBegin(n=0)
Out[189]: Timestamp('2014-02-01 00:00:00')
In [190]: pd.Timestamp('2014-01-02') + MonthEnd(n=0)
Out[190]: Timestamp('2014-01-31 00:00:00')
In [191]: pd.Timestamp('2014-01-01') + MonthBegin(n=0)
Out[191]: Timestamp('2014-01-01 00:00:00')
In [192]: pd.Timestamp('2014-01-31') + MonthEnd(n=0)
Out[192]: Timestamp('2014-01-31 00:00:00')
Праздники/Календари праздников
Праздники и календари предоставляют простой способ определения правил праздников для использования с CustomBusinessDay или в других анализах, требующих предварительно определённого набора праздничных дней. Класс AbstractHolidayCalendar предоставляет все необходимые методы для возврата списка праздничных дней, и только rules необходимо определить в конкретном классе календаря праздников. Кроме того, атрибуты класса start_date и end_date определяют диапазон дат, для которого генерируются праздничные дни. Они должны быть переопределены в классе AbstractHolidayCalendar для применения диапазона ко всем подклассам календаря. USFederalHolidayCalendar — единственный существующий календарь, который главным образом служит примером для разработки других календарей.
Для праздников, которые происходят в фиксированные даты (например, День памяти США или 4 июля), правило соблюдения определяет, когда этот праздник отмечается, если он выпадает на выходной или какой-либо другой нерабочий день. Определённые правила соблюдения:
| Правило | Описание |
|---|---|
| nearest_workday | переместить субботу на пятницу, а воскресенье на понедельник |
| sunday_to_monday | перенести воскресенье на следующий понедельник |
| next_monday_or_tuesday | переместить субботу на понедельник, а воскресенье/понедельник на вторник |
| previous_friday | переместить субботу и воскресенье на предыдущую пятницу |
| next_monday | переместить субботу и воскресенье на следующий понедельник |
Пример того, как определяются праздники и календари праздников:
In [193]: from pandas.tseries.holiday import Holiday, USMemorialDay,\
.....: AbstractHolidayCalendar, nearest_workday, MO
.....:
In [194]: 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 [195]: cal = ExampleCalendar()
In [196]: cal.holidays(datetime(2012, 1, 1), datetime(2012, 12, 31))
Out[196]: DatetimeIndex(['2012-05-28', '2012-07-04', '2012-10-08'], dtype='datetime64[ns]', freq=None)
Используя этот календарь, создание индекса или выполнение арифметических операций со смещениями пропускает выходные и праздничные дни (то есть День памяти/4 июля). Например, ниже определено пользовательское смещение рабочего дня, использующее ExampleCalendar. Как и любое другое смещение, его можно использовать для создания DatetimeIndex или добавить к datetime или Timestamp объектам.
In [197]: from pandas.tseries.offsets import CDay
In [198]: pd.DatetimeIndex(start='7/1/2012', end='7/10/2012',
.....: freq=CDay(calendar=cal)).to_pydatetime()
.....:
Out[198]:
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 [199]: offset = CustomBusinessDay(calendar=cal)
In [200]: datetime(2012, 5, 25) + offset
Out[200]: Timestamp('2012-05-29 00:00:00')
In [201]: datetime(2012, 7, 3) + offset
Out[201]: Timestamp('2012-07-05 00:00:00')
In [202]: datetime(2012, 7, 3) + 2 * offset
Out[202]: Timestamp('2012-07-06 00:00:00')
In [203]: datetime(2012, 7, 6) + offset
Out[203]: Timestamp('2012-07-09 00:00:00')
Диапазоны определяются атрибутами класса start_date и end_date класса AbstractHolidayCalendar. Значения по умолчанию приведены ниже.
In [204]: AbstractHolidayCalendar.start_date
Out[204]: Timestamp('1970-01-01 00:00:00')
In [205]: AbstractHolidayCalendar.end_date
Out[205]: Timestamp('2030-12-31 00:00:00')
Эти даты можно переопределить, установив атрибуты как datetime/Timestamp/строку.
In [206]: AbstractHolidayCalendar.start_date = datetime(2012, 1, 1) In [207]: AbstractHolidayCalendar.end_date = datetime(2012, 12, 31) In [208]: cal.holidays() Out[208]: DatetimeIndex(['2012-05-28', '2012-07-04', '2012-10-08'], dtype='datetime64[ns]', freq=None)
Каждый класс календаря доступен по имени с помощью функции get_calendar, которая возвращает экземпляр класса выходных. Любой импортированный класс календаря будет автоматически доступен с помощью этой функции. Также, HolidayCalendarFactory предоставляет простой интерфейс для создания календарей, которые являются комбинациями календарей или календарей с дополнительными правилами.
In [209]: from pandas.tseries.holiday import get_calendar, HolidayCalendarFactory,\
.....: USLaborDay
.....:
In [210]: cal = get_calendar('ExampleCalendar')
In [211]: cal.rules
Out[211]:
[Holiday: MemorialDay (month=5, day=31, offset=<DateOffset: kwds={'weekday': MO(-1)}>),
Holiday: July 4th (month=7, day=4, observance=<function nearest_workday at 0x7ff271135aa0>),
Holiday: Columbus Day (month=10, day=1, offset=<DateOffset: kwds={'weekday': MO(+2)}>)]
In [212]: new_cal = HolidayCalendarFactory('NewExampleCalendar', cal, USLaborDay)
In [213]: new_cal.rules
Out[213]:
[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 0x7ff271135aa0>),
Holiday: MemorialDay (month=5, day=31, offset=<DateOffset: kwds={'weekday': MO(-1)}>)]
Методы экземпляров, связанные с временными рядами
Сдвиг / запаздывание
Иногда нужно сдвинуть или замедлить значения во временном ряду вперед или назад во времени. Метод для этого — shift, который доступен для всех объектов pandas.
In [214]: ts = ts[:5] In [215]: ts.shift(1) Out[215]: 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 [216]: ts.shift(5, freq=offsets.BDay()) Out[216]: 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 [217]: ts.shift(5, freq='BM') Out[217]: 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 [218]: ts.tshift(5, freq='D') Out[218]: 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 [219]: dr = pd.date_range('1/1/2010', periods=3, freq=3 * offsets.BDay())
In [220]: ts = pd.Series(randn(3), index=dr)
In [221]: ts
Out[221]:
2010-01-01 0.532005
2010-01-06 0.544874
2010-01-11 -1.001788
Freq: 3B, dtype: float64
In [222]: ts.asfreq(BDay())
Out[222]:
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 [223]: ts.asfreq(BDay(), method='pad') Out[223]: 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 и, следовательно, более гибким. См. документацию изменений для сравнения с предыдущими версиями.
Pandas обладает простой, мощной и эффективной функциональностью для выполнения операций по перевыборке во время преобразования частоты (например, преобразование данных с частотой в секунду в данные с частотой в 5 минут). Это очень распространено в, но не ограничивается, финансовых приложениях.
.resample() — это группировка по времени, за которой следует метод уменьшения для каждой из ее групп.
Начиная с версии 0.18.1, функцию resample() можно использовать непосредственно из объектов DataFrameGroupBy, см. документацию по groupby.
Примечание
.resample() аналогично использованию операции .rolling() с временным сдвигом, см. обсуждение here <stats.moments.ts-versus-resampling>
См. некоторые примеры в руководстве для некоторых продвинутых стратегий
In [224]: rng = pd.date_range('1/1/2012', periods=100, freq='S')
In [225]: ts = pd.Series(np.random.randint(0, 500, len(rng)), index=rng)
In [226]: ts.resample('5Min').sum()
Out[226]:
2012-01-01 24390
Freq: 5T, dtype: int64
Функция resample очень гибкая и позволяет вам указать множество различных параметров для управления преобразованием частоты и операцией перевыборки.
Параметр how может быть именем функции или функцией numpy-массива, которая принимает массив и генерирует агрегированные значения:
In [227]: ts.resample('5Min').mean()
Out[227]:
2012-01-01 243.9
Freq: 5T, dtype: float64
In [228]: ts.resample('5Min').ohlc()
Out[228]:
open high low close
2012-01-01 161 495 1 245
In [229]: ts.resample('5Min').max()
Out[229]:
2012-01-01 495
Freq: 5T, dtype: int64
Любая функция, доступная через диспетчеризацию, может быть указана по имени для параметра how, включая sum, mean, std, sem, max, min, median, first, last, ohlc.
Для уменьшения выборки параметр closed можно установить в ‘left’ или ‘right’, чтобы указать, какой конец интервала закрыт:
In [230]: ts.resample('5Min', closed='right').mean()
Out[230]:
2011-12-31 23:55:00 161.000000
2012-01-01 00:00:00 244.737374
Freq: 5T, dtype: float64
In [231]: ts.resample('5Min', closed='left').mean()
Out[231]:
2012-01-01 243.9
Freq: 5T, dtype: float64
Параметры, такие как label и loffset, используются для управления метками результата. label указывает, метятся ли результаты началом или концом интервала. loffset выполняет временную корректировку меток результата.
In [232]: ts.resample('5Min').mean() # by default label='right'
Out[232]:
2012-01-01 243.9
Freq: 5T, dtype: float64
In [233]: ts.resample('5Min', label='left').mean()
Out[233]:
2012-01-01 243.9
Freq: 5T, dtype: float64
In [234]: ts.resample('5Min', label='left', loffset='1s').mean()
Out[234]:
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 [235]: ts[:2].resample('250L').asfreq()
Out[235]:
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 [236]: ts[:2].resample('250L').ffill()
Out[236]:
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 [237]: ts[:2].resample('250L').ffill(limit=2)
Out[237]:
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 [238]: rng = pd.date_range('2014-1-1', periods=100, freq='D') + pd.Timedelta('1s')
In [239]: ts = pd.Series(range(100), index=rng)
Если мы хотим перевыбрать весь диапазон ряда
In [240]: ts.resample('3T').sum()
Out[240]:
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 [241]: from functools import partial
In [242]: from pandas.tseries.frequencies import to_offset
In [243]: def round(t, freq):
.....: freq = to_offset(freq)
.....: return pd.Timestamp((t.value // freq.delta.value) * freq.delta.value)
.....:
In [244]: ts.groupby(partial(round, freq='3T')).sum()
Out[244]:
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 [245]: df = pd.DataFrame(np.random.randn(1000, 3),
.....: index=pd.date_range('1/1/2012', freq='S', periods=1000),
.....: columns=['A', 'B', 'C'])
.....:
In [246]: r = df.resample('3T')
In [247]: r.mean()
Out[247]:
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
Мы можем выбрать конкретный столбец или столбцы, используя стандартный метод обращения к элементу.
In [248]: r['A'].mean()
Out[248]:
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 [249]: r[['A','B']].mean()
Out[249]:
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 [250]: r['A'].agg([np.sum, np.mean, np.std])
Out[250]:
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 [251]: r['A'].agg({'result1' : np.sum,
.....: 'result2' : np.mean})
.....:
Out[251]:
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 [252]: r.agg([np.sum, np.mean])
Out[252]:
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 [253]: r.agg({'A' : np.sum,
.....: 'B' : lambda x: np.std(x, ddof=1)})
.....:
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
Имена функций также могут быть строками. Для того чтобы строка была допустимой, она должна быть реализована в объекте Resampled.
In [254]: r.agg({'A' : 'sum', 'B' : 'std'})
Out[254]:
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 [255]: r.agg({'A' : ['sum','std'], 'B' : ['mean','std'] })
Out[255]:
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
Если у DataFrame нет индекса datetimelike, но вместо этого вы хотите перевыбирать на основе столбца datetimelike в фрейме, его можно передать в ключевое слово on.
In [256]: df = pd.DataFrame({'date': pd.date_range('2015-01-01', freq='W', periods=5),
.....: 'a': np.arange(5)},
.....: index=pd.MultiIndex.from_arrays([
.....: [1,2,3,4,5],
.....: pd.date_range('2015-01-01', freq='W', periods=5)],
.....: names=['v','d']))
.....:
In [257]: df
Out[257]:
a date
v d
1 2015-01-04 0 2015-01-04
2 2015-01-11 1 2015-01-11
3 2015-01-18 2 2015-01-18
4 2015-01-25 3 2015-01-25
5 2015-02-01 4 2015-02-01
In [258]: df.resample('M', on='date').sum()
Out[258]:
a
date
2015-01-31 6
2015-02-28 4
Аналогично, если вы хотите перевыбрать по уровню datetimelike в MultiIndex, его имя или положение можно передать в ключевое слово level.
In [259]: df.resample('M', level='d').sum()
Out[259]:
a
d
2015-01-31 6
2015-02-28 4
Представление временного интервала
Регулярные интервалы времени представлены объектами Period в pandas, а последовательности объектов Period собираются в PeriodIndex, которые можно создать с помощью удобной функции period_range.
Период
Period представляет собой временной интервал (например, день, месяц, квартал и т. д.). Вы можете указать интервал с помощью ключевого слова freq с помощью псевдонима частоты, как показано ниже. Поскольку Period представляет собой интервал Period, он не может быть отрицательным, например, «-3D».
In [260]: pd.Period('2012', freq='A-DEC')
Out[260]: Period('2012', 'A-DEC')
In [261]: pd.Period('2012-1-1', freq='D')
Out[261]: Period('2012-01-01', 'D')
In [262]: pd.Period('2012-1-1 19:00', freq='H')
Out[262]: Period('2012-01-01 19:00', 'H')
In [263]: pd.Period('2012-1-1 19:00', freq='5H')
Out[263]: Period('2012-01-01 19:00', '5H')
Добавление и вычитание целых чисел из периодов сдвигает период на свою частоту. Арифметика не разрешена между Period с различной частотой (интервалом).
In [264]: p = pd.Period('2012', freq='A-DEC')
In [265]: p + 1
Out[265]: Period('2013', 'A-DEC')
In [266]: p - 3
Out[266]: Period('2009', 'A-DEC')
In [267]: p = pd.Period('2012-01', freq='2M')
In [268]: p + 2
Out[268]: Period('2012-05', '2M')
In [269]: p - 1
Out[269]: Period('2011-11', '2M')
In [270]: p == pd.Period('2012-01', freq='3M')
---------------------------------------------------------------------------
IncompatibleFrequency Traceback (most recent call last)
<ipython-input-270-ff54ce3238f5> in <module>()
----> 1 p == pd.Period('2012-01', freq='3M')
/home/joris/scipy/pandas/pandas/src/period.pyx in pandas._period._Period.__richcmp__ (pandas/src/period.c:11340)()
729 if other.freq != self.freq:
730 msg = _DIFFERENT_FREQ.format(self.freqstr, other.freqstr)
--> 731 raise IncompatibleFrequency(msg)
732 return PyObject_RichCompareBool(self.ordinal, other.ordinal, op)
733 elif other is tslib.NaT:
IncompatibleFrequency: Input has different freq=3M from Period(freq=2M)
Если частота Period суточная или выше (D, H, T, S, L, U, N)., offsets и объекты, подобные timedelta, могут быть добавлены, если результат может иметь ту же частоту. В противном случае будет выброшено исключение ValueError.
In [271]: p = pd.Period('2014-07-01 09:00', freq='H')
In [272]: p + Hour(2)
Out[272]: Period('2014-07-01 11:00', 'H')
In [273]: p + timedelta(minutes=120)
Out[273]: Period('2014-07-01 11:00', 'H')
In [274]: p + np.timedelta64(7200, 's')
Out[274]: 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 [275]: p = pd.Period('2014-07', freq='M')
In [276]: p + MonthEnd(3)
Out[276]: Period('2014-10', 'M')
In [1]: p + MonthBegin(3) Traceback ... ValueError: Input has different freq from Period(freq=M)
Вычитание экземпляров Period с одинаковой частотой вернет количество единиц частоты между ними:
In [277]: pd.Period('2012', freq='A-DEC') - pd.Period('2002', freq='A-DEC')
Out[277]: 10
PeriodIndex и period_range
Регулярные последовательности объектов Period могут быть собраны в PeriodIndex, который может быть построен с помощью удобной функции period_range:
In [278]: prng = pd.period_range('1/1/2011', '1/1/2012', freq='M')
In [279]: prng
Out[279]:
PeriodIndex(['2011-01', '2011-02', '2011-03', '2011-04', '2011-05', '2011-06',
'2011-07', '2011-08', '2011-09', '2011-10', '2011-11', '2011-12',
'2012-01'],
dtype='period[M]', freq='M')
Конструктор PeriodIndex также может быть использован напрямую:
In [280]: pd.PeriodIndex(['2011-1', '2011-2', '2011-3'], freq='M') Out[280]: PeriodIndex(['2011-01', '2011-02', '2011-03'], dtype='period[M]', freq='M')
Передача частоты, умноженной на несколько значений, выводит последовательность Period, у которой интервал умножается.
In [281]: pd.PeriodIndex(start='2014-01', freq='3M', periods=4) Out[281]: PeriodIndex(['2014-01', '2014-04', '2014-07', '2014-10'], dtype='period[3M]', freq='3M')
Так же, как и DatetimeIndex, PeriodIndex также может использоваться для индексации объектов pandas:
In [282]: ps = pd.Series(np.random.randn(len(prng)), prng) In [283]: ps Out[283]: 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 [284]: idx = pd.period_range('2014-07-01 09:00', periods=5, freq='H')
In [285]: idx
Out[285]:
PeriodIndex(['2014-07-01 09:00', '2014-07-01 10:00', '2014-07-01 11:00',
'2014-07-01 12:00', '2014-07-01 13:00'],
dtype='period[H]', freq='H')
In [286]: idx + Hour(2)
Out[286]:
PeriodIndex(['2014-07-01 11:00', '2014-07-01 12:00', '2014-07-01 13:00',
'2014-07-01 14:00', '2014-07-01 15:00'],
dtype='period[H]', freq='H')
In [287]: idx = pd.period_range('2014-07', periods=5, freq='M')
In [288]: idx
Out[288]: PeriodIndex(['2014-07', '2014-08', '2014-09', '2014-10', '2014-11'], dtype='period[M]', freq='M')
In [289]: idx + MonthEnd(3)
Out[289]: PeriodIndex(['2014-10', '2014-11', '2014-12', '2015-01', '2015-02'], dtype='period[M]', freq='M')
PeriodIndex имеет собственный тип данных, названный period, см. Типы данных Period.
Типы данных Period
Впервые добавлено в версии 0.19.0.
PeriodIndex имеет пользовательский тип данных period. Это расширенный тип данных pandas, подобный типу данных с часовым поясом сознательным о часовом поясе (datetime64[ns, tz]).
Тип данных period содержит атрибут freq и представляется с помощью period[freq], таких как period[D] или period[M], используя строки частот.
In [290]: pi = pd.period_range('2016-01-01', periods=3, freq='M')
In [291]: pi
Out[291]: PeriodIndex(['2016-01', '2016-02', '2016-03'], dtype='period[M]', freq='M')
In [292]: pi.dtype
Out[292]: period[M]
Тип данных period может использоваться в .astype(...). Он позволяет изменять freq PeriodIndex, например, .asfreq(), и преобразовывать DatetimeIndex в PeriodIndex, например, to_period():
# change monthly freq to daily freq
In [293]: pi.astype('period[D]')
Out[293]: PeriodIndex(['2016-01-31', '2016-02-29', '2016-03-31'], dtype='period[D]', freq='D')
# convert to DatetimeIndex
In [294]: pi.astype('datetime64[ns]')
Out[294]: DatetimeIndex(['2016-01-01', '2016-02-01', '2016-03-01'], dtype='datetime64[ns]', freq='MS')
# convert to PeriodIndex
In [295]: dti = pd.date_range('2011-01-01', freq='M', periods=3)
In [296]: dti
Out[296]: DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31'], dtype='datetime64[ns]', freq='M')
In [297]: dti.astype('period[M]')
Out[297]: PeriodIndex(['2011-01', '2011-02', '2011-03'], dtype='period[M]', freq='M')
Частичное строковое индексирование PeriodIndex
Вы можете передавать даты и строки в Series и DataFrame с использованием PeriodIndex, так же, как и DatetimeIndex. Подробности см. в Частичное строковое индексирование DatetimeIndex.
In [298]: ps['2011-01'] Out[298]: -1.022669594890105 In [299]: ps[datetime(2011, 12, 25):] Out[299]: 2011-12 0.492003 2012-01 0.193420 Freq: M, dtype: float64 In [300]: ps['10/31/2011':'12/31/2011'] Out[300]: 2011-10 -0.310466 2011-11 0.543957 2011-12 0.492003 Freq: M, dtype: float64
Передача строки, представляющей частоту ниже PeriodIndex, возвращает частично обрезанные данные.
In [301]: ps['2011']
Out[301]:
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 [302]: dfp = pd.DataFrame(np.random.randn(600,1),
.....: columns=['A'],
.....: index=pd.period_range('2013-01-01 9:00', periods=600, freq='T'))
.....:
In [303]: dfp
Out[303]:
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 [304]: dfp['2013-01-01 10H']
Out[304]:
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 [305]: dfp['2013-01-01 10H':'2013-01-01 11H']
Out[305]:
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 [306]: p = pd.Period('2011', freq='A-DEC')
In [307]: p
Out[307]: Period('2011', 'A-DEC')
Мы можем преобразовать его в ежемесячную частоту. С помощью параметра how мы можем указать, нужно ли вернуть начальный или конечный месяц:
In [308]: p.asfreq('M', how='start')
Out[308]: Period('2011-01', 'M')
In [309]: p.asfreq('M', how='end')
Out[309]: Period('2011-12', 'M')
Для удобства используются сокращения «s» и «e»:
In [310]: p.asfreq('M', 's')
Out[310]: Period('2011-01', 'M')
In [311]: p.asfreq('M', 'e')
Out[311]: Period('2011-12', 'M')
Преобразование в «сверхпериод» (например, годовая частота является сверхпериодом квартальной частоты) автоматически возвращает сверхпериод, который включает входной период:
In [312]: p = pd.Period('2011-12', freq='M')
In [313]: p.asfreq('A-NOV')
Out[313]: Period('2012', 'A-NOV')
Обратите внимание, что так как мы перешли к годовой частоте, заканчивающейся в ноябре, ежемесячный период декабря 2011 года фактически находится в периоде 2012 A-НОЯБРЯ.
Преобразования периодов с привязанными частотами особенно полезны при работе с различными квартальными данными, распространёнными в экономике, бизнесе и других областях. Многие организации определяют кварталы относительно месяца, в котором начинается и заканчивается их финансовый год. Таким образом, первый квартал 2011 года мог начаться в 2010 году или в течение нескольких месяцев 2011 года. С помощью привязанных частот pandas работает со всеми квартальными частотами Q-JAN через Q-DEC.
Q-DEC определяет регулярные календарные кварталы:
In [314]: p = pd.Period('2012Q1', freq='Q-DEC')
In [315]: p.asfreq('D', 's')
Out[315]: Period('2012-01-01', 'D')
In [316]: p.asfreq('D', 'e')
Out[316]: Period('2012-03-31', 'D')
Q-MAR определяет конец финансового года в марте:
In [317]: p = pd.Period('2011Q4', freq='Q-MAR')
In [318]: p.asfreq('D', 's')
Out[318]: Period('2011-01-01', 'D')
In [319]: p.asfreq('D', 'e')
Out[319]: Period('2011-03-31', 'D')
Преобразование между представлениями
Данные, помеченные временной меткой, можно преобразовать в данные PeriodIndex, используя to_period, и наоборот, используя to_timestamp:
In [320]: rng = pd.date_range('1/1/2012', periods=5, freq='M')
In [321]: ts = pd.Series(np.random.randn(len(rng)), index=rng)
In [322]: ts
Out[322]:
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 [323]: ps = ts.to_period()
In [324]: ps
Out[324]:
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 [325]: ps.to_timestamp()
Out[325]:
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 [326]: ps.to_timestamp('D', how='s')
Out[326]:
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 часов утра конца месяца, следующего за концом квартала:
In [327]: prng = pd.period_range('1990Q1', '2000Q4', freq='Q-NOV')
In [328]: ts = pd.Series(np.random.randn(len(prng)), prng)
In [329]: ts.index = (prng.asfreq('M', 'e') + 1).asfreq('H', 's') + 9
In [330]: ts.head()
Out[330]:
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 [331]: span = pd.period_range('1215-01-01', '1381-01-01', freq='D')
In [332]: span
Out[332]:
PeriodIndex(['1215-01-01', '1215-01-02', '1215-01-03', '1215-01-04',
'1215-01-05', '1215-01-06', '1215-01-07', '1215-01-08',
'1215-01-09', '1215-01-10',
...
'1380-12-23', '1380-12-24', '1380-12-25', '1380-12-26',
'1380-12-27', '1380-12-28', '1380-12-29', '1380-12-30',
'1380-12-31', '1381-01-01'],
dtype='period[D]', length=60632, freq='D')
Для преобразования из представления int64 YYYYMMDD.
In [333]: s = pd.Series([20121231, 20141130, 99991231])
In [334]: s
Out[334]:
0 20121231
1 20141130
2 99991231
dtype: int64
In [335]: def conv(x):
.....: return pd.Period(year = x // 10000, month = x//100 % 100, day = x%100, freq='D')
.....:
In [336]: s.apply(conv)
Out[336]:
0 2012-12-31
1 2014-11-30
2 9999-12-31
dtype: object
In [337]: s.apply(conv)[2]
Out[337]: Period('9999-12-31', 'D')
Эти значения легко преобразовать в PeriodIndex
In [338]: span = pd.PeriodIndex(s.apply(conv)) In [339]: span Out[339]: PeriodIndex(['2012-12-31', '2014-11-30', '9999-12-31'], dtype='period[D]', freq='D')
Обработка часовых поясов
Pandas предоставляет широкую поддержку работы с временными метками в разных часовых поясах с использованием библиотек pytz и dateutil. Поддержка dateutil появилась в версии 0.14.1 и в настоящее время поддерживается только для фиксированных смещений и зон tzfile. По умолчанию используется библиотека pytz. Поддержка dateutil предоставляется для совместимости с другими приложениями, например, если вы используете dateutil в других пакетах python.
Работа с часовыми поясами
По умолчанию объекты pandas не учитывают часовой пояс:
In [340]: rng = pd.date_range('3/6/2012 00:00', periods=15, freq='D')
In [341]: rng.tz is None
Out[341]: True
Для задания часового пояса можно использовать ключевое слово tz к date_range и другим функциям. Строки часовых поясов Dateutil отличаются от часовых поясов pytz тем, что начинаются с dateutil/.
- В
pytzвы можете найти список распространённых (и менее распространённых) часовых поясов, используяfrom pytz import common_timezones, all_timezones. -
dateutilиспользует часовые пояса ОС, поэтому доступный фиксированный список отсутствует. Для распространённых зон имена совпадают сpytz.
# pytz
In [342]: rng_pytz = pd.date_range('3/6/2012 00:00', periods=10, freq='D',
.....: tz='Europe/London')
.....:
In [343]: rng_pytz.tz
Out[343]: <DstTzInfo 'Europe/London' LMT-1 day, 23:59:00 STD>
# dateutil
In [344]: rng_dateutil = pd.date_range('3/6/2012 00:00', periods=10, freq='D',
.....: tz='dateutil/Europe/London')
.....:
In [345]: rng_dateutil.tz
Out[345]: tzfile('/usr/share/zoneinfo/Europe/London')
# dateutil - utc special case
In [346]: rng_utc = pd.date_range('3/6/2012 00:00', periods=10, freq='D',
.....: tz=dateutil.tz.tzutc())
.....:
In [347]: rng_utc.tz
Out[347]: tzutc()
Обратите внимание, что часовой пояс UTC является особым случаем в dateutil и должен быть явно создан как экземпляр dateutil.tz.tzutc. Вы также можете сначала явно создать другие часовые пояса, что даёт вам больший контроль над используемым часовым поясом:
# pytz
In [348]: tz_pytz = pytz.timezone('Europe/London')
In [349]: rng_pytz = pd.date_range('3/6/2012 00:00', periods=10, freq='D',
.....: tz=tz_pytz)
.....:
In [350]: rng_pytz.tz == tz_pytz
Out[350]: True
# dateutil
In [351]: tz_dateutil = dateutil.tz.gettz('Europe/London')
In [352]: rng_dateutil = pd.date_range('3/6/2012 00:00', periods=10, freq='D',
.....: tz=tz_dateutil)
.....:
In [353]: rng_dateutil.tz == tz_dateutil
Out[353]: True
Временные метки, подобно объекту Python datetime.datetime, могут быть либо без часового пояса, либо с часовым поясом. Объекты временных рядов и DatetimeIndex без часового пояса могут быть локализованы с помощью tz_localize:
In [354]: ts = pd.Series(np.random.randn(len(rng)), rng)
In [355]: ts_utc = ts.tz_localize('UTC')
In [356]: ts_utc
Out[356]:
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 [357]: ts_utc.tz_convert('US/Eastern')
Out[357]:
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'))). Вместо этого необходимо локализовать дату и время, используя метод localize для часового пояса.
Внутри все временные метки хранятся в формате UTC. Скалярные значения из DatetimeIndex с часовым поясом будут иметь свои поля (день, час, минута) локализованные в часовом поясе. Однако временные метки с одинаковым значением UTC по-прежнему считаются равными, даже если они находятся в разных часовых поясах:
In [358]: rng_eastern = rng_utc.tz_convert('US/Eastern')
In [359]: rng_berlin = rng_utc.tz_convert('Europe/Berlin')
In [360]: rng_eastern[5]
Out[360]: Timestamp('2012-03-10 19:00:00-0500', tz='US/Eastern', freq='D')
In [361]: rng_berlin[5]
Out[361]: Timestamp('2012-03-11 01:00:00+0100', tz='Europe/Berlin', freq='D')
In [362]: rng_eastern[5] == rng_berlin[5]
Out[362]: True
Как и Series, DataFrame, и DatetimeIndex, Timestamp``s can be converted to other
time zones using ``tz_convert:
In [363]: rng_eastern[5]
Out[363]: Timestamp('2012-03-10 19:00:00-0500', tz='US/Eastern', freq='D')
In [364]: rng_berlin[5]
Out[364]: Timestamp('2012-03-11 01:00:00+0100', tz='Europe/Berlin', freq='D')
In [365]: rng_eastern[5].tz_convert('Europe/Berlin')
Out[365]: Timestamp('2012-03-11 01:00:00+0100', tz='Europe/Berlin')
Локализация функций Timestamp работает так же, как DatetimeIndex и Series:
In [366]: rng[5]
Out[366]: Timestamp('2012-03-11 00:00:00', freq='D')
In [367]: rng[5].tz_localize('Asia/Shanghai')
Out[367]: Timestamp('2012-03-11 00:00:00+0800', tz='Asia/Shanghai')
Операции между Series в разных часовых поясах будут возвращать Series в UTC, выравнивая данные по временным меткам UTC:
In [368]: eastern = ts_utc.tz_convert('US/Eastern')
In [369]: berlin = ts_utc.tz_convert('Europe/Berlin')
In [370]: result = eastern + berlin
In [371]: result
Out[371]:
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 [372]: result.index
Out[372]:
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 [373]: didx = pd.DatetimeIndex(start='2014-08-01 09:00', freq='H', periods=10, tz='US/Eastern')
In [374]: didx
Out[374]:
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 [375]: didx.tz_localize(None)
Out[375]:
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 [376]: didx.tz_convert(None)
Out[376]:
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 [377]: didx.tz_convert('UCT').tz_localize(None)
Out[377]:
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 [378]: 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 [379]: rng_hourly_eastern = rng_hourly.tz_localize('US/Eastern', ambiguous='infer')
In [380]: rng_hourly_eastern.tolist()
Out[380]:
[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 [381]: rng_hourly_dst = np.array([1, 1, 0, 0, 0])
In [382]: rng_hourly.tz_localize('US/Eastern', ambiguous=rng_hourly_dst).tolist()
Out[382]:
[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 [383]: rng_hourly.tz_localize('US/Eastern', ambiguous='NaT').tolist()
Out[383]:
[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 [384]: didx = pd.DatetimeIndex(start='2014-08-01 09:00', freq='H', periods=10, tz='US/Eastern')
In [385]: didx
Out[385]:
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 [386]: didx.tz_localize(None)
Out[386]:
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 [387]: didx.tz_convert(None)
Out[387]:
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 [388]: didx.tz_convert('UCT').tz_localize(None)
Out[388]:
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')
Типы данных с часовым поясом
Впервые добавлено в версии 0.17.0.
Series/DatetimeIndex с неявным значением часового пояса представлены типом данных datetime64[ns].
In [389]: s_naive = pd.Series(pd.date_range('20130101',periods=3))
In [390]: s_naive
Out[390]:
0 2013-01-01
1 2013-01-02
2 2013-01-03
dtype: datetime64[ns]
Series/DatetimeIndex со явным значением часового пояса представлены типом данных datetime64[ns, tz].
In [391]: s_aware = pd.Series(pd.date_range('20130101',periods=3,tz='US/Eastern'))
In [392]: s_aware
Out[392]:
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 [393]: s_naive.dt.tz_localize('UTC').dt.tz_convert('US/Eastern')
Out[393]:
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 [394]: s_naive.astype('datetime64[ns, US/Eastern]')
Out[394]:
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 [395]: s_aware.astype('datetime64[ns]')
Out[395]:
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 [396]: s_aware.astype('datetime64[ns, CET]')
Out[396]:
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 [397]: s_naive.values
Out[397]:
array(['2013-01-01T00:00:00.000000000', '2013-01-02T00:00:00.000000000',
'2013-01-03T00:00:00.000000000'], dtype='datetime64[ns]')
In [398]: s_aware.values
Out[398]:
array(['2013-01-01T05:00:00.000000000', '2013-01-02T05:00:00.000000000',
'2013-01-03T05:00:00.000000000'], dtype='datetime64[ns]')
Обратите также внимание, что после преобразования в массив numpy эти значения потеряют информацию о часовом поясе.
In [399]: pd.Series(s_aware.values) Out[399]: 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 [400]: pd.Series(s_aware.values).dt.tz_localize('UTC').dt.tz_convert('US/Eastern')
Out[400]:
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.19.2/timeseries.html