Использование смещения даты Pandas с псевдонимами смещения напрямую

Я уверен, что просто не ищу правильную фразу, чтобы ответить на этот вопрос, но в Pandas можно взять любой объект DateTime и добавить или вычесть из него, используя DateOffset, например:

pd.datetime.now() + pd.DateOffset(months=2)

pd.datetime.now() + pd.DateOffset(weeks=4)

pd.datetime.now() + pd.DateOffset(days=2)

и т.п.

Но в другом контексте также используются псевдонимы смещения: https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases

Мой вопрос: есть ли способ использовать DateOffset и передать псевдоним смещения для определения основы смещения?

Например: pd.datetime.now() + pd.DateOffset(n=some_int, freq='W') за some_int недель?

Если это невозможно, где я должен копать, чтобы получить что-то в этом роде?

(Причина в том, что есть функция, в которой я использую псевдоним смещения, и я не хочу создавать длинный оператор if...else для преобразования строки частоты в разные экземпляры pd.DateOffset(weeks=n | years=n | months=n | etc.). Я предпочитаю, чтобы строка частоты диктовала DateOffset в одной строке кода и по-прежнему быть динамическим для обработки разных частот времени.)

Отредактировано: чтобы добавить пользовательскую функцию, которая заботится о том, что мне нужно, но было бы неплохо иметь решение в to_timedelta или DateOffset, чтобы решение было выше по течению и было более эффективным. Например, я хотел бы использовать бизнес-версии всех параметров freq, которые я использую, чтобы n мог воспринимать информацию более естественно и необработанно по сравнению с источником, из которого она поступила.

def datedelta(date, n=0, freq='M'):
    from pandas import to_datetime, DateOffset
    
    if n == 0:
        date_sign = 1
    else:
        date_sign = np.abs(n)/n
        
    freq = freq.lower()
    
    if freq == 'y':
        dtOff = DateOffset(years=abs(n))
    elif freq == 'q':
        dtOff = DateOffset(quarters=abs(n))
    elif freq == 'm':
        dtOff = DateOffset(months=abs(n))
    elif freq == 'w':
        dtOff = DateOffset(weeks=abs(n))
    elif freq == 'd':
        dtOff = DateOffset(days=abs(n))
    else:
        raise ValueError("The freq parameter not one of the following: {'Y', 'Q', 'M', 'W', 'D'}")
        
    return to_datetime(date) + date_sign * dtOff

person ssurendr    schedule 23.09.2020    source источник


Ответы (2)


Я думаю, что функция to_timedelta делает то, что ты хочешь.

time = pd.datetime.now() + pd.to_timedelta(5, unit='W')

Обновить
Функция to_timedelta больше не поддерживает месяц, квартал и год. Существует два типа объектов для смещения времени:

  • DateOffset, которые учитывают календарное время:
  • TimeOffset, который является абсолютным временем (разница возникает в дни летнего времени).

Дополнительные сведения см. в документации об этом. Функция to_timedelta не может знать, какой из них вы хотите использовать.

person ndclt    schedule 24.09.2020
comment
Большое спасибо. Это так близко к тому, что мне нужно, если бы это было больше, чем Уикс. Судя по документации, это не идет выше, и приложение, которое мне нужно, будет иметь недели, чтобы быть наименьшей дельтой, которую я бы использовал или нуждался. В основном год, квартал и месяц. - person ssurendr; 24.09.2020
comment
О, да, я вижу. Я получаю сообщение об ошибке при выполнении pd.to_timedelta(4, unit='Y'): Units 'M', 'Y', and 'y' are no longer supported, as they do not represent unambiguous timedelta values durations. Я обновляю свой ответ. - person ndclt; 24.09.2020

Может быть, эта функция будет полезна?

def offset(freq:str, n:int = 0):
    """
    Pandas DateOffset wrapper
    import pandas as pd
    """
    # =============================================================================
    # offsets_df = pd.read_html('https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html')[2]
    # offsets_df.drop(columns = ['Description'], inplace = True)
    # offsets_df.replace('None', np.NaN, inplace = True)
    # offsets_df.dropna(subset = ['Frequency String'], inplace = True)
    # offsets_df['Frequency String'] = offsets_df['Frequency String'].str.replace("\'", '')
    # for x in ['Date Offset', 'Frequency String']:
    #    offsets_df[x] =  offsets_df[x].str.split(' or ')
    # offsets_df['Date Offset'] = offsets_df['Date Offset'].map(lambda x: x[0])
    # offsets_df = explode_rows(offsets_df, 'Frequency String', fill_value = '')
    # offsets_df.drop_duplicates(subset = ['Frequency String'], inplace = True)
    # offsets_d = dict(zip(offsets_df['Frequency String'], offsets_df['Date Offset']))
    # =============================================================================
    offsets_d = {'B': 'BDay',
                 'C': 'CDay',
                 'W': 'Week',
                 'WOM': 'WeekOfMonth',
                 'LWOM': 'LastWeekOfMonth',
                 'M': 'MonthEnd',
                 'MS': 'MonthBegin',
                 'BM': 'BMonthEnd',
                 'BMS': 'BMonthBegin',
                 'CBM': 'CBMonthEnd',
                 'CBMS': 'CBMonthBegin',
                 'SM': 'SemiMonthEnd',
                 'SMS': 'SemiMonthBegin',
                 'Q': 'QuarterEnd',
                 'QS': 'QuarterBegin',
                 'BQ': 'BQuarterEnd',
                 'BQS': 'BQuarterBegin',
                 'REQ': 'FY5253Quarter',
                 'A': 'YearEnd',
                 'AS': 'YearBegin',
                 'BYS': 'YearBegin',
                 'BA': 'BYearEnd',
                 'BAS': 'BYearBegin',
                 'RE': 'FY5253',
                 'BH': 'BusinessHour',
                 'CBH': 'CustomBusinessHour',
                 'D': 'Day',
                 'H': 'Hour',
                 'T': 'Minute',
                 'min': 'Minute',
                 'S': 'Second',
                 'L': 'Milli',
                 'ms': 'Milli',
                 'U': 'Micro',
                 'us': 'Micro',
                 'N': 'Nano'}
    return eval(f'pd.offsets.{offsets_d[freq]}({n})')
person Pawel Kranzberg    schedule 01.12.2020