Pandas groupby 聚合以截断最早日期而不是最旧日期

Posted

技术标签:

【中文标题】Pandas groupby 聚合以截断最早日期而不是最旧日期【英文标题】:Pandas groupby aggregation to truncate earliest date instead of oldest date 【发布时间】:2018-08-23 04:47:45 【问题描述】:

我正在尝试从日期范围的末尾而不是从开头进行聚合。尽管我认为将 closed='right' 添加到 grouper 可以解决问题,但事实并非如此。请让我知道如何实现底部显示的所需输出,谢谢。

import pandas as pd
df = pd.DataFrame(columns=['date','number'])
df['date'] = pd.date_range('1/1/2000', periods=8, freq='T')
df['number'] = pd.Series(range(8))
df

    date                number
0   2000-01-01 00:00:00 0
1   2000-01-01 00:01:00 1
2   2000-01-01 00:02:00 2
3   2000-01-01 00:03:00 3
4   2000-01-01 00:04:00 4
5   2000-01-01 00:05:00 5
6   2000-01-01 00:06:00 6
7   2000-01-01 00:07:00 7

通过 groupby 和日期的聚合,我得到以下信息。由于我有 8 个日期并且我按 3 个周期进行分组,因此它必须选择是截断最早的日期组还是最旧的日期组,并且它选择最旧的日期组(最旧的日期组的计数为 2):

df.groupby(pd.Grouper(key='date', freq='3T')).agg('count')

date                number
2000-01-01 00:00:00 3
2000-01-01 00:03:00 3
2000-01-01 00:06:00 2

我想要的输出是截断 最早 日期组:

date                number
2000-01-01 00:00:00 2
2000-01-01 00:02:00 3
2000-01-01 00:05:00 3

请让我知道如何实现这一点,我希望只有一个可以设置的参数被我忽略了。请注意,这类似于this 问题,但我的问题是针对日期截断的。

编辑:为了重新构建问题(感谢 Alexdor),pandas 中的默认行为是按 [0, 3)、[3, 6)、[6, 9) 周期分箱,但我想按 ( -1, 2], (2, 5], (5, 8]

【问题讨论】:

@jpp 这不会解决问题,因为日期不会像我想要的输出那样。但如果我误解你,请纠正我。 【参考方案1】:

似乎 grouper 函数从您传递给它的系列中最早的时间开始构建垃圾箱。我看不出有什么方法可以让它从最新开始构建垃圾箱,但从头开始构建垃圾箱相当容易。

freq = '3min'

minTime = df.date.min()
maxTime = df.date.max()
deltaT = pd.Timedelta(freq)
minTime -= deltaT - (maxTime - minTime) % deltaT # adjust min time to start of first bin
r = pd.date_range(start=minTime, end=maxTime, freq=freq)

df.groupby(pd.cut(df["date"], r)).agg('count')

给予

date                                     date number        
(1999-12-31 23:58:00, 2000-01-01 00:01:00]  2   2
(2000-01-01 00:01:00, 2000-01-01 00:04:00]  3   3
(2000-01-01 00:04:00, 2000-01-01 00:07:00]  3   3

【讨论】:

输出中的第二个日期列是什么? 我的格式不是很好。第一个日期是索引,第二个是df的日期列中的计数 作为以后任何人的参考,您可以将上面的“间隔”日期索引转换为正常日期,如下所示:df.index = pd.to_datetime(pd.Series(df.index).apply(lambda x: x.right.date()))。如果您想保留时间戳部分,请忽略 .date()【参考方案2】:

这是一个技巧,让您可以按恒定的组大小进行分组,自下而上计数。

from itertools import chain

def grouper(x, k=3):
    n = len(df.index)
    return list(chain.from_iterable([[0]*int(n//k)] + [[i]*k for i in range(1, int(n/k)+1)]))

df['grouper'] = grouper(df, 3)

res = df.groupby('grouper', as_index=False)\
        .agg('date': 'first', 'number': 'count')\
        .drop('grouper', 1)

#                  date  number
# 0 2000-01-01 00:00:00       2
# 1 2000-01-01 00:02:00       3
# 2 2000-01-01 00:05:00       3

【讨论】:

问题很明显。他希望日期落入 (-2, 1]、(1, 4] 和 (4, 7] 的箱中,而不是 [0, 3)、[3, 6)、[6, 9] 的默认行为)。您所做的只是对列进行排序以适合示例输出 @jpp 我的疑虑是您的“解决方案”没有任何意义——您将最后一组中的计数分配给第一组。如果示例不是对称的(每个间隔的计数不相等),则此方法不起作用。我可以将示例更改为更复杂,但我宁愿不这样做。编辑:alexdor 明白了。

以上是关于Pandas groupby 聚合以截断最早日期而不是最旧日期的主要内容,如果未能解决你的问题,请参考以下文章

pandas groupby 可以聚合成一个列表,而不是 sum、mean 等吗?

pandas聚合和分组运算之groupby

将 pandas GroupBy 中的列值聚合为 dict

为 pandas groupby 中的不同特征分配不同的聚合函数

将 pandas GroupBy 中的多个列值聚合为 dict

在 pandas 数据帧上同时操作 groupby 和 resample?