Панды - Получить все строки между двумя датами, но только конкретные дни недели и периоды времени - PullRequest
0 голосов
/ 27 сентября 2018

Скажем, у меня есть фрейм данных, который выглядит следующим образом:

                     usage_price
2017-04-01 00:00:00            1
2017-04-01 00:30:00            1
2017-04-01 01:00:00            1
2017-04-01 01:30:00            1
2017-04-01 02:00:00            1
...                          ...
2018-12-31 22:00:00            1
2018-12-31 22:30:00            1
2018-12-31 23:00:00            1
2018-12-31 23:30:00            1

Я хочу обновить usage_price для определенных полей.В моем случае я хочу обновить на основе этого объекта:

{'day': '1', 'timerange': ['01 01 00:00', '31 12 08:00']}

Что означает:

  • Обновить все понедельники ('день': '1')
  • Между 00:00 и 08:00
  • В любой понедельник с 01-01 (1 января) и 31-12 (31 декабря) ( без учета года )

Я знаю, как сделать некоторые из этих вещей отдельно:

  • df_timeseries[df_timeseries.index.weekday==0, 'usage_price]
  • df_timeseries.loc[df_timeseries.between_time('00:00', '08:00', include_end=False).index,'usage_price']

НоЯ немного застрял в том, как получать строки между датами (игнорируя годы) и как комбинировать все вместе, поэтому помощь будет принята с благодарностью!

Редактировать: Это как далекоМне удалось получить, но я не могу заставить его работать (я получаю синтаксические ошибки), я не думаю, что собираюсь корректно построить маску:

def _create_mask_from_tpr(self, df: pd.DataFrame, tpr: Dict[str, Union[str, List[str]]]) -> Tuple:
    print(tpr)
    weekday = int(tpr['day']) - 1 # Offset.
    start_day, start_month, start_time = tpr['timerange'][0].split(" ")
    end_day, end_month, end_time = tpr['timerange'][1].split(" ")
    start_year, end_year = df.index.min().year, df.index.max().year
    selection_weekday = (df.index.weekday == weekday)
    selection_time = (df.between_time(start_time, end_time))

    selection_date = None
    for year in range(start_year, end_year + 1):
        start_date = pd.to_datetime("{}-{}-{}".format(year, start_month, start_day))
        end_date = pd.to_datetime("{}-{}-{}".format(year, end_month, end_day))
        selection_date = selection_date | (df.index.date() >= start_date & df.index.date() <= end_date)
    mask = (selection_weekday & selection_time & selection_date)
    print(mask)

Ответы [ 2 ]

0 голосов
/ 27 сентября 2018

Окончательное решение:

def _create_mask_from_tpr(self, df: pd.DataFrame, tpr: Dict[str, Union[str, List[str]]]) -> List[bool]:
    weekday = int(tpr['day']) - 1 # Offset.
    start_day, start_month, start_time = tpr['timerange'][0].split(" ")
    end_day, end_month, end_time = tpr['timerange'][1].split(" ")
    start_year, end_year = df.index.min().year, df.index.max().year
    selection_weekday = (df.index.weekday == weekday)

    start_time = datetime.datetime.strptime(start_time, '%H:%M').time()
    end_time = datetime.datetime.strptime(end_time, '%H:%M').time()
    selection_time = ((df.index.time >= start_time) & (df.index.time <= end_time))

    selection_date = None
    for year in range(start_year, end_year + 1):
        start_date = pd.Timestamp("{}-{}-{}".format(year, start_month, start_day))
        end_date = pd.Timestamp("{}-{}-{}".format(year, end_month, end_day))
        if selection_date:
            selection_date = selection_date | ((df.index >= start_date) & (df.index <= end_date))
        else:
            selection_date = ((df.index >= start_date) & (df.index <= end_date))
    return (selection_weekday & selection_time & selection_date)
0 голосов
/ 27 сентября 2018

Не проверено, но может сработать что-то вроде следующих строк:

selection = ((df_timeseries.index.weekday == 0) & 
             (df_timeseries.between_time('00:00', '08:00', include_end=False)))
result = df_timeseries[selection, 'usage_price']

Обычно вы можете комбинировать сравнения с операторами | или & (но используйте скобки).Так как даты начала и окончания включают полный год, я не фильтровал это.

Если вы хотите выбрать на дату без указания года , вы столкнетесь с проблемами при выполнении, например: вам, вероятно, придется сделать что-то следующее:

selection = ((df_timeseries.index.day >= 5) &
             (df_timeseries.index.day <= 20) &
             (df_timeseries.index.day >= 2) &
             (df_timeseries.index.day <= 3))

, поскольку теперь вы пропустите конец февраля (дни> 20) и начало марта (дни <3). </p>

Использование взамен df_timeseries.index.dayofyear может работать, за исключением во времявисокосный год: вы бы пропустили день в конце своего диапазона дат.

Я не знаю простого способа фильтрации по диапазону дат, игнорируя год.Возможно, вам придется создать цикл по интересующим годам и сравнить полный диапазон год-месяц-день для каждого года, комбинируя каждый поднабор с |.Это также служит еще одним примером объединения более сложных выделений с использованием | и &:

start = '02-05'
end = '03-02'
subsel = np.zeros(len(df), dtype=np.bool)  # include no dates by default
years = np.range(2018, 2050, dtype=np.int)
for year in years:
    startdate = (pd.to_datetime(str(year) + '-' + start)).date()
    enddate = (pd.to_datetime(str(year) + '-' + end)).date()
    subsel = subsel | (df.index.date >= startdate & df.index.date <= enddate)
selection = selection & subsel
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...