合并Python中的时间戳列表

时间:2015-01-21 00:14:08

标签: python list collections timestamp

请帮我找到一个不需要大量循环的解决方案。我有一个时间戳列表,例如

["2014-04-11 08:00:00.000000",
 "2014-04-11 09:35:00.000000",
 "2014-04-11 09:35:00.000000",
 "2014-04-11 09:40:00.000000",
 "2014-04-11 11:00:00.000000",
 ...]

我想在列表中'合并'时间戳,使得彼此的公共窗口(例如10分钟)内的时间戳只变成一个条目。因此上面的示例列表将成为

["2014-04-11 08:00:00.000000",
 "2014-04-11 09:35:00.000000",
 "2014-04-11 11:00:00.000000",
 ...]

另请注意,合并的三个时间戳对“9:35”值而不是“9:40”进行了修改。我想合并时间戳去参加最频繁的参赛。如果存在平局,则合并早期/最频繁的时间戳。

我还试图跟踪合并的时间戳数量。因此,对于上面的示例,保持计数的列表将为[1,3,1,...]

4 个答案:

答案 0 :(得分:2)

这可以解决如下:

import datetime

data = ["2014-04-11 08:00:00.000000", "2014-04-11 09:35:00.000000", "2014-04-11 09:35:00.000000", "2014-04-11 09:40:00.000000", "2014-04-11 11:00:00.000000"]

delta = datetime.timedelta(minutes=10)
result = []
bucket = []
current = None
for item in data:
    datetime_obj = datetime.datetime.strptime(item, '%Y-%m-%d %H:%S:%M.%f')
    if current is None:
        current = datetime_obj
        bucket = [current]
        continue
    if (datetime_obj - current) <= delta:
        bucket.append(datetime_obj)
    else:
        result.append(bucket)
        current = datetime_obj
        bucket = [current]

if bucket:
    result.append(bucket)

for bucket in result:
    print(bucket)

示例:

>>> for bucket in result:
...     print(bucket)
...
[datetime.datetime(2014, 4, 11, 8, 0)]
[datetime.datetime(2014, 4, 11, 9, 0, 35), datetime.datetime(2014, 4, 11, 9, 0, 40)]
[datetime.datetime(2014, 4, 11, 11, 0)]

result数据结构可用于计算所需的值:标识窗口的每个时间戳和可用于创建该窗口的时间戳数量(&#34;消耗&#34;)。 / p>

答案 1 :(得分:1)

假设时间戳已排序,那么......:

import datetime

def merged_ts(timestamps):
    merged_strings = []
    counts = []
    for ts in timestamps:
        dt = datetime.datetime.strptime(ts, '%Y-%m-%d %H:%M:%S.%f')
        if not merged_strings:  # first-time switch
            merged_string.append(ts)
            counts.append(1)
            oldt = dt
            continue
        dif = dt - oldt
        if dif.total_seconds < 300:  # 5 minutes
            counts[-1] += 1
            continue
        merged_string.append(ts)
        counts.append(1)
        oldt = dt
    return merged_strings, counts

补充:OP指定时间戳最初没有排序(但可以为此目的排序),如果时间戳是T,T + 4分钟,T + 8分钟,T + 12分钟等,他们必须合并到一个插槽(适当的计数)。这个版本需要很少的改变,然后......:

import datetime

def merged_ts(timestamps):
    merged_strings = []
    counts = []
    for ts in sorted(timestamps):
        dt = datetime.datetime.strptime(ts, '%Y-%m-%d %H:%M:%S.%f')
        if not merged_strings:  # first-time switch
            merged_string.append(ts)
            counts.append(1)
            oldt = dt
            continue
        dif = dt - oldt
        oldt = dt
        if dif.total_seconds < 300:  # 5 minutes
            counts[-1] += 1
        else:
            merged_string.append(ts)
            counts.append(1)
    return merged_strings, counts

我刚刚添加了一个sorted调用,并将oldt = dt移动到循环的每一段发生的位置(第一次切换除外) - 以便每个新传入的ts将检查当前存储桶中“最近”(最新)的日期戳,而不是之前的“第一”(最旧)日期戳。 (仅作为一种风格问题,我最后将条件更改为if / else而不是使用continue,因为条件的两条腿现在已经很平衡了)。

首次使用的开关很傻,但删除这个开关(不重复strptime)需要稍微简单的代码,例如:

if not timestamps: return [], []
it = iter(sorted(
    (ts,datetime.datetime.strptime(ts, '%Y-%m-%d %H:%M:%S.%f'))
    for ts in timestamps))
first = next(it)
merged_strings = [first[1]]
oldt = first[0]
counts = [1]
for ts, st in it:
    dif = dt - oldt
    oldt = dt
    if dif.total_seconds < 300:  # 5 minutes
        counts[-1] += 1
    else:
        merged_string.append(ts)
        counts.append(1)
return merged_strings, counts

带有第一次开关的版本似乎比我更好,在这种情况下,纯粹是基于风格的理由。

答案 2 :(得分:0)

如果您还没有看过它,在这种情况下将其转换为pandas DataFrame将证明是有效的。

实现目标的一种方法IMO是将这些时间戳为index且计数为column的数据帧。然后,通过循环一次,您可以删除位于同一公共窗口中的那些行(使用datetime.timedeltanumpy.timedelta64)并更新该行的列count的值。

获得更多信息有助于提供更详细的答案。例如,您的列表是否已排序,如果不是,它必须保持合并前的顺序相同吗? (从你的例子来看,它似乎已经排序了)

答案 3 :(得分:0)

您可以将groupby与在组之间“切换”的特殊键一起使用。首先准备数据:

from itertools import groupby
from datetime import datetime

l = ["2014-04-11 08:00:00.000000",
 "2014-04-11 09:35:00.000000",
 "2014-04-11 09:35:00.000000",
 "2014-04-11 09:40:00.000000",
 "2014-04-11 11:00:00.000000"]
l = map(lambda x: datetime.strptime(x, "%Y-%m-%d %H:%M:%S.%f"), l)

现在你可以做到:

class grouper():
    def __call__(self, d):
        if not hasattr(self, 'prev'):    # first element: init switch
            self.switch = 1
        elif (d - self.prev).total_seconds() > 10*60:    # 10min
            self.switch *= -1
        self.prev = d                    # save current value
        return self.switch


def most_common(group):
    lst = list(group)
    return lst[0]   # choose the first element in the group

>>> [most_common(g) for k, g in groupby(l, key = grouper())]
[datetime.datetime(2014, 4, 11, 8, 0),
 datetime.datetime(2014, 4, 11, 9, 35),
 datetime.datetime(2014, 4, 11, 11, 0)]

您可以调整most_common功能以符合您的条件。