【问题标题】:Pandas dataframe: Number of dates in group prior to row date熊猫数据框:行日期之前组中的日期数
【发布时间】:2018-04-18 12:30:39
【问题描述】:

我想在数据框中添加一列,其中包含每个组 G 在时间 t 之前发生的变量 x 中不同观察的数量.
注意:t 是日期时间格式,数据中可能存在缺失值,但可以忽略。同一个x 可以在一个组中出现多次,但随后被分配相同的日期。分配给x 的时间在各组中不同。

我希望这个例子有帮助:

输入:

Group   x        t  

1       a      2013-11-01   
1       b      2015-04-03  
1       b      2015-04-03  
1       c      NaT  
2       a      2017-03-01  
2       c      2013-11-06   
2       d      2015-04-26  
2       d      2015-04-26  
2       d      2015-04-26
2       b      NaT  

输出:

Group   x        t         Number of unique x before time t

1       a      2013-11-01      0
1       b      2015-04-03      1
1       b      2015-04-03      1
1       c      NaT             NaN
2       a      2017-03-01      2
2       c      2013-11-06      0
2       d      2015-04-26      1
2       d      2015-04-26      1
2       d      2015-04-26      1
2       b      NaT             NaN

数据集非常大,所以我想知道是否有任何矢量化方式可以做到这一点(例如使用groupby)。

非常感谢

【问题讨论】:

    标签: python-3.x pandas dataframe apply


    【解决方案1】:

    这是另一种方法。

    • 初始排序使fillna 稍后可以工作。
    • 创建df2,它计算每个组在该日期之前的唯一天数。
    • 将天数合并回原来的dffillna 然后处理重复的日期(排序确保这正确发生)
    • NaT 的日期被放在cumsum 的末尾,所以只需将它们重置为NaN

    如果你想在末尾重新排序到原来的顺序,只需排序索引df.sort_index(inplace=True)

    import pandas as pd
    import numpy as np
    
    df = df.sort_values(by=['Group', 't'])
    df['t'] = pd.to_datetime(df.t)
    
    df2 = df
    df2 = df2[df2.t.notnull()]
    df2 = df2.drop_duplicates()
    df2['temp'] = 1
    df2['num_b4'] = df2.groupby('Group').temp.cumsum()-1
    
    df = df.merge(df2[['num_b4']], left_index=True, right_index=True, how='left')
    df['num_b4'] = df['num_b4'].fillna(method='ffill')
    df.loc[df.t.isnull(), 'num_b4'] = np.NaN
    
    #   Group  x          t  num_b4
    #0      1  a 2013-11-01     0.0
    #1      1  b 2015-04-03     1.0
    #2      1  b 2015-04-03     1.0
    #3      1  c        NaT     NaN
    #5      2  c 2013-11-06     0.0
    #6      2  d 2015-04-26     1.0
    #7      2  d 2015-04-26     1.0
    #8      2  d 2015-04-26     1.0
    #4      2  a 2017-03-01     2.0
    #9      2  b        NaT     NaN
    

    IIUUC 对于新的案例,你想在上面的代码中改变一行。

    # df2 = df2.drop_duplicates()
    df2 = df2.drop_duplicates(['Group', 't'])
    

    这样,同一天分配了多个 x 值不会导致观察次数增加。请参阅下面第 3 组的输出,其中我向您的初始数据添加了 4 行。

    Group   x        t  
    3       a      2015-04-03
    3       b      2015-04-03
    3       c      2015-04-03
    3       c      2015-04-04 
    
    ## Apply the Code changing the drop_duplicates() line
        Group  x          t  num_b4
    0       1  a 2013-11-01     0.0
    1       1  b 2015-04-03     1.0
    2       1  b 2015-04-03     1.0
    3       1  c        NaT     NaN
    5       2  c 2013-11-06     0.0
    6       2  d 2015-04-26     1.0
    7       2  d 2015-04-26     1.0
    8       2  d 2015-04-26     1.0
    4       2  a 2017-03-01     2.0
    9       2  b        NaT     NaN
    10      3  a 2015-04-03     0.0
    11      3  b 2015-04-03     0.0
    12      3  c 2015-04-03     0.0
    13      3  c 2015-04-04     1.0
    

    【讨论】:

    • 谢谢。我喜欢这个解决方案,因为它非常快。但是,如果 X 具有相同的日期,则会出现一个问题:
    • 谢谢。我喜欢这个解决方案,因为它非常快。但是,如果 x 具有相同的日期,则会出现一个问题(我以前没有想到过这种情况,但应该 num_b4 应该具有相同的值):例如,如果在观察 #2 中,x 被替换为S,那么 num_b4 应该仍然显示 1(因为之前只有 x=a),但它会显示 2。您知道如何解决它吗?
    • @JanN。查看更新。您只需要更改drop_duplicates 行,您就应该获得所需的输出。
    • 感谢您的修改。在您示例的最后一行 (13) 中,理论结果应为 3(因为 a、b、c 之前发生过)。有什么快速修复的想法吗?
    • @JanN。应该可以做到。在计算 cumsum 之前,您应该只需要计算出需要添加到 df2['temp'] 的天数。如果您只想要不同的x,您可以使用df.groupby(['Group', 't']).x.nunique()-1 来计算,或者如果您想要观察的总数,您可以使用df.groupby(['Group', 't']).x.count()-1。问题是您想将此号码添加到组中的 NEXT DATE。我在这样做时遇到了麻烦,尽管我认为可以使用merge_asof() 来完成。您可能会很幸运地在另一个问题中提出这个问题。
    【解决方案2】:

    您是否可以使用自定义设计的函数来执行此操作,使用 merge 进行自联接,groupbynunique 计算唯一值:

    def countunique(x):
        df_out = x.merge(x, on='Group')\
                  .query('x_x != x_y and t_y < t_x')\
                  .groupby(['x_x','t_x'])['x_y'].nunique()\
                  .reset_index()
        result = x.merge(df_out, left_on=['x','t'], 
                         right_on=['x_x','t_x'],
                         how='left')
        result = result[['Group','x','t','x_y']]
        result.loc[result.t.notnull(),'x_y'] = result.loc[result.t.notnull(),'x_y'].fillna(0)
        return result.rename(columns={'x_y':'No of unique x before t'})
    
    df.groupby('Group', group_keys=False).apply(countunique)
    

    输出:

       Group  x          t  No of unique x before t
    0      1  a 2013-11-01                      0.0
    1      1  b 2015-04-03                      1.0
    2      1  b 2015-04-03                      1.0
    3      1  c        NaT                      NaN
    0      2  a 2017-03-01                      2.0
    1      2  c 2013-11-06                      0.0
    2      2  d 2015-04-26                      1.0
    3      2  d 2015-04-26                      1.0
    4      2  d 2015-04-26                      1.0
    5      2  b        NaT                      NaN
    

    解释:

    对于每个组,

    1. 使用“组”上的合并执行自联接
    2. 仅在 当前记录。
    3. 使用 groupby 和 nunique 仅计算 x 的唯一值 自加入。
    4. 将 x 的计数合并回原始数据帧,保留所有行使用 如何='左'
    5. 在有时间的地方用零填充 NaN 值
    6. 重命名列标题

    【讨论】:

    • 非常感谢!它可以正常工作,但对于大量组 (~10000) 运行缓慢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-14
    • 2021-10-21
    • 1970-01-01
    • 2017-12-01
    • 2023-03-20
    • 2021-07-25
    • 2021-03-21
    相关资源
    最近更新 更多