Первая доступная дата предыдущего дня/месяца/года из диапазона даты и времени

У меня есть экземпляр datetime с датами (dfDates):

2017-03-01 00:00
2017-03-02 00:00
2017-03-04 00:00
...

В течение последнего дня (здесь: 2017-03-04) я вычисляю предыдущий день/месяц/год/и т.д. следующим образом:

def previous_day(dtToday):
    return dtToday - pd.DateOffset(days=1)

Это возвращает 2017-03-03. Однако этот рабочий день недоступен в моем диапазоне дат (dfDates).

Поэтому я ищу надежный способ найти дату, которая ближе всего к предыдущему дню/месяцу/году/и т.д. В этом случае за предыдущий день он должен вернуться 2017-03-02.

Заметьте, я понимаю, что вы можете сделать что-то вроде индекса -1, чтобы получить предыдущий день. Это становится все же сложным, если принять предыдущий месяц (не всегда 30 дней в месяц) и даже в предыдущем году (не всегда 252 рабочих дней в году). Поэтому существует надежный метод получения ближайшей доступной даты?

Обновление

Я также понимаю, что вы можете использовать timedelta следующим образом:

from datetime import datetime, timedelta

d = datetime.today() - timedelta(days=days_to_subtract)

Однако, как это связано с dtToday и как связать его с dfDates? dtToday в моем случае не всегда datetime.today(). Иногда это случайная дата.

Ответ 1

Я решил это так:

  • dtToday= контрольная дата
  • dtDates= последовательность datetime доступные даты
  • nbOffset= количество дней/месяцев/лет, которые мы хотим вернуться

код:

def previous_day(dtToday, dtDates, nbOffset):
    prevx   = dtToday - pd.DateOffset(days=nbOffset)
    return test_day_in(prevx, dtDates)

def previous_month(dtToday, dtDates, nbOffset):
    prevx = dtToday - pd.DateOffset(months=nbOffset)
    return test_day_in(prevx, dtDates)

def previous_year(dtToday, dtDates, nbOffset):
    prevx = dtToday - pd.DateOffset(years=nbOffset)
    return test_day_in(prevx, dtDates)

def test_day_in(dtTest, dtDates):
    if dtTest in dtDates:
        return dtTest
    else:
        return tryNextDay(dtTest, dtDates)

def tryNextDay(dtTest, dtDates):

    # if not outside the bound
    if (dtTest < dtDates.min()):
        return dtDates.min()

    # check if next day exist
    if (dtTest + pd.DateOffset(days=1) <= dtDates.max()):
        return previous_day(dtTest + pd.DateOffset(days=2), dtDates, 1) # 2-1
    else:
        print('warning, tryNextDay triggered')
        # should not be triggered, it should take by default the dtDates.min() if far outside range
        return dtTest

Ответ 2

Не самый эффективный, но вы можете попробовать:

# From your function
day_minus_one = previous_day(dtToday)

# Return LAST element in INDEX of DF FROM START TO DAY_MINUS_ONE
actual = df.loc[:day_minus_one].index[-1]

Это по существу возвращает вам последний индекс копии вашего df, до и включая day_minus_one, если существует какая-либо из дат. Это должно дать вам ближайшую дату или дату.

Вы можете попробовать:

# Returns LAST element of INDEX of df from DAY_MINUS_ONE_HUNDRED to DAY_MINUS_ONE
actual_better = df.loc[day_minus_one_hundred:day_minus_one].index[-1]

Оглядывать назад только один_hundred дней с minus_one, если ваш набор данных огромен, поэтому вам не нужно возвращать огромный массив, чтобы найти одну дату.

Ответ 3

Если я правильно понимаю, вы не хотите вычитать 1 день, вы хотите получить предыдущий доступный день из списка доступных дат. В этом случае рассмотрим следующее:

available_dates = [
  2017-03-01 00:00,
  2017-03-02 00:00,
  2017-03-04 00:00,
  ...
]

def previous_day(dtToday):
    today_index = available_dates.index(dtToday)
    return available_dates[today_index-1]

Это предполагает, конечно, что ваш available_dates сортируется

ИЗМЕНИТЬ:

Если вы хотите, чтобы вы могли вычесть месяц и годы, необходимо немного более сложное:

# Sorted
available_dates = [
  2017-03-01 00:00,
  2017-03-02 00:00,
  2017-03-04 00:00,
  ...
]

subtract_from_date(date, day=None, month=None, year=None):
  # check if it day/month/yeah
  ...
  # do the actual subtraction and store it in substracted_date
  ...
  # get the closest date
  for index, date in enumerate(available_dates):
     if date > substracted_date:
       return available_dates[index-1]