Pandas使用前一行中的信息填写每个组中的缺失日期

时间:2018-05-21 22:48:09

标签: python pandas pandas-groupby

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组,但我觉得我们可以做得更好。

谢谢!

4 个答案:

答案 0 :(得分:2)

resamplegroupby

一起使用
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