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')要快得多。注意,这个答案要求数据按开始日期排序!