Панды: Выберите самый высокий день недели, исключая выходные, если только не одна запись - PullRequest
0 голосов
/ 27 ноября 2018

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

Пример данных можно настроить следующим образом:

dates = pd.Series(data=['2018-11-05', '2018-11-06', '2018-11-07', '2018-11-08', '2018-11-09',
                        '2018-11-12', '2018-11-13', '2018-11-14', '2018-11-15', '2018-11-17',
                        '2018-11-19',
                        '2018-12-01',
                        ])
nums = np.random.randint(50, 100, 12)
# nums
# array([95, 80, 81, 51, 98, 62, 50, 55, 59, 77, 69])

df = pd.DataFrame(data={'dates': dates, 'nums': nums})
df['dates'] = pd.to_datetime(df['dates'])

Записи, которые я хочу:

  • 2018-11-09 - пятница
  • 2018-11-15 - четверг (не 2018-11-17, потому что это суббота)
  • 2018-11-19 - понедельник и единственный рекорд за эту неделю
  • 2018-12-01 - суббота, но единственная запись за эту неделю

Мое текущее решение в ответе ниже , но я не думаю, что он идеален и имеет некоторые проблемы, с которыми мне пришлось работать,Вкратце, это:

  1. групповая неделя: df.groupby(df['dates'].dt.week).apply(some_function)
  2. , если для этой недели есть только одна запись, верните ее
  3. , в противном случае выберите самую высокую / последнюю записьс днем ​​<= пятница и верните это </li>

В идеале я хотел бы написать:

[latest Mon-Fri record] if [has Mon-Fri record] else [latest Sat-Sun record]

Ответы [ 2 ]

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

Создайте новую иерархию дней недели, где суббота и воскресенье имеют самый низкий приоритет.Затем sort_values в этом новом рейтинге + groupby + .tail(1).

import numpy as np

wd_map = dict(zip(np.arange(0,7,1), np.roll(np.arange(0,7,1),-2)))
# {0: 2, 1: 3, 2: 4, 3: 5, 4: 6, 5: 0, 6: 1}
df = df.assign(day_mapped = df.dates.dt.weekday.map(wd_map)).sort_values('day_mapped')

df.groupby(df.dates.dt.week).tail(1).sort_index()

Вывод

        dates  nums  day_mapped
4  2018-11-09    57           6
8  2018-11-15    83           5
10 2018-11-19    96           2
11 2018-12-01    66           0

Если ваши данные охватывают несколько лет, вам нужно сгруппироваться пооба Year + week.

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

Я написал функцию для выбора действительной наивысшей записи за неделю, которую нужно было бы использовать для еженедельной групповой работы:

def last_valid_report(recs):
    if len(recs) == 1:
        return recs
    recs = recs.copy()
    # recs = recs[recs['dates'].dt.weekday <= 4].nlargest(1, recs['dates'].dt.weekday)  # doesn't work
    recs['weekday'] = recs['dates'].dt.weekday  # because nlargest() needs a column name
    recs = recs[recs['weekday'] <= 4].nlargest(1, 'weekday')
    del recs['weekday']
    return recs
    # could have also done:
    # return recs[recs['weekday'] <= 4].nlargest(1, 'weekday').drop('weekday', axis=1)

При вызове с правильными группами я получаю:

In [155]: df2 = df.groupby(df['dates'].dt.week).apply(last_valid_report)

In [156]: df2
Out[156]:
              dates  nums
dates
45    4  2018-11-09    63
46    8  2018-11-15    90
47    10 2018-11-19    80
48    11 2018-12-01    94

Пара проблем с этим:

  1. Если я не поставлю recs.copy(), я получу ValueError: Shape of passed values is (3, 12), indices imply (3, 4)

  2. pandas 'nlargest будет использовать только имена столбцов, а не выражения.

    • , поэтому мне нужно создать дополнительный столбец в функции и удалить / удалитьэто, прежде чем вернуть его. Я мог бы также создать это в оригинальном df и отбросить его после .apply().
  3. Я получаю дополнительные даты столбца индекса '', из groupby + применить и должен быть явно отброшен :

    In [157]: df2.index = df2.index.droplevel(); df2
    Out[157]:
            dates  nums
    4  2018-11-09    63
    8  2018-11-15    90
    10 2018-11-19    80
    11 2018-12-01    94
    
  4. Если я получузапись с данными субботы и воскресенья (2 дня), мне нужно добавить проверку, если recs[recs['weekday'] <= 4] пусто, а затем просто использовать .nlargest(1, 'weekday') без фильтрации weekday <= 4;но это помимо сути вопроса.

...