每组插入天数

时间:2018-12-05 12:44:42

标签: python pandas multi-index

我有一个可以使用MultiIndex,reindex和period_range解决的问题,但是我不知道如何精确地做到这一点。

我有以下数据框:

proj_id   date_from    date_to      some_value
abc1001   2017-10-20   2017-10-21            7
abc1002   2017-10-29   2017-11-03           10
abc1002   2017-09-05   2017-09-07            9
abc1003   2017-09-05   2017-09-05            3

我想将其转换为类似这样的内容:

proj_id   date         some_value
abc1001   2017-10-20            7
abc1001   2017-10-21            7
abc1002   2017-10-29           10
abc1002   2017-10-30           10
abc1002   2017-10-31           10
abc1002   2017-11-01           10
abc1002   2017-11-02           10
abc1002   2017-11-03           10
abc1002   2017-09-05            9
abc1002   2017-09-06            9
abc1002   2017-09-07            9
abc1003   2017-09-05            3

因此,基本上每个项目都有一些具有一定价值的日期范围,并且想为这些范围之间的每一天生成记录,但是每个项目的范围都不同,甚至每个项目甚至可以有很多单独的范围。

我认为,如果我能以某种方式设法构建MultIindex:

                    some_value
abc1001 2017-10-20           7
        2017-10-21         NaN
abc1002 2017-09-05           9
        2017-09-06         NaN
        2017-09-07         NaN
        2017-10-29          10
        2017-10-30         NaN
        2017-10-31         NaN
        2017-11-01         NaN
        2017-11-02         NaN
        2017-11-03         NaN
abc1003 2017-09-05           3

然后我可以使用DataFrame.fillna(method='ffill')来填充缺少的值,但是问题是我不知道如何创建这样的索引。

当然,这只是一个简化的示例,实际上,项目的数量很大。

2 个答案:

答案 0 :(得分:2)

使用melt重塑DataFrame,然后使用firstgroupby并最后调用ffill

通知:

如果数据中没有缺失值,则解决方案有效。

df = (df.reset_index()
        .melt(['proj_id','some_value', 'index'], value_name='date')
        .set_index('date')
        .groupby(['proj_id', 'index'])['some_value']
        .resample('d')
        .first()
        .reset_index(level=1, drop=True)
        .ffill()
        .reset_index()
       )
print (df)
    proj_id       date  some_value
0   abc1001 2017-10-20         7.0
1   abc1001 2017-10-21         7.0
2   abc1002 2017-10-29        10.0
3   abc1002 2017-10-30        10.0
4   abc1002 2017-10-31        10.0
5   abc1002 2017-11-01        10.0
6   abc1002 2017-11-02        10.0
7   abc1002 2017-11-03        10.0
8   abc1002 2017-09-05         9.0
9   abc1002 2017-09-06         9.0
10  abc1002 2017-09-07         9.0
11  abc1003 2017-09-05         3.0

另一种解决方案:

s = pd.concat([pd.Series(r.Index,pd.date_range(r.date_from, r.date_to)) 
               for r in df.itertuples()])

df1 = df[['proj_id','some_value']].join(pd.Series(s.index, s.values).rename('date'))
print (df1)
   proj_id  some_value       date
0  abc1001           7 2017-10-20
0  abc1001           7 2017-10-21
1  abc1002          10 2017-10-29
1  abc1002          10 2017-10-30
1  abc1002          10 2017-10-31
1  abc1002          10 2017-11-01
1  abc1002          10 2017-11-02
1  abc1002          10 2017-11-03
2  abc1002           9 2017-09-05
2  abc1002           9 2017-09-06
2  abc1002           9 2017-09-07
3  abc1003           3 2017-09-05

答案 1 :(得分:0)

jazrael第一个答案是正确的,但是我不正确地移植了他的代码(因为就像我说的示例是实际问题的简化版本一样),或者它确实存在一些性能问题。无论如何,我实施了另一个对我来说足够快的解决方案。如果有人感兴趣,请在此处发布:

non_start_end_cols = [col for col in df.columns if col not in ['date_from', 'date_to']]

rows = []
def process_row(row):
    non_date_row_data = [row[col] for col in non_start_end_cols]
    for day in pd.date_range(start=row['date_from'], end=row['date_to']).to_pydatetime():
        rows.append(non_date_row_data + [day])

_ = df.apply(process_row, axis=1)
new_df = pd.DataFrame(rows, columns=non_start_end_cols + ['date'])

如果日期周期重叠,则可以通过简单的合并解决这种情况:

groupby_cols = non_start_end_cols.copy()
groupby_cols.append('date')
groupby_cols.remove('some_value')

aggregated = new_df \
    .groupby(groupby_cols) \
    .agg(np.sum) \
    .reset_index()