加入并总结数据帧中的行子集

时间:2016-07-25 18:41:01

标签: python pandas dataframe

我有一个pandas数据框,用于存储日期范围和一些相关的列:

        date_start    date_end ... lots of other columns ...
1       2016-07-01  2016-07-02
2       2016-07-01  2016-07-03
3       2016-07-01  2016-07-04
4       2016-07-02  2016-07-07
5       2016-07-05  2016-07-06

另一个按日期索引的皮卡丘目击数据框:

              pikachu_sightings
      date
2016-07-01                    2
2016-07-02                    4
2016-07-03                    6
2016-07-04                    8
2016-07-05                   10
2016-07-06                   12
2016-07-07                   14

对于第一个df中的每一行,我想计算该日期范围内的pikachu_sightings总和(即date_startdate_end)并将其存储在新列中。所以最终会得到这样的df(为了清晰起见留下数字):

        date_start    date_end    total_pikachu_sightings
1       2016-07-01  2016-07-02                      2 + 4
2       2016-07-01  2016-07-03                  2 + 4 + 6
3       2016-07-01  2016-07-04              2 + 4 + 6 + 8
4       2016-07-02  2016-07-07   4 + 6 + 8 + 10 + 12 + 14
5       2016-07-05  2016-07-06                    10 + 12

如果我是迭代地执行此操作,我会迭代日期范围表中的每一行,选择与日期范围匹配的目标表中的行子集并对其执行求和 - 但这是对我的数据集来说太慢了:

for range in ranges.itertuples():
    sightings_in_range = sightings[(sightings.index >= range.date_start) & (sightings.index <= range.date_end)]
    sum_sightings_in_range = sightings_in_range["pikachu_sightings"].sum()
    ranges.set_value(range.Index, 'total_pikachu_sightings', sum_sightings_in_range)

这是我尝试使用pandas,但失败了,因为两个数据帧的长度不匹配(即使它们确实存在,我的方法可能还有一些其他缺陷):

range["total_pikachu_sightings"] =
    sightings[(sightings.index >= range.date_start) & (sightings.index <= range.date_end)
             ["pikachu_sightings"].sum()

我试图了解一般方法/设计应该是什么样子,因为我也希望与其他功能聚合,sum似乎是最简单的例子。对不起,如果这是一个显而易见的问题 - 我是熊猫新手!

2 个答案:

答案 0 :(得分:2)

矢量化解决方案的草图:

在piRSquared的回答中以p开头。

确保date_ cols有datetime64个dtypes,即:

df['date_start'] = pd.to_datetime(df.date_time)

然后计算累积总和:

psums = p.cumsum()

result = psums.asof(df.date_end) - psums.asof(df.date_start)
但是,它还没有结束。 asof会返回最后一个正常值,因此有时会采用确切的开始日期,有时则不会(取决于您的数据)。所以,你必须调整它。 (如果日期频率为day,则可能会将p的索引向后移动一小时,例如-pd.Timedelta(1, 'h'),然后添加p.asof(df.start_date)可能会有效。)

答案 1 :(得分:1)

首先确保pikachu_sightings具有日期时间索引并已排序。

p = pikachu_sightings.squeeze() # force into a series
p.index = pd.to_datetime(p.index)
p = p.sort_index()

然后确保您的date_startdate_end为日期时间。

df.date_start = pd.to_datetime(df.date_start)
df.date_end   = pd.to_datetime(df.date_end)

然后就是

df.apply(lambda x: p[x.date_start:x.date_end].sum(), axis=1)

0     6
1    12
2    20
3    54
4    22
dtype: int64