pandas расчет среднего количества значений в неделю по месяцам
Нужно посчитать среднее количество покупок в неделю по месяцам для каждого id. Каждая дата означает одну совершенную покупку.
Датафрейм:
df = pd.DataFrame({
"id": [
"01",
"01",
"01",
"01",
"02",
"02",
"02",
"02"
],
"date": [
"2022-07-06",
"2022-07-08",
"2022-07-15",
"2022-08-11",
"2022-07-06",
"2022-07-08",
"2022-07-15",
"2022-08-11"
]
})
Нужно получить:
id month week_mean
01 07 1.5
01 08 1
02 07 1.5
02 08 1
Мой ход решения такой. Даты, например, 2022-07-06 и 2022-07-08 относятся к одной неделе, т.е. внутри это недели эти даты (покупки) нужно сложить. Далее выводить среднее между всеми неделями месяца для каждого id.
df['date'] = pd.to_datetime(df['date']) # перевожу в формат даты
df['week'] = df['date'].dt.strftime('%U') # добавляю колонку с номером недели
df['date'] = df['date'].astype('str')
df['date'] = df['date'].str.split('-').str[0]+'-'+ df['date'].str.split('-').str[1] # привожу колонку к формату "год-месяц"
df1 = pd.merge(df, df.groupby(['id', 'week'], as_index = False).agg({'date':'count'})\
.rename(columns={'date' : 'buy_count'}), on=('id','week')).drop_duplicates() # считаю количество заказов в каждую неделю и присоединяю колонку date
df1.groupby(['id', 'date'], as_index = False).agg({'buy_count':'mean'}) # считаю среднее по неделям
В результате получаю такой датафрейм:
id date buy_count
0 01 2022-07 1.5
1 01 2022-08 1.0
2 02 2022-07 1.5
3 02 2022-08 1.0
И он вроде бы ок, но что, если одна из недель будет принадлежать сразу двум месяцам? Датафрейм, например, будет такой:
df = pd.DataFrame({"id": ["01", "01", "01", "01", "02", "02", "02", "02"],
"date": ["2022-06-29", "2022-07-02", "2022-07-15", "2022-08-11",
"2022-07-06", "2022-07-08", "2022-07-15", "2022-08-11"]})
И тогда приведенный выше код будет давать неправильный результат:
id date buy_count
0 01 2022-06 2.0
1 01 2022-07 1.5
2 01 2022-08 1.0
3 02 2022-07 1.5
4 02 2022-08 1.0
Что говорит о том, что мой подход с нумерацией недель не верный.
Ответы (2 шт):
Что-то у меня неуклюжее решение получается. возможно, я чего-то не понял:
res = pd.DataFrame()
for i in df["id"].unique(): # разбираем датафрейм на части по значению id
t = df[df["id"]==i].set_index("date").resample("W-MON").count()
#^^ выбираем данные для текущего id, устанавливаем колонку с датой в
# качестве индекса, чтобы можно было делать ресамплинг
# делаем ресамплинг данных по календарным неделям и считаем количество
# id в каждой неделе
t = t.loc[~(t==0).all(axis=1)]
# ^^ выбираем только те недели, в которых есть покупки
s = pd.DataFrame(t.groupby(t.index.to_period("M"))["id"].mean())
# ^^ группируем по месяцам и считаем среднее
s.columns = ["buy_value"] # переименовываем колонку
s["id"] = i # создаем колонку с именем id
s = s.reset_index() # восстанавливаем индекс
res = pd.concat([res, s]) # добавляем полученное в результат
print(res)
альтернативный вариант:
res = (df.groupby(['id',df['date'].dt.month,df['date'].dt.isocalendar().week]).
count().
groupby(level=[0,1])['date'].
mean().
rename('week_mean').
reset_index())
print(res)
'''
id date week_mean
0 01 7 1.5
1 01 8 1.0
2 02 7 1.5
3 02 8 1.0