与this one类似的问题,但有一些修改:
我们只应填写该组的最小值和最大值之间的日期,而不是在整个列的最小和最大日期之间填写每个组的缺失日期。包含每组中最后一行的数据框
可重复的例子:
x = pd.DataFrame({'dt': ['2016-01-01','2016-01-03', '2016-01-04','2016-01-01','2016-01-01','2016-01-04']
,'amount': [10.0,30.0,40.0,78.0,80.0,82.0]
, 'sub_id': [1,1,1,2,2,2]
})
目视:
dt sub_id amount
0 2016-01-01 1 10.0
1 2016-01-03 1 30.0
2 2016-01-04 1 40.0
3 2017-01-01 2 78.0
4 2017-01-01 2 80.0
5 2017-01-04 2 82.0
我需要的输出:
dt sub_id amount
0 2016-01-01 1 10.0
1 2016-01-02 1 10.0
2 2016-01-03 1 30.0
3 2016-01-04 1 40.0
4 2017-01-01 2 80.0
5 2017-01-02 2 80.0
6 2017-01-03 2 80.0
7 2017-01-04 2 82.0
我们按照dt和sub_id进行分组。如您所见,在sub_id = 1中,为2016-01-02添加了一行,并且在前一行为10.0时将金额估算为10.0(假设数据预先排序以启用此功能)。对于sub_id = 2,为2017-01-02和2017-01-03添加了行,金额为80.0,因为这是此日期之前的最后一行。 2017-01-01的第一行也被删除了,因为我们只想保留每个日期和sub_id的最后一行。
寻找最有效的方法,因为真实数据有数百万行。我有一个使用lambda函数的当前方法并将它们应用于sub_id组,但我觉得我们可以做得更好。
谢谢!
答案 0 :(得分:2)
将resample
与groupby
x.dt=pd.to_datetime(x.dt)
x.set_index('dt').groupby('sub_id').apply(lambda x : x.resample('D').max().ffill()).reset_index(level=1)
Out[265]:
dt amount sub_id
sub_id
1 2016-01-01 10.0 1.0
1 2016-01-02 10.0 1.0
1 2016-01-03 30.0 1.0
1 2016-01-04 40.0 1.0
2 2016-01-01 80.0 2.0
2 2016-01-02 80.0 2.0
2 2016-01-03 80.0 2.0
2 2016-01-04 82.0 2.0
答案 1 :(得分:1)
当然是正确的日期:
x.dt = pd.to_datetime(x.dt)
然后这个:
cols = ['dt', 'sub_id']
pd.concat([
d.asfreq('D').ffill(downcast='infer')
for _, d in x.drop_duplicates(cols, keep='last')
.set_index('dt').groupby('sub_id')
]).reset_index()
dt amount sub_id
0 2016-01-01 10 1
1 2016-01-02 10 1
2 2016-01-03 30 1
3 2016-01-04 40 1
4 2016-01-01 80 2
5 2016-01-02 80 2
6 2016-01-03 80 2
7 2016-01-04 82 2
答案 2 :(得分:1)
使用asfreq
& groupby
首先将dt
转换为datetime
&摆脱重复
然后为每组sub_id
使用asfreq('D', method='ffill')
生成缺少日期和估算金额
reset_index
列上的amount
,因为有一个重复的sub_id
列和索引。
x.dt = pd.to_datetime(x.dt)
x.drop_duplicates(
['dt', 'sub_id'], 'last'
).groupby('sub_id').apply(
lambda x: x.set_index('dt').asfreq('D', method='ffill')
).amount.reset_index()
# output:
sub_id dt amount
0 1 2016-01-01 10.0
1 1 2016-01-02 10.0
2 1 2016-01-03 30.0
3 1 2016-01-04 40.0
4 2 2016-01-01 80.0
5 2 2016-01-02 80.0
6 2 2016-01-03 80.0
7 2 2016-01-04 82.0
答案 3 :(得分:0)
以下对我有用,看起来非常有效,但我不能说它是否足够有效。它确实避免了lambdas。
我调了你的数据df
。
使用整个日期/子网格创建base_df
:
import pandas as pd
from itertools import product
base_grid = product(pd.date_range(df['dt'].min(), df['dt'].max(), freq='D'), list(range(df['sub_id'].min(), df['sub_id'].max() + 1, 1)))
base_df = pd.DataFrame(list(base_grid), columns=['dt', 'sub_id'])
从df
获取每dt / sub_id的最大值:
max_value_df = df.loc[df.groupby(['dt', 'sub_id'])['amount'].idxmax()]
max_value_df['dt'] = max_value_df['dt'].apply(pd.Timestamp)
在最大值上合并base_df:
merged_df = base_df.merge(max_value_df, how='left', on=['dt', 'sub_id'])
排序和转发填充最大值:
merged_df = merged_df.sort_values(by=['sub_id', 'dt', 'amount'], ascending=True)
merged_df['amount'] = merged_df.groupby(['sub_id'])['amount'].fillna(method='ffill')
结果:
dt sub_id amount
0 2016-01-01 1 10.0
2 2016-01-02 1 10.0
4 2016-01-03 1 30.0
6 2016-01-04 1 40.0
1 2016-01-01 2 80.0
3 2016-01-02 2 80.0
5 2016-01-03 2 80.0
7 2016-01-04 2 82.0