根据第二个数据框中的日期范围对一个数据框中的值求和

Sum values in one dataframe based on date range in a second dataframe

我有两个数据框(下面的简化示例)。一个包含一系列日期和值 (df1),第二个包含日期范围 (df2)。我想 identify/select/mask df1 中 df2 的日期范围,对关联的 df1 值求和并将它们添加到 df2 中的新列。

我是新手,尝试过的所有技巧都没有成功——错误方法的组合、不兼容方法的组合、语法错误等等。我在这里搜索了问答,但是 none 已经很好地解决了这个问题。

import pandas as pd
#********** df1: dates and values ***********
rng = pd.date_range('2012-02-24', periods=12, freq='D')
df1 = pd.DataFrame({ 'STATCON': ['C00028', 'C00489', 'C00038', 'C00589', 'C10028', 'C00499', 'C00238', 'C00729',
                            'C10044', 'C00299', 'C00288', 'C00771'],
'Date': rng,
'Val': [0.96, 0.57, 0.39, 0.17, 0.93, 0.86, 0.54, 0.58, 0.43, 0.19, 0.40, 0.32]
                 })

#********** df2: date range ***********
df2 = pd.DataFrame({
                'BCON': ['B002', 'B004', 'B005'],
                'Start': ['2012-02-25', '2012-02-28', '2012-03-01'],
                'End': ['2012-02-29', '2012-03-04', '2012-03-06']
               })
df2[['Start','End']] = df2[['Start','End']].apply(pd.to_datetime)

#********** Desired Output:  df2 -- date range with summed values ***********
df3 = pd.DataFrame({
                'BCON': ['B002', 'B004', 'B005'],
                'Start': ['2012-02-25', '2012-02-28', '2012-03-01'],
                'End': ['2012-02-29', '2012-03-04', '2012-03-06'],
                'Sum_Val': [2.92, 3.53, 2.46]
               })

您可以使用 Dataframe.apply 函数解决此问题,如下所示:

def to_value(row):
  return df1[(row['Start'] <= df1['Date']) & (df1['Date'] <= row['End'])]['Val'].sum()

df3 = df2.copy()
df3['Sum_Val'] = df3.apply(to_value, axis=1)

对 df3 数据帧的每一行调用 to_value 函数。

请在此处查看解决方案的实时实施:https://1000words-hq.com/n/TcYN1Fz6Izp

一个选项是 conditional_join from pyjanitor - 它试图避免搜索每一行(这可能会消耗内存,具体取决于数据大小):

# pip install pyjanitor
import pandas as pd
import numpy as np

df2 = df2.astype({'Start':np.datetime64, 'End':np.datetime64})

(df1
.conditional_join(
    df2, 
    ('Date', 'Start', '>='), 
    ('Date', 'End', '<='))
.loc[:, ['BCON', 'Start', 'End', 'Val']]
.groupby(['BCON', 'Start', 'End'], as_index = False)
.agg(sum_val = ('Val', 'sum'))
)

   BCON      Start        End  sum_val
0  B002 2012-02-25 2012-02-29     2.92
1  B004 2012-02-28 2012-03-04     3.53
2  B005 2012-03-01 2012-03-06     2.46