Во многих случаях более естественно связывать какие-то данные, с временным интервалом (промежутком времени). Для обычных промежутков времени
pandas
использует объектыPeriod
иPeriodIndex
для последовательностей промежутков времени (интервалов). В будущих выпусках > 2.2 появится улучшенная поддержка нерегулярных интервалов с произвольными начальной и конечной точками.
pandas.Period
представляет собой промежуток/период/интервал времени;pandas.PeriodIndex
представляет собой неизменяемый numpy.ndarray()
, содержащий значения, указывающие регулярные промежутки/периоды/интервалы времени.DataFrame.to_period
преобразовывает индекс DataFrame
/DatetimeIndex
в PeriodIndex
.pandas.period_range()
создает PeriodIndex
с фиксированной частотой по заданным параметрам.pandas.Period(value=None, freq=None, ordinal=None, year=None, month=None, quarter=None, day=None, hour=None, minute=None, second=None)
:Класс pandas.Period()
представляет собой промежуток времени (например, день, месяц, квартал и т.д.).
Принимаемые аргументы:
value=None
- период времени (например, ‘4Q2005’). Это ни начало, ни конец периода, а скорее сам весь период. Принимает следующие объекты: str
, datetime
, date
или pandas.Timestamp
.freq=None
- строка периода pandas
или соответствующих объектов. Если значение равно datetime, то требуется частота.ordinal=None
- смещение от предшествовавшей григорианской эпохе.year=None
- год периода.month=None
- месяц периода.quarter=None
- квартал периода.day=None
- день периода.hour=None
- час периода.minute=None
- минута периода.second=None
- секунда периода.Промежуток времени можно указать с помощью ключевого аргумента freq
, используя псевдоним частоты. Так как freq
представляет собой промежуток времени, он не может быть отрицательным, например '-3D'
.
>>> import pandas as pd >>> pd.Period("2024", freq="Y") # Period('2024', 'Y-DEC') >>> pd.Period("2024-1-1", freq="D") # Period('2024-01-01', 'D') >>> pd.Period("2024-1-1 19:00", freq="h") # Period('2024-01-01 19:00', 'h') >>> pd.Period("2024-1-1 19:00", freq="5h") # Period('2024-01-01 19:00', '5h')
Чтобы преобразовать представление YYYYMMDD
на основе int64
в Period
:
>>> s = pd.Series([20241231, 20191130, 20131231]) >>> s # 0 20241231 # 1 20191130 # 2 20131231 # dtype: int64 # определим функцию преобразования def conv(x): return pd.Period(year=x // 10000, month=x // 100 % 100, day=x % 100, freq="D") >>> s.apply(conv) # 0 2024-12-31 # 1 2019-11-30 # 2 2013-12-31 # dtype: period[D] >>> s.apply(conv)[2] # Period('2013-12-31', 'D')
Сложение и вычитание целых чисел из периодов сдвигает период на его собственную частоту. Арифметические действия между периодами с разной частотой (диапазоном) не допускаются.
>>> p = pd.Period("2024", freq="Y") >>> p + 1 # Period('2025', 'A-DEC') >>> p - 3 # Period('2021', 'A-DEC') >>> p = pd.Period("2024-01", freq="2M") >>> p + 2 # Period('2024-05', '2M') >>> p - 1 # Period('2023-11', '2M') >>> p == pd.Period("2024-01", freq="3M") # False
Если частота периода ежедневная или выше (D
, h
, min
, s
, ms
, us
и ns
), то можно добавить timedelta
-подобные смещения, если результат имеет ту же частоту. В противном случае будет поднят параметр ValueError
.
>>> p = pd.Period("2014-07-01 09:00", freq="h") >>> p + pd.offsets.Hour(2) # Period('2014-07-01 11:00', 'H') >>> import datetime >>> p + datetime.timedelta(minutes=120) # Period('2014-07-01 11:00', 'H') >>> import numpy as np >>> p + np.timedelta64(7200, "s") # Period('2014-07-01 11:00', 'H') >>> p + pd.offsets.Minute(5) # Traceback (most recent call last): # ... # ... Input cannot be converted to Period(freq=H)
Получение разницы экземпляров Period
с одинаковой частотой вернет количество единиц частоты между ними:
>>> pd.Period("2024", freq="Y") - pd.Period("2014", freq="Y") # <10 * YearEnds: month=12>
Period
с помощью метода .asfreq()
Частоту pandas.Period
можно преобразовать с помощью метода .asfreq()
. Например, 2023 финансовый год, закончился в декабре:
>>> p = pd.Period("2023", freq="Y-DEC") >>> p # Period('2023', 'Y-DEC')
Можно преобразовать его в ежемесячную частоту. Используя аргумент how
, можно указать, следует ли возвращать начальный или конечный месяц:
>>> p.asfreq("M", how="start") # Period('2023-01', 'M') # для удобства можно использовать сокращения: # `s` - start # `e`- end # например, how="e" >>> p.asfreq("M", how="e") # Period('2023-12', 'M')
Преобразование в "суперпериод" (например, годовая частота с квартальной частотой) автоматически возвращает суперпериод, включающий входной период:
>>> p = pd.Period("2022-12", freq="M") >>> p.asfreq("Y-NOV") # Period('2023', 'Y-NOV')
Обратите внимание: т.к. в примере осуществлен переход на годовую периодичность, заканчивающуюся в ноябре, то ежемесячный период декабря 2022 года фактически приходится на период Y-NOV 2023 года.
Преобразования периодов с привязанными частотами особенно полезны для работы с различными квартальными данными, общими для бизнеса и других областей. Многие организации определяют кварталы относительно месяца, в котором начинается и заканчивается их финансовый год. Таким образом, первый квартал 2024 года может начаться в 2023 году или через несколько месяцев 2024 года. Благодаря закрепленным частотам pandas
работает для всех квартальных частот от Q-JAN
до Q-DEC
.
Q-DEC
определяет регулярные календарные кварталы:
>>> p = pd.Period("2024Q1", freq="Q-DEC") >>> p.asfreq("D", "s") # Period('2024-01-01', 'D') >>> p.asfreq("D", "e") # Period('2024-03-31', 'D')
Q-MAR
определяет конец финансового года в марте:
>>> p = pd.Period("2024Q4", freq="Q-MAR") >>> p.asfreq("D", "s") # Period('2024-01-01', 'D') >>> p.asfreq("D", "e") # Period('2024-03-31', 'D')
pandas.PeriodIndex(data=None, ordinal=None, freq=None, dtype=None, copy=False, name=None, **fields)
:Класс pandas.PeriodIndex()
представляет собой неизменяемый numpy.ndarray()
, содержащий порядковые значения, указывающие регулярные периоды времени. Индексные ключи привязаны к объектам Period
, которые содержат метаданные (например, информацию о частоте).
Принимаемые аргументы:
data=None
- необязательные данные, подобные периодам, для построения индекса.ordinal=None
- freq=None
- строка периода pandas
.dtype=None
- строка или PeriodDtype
,copy=False
- создает копию входного ndarray
.name=None
- имя индексаДля создания PeriodIndex
можно использовать конструктор напрямую:
>>> import pandas as pd >>> pd.PeriodIndex(["2023-1", "2023-2", "2023-3"], freq="M") # PeriodIndex(['2023-01', '2023-02', '2023-03'], dtype='period[M]') # для создания PeriodIndex из чисел, представляющих год, месяц, день и т.д. # с версии 2.2 используем метод `PeriodIndex.from_fields()` >>> idx = pd.PeriodIndex.from_fields(year=[2023, 2024], quarter=[1, 3]) >>> idx # PeriodIndex(['2023Q1', '2024Q3'], dtype='period[Q-DEC]') # до версии 2.2 аргументы принимал сам конструктор `PeriodIndex` >>> pd.PeriodIndex(year=[2023, 2024], quarter=[1, 3]) # PeriodIndex(['2023Q1', '2024Q3'], dtype='period[Q-DEC]')
Для преобразования представления YYYYMMDD
на основе int64
в PeriodIndex
необходимо выполнить следующие действия:
# допустим дана серия >>> s = pd.Series([20241231, 20191130, 20131231]) >>> s # 0 20241231 # 1 20191130 # 2 20131231 # dtype: int64 # определим функцию преобразования `int64` в `Period` def conv(x): return pd.Period(year=x // 10000, month=x // 100 % 100, day=x % 100, freq="D") # преобразуем в `PeriodIndex` >>> span = pd.PeriodIndex(s.apply(conv)) >>> span # PeriodIndex(['2024-12-31', '2019-11-30', '2013-12-31'], dtype='period[D]')
Последовательности объектов Period
можно собрать в PeriodIndex
, который удобно создается при помощи функции pandas.period_range()
:
>>> prng = pd.period_range("1/1/2023", "1/1/2024", freq="M") >>> prng # PeriodIndex(['2023-01', '2023-02', '2023-03', '2023-04', '2023-05', '2023-06', # '2023-07', '2023-08', '2023-09', '2023-10', '2023-11', '2023-12', # '2024-01'], # dtype='period[M]')
При передаче умноженной частоты freq="3M"
в pandas.period_range()
выводится последовательность периодов с умноженным диапазоном.
>>> pd.period_range(start="2023-01", freq="3M", periods=4) # PeriodIndex(['2023-01', '2023-04', '2023-07', '2023-10'], dtype='period[3M]')
Если start
или end
являются объектами Period
, то они будут использоваться в качестве конечных точек привязки для PeriodIndex
с частотой, соответствующей частоте freq
, переданной в функцию period_range()
.
>>> pd.period_range( ... start=pd.Period("2024Q1", freq="Q"), end=pd.Period("2024Q2", freq="Q"), freq="M" ... ) # PeriodIndex(['2024-03', '2024-04', '2024-05', '2024-06'], dtype='period[M]')
Объект pandas.PeriodIndex
удобно использовать для индексации объектов pandas
:
>>> import numpy as np >>> ps = pd.Series(np.random.randn(len(prng)), prng) >>> ps # 2023-01 -0.187288 # 2023-02 0.953165 # 2023-03 1.141206 # 2023-04 -1.619246 # 2023-05 -0.518793 # 2023-06 -2.186372 # 2023-07 -1.063065 # 2023-08 -0.880710 # 2023-09 1.914001 # 2023-10 0.549167 # 2023-11 0.557771 # 2023-12 0.310108 # 2024-01 0.113552 # Freq: M, dtype: float64
PeriodIndex
поддерживает сложение и вычитание по тем же правилам, что и Period
.
>>> idx = pd.period_range("2024-07-01 09:00", periods=5, freq="h") >>> idx # PeriodIndex(['2024-07-01 09:00', '2024-07-01 10:00', '2024-07-01 11:00', # '2024-07-01 12:00', '2024-07-01 13:00'], # dtype='period[h]') >>> idx + pd.offsets.Hour(2) # PeriodIndex(['2024-07-01 11:00', '2024-07-01 12:00', '2024-07-01 13:00', # '2024-07-01 14:00', '2024-07-01 15:00'], # dtype='period[h]') >>> idx = pd.period_range("2024-07", periods=5, freq="M") >>> idx # PeriodIndex(['2024-07', '2024-08', '2024-09', '2024-10', '2024-11'], dtype='period[M]') >>> idx + pd.offsets.MonthEnd(3) # PeriodIndex(['2024-10', '2024-11', '2024-12', '2025-01', '2025-02'], dtype='period[M]')
PeriodIndex
имеет собственный тип dtype
. Этот dtype
расширения pandas
, аналогичный dtype
с учетом часового пояса (datetime64[ns, tz]
). Тип периода dtype
содержит атрибут freq
и представлен с помощью period[freq]
, например period[D]
или period[M]
, с использованием строк частоты.
>>> pi = pd.period_range("2024-01-01", periods=3, freq="M") >>> pi # PeriodIndex(['2024-01', '2024-02', '2024-03'], dtype='period[M]') >>> pi.dtype # period[M]
Тип периода можно использовать в методе PeriodIndex.astype(...)
, что позволяет изменить частоту PeriodIndex
, и преобразовать pandas.DatetimeIndex
в pandas.PeriodIndex
, подобно методу DatetimeIndex.to_period()
:
# меняем ежемесячный период на ежедневный >>> pi.astype("period[D]") # PeriodIndex(['2024-01-31', '2024-02-29', '2024-03-31'], dtype='period[D]') # конвертируем `PeriodIndex` в `DatetimeIndex` >>> pi.astype("datetime64[ns]") # DatetimeIndex(['2024-01-01', '2024-02-01', '2024-03-01'], dtype='datetime64[ns]', freq='MS') # создадим `DatetimeIndex` >>> dti = pd.date_range("2024-01-01", freq="ME", periods=3) >>> dti # DatetimeIndex(['2024-01-31', '2024-02-29', '2024-03-31'], dtype='datetime64[ns]', freq='ME') # теперь конвертируем его в `PeriodIndex` >>> dti.astype("period[M]") # PeriodIndex(['2024-01', '2024-02', '2024-03'], dtype='period[M]')
PeriodIndex
Объект PeriodIndex
поддерживает срезы дат с немонотонными индексами.
Для выбора данных с PeriodIndex
можно передавать даты и строки дат в Series
и DataFrame
так же, как и DatetimeIndex
.
>>> ps["2024-01"] # 0.11355172009101018 >>> import datetime >>> ps[datetime.datetime(2023, 12, 25):] # 2023-12 0.310108 # 2024-01 0.113552 # Freq: M, dtype: float64 >>> ps["10/31/2023":"12/31/2023"] # 2023-10 0.549167 # 2023-11 0.557771 # 2023-12 0.310108 # Freq: M, dtype: float64
Передача строки с датой, представляющей более низкий период, чем PeriodIndex
, возвращает частичные срезы данных.
>>> ps["2023"] # 2023-01 -0.187288 # 2023-02 0.953165 # 2023-03 1.141206 # 2023-04 -1.619246 # 2023-05 -0.518793 # 2023-06 -2.186372 # 2023-07 -1.063065 # 2023-08 -0.880710 # 2023-09 1.914001 # 2023-10 0.549167 # 2023-11 0.557771 # 2023-12 0.310108 # Freq: M, dtype: float64 # создадим новый `DataFrame` dfp = pd.DataFrame( np.random.randn(600, 1), columns=["A"], index=pd.period_range("2024-01-01 9:00", periods=600, freq="min"), ) >>> dfp # A # 2024-01-01 09:00 1.387812 # 2024-01-01 09:01 0.937993 # 2024-01-01 09:02 0.914350 # 2024-01-01 09:03 -2.258870 # 2024-01-01 09:04 1.173190 # ... ... # 2024-01-01 18:55 0.314326 # 2024-01-01 18:56 1.475295 # 2024-01-01 18:57 -1.081923 # 2024-01-01 18:58 -1.080603 # 2024-01-01 18:59 0.394975 # # [600 rows x 1 columns] >>> dfp.loc["2024-01-01 10h"] # A # 2024-01-01 10:00 -1.076145 # 2024-01-01 10:01 -0.754348 # 2024-01-01 10:02 1.073532 # 2024-01-01 10:03 -0.848630 # 2024-01-01 10:04 -2.349152 # ... # 2024-01-01 10:55 0.027428 # 2024-01-01 10:56 -1.752014 # 2024-01-01 10:57 0.839968 # 2024-01-01 10:58 0.248728 # 2024-01-01 10:59 0.601582
Конечные точки будут включены в результат. В приведенном ниже примере данные фрагментируются с 10:00 до 11:59.
>>> dfp["2024-01-01 10h":"2024-01-01 11h"] # A # 2024-01-01 10:00 -1.076145 # 2024-01-01 10:01 -0.754348 # 2024-01-01 10:02 1.073532 # 2024-01-01 10:03 -0.848630 # 2024-01-01 10:04 -2.349152 # ... ... # 2024-01-01 11:55 0.194616 # 2024-01-01 11:56 -1.916404 # 2024-01-01 11:57 1.751644 # 2024-01-01 11:58 1.221067 # 2024-01-01 11:59 -0.547866 # # [120 rows x 1 columns]
Индекс pandas.PeriodIndex
как и объект Period
поддерживает повторную выборку с помощью метода .asfreq()
.
DatetimeIndex
и PeriodIndex
Данные с отметкой времени можно преобразовать в данные PeriodIndex
с помощью методов Timestamp.to_period()
и наоборот с помощью метода PeriodIndex.to_timestamp(freq=None, how='start')
:
>>> rng = pd.date_range("1/1/2024", periods=5, freq="ME") >>> ts = pd.Series(np.random.randn(len(rng)), index=rng) >>> ts # 2024-01-31 0.995308 # 2024-02-29 -0.040653 # 2024-03-31 -2.594716 # 2024-04-30 1.504875 # 2024-05-31 0.704241 # Freq: ME, dtype: float64 >>> ps = ts.to_period() >>> ps # 2024-01 0.995308 # 2024-02 -0.040653 # 2024-03 -2.594716 # 2024-04 1.504875 # 2024-05 0.704241 # Freq: M, dtype: float64 >>> ps.to_timestamp() # 2024-01-01 0.995308 # 2024-02-01 -0.040653 # 2024-03-01 -2.594716 # 2024-04-01 1.504875 # 2024-05-01 0.704241 # Freq: MS, dtype: float64
Помним, что 's'
и 'e'
можно использовать для возврата временных меток в начале или конце периода:
>>> ps.to_timestamp("D", how="s") # 2024-01-01 0.995308 # 2024-02-01 -0.040653 # 2024-03-01 -2.594716 # 2024-04-01 1.504875 # 2024-05-01 0.704241 # Freq: MS, dtype: float64
Преобразование между периодом и меткой времени позволяет использовать некоторые удобные арифметические функции. В следующем примере преобразуем квартальную частоту с годом, заканчивающимся в ноябре, в 9 утра конца месяца, следующего за окончанием квартала:
>>> prng = pd.period_range("2014Q1", "2024Q4", freq="Q-NOV") >>> ts = pd.Series(np.random.randn(len(prng)), prng) >>> ts.index = (prng.asfreq("M", "e") + 1).asfreq("h", "s") + 9 >>> ts.head() # 2014-03-01 09:00 0.045042 # 2014-06-01 09:00 0.530235 # 2014-09-01 09:00 -0.598684 # 2014-12-01 09:00 0.611139 # 2015-03-01 09:00 -1.086792 # Freq: h, dtype: float64