请帮我找到一个不需要大量循环的解决方案。我有一个时间戳列表,例如
["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,...]
。
答案 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.timedelta
或numpy.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
功能以符合您的条件。