Pandas date_range - вычитание numpy timedelta дает нечетный результат, время становится не 0:00:00

Я пытаюсь создать набор дат с функциональностью pandas date_range. Затем я хочу перебрать этот диапазон и вычесть несколько месяцев из каждой из дат (точное число месяцев определяется в цикле), чтобы получить новую дату.

Я получаю очень странные результаты, когда делаю это.

Самый ценный игрок:

#get date range
dates = pd.date_range(start = '1/1/2013', end='1/1/2018', freq=str(test_size)+'MS', closed='left', normalize=True)
#take first date as example
date = dates[0]
date
Timestamp('2013-01-01 00:00:00', freq='3MS')

Все идет нормально.

Теперь предположим, что я хочу вернуться всего на один месяц назад с этой даты. Я определяю numpy timedelta (он поддерживает месяцы для определения, а timedelta pandas - нет):

#get timedelta of 1 month
deltaGap = np.timedelta64(1,'M')
#subtract one month from date
date - deltaGap
Timestamp('2012-12-01 13:30:54', freq='3MS')

Почему так? Почему я получаю 13:30:54 по компоненту времени вместо полуночи.

Более того, если я вычитаю более 1 месяца, сдвиг становится настолько большим, что я теряю целый день:

#let's say I want to subtract both 2 years and then 1 month
deltaTrain = np.timedelta64(2,'Y')
#subtract 2 years and then subtract 1 month 
date - deltaTrain - deltaGap
Timestamp('2010-12-02 01:52:30', freq='3MS')

person Maksim Khaitovich    schedule 06.04.2018    source источник


Ответы (2)


У меня были похожие проблемы с timedelta, и решение, которое я в итоге использовал, заключалось в использовании relativedelta из dateutil, которое специально создано для такого рода приложений (принимая во внимание все странности календаря, такие как високосные годы, дни недели и т. д.). .). Например, дано:

from dateutil.relativedelta import relativedelta

date = dates[0]

>>> date
Timestamp('2013-01-01 00:00:00', freq='10MS')

deltaGap = relativedelta(months=1)

>>> date-deltaGap
Timestamp('2012-12-01 00:00:00', freq='10MS')

deltaGap = relativedelta(years=2, months=1)

>>> date-deltaGap
Timestamp('2010-12-01 00:00:00', freq='10MS')

Дополнительные сведения о relativedelta см. в документации.

Проблемы с numpy.timedelta64

Я думаю, что проблема с np.timedelta раскрывается в этих 2 частях документы:

Существуют две единицы Timedelta («Y», годы и «M», месяцы), которые обрабатываются особым образом, поскольку время, которое они представляют, меняется в зависимости от того, когда они используются. В то время как единица измерения дня timedelta эквивалентна 24 часам, преобразовать единицу измерения месяца в дни невозможно, поскольку в разных месяцах разное количество дней.

и

Длина диапазона — это диапазон 64-битного целого числа, умноженный на длину даты или единицы измерения. Например, временной интервал для «W» (неделя) ровно в 7 раз больше, чем временной интервал для «D» (день), а временной интервал для «D» (день) ровно в 24 раза больше, чем временной интервал. для «ч» (час).

Таким образом, временные дельты подходят для часов, недель, месяцев, дней, потому что это неизменяемые промежутки времени. Однако месяцы и годы имеют переменную длину (например, високосные годы), и поэтому, чтобы принять это во внимание, numpy принимает своего рода «среднее» (я думаю). Один numpy «год» кажется равным одному году, 5 часам, 49 минутам и 12 секундам, а один numpy «месяц» — 30 дням, 10 часам, 29 минутам и 6 секундам.

# Adding one numpy month adds 30 days + 10:29:06:
deltaGap = np.timedelta64(1,'M')
date+deltaGap
# Timestamp('2013-01-31 10:29:06', freq='10MS')

# Adding one numpy year adds 1 year + 05:49:12:
deltaGap = np.timedelta64(1,'Y')
date+deltaGap
# Timestamp('2014-01-01 05:49:12', freq='10MS')

С этим не так просто работать, поэтому я бы просто пошел к relativedelta, который гораздо более интуитивен (для меня).

person sacuL    schedule 06.04.2018
comment
Благодарю вас! Имеет смысл. Как-то упустил относительную дельту при поиске подходов - person Maksim Khaitovich; 07.04.2018
comment
Без проблем! Да, это как-то странно. Мне нравится эта часть документации numpy: нельзя преобразовать месяц в дни, потому что в разных месяцах разное количество дней, relativedelta, по-видимому, делает невозможное! - person sacuL; 07.04.2018

Вы можете попробовать использовать pd.DateOffset, который в основном используется для применение логики смещения (месяц, год, час) в формате даты.

# get random dates
dates = pd.date_range(start = '1/1/2013', freq='H',periods=100,closed='left', normalize=True)

#take first date as example
date = dates[0]

# subtract a month
dates[0] - pd.DateOffset(months=1)
Timestamp('2012-12-01 00:00:00')

# to apply this on all dates
new_dates = list(map(lambda x: x - pd.DateOffset(months=1), dates))
person YOLO    schedule 07.04.2018