Python 熊猫用前一行中的信息填写每组中缺失的日期
与问题类似,但有一些修改: 我们不应该为整个列的最小日期和最大日期之间的每个组填充缺失的日期,而应该只填充该组的最小日期和最大日期之间的日期,并输出一个包含每个组中最后一行的数据帧 可复制示例:Python 熊猫用前一行中的信息填写每组中缺失的日期,python,pandas,pandas-groupby,Python,Pandas,Pandas Groupby,与问题类似,但有一些修改: 我们不应该为整个列的最小日期和最大日期之间的每个组填充缺失的日期,而应该只填充该组的最小日期和最大日期之间的日期,并输出一个包含每个组中最后一行的数据帧 可复制示例: 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,
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的第一行也被删除,因为我们只想保留每个日期和子项目id的最后一行
寻找最有效的方法来实现这一点,因为实际数据有数百万行。我有一个使用lambda函数的当前方法,并将它们应用于子类id组,但我觉得我们可以做得更好
谢谢 使用
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
下面的内容对我来说很有用,看起来相当有效,但我不能说它是否足够有效。它确实避免了lambdas tho 我调用了您的数据
df
使用整个日期/子id网格创建一个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)
根据最大值合并基本值:
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
确定正确的日期当然:
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
使用
asfreq
和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
首先将dt
转换为datetime
&消除重复项
然后,对于每组sub_id
使用asfreq('D',method='ffill')
生成缺失日期并估算金额
最后,amount
列上的reset_index
,因为有重复的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
谢谢,这是非常干净的,但我有一个类似的解决方案,因为它使用应用程序,性能相当慢。piRSquared的解决方案最适合meI收回上述评论。此解决方案与其他解决方案之间的性能差异是由于重采样('D').max()步骤造成的。首先使用drop duplicates,然后执行asfreq('D')要快得多。注意,这个答案要求数据按开始日期排序!