Данные интервала Pandas (действительны от и действительны до) до конца месяца, повторная выборка
У меня есть данные, извлеченные из базы данных, где доходы для набора пользователей хранятся с датами valid_from и valid_to (возможен множественный доход на пользователя). Я хотел бы сделать построчный набор данных. В каждой строке я хотел бы показать ситуацию в конце месяца, сгруппированную по пользователям.
На данный момент я создаю новый фрейм данных со всеми возможными окончаниями месяцев в индексе. Затем я зацикливаюсь на этом индексе и соответствующим образом заполняю фрейм данных (путем фильтрации из исходного набора данных).
import pandas as pd
import numpy as np
test_dict = {'valid_from': [np.datetime64('2001-01-01'),np.datetime64('2001-01-15'),np.datetime64('2001-06-20')],\
'valid_to': [np.datetime64('2001-12-31'),np.datetime64('2001-04-15'),np.datetime64('2001-11-10')],\
'user' : ['a','b','a'],\
'income': [200,300,400]}
interval_data = pd.DataFrame(test_dict, columns=['valid_from','valid_to','user','income'])
end_of_month = pd.date_range(start = interval_data.valid_from.min(), end=interval_data.valid_to.max(), freq='M')
dfs = []
for x in end_of_month:
fltr = (x >= interval_data.valid_from) & (x <= interval_data.valid_to)
df = interval_data.loc[fltr, :].copy()
df['date'] = x
dfs.append(df)
df = pd.concat(dfs)
group = df.loc[:,['date','user','income']].groupby(['date','user']).sum()
print(interval_data)
print(group)
Тем не менее, я думаю, что есть более эффективный способ сделать логику выше, используя различные функции времени и даты, представленные в Pandas.
Какие-либо предложения?
1 ответ
Решение
IIUC:
interval_data.groupby(level=0,group_keys=False, as_index=False)\
.apply(lambda x: pd.DataFrame({'user':x.user.values,'income':x.income.values},
index=pd.date_range(x.valid_from.values[0],
x.valid_to.values[0],
freq='M')))\
.set_index('user',append=True).sort_index().sum(level=[0,1])
Выход:
income
user
2001-01-31 a 200
b 300
2001-02-28 a 200
b 300
2001-03-31 a 200
b 300
2001-04-30 a 200
2001-05-31 a 200
2001-06-30 a 600
2001-07-31 a 600
2001-08-31 a 600
2001-09-30 a 600
2001-10-31 a 600
2001-11-30 a 200
2001-12-31 a 200