Данные с метками времени - это самый простой тип данных временных рядов, который связывает значения с моментами времени. Для объектов
pandas
это означает использование объектаTimestamp
. Список объектовTimestamp
автоматически приводятся кDatetimeIndex
.
pandas.Timestamp()
это pandas-эквивалент объекта datetime.datetime
в python;pandas.DatetimeIndex
представляет собой неизменяемый массив данных datetime64
, подобный numpy.ndarray
;pandas.to_datetime()
преобразовывает различные данные, похожие на дату/время в объект pandas.Timestamp
;pandas.date_range()
создает DatetimeIndex
на основе даты и времени;pandas.Timestamp(ts_input=<object>, year=0, month=0, day=0, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, nanosecond=0, tz=None, unit=None, fold=None)
:Объект pandas.Timestamp
это pandas-эквивалент объекта datetime.datetime
в python и в большинстве случаев взаимозаменяем с ним. Это тип, используемый для записей, составляющих DatetimeIndex
, и других ориентированных на временные ряды структур данных.
Принимаемые аргументы:
ts_input
- Значение, которое будет преобразовано во временную метку. Может быть объект похожий на datetime
, строка похожая на дату, числа int
или float
.year
, month
, day
- целое число int
(комментарии излишни)hour
, minute
, second
, microsecond
- целое число int
(комментарии излишни)tzinfo
- принимает datetime.tzinfo
nanosecond
- целое число int
(комментарии излишни)tz
- Часовой пояс, который будет иметь результирующий Timestamp
. Может быть строкой str
, объектами pytz.timezone
или dateutil.tz.tzfile
unit
- единица измерения, используемая для преобразования, если аргумент ts_input
имеет тип int
или float
. Допустимыми значениями являются ‘D’
, ‘h’
, ‘m’
, ‘s’
, ‘ms’
, ‘us’
и ‘ns’
. Например, 's'
означает секунды, а 'ms'
- миллисекунды.
Для входных данных float
- результат будет сохранен в наносекундах, а unit
будет установлен как 'ns'
.
fold
- из-за перехода на летнее время, время может совпадать дважды при переходе с летнего на зимнее время. Так вот этот аргумент описывает, соответствует ли значение даты/времени первому (0) или второму (1) времени, когда часы показывают неоднозначное время.
По сути, существует три соглашения о вызовах конструктора. Основная форма принимает четыре аргумента. Они могут передаваться по позиции или ключевому слову.
Две другие формы имитируют аргументы из datetime.datetime()
. Они могут передаваться либо по позиции, либо по ключевому слову, но не по тому и другому вместе.
Объекты
Series.dt
иindex.dt
дают доступ к атрибутам и методам объектаTimestamp
, хранящихся вSeries
/Index
соответственно.
pandas.Timestamp()
:Код ниже преобразует строку, похожую на дату/время:
>>> import pandas as pd >>> pd.Timestamp('2024-01-01T12') # Timestamp('2024-01-01 12:00:00')
Следующий вызов преобразует значение float
, представляющее эпоху Unix в секундах:
>>> pd.Timestamp(1706275662, unit='s') # Timestamp('2024-01-26 13:27:42')
Преобразуем значение int
, представляющее эпоху Unix в секундах и для определенного часового пояса:
>>> pd.Timestamp(1706275662, unit='s', tz='Europe/Moscow') # Timestamp('2024-01-26 16:27:42+0300', tz='Europe/Moscow')
Используем две другие формы соглашения о вызовах конструктора, которые имитируют API для объекта datetime.datetime()
:
>>> pd.Timestamp(2017, 1, 1, 12) # Timestamp('2017-01-01 12:00:00') >>> pd.Timestamp(year=2017, month=1, day=1, hour=12) # Timestamp('2017-01-01 12:00:00')
pandas.DatetimeIndex(...)
:Объект pandas.DatetimeIndex
представляет собой неизменяемые данные datetime64
, подобные numpy.ndarray
. Внутренне представлен как int64
и может быть упакован в объекты Timestamp
, которые являются подклассами datetime
и содержат метаданные.
С версии psndas 2.0: различные числовые атрибуты даты/времени (день, месяц, год и т. д.) теперь имеют тип
dtype: int32
. Раньше у них былdtype: int64
.Объект
DatetimeIndex.dt
]Series.dt дает доступ к атрибутам и методам объектаTimestamp
, который хранится вDatetimeIndex
как элемент массива.
Здесь не будет описание аргументов конструктора класса DatetimeIndex
, т.к. указание столбца с объектами Timestamp
в качестве индекса Series
/DataFrame
автоматически конвертируется в DatetimeIndex
.
Одно из основных применений DatetimeIndex
- индекс для объектов pandas
. Класс DatetimeIndex
содержит множество оптимизаций, связанных с временными рядами. Объекты DatetimeIndex
обладают всеми базовыми функциями обычных объектов Index
, а также набором расширенных методов, специфичных для временных рядов, для простой обработки временных интервалов.
DatetimeIndex
можно использовать как обычный индекс, он предлагает все интеллектуальные функции, такие как выбор/индексация данных по меткам даты/времени, срезы данных по промежуткам времени и т.д.
>>> import pandas as pd >>> import numpy as np >>> import datetime >>> start = datetime.datetime(2023, 1, 1) >>> end = datetime.datetime(2024, 1, 1) >>> rng = pd.date_range(start, end, freq="BME") >>> ts = pd.Series(np.random.randn(len(rng)), index=rng) >>> ts.index # DatetimeIndex(['2023-01-31', '2023-02-28', '2023-03-31', '2023-04-28', # '2023-05-31', '2023-06-30', '2023-07-31', '2023-08-31', # '2023-09-29', '2023-10-31', '2023-11-30', '2023-12-29'], # dtype='datetime64[ns]', freq='BME') >>> ts[:5].index # DatetimeIndex(['2023-01-31', '2023-02-28', '2023-03-31', '2023-04-28', # '2023-05-31'], # dtype='datetime64[ns]', freq='BME') >>> ts[::2].index # DatetimeIndex(['2023-01-31', '2023-03-31', '2023-05-31', '2023-07-31', # '2023-09-29', '2023-11-30'], # dtype='datetime64[ns]', freq='2BME')
DatetimeIndex
Строки с датой/временем могут быть переданы в качестве индексации, они преобразуются в метки даты/времени:
>>> ts["1/31/2023"] # 1.1121734807631107 >>> ts[datetime.datetime(2023, 12, 25):] # 2023-12-29 0.864635 # Freq: BME, dtype: float64 >>> ts["10/31/2023":"12/31/2023"] # 2023-10-31 0.009955 # 2023-11-30 -0.645742 # 2023-12-29 0.864635 # Freq: BME, dtype: float64
Чтобы обеспечить удобство доступа к более длинным временным рядам, можно передать год или год и месяц в виде строки:
>>> ts["2023"] # 2023-01-31 1.112173 # 2023-02-28 0.878389 # 2023-03-31 -0.551219 # 2023-04-28 1.606600 # 2023-05-31 0.607183 # 2023-06-30 1.789773 # 2023-07-31 0.275235 # 2023-08-31 1.386999 # 2023-09-29 -0.599426 # 2023-10-31 0.009955 # 2023-11-30 -0.645742 # 2023-12-29 0.864635 # Freq: BME, dtype: float64
Этот тип среза также будет работать с DataFrame
с индексом DatetimeIndex
. Так как частичный выбор данных является формой среза по индексной метке, то конечные точки среза будут включены. То есть срез будет включать совпадающее время в указанную дату:
dft = pd.DataFrame( np.random.randn(100000, 1), columns=["A"], index=pd.date_range("20230101", periods=100000, freq="min"), ) >>> dft # A # 2023-01-01 00:00:00 0.769829 # 2023-01-01 00:01:00 -0.666003 # 2023-01-01 00:02:00 0.625112 # 2023-01-01 00:03:00 0.843997 # 2023-01-01 00:04:00 0.196839 # ... ... # 2023-03-11 10:35:00 0.905294 # 2023-03-11 10:36:00 0.330765 # 2023-03-11 10:37:00 -0.490381 # 2023-03-11 10:38:00 0.763424 # 2023-03-11 10:39:00 -1.347089 # # [100000 rows x 1 columns] >>> dft.loc["2023"] # A # 2023-01-01 00:00:00 0.769829 # 2023-01-01 00:01:00 -0.666003 # 2023-01-01 00:02:00 0.625112 # 2023-01-01 00:03:00 0.843997 # 2023-01-01 00:04:00 0.196839 # ... ... # 2023-03-11 10:35:00 0.905294 # 2023-03-11 10:36:00 0.330765 # 2023-03-11 10:37:00 -0.490381 # 2023-03-11 10:38:00 0.763424 # 2023-03-11 10:39:00 -1.347089 # # [100000 rows x 1 columns]
Срез начинается в самый первый день месяца и включает последнюю дату/время месяца:
>>> dft["2023-1":"2023-2"] # A # 2023-01-01 00:00:00 0.769829 # 2023-01-01 00:01:00 -0.666003 # 2023-01-01 00:02:00 0.625112 # 2023-01-01 00:03:00 0.843997 # 2023-01-01 00:04:00 0.196839 # ... ... # 2023-02-28 23:55:00 -1.354526 # 2023-02-28 23:56:00 1.456907 # 2023-02-28 23:57:00 0.668234 # 2023-02-28 23:58:00 1.817265 # 2023-02-28 23:59:00 -0.101594 # # [84960 rows x 1 columns]
Укажем в качестве конца среза - время, которое включает все время последнего дня:
>>> dft["2023-1":"2023-2-28"] # A # 2023-01-01 00:00:00 0.769829 # 2023-01-01 00:01:00 -0.666003 # 2023-01-01 00:02:00 0.625112 # 2023-01-01 00:03:00 0.843997 # 2023-01-01 00:04:00 0.196839 # ... ... # 2023-02-28 23:55:00 -1.354526 # 2023-02-28 23:56:00 1.456907 # 2023-02-28 23:57:00 0.668234 # 2023-02-28 23:58:00 1.817265 # 2023-02-28 23:59:00 -0.101594 # # [84960 rows x 1 columns]
В следующем примере укажем точное время конца среза (не совпадает с указанным выше):
>>> dft["2023-1":"2023-2-28 00:00:00"] # A # 2023-01-01 00:00:00 0.769829 # 2023-01-01 00:01:00 -0.666003 # 2023-01-01 00:02:00 0.625112 # 2023-01-01 00:03:00 0.843997 # 2023-01-01 00:04:00 0.196839 # ... ... # 2023-02-27 23:56:00 0.112822 # 2023-02-27 23:57:00 0.248225 # 2023-02-27 23:58:00 0.317921 # 2023-02-27 23:59:00 0.478025 # 2023-02-28 00:00:00 0.636293 # # [83521 rows x 1 columns]
Выбор данных останавливаемся на включенной конечной точке, т.к. она является частью индекса:
>>> dft["2023-1-15":"2023-1-15 12:30:00"] # A # 2023-01-15 00:00:00 -1.098676 # 2023-01-15 00:01:00 0.123529 # 2023-01-15 00:02:00 0.749259 # 2023-01-15 00:03:00 1.322490 # 2023-01-15 00:04:00 0.339014 # ... ... # 2023-01-15 12:26:00 -1.590723 # 2023-01-15 12:27:00 0.048567 # 2023-01-15 12:28:00 0.095829 # 2023-01-15 12:29:00 -0.202293 # 2023-01-15 12:30:00 -0.877787 # # [751 rows x 1 columns]
Частичная выбор данных DatetimeIndex
также работает в DataFrame
с a MultiIndex
:
dft2 = pd.DataFrame( np.random.randn(20, 1), columns=["A"], index=pd.MultiIndex.from_product( [pd.date_range("20230101", periods=10, freq="12h"), ["a", "b"]] ), ) >>> dft2 # A # 2023-01-01 00:00:00 a 1.487131 # b 0.942668 # 2023-01-01 12:00:00 a 0.554916 # b -1.761021 # 2023-01-02 00:00:00 a 1.717367 # b -0.821675 # 2023-01-02 12:00:00 a 0.737373 # b 0.348934 # 2023-01-03 00:00:00 a 0.102362 # b -1.056135 # 2023-01-03 12:00:00 a 1.307184 # b -1.302244 # 2023-01-04 00:00:00 a 0.768927 # b 1.215484 # 2023-01-04 12:00:00 a 1.551029 # b 0.231425 # 2023-01-05 00:00:00 a 0.888348 # b 0.022093 # 2023-01-05 12:00:00 a -0.726614 # b -0.283663 >>> dft2.loc["2023-01-05"] # A # 2023-01-05 00:00:00 a 0.888348 # b 0.022093 # 2023-01-05 12:00:00 a -0.726614 # b -0.283663 >>> idx = pd.IndexSlice >>> dft2 = dft2.swaplevel(0, 1).sort_index() >>> dft2.loc[idx[:, "2023-01-05"], :] # A # a 2023-01-05 00:00:00 0.888348 # 2023-01-05 12:00:00 -0.726614 # b 2023-01-05 00:00:00 0.022093 # 2023-01-05 12:00:00 -0.283663
Одна и та же строка с датой/временем, используемая в качестве индексации, может обрабатываться либо как фрагмент, либо как точное совпадение в зависимости от разрешения DatetimeIndex
. Если строка менее точна, чем индекс, то она будет рассматриваться как фрагмент, в противном случае как точное совпадение.
Рассмотрим объект серии с индексом минимального разрешения:
series_minute = pd.Series( [1, 2, 3], pd.DatetimeIndex( ["2023-12-31 23:59:00", "2024-01-01 00:00:00", "2024-01-01 00:02:00"] ), ) >>> series_minute.index.resolution # 'minute'
Строка метки даты/времени менее точная, чем минута дает объект Series
.
>>> series_minute["2023-12-31 23"] # 2023-12-31 23:59:00 1 # dtype: int64
Строка метки даты/времени с минутным разрешением (или более точным) дает скаляр, т. е. она не преобразуется к фрагменту.
>>> series_minute["2023-12-31 23:59"] # 1 >>> series_minute["2023-12-31 23:59:00"] # 1
Если разрешение DatetimeIndex
секундное, то строка с меткой даты/времени с точностью до минуты дает объект Series
.
series_second = pd.Series( [1, 2, 3], pd.DatetimeIndex( ["2023-12-31 23:59:59", "2024-01-01 00:00:00", "2024-01-01 00:00:01"] ), ) >>> series_second.index.resolution # 'second' >>> series_second["2023-12-31 23:59"] # 2023-12-31 23:59:59 1 # dtype: int64
Если строка с меткой даты/времени обрабатывается как фрагмент, то ее также можно использовать для индексации фрейма данных с помощью свойства DataFrame.loc[]
.
dft_minute = pd.DataFrame( {"a": [1, 2, 3], "b": [4, 5, 6]}, index=series_minute.index ) >>> dft_minute.loc["2023-12-31 23"] # a b # 2023-12-31 23:59:00 1 4
Предупреждение: если строка с меткой даты/времени рассматривается как точное совпадение, то выбор
DataFrame[...]
будет осуществляться по столбцам, а не по строкам. Например,dft_minute['2023-12-31 23:59']
вызовет ошибкуKeyError
, т.к. '2023-12-31 23:59' имеет то же разрешение, что и индекс, а столбца с таким именем нет :Чтобы всегда иметь однозначный выбор, независимо от того, рассматривается ли строка как срез или как одиночный выбор, используйте
DataFrame.loc
.>>> dft_minute.loc["2023-12-31 23:59"] # a 1 # b 4 # Name: 2023-12-31 23:59:00, dtype: int64
Также обратите внимание, что разрешение DatetimeIndex
не может быть менее точным, чем день.
series_monthly = pd.Series( # обратите внимание, что `DatetimeIndex` # строится с периодичностью - месяц [1, 2, 3], pd.DatetimeIndex(["2023-12", "2024-01", "2024-02"]) ) # а разрешение `DatetimeIndex` - 1 день >>> series_monthly.index.resolution # 'day' # возвращает `Series` >>> series_monthly["2023-12"] # 2023-12-01 1 # dtype: int64
datetime
для выбора данныхИндексирование DatetimeIndex
с указанием строки с частичной датой/временем зависит от "точности" интервала. Другими словами, насколько специфичен интервал по отношению к разрешению индекса. Напротив, индексация/выбор данных с помощью объектов Timestamp
или datetime.datetime()
является точной, т.к. объекты имеют точное значение. Они также соответствуют семантике включения обеих конечных точек.
Объекты Timestamp
и datetime
имеют точные часы, минуты и секунды, даже если они не были указаны явно (они равны 0).
>>> dft[datetime.datetime(2023, 1, 1): datetime.datetime(2023, 2, 28)] # A # 2023-01-01 00:00:00 0.769829 # 2023-01-01 00:01:00 -0.666003 # 2023-01-01 00:02:00 0.625112 # 2023-01-01 00:03:00 0.843997 # 2023-01-01 00:04:00 0.196839 # ... ... # 2023-02-27 23:56:00 0.112822 # 2023-02-27 23:57:00 0.248225 # 2023-02-27 23:58:00 0.317921 # 2023-02-27 23:59:00 0.478025 # 2023-02-28 00:00:00 0.636293 # # [83521 rows x 1 columns]
Без каких-либо настроек по умолчанию.
>>> dft[ ... datetime.datetime(2023, 1, 1, 10, 12, 0): datetime.datetime( ... 2023, 2, 28, 10, 12, 0 ... ) ... ] # A # 2023-01-01 10:12:00 -2.073523 # 2023-01-01 10:13:00 1.568231 # 2023-01-01 10:14:00 0.093409 # 2023-01-01 10:15:00 -1.062306 # 2023-01-01 10:16:00 0.823143 # ... ... # 2023-02-28 10:08:00 -1.508005 # 2023-02-28 10:09:00 1.255278 # 2023-02-28 10:10:00 -1.354728 # 2023-02-28 10:11:00 -0.564761 # 2023-02-28 10:12:00 -0.923603 # # [83521 rows x 1 columns]
.truncate()
и необычная индексацияОбъекты Series
и DataFrame
предоставляют удобный метод .truncate()
, аналогичный срезу данных, который усекает строки до и после некоторого значения индекса. Обратите внимание, что усечение DatetimeIndex
предполагает значение 0 для любого неуказанного компонента даты, в отличие от среза, который возвращает любые частично совпадающие даты:
>>> rng2 = pd.date_range("2023-01-01", "2024-01-01", freq="W") >>> ts2 = pd.Series(np.random.randn(len(rng2)), index=rng2) >>> ts2.truncate(before="2023-11", after="2023-12") # 2023-11-05 -0.380798 # 2023-11-12 0.573212 # 2023-11-19 -0.375979 # 2023-11-26 0.970585 # Freq: W-SUN, dtype: float64 >>> ts2["2023-11":"2023-12"] # 2023-11-05 -0.380798 # 2023-11-12 0.573212 # 2023-11-19 -0.375979 # 2023-11-26 0.970585 # 2023-12-03 -0.739985 # 2023-12-10 -0.616425 # 2023-12-17 0.858252 # 2023-12-24 -1.481935 # 2023-12-31 -1.929553 # Freq: W-SUN, dtype: float64
Даже сложная причудливая индексация, которая нарушает регулярность частоты DatetimeIndex
, приведет к созданию DatetimeIndex
, при этом частота будет потеряна:
>>> ts2.iloc[[0, 2, 6]].index # DatetimeIndex(['2023-01-01', '2023-01-15', '2023-02-12'], dtype='datetime64[ns]', freq=None)