我有一个可以使用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')
来填充缺少的值,但是问题是我不知道如何创建这样的索引。
当然,这只是一个简化的示例,实际上,项目的数量很大。
答案 0 :(得分:2)
使用melt
重塑DataFrame,然后使用first
和groupby
并最后调用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()