Plotly 合并多个数据源后折线异常,如何正确处理时间轴?
先把横轴数据转换为时间类型,再对每条曲线按时间排序。如果要在同一条“当天时间”轴上比较不同日期的数据,还需要把日期统一到一个参考日。
现有代码中,建议先检查 last_updated:正则表达式把完整时间戳截成了 "00:01" 这样的字符串。Plotly 可能将这些值识别为分类标签,使横轴顺序与 DataFrame 中的时间顺序不一致。仅凭当前数据片段还无法确认全部原因,不过这与“采样时刻不一致时才出现异常”的现象吻合。
为什么 DataFrame 已排序,图上仍会折返?
sort_values() 改变的是数据行顺序。在分类轴上,每个标签的位置还取决于图表的类别顺序。
例如,第一条曲线包含这些时间字符串:
00:01, 00:06, 00:11
第二条曲线包含:
00:01, 00:07, 00:11
假设横轴是按类别首次出现的顺序排列的分类轴。第一条曲线先建立了三个类别,第二条曲线新增的 00:07 就可能被追加到后面。这样一来,第二条曲线虽然按 00:01 → 00:07 → 00:11 连接,图上的位置却可能先向右、再向左。
因此,异常点不一定出现在 DataFrame 末尾。数据行的顺序和横轴类别的位置需要分开看。
将时间转换为 datetime,并统一比较日期
示例数据包含不同日期。如果要比较这些日期里相同钟点的排队时间,可以保留原始时间戳,新增一个 plot_time 列用于绘图。
下面的代码假设 CSV 中的 last_updated 都是完整时间戳,且具有相同的 +09:00 偏移,这与原正则表达式的预期一致。请重新加载原始 CSV,替换原先截取 HH:MM 的逻辑。
先在类的初始化方法中设置:
self.frame = pd.DataFrame()
然后替换两个方法:
import os
import pandas as pd
import plotly.express as px
def load_data(self, date, ride_name, park_name):
filename = os.path.join(
os.environ["SAVE_PATH"],
park_name,
str(date),
f"{date}_{ride_name}.csv",
)
df = pd.read_csv(filename, sep=",", skipinitialspace=True)
timestamps = pd.to_datetime(df["last_updated"], errors="raise")
df["last_updated"] = timestamps
# 保留当天经过的时长,包括秒,并统一到同一个参考日。
time_of_day = timestamps - timestamps.dt.normalize()
df["plot_time"] = pd.Timestamp("2000-01-01") + time_of_day
df["ride"] = f"{date}-{ride_name}"
return df
def update_data_frame(self, name, date):
name = name.replace(" ", "_")
df = self.load_data(date, name, "tds")
self.frame = pd.concat(
[self.frame, df],
axis=0,
ignore_index=True,
)
self.frame = self.frame.sort_values(
by=["ride", "plot_time"],
kind="stable",
ignore_index=True,
)
self.fig = px.line(
self.frame,
x="plot_time",
y="wait_time",
color="ride",
title="Wait Times",
labels={
"plot_time": "Last Updated",
"wait_time": "Wait Time (min)",
},
)
self.fig.update_xaxes(type="date", tickformat="%H:%M")
这里的 2000-01-01 是用于对齐钟点的参考日。原始日期仍保存在 last_updated 中,ride 也继续区分日期和游乐设施。
tickformat="%H:%M" 只控制刻度显示,不会删除数据中的秒数。不同曲线也不必在相同时刻采样:真实时间轴可以正确放置 00:06 和 00:07,无需为了修复折返而强行对齐采样点。
如果需要展示真实日期顺序
如果要查看跨日期的连续变化,可以直接使用转换后的 last_updated 绘图,无需创建统一参考日:
self.frame = self.frame.sort_values(
["ride", "last_updated"],
kind="stable",
)
self.fig = px.line(
self.frame,
x="last_updated",
y="wait_time",
color="ride",
)
self.fig.update_xaxes(type="date", tickformat="%m-%d %H:%M")
不同日期会位于横轴上的不同位置。是否按钟点重叠比较,取决于仪表板的用途。
修改后还需要检查什么?
可以检查绘图列的类型,并查看每条曲线实际收到的横轴数据:
print(self.frame[["last_updated", "plot_time"]].dtypes)
for trace in self.fig.data:
print(trace.name, list(trace.x[:5]))
绘图列应为 datetime 类型。只给 "HH:MM" 字符串设置 type="date",不能代替时间解析。
同一条曲线在同一时刻有多条记录时,仍可能出现竖线;反复加载同一个 CSV 也可能造成数据重复。是否去重或聚合,应先弄清这些记录的含义,不能直接删除。
如果文件中混有不同时区,应先明确仪表板使用哪个时区,转换到该时区后再提取当天时间。上面的对齐方法以时间戳具有相同的 +09:00 偏移为前提。跨午夜的连续运行记录也要单独考虑,因为按钟点对齐后,午夜后的数据会回到横轴起点。
备注:内容仅供参考。
