Данные интервала 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
Другие вопросы по тегам