【问题标题】:Subtract one column by all values in another column only if condition is met仅当满足条件时,才用另一列中的所有值减去一列
【发布时间】:2022-01-11 23:35:15
【问题描述】:

我有一个时间序列的数据框,我想获得问题的CLOSESUBMISSION 之间差异的累积和。但是,我希望它仅在 CLOSE 值高于 SUBMISSION 值时减去。以下是数据点(按CLOSE 排序)、预期输出和我尝试的代码:

df = pd.DataFrame({'REF_KEY': [1, 2, 3, 4, 5], 'SUBMISSION': ['2018-08-21', '2018-09-03', '2018-09-07', '2018-09-06', '2018-08-28'], 'CLOSE': ['2018-09-05', '2018-09-12', '2018-09-18', '2018-09-24', '2018-09-27']})
df['CLOSE'] = df['CLOSE'].astype('datetime64[ns]')
df['SUBMISSION'] = df['SUBMISSION'].astype('datetime64[ns]')

对于REF_KEY == 1ACCUM_DATE_DELTA 应该是:

  • ('2018-09-05' - '2018-08-21')的15天差
  • ('2018-09-05' - '09-03-2018') 之间相差 2 天
  • ('2018-09-05' - '2018-08-28') 之间相差 8 天,达到 26

对于REF_KEY == 2,你会得到:

  • ('2018-09-12' - '2018-08-21') 之间相差 22 天
  • ('2018-09-12' - '2018-09-03') 之间相差 9 天
  • ('2018-09-12' - '2018-09-07')之间有 5 天的差异
  • ('2018-09-12' - '2018-09-06')之间有 6 天的差异
  • ('2018-09-12' - '2018-08-28')之间相差 15 天

所以对于REF_KEY == 1,你可以看到它的关闭日期之间的差异包括REF_KEY == [3, 4],这是因为CLOSE大于SUBMISSION。因此,我产生了创建CLOSE 日期必须大于SUBMISSION 日期的条件。

df_2 = pd.DataFrame({'REF_KEY': [1, 2, 3, 4, 5], 
                     'SUBMISSION': ['2018-08-21', '2018-09-03', '2018-09-07', '2018-09-06', '2018-08-28'], 'CLOSE': ['2018-09-05', '2018-09-12', '2018-09-18', '2018-09-24', '2018-09-27'], 'ACCUM_DATE_DELTA': [25, 57, 86, 116, 131]})
df_2['CLOSE'] = df['CLOSE'].astype('datetime64[ns]')
df_2['SUBMISSION'] = df['SUBMISSION'].astype('datetime64[ns]')

尝试的代码:

df_2['ACCUM_DATE_DELTA'] = df_2['CLOSE']*len(df_2[df_2['CLOSE'] - df_2['SUBMISSION]]['SUBMISSION'].cumsum()) - df_2[df_2['CLOSE'] - df_2['SUBMISSION]]['SUBMISSION'].cumsum()

【问题讨论】:

    标签: python pandas dataframe datetime


    【解决方案1】:

    我的计算不正确。如果是我的,请告诉我这是否是您想要的。

    对于'REF_KEY' == 1,您可以使用它来查找累计天数

    (df['CLOSE'][0] - df['SUBMISSION']).dt.days.clip(lower=0).sum()
    

    df['CLOSE'][0] 是根据所有提交日期计算的第一个关闭日期; dt.days 以整数形式给出天数

    (df['CLOSE'][0] - df['SUBMISSION']).dt.days
    
    0    15
    1     2
    2    -2
    3    -1
    4     8
    Name: SUBMISSION, dtype: int64
    

    使用clip(lower=0).sum() 将负值更改为零并求和

    (df['CLOSE'][0] - df['SUBMISSION']).dt.days.clip(lower=0).sum()
    result = 25
    

    要自动执行此操作,请将 apply() 与自定义函数一起使用

    def calc(x):
        # print((x - df['SUBMISSION']).dt.days.clip(lower=0).sum())
        return (x - df['SUBMISSION']).dt.days.clip(lower=0).sum()
    
    df
    
       REF_KEY SUBMISSION      CLOSE
    0        1 2018-08-21 2018-09-05
    1        2 2018-09-03 2018-09-12
    2        3 2018-09-07 2018-09-18
    3        4 2018-09-06 2018-09-24
    4        5 2018-08-28 2018-09-27
    
    df['ACCUM_DATE_DELTA'] = df.apply(lambda x: calc(x['CLOSE']), axis=1)
    
       REF_KEY SUBMISSION      CLOSE  ACCUM_DATE_DELTA
    0        1 2018-08-21 2018-09-05                25
    1        2 2018-09-03 2018-09-12                57
    2        3 2018-09-07 2018-09-18                87
    3        4 2018-09-06 2018-09-24               117
    4        5 2018-08-28 2018-09-27               132
    

    【讨论】:

      【解决方案2】:
      1. Cross-merge 生成他们的笛卡尔积SUBMISSION x CLOSE
      2. 只保留行 where CLOSE > SUBMISSION
      3. groupby CLOSE 日期和组的 CLOSE - SUBMISSION 天数
      4. mergeACCUM 值恢复为原始df
      m = pd.merge(df.SUBMISSION, df.CLOSE, how='cross') # cross-merge for all SUBMISSION x CLOSE combos
      
      accum = (m.where(m.CLOSE > m.SUBMISSION)           # limit to CLOSE > SUBMISSION
                .groupby('CLOSE').SUBMISSION             # group by CLOSE
                .apply(lambda g: (g.name - g).sum())     # sum of all (CLOSE - SUBMISSION)
                .rename('ACCUM'))
      
      df.merge(accum, on='CLOSE')                        # merge back to df
      

      输出:

         REF_KEY  SUBMISSION       CLOSE     ACCUM
      0        1  2018-08-21  2018-09-05   25 days
      1        2  2018-09-03  2018-09-12   57 days
      2        3  2018-09-07  2018-09-18   87 days
      3        4  2018-09-06  2018-09-24  117 days
      4        5  2018-08-28  2018-09-27  132 days
      

      注意事项:

      • how='cross' 需要 pandas 1.2.0+,因此对于早期版本,merge 在一个虚拟的 key 列上:

        m = df[['SUBMISSION']].assign(key=0).merge(df[['CLOSE']].assign(key=0), on='key').drop(columns='key')
        
      • 与 Jonathan 的解决方案一样,与您的输出相比,这些天数相差 1。

      【讨论】:

        猜你喜欢
        • 2021-04-02
        • 2021-08-25
        • 1970-01-01
        • 2017-10-14
        • 1970-01-01
        • 1970-01-01
        • 2022-12-16
        • 1970-01-01
        • 2021-08-02
        相关资源
        最近更新 更多