如何在Python中创建timedelta列表?

0 投票
2 回答
983 浏览
提问于 2025-04-17 06:55

我在这个网站上搜索了很久,看到很多关于时间差的讨论,但还没找到我想要的东西。

简单来说,我有一份消息列表,这些消息是由一个通信服务器接收到的。我想计算每条消息发送和接收之间的延迟时间。它的格式是这样的:

161336.934072 - TMsg out: [O] enter order. RefID [123] OrdID [4568]
161336.934159 - TMsg in: [A] accepted. ordID [456]  RefNumber [123] 

这些消息中夹杂着其他消息,但我只想计算那些具有相同RefID的发送消息和接收消息之间的时间差。

到目前为止,为了从主日志中筛选出哪些是T消息,我一直在这样做,但效率真的很低。我不想每次都创建新文件:

big_file = open('C:/Users/kdalton/Documents/Minicomm.txt', 'r')
small_file1 = open('small_file1.txt', 'w')
for line in big_file:
    if 'T' in line: small_file1.write(line)
big_file.close()
small_file1.close()

我该如何计算这两条消息之间的时间差,并从主日志中筛选出这些消息呢?

2 个回答

1

首先,不要直接写出原始的日志行。其次,使用字典。

tdeltas = {} # this is an empty dict
if "T" in line:
   get Refid number
   if Refid in tedeltas:
      tdeltas[Refid] = timestamp - tdeltas[Refid]
   else:
      tdeltas[Refid] = timestamp

然后在最后,把它转换成列表并打印出来。

allRefids = sorted(tdeltas.keys())
for k in allRefids:
   print k+": "+tdeltas[k]+" secs"

你可能想把日期转换成来自datetime模块的time对象,然后使用时间差对象存储在字典里。虽然这次任务可能不太值得这样做,但学习如何使用datetime模块是很有用的。

另外,我没有详细讲解如何从输入字符串中解析出Refid,以及将时间从字符串转换为浮点数再转换回来的可能问题。

实际上,仅仅存储时间差会造成混淆,如果你遇到一个不被接受的Refid。如果我真的在做这个,我会在值里存储一个元组,包含开始时间、结束时间和时间差。对于一个新记录,它看起来会像这样:(161336.934072,0,0),而在检测到接受后,它会变成这样:(161336.934072,161336.934159,.000087)。如果日志记录活动是持续的,比如一个24小时不间断的全球电商网站,那么我会定期检查字典,找出任何时间差不为零的条目,报告它们并删除。然后我会把剩下的值按开始时间排序,报告并删除那些开始时间太旧的记录,因为这意味着这些交易已经失败,永远不会完成。

此外,在一个真实的电商网站中,我可能会考虑使用像Redis或Memcache这样的外部字典,这样报告和维护可以由其他服务器或应用程序来完成。

0

这个生成器函数会返回一个元组,里面包含了消息的ID和发送时间的差值。(如果你想对时间差做更复杂的处理,可以看看 datetime.timedelta)。需要注意的是,这里假设发送的消息总是在接收的消息之前出现。

def get_time_deltas(infile):
    entries = (line.split() for line in open(INFILE, "r"))
    ts = {} 
    for e in entries:
        if len(e) == 11 and " ".join(e[2:5]) == "TMsg out: [O]":
            ts[e[8]] = e[0]   # store timestamp for id
        elif len(e) == 10 and " ".join(e[2:5]) == "TMsg in: [A]":   
            in_ts, ref_id = e[0], e[9]
            # Raises KeyError if out msg not seen yet. Handle if required.
            out_ts = ts.pop(ref_id)   # get ts for this id
            yield (ref_id[1:-1], float(in_ts) - float(out_ts))

现在你可以从中获取一个列表:

>>> INFILE = 'C:/Users/kdalton/Documents/Minicomm.txt'
>>> list(get_time_deltas(INFILE))
[('123', 8.699999307282269e-05), ('1233', 0.00028700000257231295)]

或者把它写入一个文件:

>>> with open("out.txt", "w") as outfile:
...     for id, td in get_time_deltas(INFILE):
...          outfile.write("Msg %s took %f seconds\n", (id, td))

或者把它串联到一个更复杂的工作流程中。


更新:

(这是根据实际数据的观察做出的回应)

试试这个:

def get_time_deltas(infile):
    entries = (line.split() for line in open(INFILE, "r"))
    ts = {} 
    for e in entries:
        if " ".join(e[2:5]) == "OuchMsg out: [O]":
            ts[e[8]] = e[0]   # store timestamp for id
        elif " ".join(e[2:5]) == "OuchMsg in: [A]":   
            in_ts, ref_id = e[0], e[7]
            out_ts = ts.pop(ref_id, None)   # get ts for this id
            # TODO: handle case where out_ts = None (no id found)
            yield (ref_id[1:-1], float(in_ts) - float(out_ts))

INFILE = 'C:/Users/kdalton/Documents/Minicomm.txt'
print list(get_time_deltas(INFILE))

这个版本的变化:

  • 字段的数量和问题中提供的示例输入不一致。移除了基于条目数量的检查
  • in 消息的 ordID 是和 out 消息中的 refID 匹配的
  • 使用了 OuchMsg 而不是 TMsg

更新 2

要计算时间差的平均值:

deltas = [d for _, d in get_time_deltas(INFILE)] 
average = sum(deltas) / len(deltas)

或者,如果你之前生成过一个包含所有数据的列表,我们可以重用它,而不是重新解析文件:

data = list(get_time_deltas(INFILE))
# .. use data for something some operation ...

# calculate average using the list
average = sum(d for _, d in data) / len(data)

撰写回答