最近需要从.log文件中读取大量数据进行可视化,处理读取的时间戳和鼠标点击事件碰到一些坑,特此记录一下。
本文代码基于Ubuntu的pycharm进行开发,如遇问题多多交流。
本文处理的.log文件内容形式
09:46:26.048 value1: 100, value2: 1.1
09:46:26.055 value1: 200, value2: 2.2
09:46:26.067 value1: 150, value2: 3.6
09:46:26.075 value1: 300, value2: 5.8
09:46:26.088 value1: 500, value2: 4.7
读取.log文件内容并进行关键词提取
from datetime import datetime
value_t = []
time_stp = []
with open('ap_ctrl_2025-03-12_26.log', 'r') as file: #'r'代表以只读模式打开文件
for line in file:
line = line.strip() # 去除两端空白符
if not line.startswith('#'): # 忽略注释行
parts = line.split() # 每行根据空格分割字段
if len(parts) > 2: # 根据实际字段数调整关键字索引
# 根据关键字查找对应的值
if 'value1:' in parts:
try:
key_word = 'value1:'
# 根据关键字查找其在parts中的下标,把下标的下一位根据逗号分割后,获得对应值
value_t.append(float(parts[parts.index(keyword) + 1].split(',')[0]))
# parts第一个元素是时间戳,需要使用datetime.strptime将字符串转换为时间格式,涉及库datetime
time_stp.append(datetime.strptime(parts[0], '%H:%M:%S.%f'))
except ValueError:
pass # 如果数据格式错误,则跳过此行
以第一行为例,经过line.split()按空格分割字段后,parts的内容应该是下面这样
parts = ["09:46:26.048", "value1:", "100,", "value2:", "1.1"]
而查找完整个.log文件后,value和time_stp的内容应该是这样
value_t = [100, 200, 150, 300, 500]
time_stp = [datetime.datetime(1900, 1, 1, 9, 46, 26, 048000),
datetime.datetime(1900, 1, 1, 9, 46, 26, 055000),
datetime.datetime(1900, 1, 1, 9, 46, 26, 067000),
datetime.datetime(1900, 1, 1, 9, 46, 26, 075000),
datetime.datetime(1900, 1, 1, 9, 46, 26, 088000)]
打印图像
把上面的数据用matplotlib.pyplot库打印成图像
需要注意的是,Windows和Ubuntu下用scatter打印散点图的操作有点区别,如果x轴打印的是上面的datetime类型,那么Ubuntu里得多加一句限制x轴打印范围的语句,不然打出来的图像里x轴的时间范围会很大,Windows里则不需要,打印出来会自适应数据范围,不知道是不是我电脑处理的个例。
其他的就是常规打印操作了,这里用plot直接打印折线图了,鼠标点击操作语句如果到此为止不需要的话就屏蔽叭
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(15,5))
cid = fig.canvas.mpl_connect('button_press_event', mouse_event) # 鼠标事件相关
plt.plot(time_stp, value_t, marker='.', markersize=5)
plt.title('Time-Value')
plt.xlabel('Time')
plt.ylabel('Value')
plt.grid(True)
在Ubuntu中用scatter打印散点图时,如果x轴时间打出来不对劲,可以试试这么处理
from datetime import timedelta
fig = plt.figure(figsize=(15,5))
plt.scatter(time_stp, value_t, marker='o', s=5.)
# 限制x轴范围为第一个时间前5秒和最后一个时间后5秒,下标[-1]是从序列尾部逆序进行查找
plt.xlim(time_stp[0] - timedelta(seconds=5.), time_stp[-1] + timedelta(seconds=5.))
plt.title('Time-Value')
plt.xlabel('Time')
plt.ylabel('Value')
plt.grid(True)
鼠标点击图像上的点,在终端显示对应坐标值
操作分为这几步:
- 检测鼠标点击
- 根据点击的坐标查找距离最近的打印点
- 在图像上显示所选的点,并在终端打印坐标值
处理鼠标点击处的坐标时,总是转换不对时间,直接打印的值也很奇怪,后来发现需要用datas库的num2date进行转换,转来转去~转来转去~
import matplotlib.dates as mdates
# 定义鼠标点击事件,调用此函数的语句在上一节
def mouse_event(event):
# 判断点击是否在坐标系内
if not event.inaxes:
return
# 获取鼠标点击处的坐标信息
x_click, y_click = event.xdata, event.ydata
# 将x轴的坐标转换为时间,便于后面计算时间差,[:-3]裁剪掉末尾三位数,此处是为了仅保留小数点后3位
t_click = mdates.num2date(x_click).strftime("%H:%M:%S.%f")[:-3]
# 将上一句中被strftime转换为字符串的时间转换回datetime类型
time_click = datetime.strptime(t_click, '%H:%M:%S.%f')
# 计算每个打印点与点击处的距离,其中时间差为timedelta类型,从timedelta类型中得到秒与毫秒计算时间差
dists = [((time_click - x_time).seconds + (time_click - x_time).microseconds*1e-6)**2 + (y - y_click)**2 for x_time, y in zip(time_stp, value_t)]
dist_idx = dists.index(min(dists))
# 如果距离过大,则需要避免误检
if min(dists) > 5.0:
print('Too far from plot points!')
return
closet_time = time_stp[dist_idx]
print(f'time: {closet_time.strftime("%H:%M:%S.%f")[:-3]}, value_t: {value_t[time_idx]}') #使用[:-3]截掉最后的3位数
print(f'click point time: {mdates.num2date(x_click).strftime("%H:%M:%S.%f")[:-3]}, value_t: {y_click}')
plt.plot(closet_time, value_t[time_idx], 'o', color='red')
plt.draw()
总的代码由上面部分组成,各部分集合起来的时候,把引入的库放一起就行,然后注意下定义的函数的位置。文中为了方便展示各部分所需的库,所以把库和部分代码放到一起了。
权当留个笔记给自己翻,也希望能帮到有需要的人叭