熊猫组按时间窗口

时间:2014-03-31 18:11:49

标签: python pandas

编辑:Session generation from log file analysis with pandas似乎正是我所寻找的。

我有一个包含非唯一时间戳的数据框,我想按时间窗口对它们进行分组。基本逻辑是 -

1)通过在时间戳之前和之后添加n分钟,从每个时间戳创建时间范围。

2)按重叠的时间范围分组。这里的最终效果是时间窗口将小到单个时间戳+/-时间缓冲区,但是时间窗口的大小没有上限,只要多个事件的距离小于时间缓冲区

感觉就像df.groupby(pd.TimeGrouper(minutes = n))是正确答案,但我不知道如何让TimeGrouper在看到时间缓冲区内的事件时创建动态时间范围。

例如,如果我在一组事件中尝试TimeGrouper('20s'):10:3​​4:00,10:34:08,10:34:08,10:34:15,10:34: 28和10:34:54,那么大熊猫会给我三组(事件发生在10:34:00 - 10:34:20,10:34:20-10:34:40和10:34:40- 10:35:00)。我想回到两个小组,10:34:00-10:34:28,因为在那个时间范围内事件之间的差距不超过20秒,第二组是10:34:54

找到时间范围不是静态区间的时间窗口的最佳方法是什么?

鉴于系列看起来像 -

      time
0     2013-01-01 10:34:00+00:00
1     2013-01-01 10:34:12+00:00
2     2013-01-01 10:34:28+00:00
3     2013-01-01 10:34:54+00:00
4     2013-01-01 10:34:55+00:00
5     2013-01-01 10:35:19+00:00
6     2013-01-01 10:35:30+00:00

如果我在该系列赛上进行df.groupby(pd.TimeGrouper('20s')),我会回到5组,10:34:00-:20,:20-:40,:40-10 :35:00等我想做的是有一些创建弹性时间范围的功能..只要事件在20秒内,扩展时间范围。所以我希望能回来 -

2013-01-01 10:34:00 - 2013-01-01 10:34:48 
    0 2013-01-01 10:34:00+00:00
    1 2013-01-01 10:34:12+00:00
    2 2013-01-01 10:34:28+00:00

2013-01-01 10:34:54 - 2013-01-01 10:35:15
    3 2013-01-01 10:34:54+00:00
    4 2013-01-01 10:34:55+00:00

2013-01-01 10:35:19 - 2013-01-01 10:35:50
    5 2013-01-01 10:35:19+00:00
    6 2013-01-01 10:35:30+00:00

感谢。

3 个答案:

答案 0 :(得分:9)

这是用于创建自定义分组器的方法。 (需要pandas> = 0.13)进行timedelta计算,否则可以在其他版本中使用。

制作你的系列

In [31]: s = Series(range(6),pd.to_datetime(['20130101 10:34','20130101 10:34:08', '20130101 10:34:08', '20130101 10:34:15', '20130101 10:34:28', '20130101 10:34:54','20130101 10:34:55','20130101 10:35:12']))

In [32]: s
Out[32]: 
2013-01-01 10:34:00    0
2013-01-01 10:34:08    1
2013-01-01 10:34:08    2
2013-01-01 10:34:15    3
2013-01-01 10:34:28    4
2013-01-01 10:34:54    5
2013-01-01 10:34:55    6
2013-01-01 10:35:12    7
dtype: int64

这只是计算连续元素之间的时间差(以秒为单位),但实际上可以是任何

In [33]: indexer = s.index.to_series().order().diff().fillna(0).astype('timedelta64[s]')

In [34]: indexer
Out[34]: 
2013-01-01 10:34:00     0
2013-01-01 10:34:08     8
2013-01-01 10:34:08     0
2013-01-01 10:34:15     7
2013-01-01 10:34:28    13
2013-01-01 10:34:54    26
2013-01-01 10:34:55     1
2013-01-01 10:35:12    17
dtype: float64

Arbitrariy分配东西< 20s到0组,否则到1组。这也可能更随意。如果之前的差异是< 0但是总差异(从第一个)是> 50组在第2组。

In [35]: grouper = indexer.copy()

In [36]: grouper[indexer<20] = 0

In [37]: grouper[indexer>20] = 1

In [95]: grouper[(indexer<20) & (indexer.cumsum()>50)] = 2

In [96]: grouper
Out[96]: 
2013-01-01 10:34:00    0
2013-01-01 10:34:08    0
2013-01-01 10:34:08    0
2013-01-01 10:34:15    0
2013-01-01 10:34:28    0
2013-01-01 10:34:54    1
2013-01-01 10:34:55    2
2013-01-01 10:35:12    2
dtype: float64

Groupem(也可以在这里申请)

In [97]: s.groupby(grouper).sum()
Out[97]: 
0    10
1     5
2    13
dtype: int64

答案 1 :(得分:1)

您可能需要考虑使用apply

def my_grouper(datetime_value):
    return some_group(datetime_value)

df.groupby(df['date_time'].apply(my_grouper))

由您来实现石斑鱼功能中的任何分组逻辑。顺便说一下,合并重叠时间范围是一种迭代任务:例如,A =(0,10),B =(20,30),C =(10,20)。出现C后,应该合并所有三个,A,B和C.

<强> UPD:

这是我丑陋的合并算法版本:

groups = {}

def in_range(val, begin, end):
    return begin <= val <= end

global max_group_id
max_group_id = 1

def find_merged_group(begin, end):
    global max_group_id
    found_common_group = None
    full_wraps = []

    for (group_start, group_end), group in groups.iteritems():
        begin_inclusion = in_range(begin, group_start, group_end)
        end_inclusion = in_range(end, group_start, group_end)
        full_inclusion = begin_inclusion and end_inclusion
        full_wrap = not begin_inclusion and not end_inclusion and in_range(group_start, begin, end) and in_range(group_end, begin, end)
        if full_inclusion:
            groups[(begin, end)] = group
            return group
        if full_wrap:
            full_wraps.append(group)
        elif begin_inclusion or end_inclusion:
            if not found_common_group:
                 found_common_group = group
            else:  # merge
                for range, g in groups.iteritems():
                    if g == group:
                        groups[range] = found_common_group

    if not found_common_group:
        found_common_group = max_group_id
        max_group_id += 1
    groups[(begin, end)] = found_common_group
    return found_common_group

def my_grouper(date_time):
    return find_merged_group(date_time - 1, date_time + 1)

df['datetime'].apply(my_grouper) # first run to fill groups dict
grouped = df.groupby(df['datetime'].apply(my_grouper))  # this run is using already merged groups

答案 2 :(得分:0)

试试这个:

  • 创建一个列tsdiff,其中连续时间之间存在差异(使用shift
  • df['new_group'] = df.tsdiff > timedelta
  • {li> fillna new_group
  • groupby该列

这只是一个非常粗糙的伪代码,但解决方案就在那里...