【问题标题】:Groupby Pandas , calculate multiple columns based on date differenceGroupby Pandas,根据日期差异计算多列
【发布时间】:2021-10-13 02:16:17
【问题描述】:

我有一个如下所示的熊猫数据框:

CID RefID   Date        Group   MID 
100     1   1/01/2021       A                       
100     2   3/01/2021       A                       
100     3   4/01/2021       A   101             
100     4   15/01/2021      A                           
100     5   18/01/2021      A                   
200     6   3/03/2021       B                       
200     7   4/04/2021       B                       
200     8   9/04/2021       B   102             
200     9   25/04/2021      B                       
300     10  26/04/2021      C                       
300     11  27/05/2021      C           
300     12  28/05/2021      C   103 

我要创建三列:

days_diff:

  1. 这必须以一种方式创建,如果第一个日期和相应行的 b/w 差异大于属于同一 CID 的 30,则将“NAT”或 0 分配给下一行(重置),然后减去此行对应以下值的日期

  2. 如果 MIDis 不为 null 并且属于同一 CID 组,则将 'NAT' 或 0 分配给下一行(重置) 然后用这一行减去以下值的日期

否则,只需获取对应行属于同一 CID 的第一行的日期差异

答: 这取决于 days_diff 列,该列就像一个计数器,它只会在同一个 CID 发生另一个 NAT 并为每个 CID 重置时才会更改/递增。

B:此列依赖于 A 列,如果 A 中的值保持不变,则不会改变,否则会递增

解释有点复杂,请参考下面的输出。我已经使用 .groupby() .diff() .shift() 方法来创建多个虚拟列来计算这个并且仍在处理它,请让我知道最好的方法,谢谢

我的预期输出:

CID RefID   Date        Group   MID     days_diff       A   B
100     1   1/01/2021       A           NAT             1   1
100     2   3/01/2021       A           2 days          1   1
100     3   4/01/2021       A   101     3 days          1   1
100     4   15/01/2021      A           NAT             2   4
100     5   18/01/2021      A           3 days          2   4
200     6   3/03/2021       B           NAT             1   6
200     7   4/04/2021       B           NAT             2   7
200     8   9/04/2021       B   102     5 days          2   7
200     9   25/04/2021      B           NAT             3   9
300     10  26/04/2021      C           NAT             1   10
300     11  27/05/2021      C           NAT             2   11
300     12  28/05/2021      C   103     1 day           2   11

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    你可以这样做:

    def days_diff(sdf):
        result = pd.DataFrame(
            {"days_diff": pd.NaT, "A": None}, index=sdf.index
        )
        start = sdf.at[sdf.index[0], "Date"]
        for index, day, next_MID_is_na in zip(
            sdf.index[1:], sdf.Date[1:], sdf.MID.shift(1).isna()[1:]
        ):
            diff = (day - start).days
            if diff <= 30 and next_MID_is_na:
                result.at[index, "days_diff"] = diff
            else:
                start = day
        result.A = result.days_diff.isna().cumsum()
        return result
    
    df[["days_diff", "A"]] = df[["CID", "Date", "MID"]].groupby("CID").apply(days_diff)
    df["B"] = df.RefID.where(df.A != df.A.shift(1)).ffill()
    

    df 的结果创建者

    from io import StringIO
    data = StringIO(
    '''
    CID RefID   Date        Group   MID 
    100     1   1/01/2021       A                       
    100     2   3/01/2021       A                       
    100     3   4/01/2021       A   101             
    100     4   15/01/2021      A                           
    100     5   18/01/2021      A                   
    200     6   3/03/2021       B                       
    200     7   4/04/2021       B                       
    200     8   9/04/2021       B   102             
    200     9   25/04/2021      B                       
    300     10  26/04/2021      C                       
    300     11  27/05/2021      C           
    300     12  28/05/2021      C   103
    ''')
    df = pd.read_csv(data, delim_whitespace=True)
    df.Date = pd.to_datetime(df.Date, format="%d/%m/%Y")
    

        CID  RefID       Date Group    MID days_diff  A     B
    0   100      1 2021-01-01     A    NaN       NaT  1   1.0
    1   100      2 2021-01-03     A    NaN         2  1   1.0
    2   100      3 2021-01-04     A  101.0         3  1   1.0
    3   100      4 2021-01-15     A    NaN       NaT  2   4.0
    4   100      5 2021-01-18     A    NaN         3  2   4.0
    5   200      6 2021-03-03     B    NaN       NaT  1   6.0
    6   200      7 2021-04-04     B    NaN       NaT  2   7.0
    7   200      8 2021-04-09     B  102.0         5  2   7.0
    8   200      9 2021-04-25     B    NaN       NaT  3   9.0
    9   300     10 2021-04-26     C    NaN       NaT  1  10.0
    10  300     11 2021-05-27     C    NaN       NaT  2  11.0
    11  300     12 2021-05-28     C  103.0         1  2  11.0
    

    几个解释:

    • 函数days_diff 生成一个包含days_diffA 两列的数据框。它应用于df 的按列CID 分组的子数据帧。
    • 第一步:初始化结果数据框result(列days_diff填充NaT,列ANone),并将起始值start设置为与第一天的差异在小组中。
    • 之后基本上循环遍历子数据帧第一个索引之后,从而获取索引、列Date中的值和一个布尔值next_MID_is_na,它表示如果next 行中的MID 列是NaN(通过.shift(1).isna())。
    • 在循环的每一步:
      1. 计算当天与开始日的差异。
      2. 检查days_diff 列的规则:
        • 如果当前日期和开始日期的差异 并且 NaN 在下一个 MID-row -> 日期差异。
        • 否则 -> 将 start 重置为当前日期。
    • 在完成列days_diff 计算列A 后:result.days_diff.isna()True (== 1) 当days_diffNaN,否则False (== 0)。因此,累积和 (.cumsum()) 给出了所需的结果。
    • groupby-apply 生成列days_diffA 之后,最后计算列B:选择RefID-values,其中A 的值发生变化(通过.where(df.A != df.A.shift(1))),以及然后 forward 填充剩余的NaNs。

    【讨论】:

    • @Ash 我还是一头雾水:2021-02-182021-01-15 之间的区别是 34 天,因此 &gt; 30?
    • @Ash 好的,现在明白了!我已经用新的输入更新了结果。
    • 传奇,答案已被接受,您是否也可以使用您如何创建相同的示例数据集来更新答案
    • @Ash 当然,查看最后的编辑。
    • 感谢@Timus 非常感谢你,现在我要做的就是尝试分解代码并尝试理解你所做的事情,再次感谢大佬
    猜你喜欢
    • 2021-02-18
    • 2015-08-23
    • 1970-01-01
    • 2019-08-24
    • 1970-01-01
    • 2017-11-01
    • 1970-01-01
    • 2020-07-03
    • 1970-01-01
    相关资源
    最近更新 更多