如何用Plotly绘制带时间维度的开关状态图并支持图例切换?
嘿,我刚好有个完美的解决方案帮你解决这个问题!之前用layout.shape手动画矩形确实没法支持图例的显示/隐藏交互,咱们换个思路——把每个设备的开关时段作为单独的trace来处理,这样Plotly自带的图例交互功能就能直接生效啦。
实现开关状态时间图表(支持图例交互)
核心思路
不用手动绘制shape元素,而是利用Plotly的原生trace类型(比如条形图或带填充的散点图),让每个设备对应一个独立的trace。这样点击图例中的设备名称,就能轻松控制该设备状态的显示与隐藏,完全符合你的需求。
方法一:使用条形图(Bar Trace)
这种方法最直观,把每个设备的每一段开启时间转换成横向条形,条形的起始位置对应开启时间,长度对应持续时长,y轴对应设备名称。
示例代码
import plotly.graph_objects as go import pandas as pd # 模拟设备开关数据:设备名称、开启时间、关闭时间 device_data = [ {"device": "屏幕", "start": "2024-05-20 08:00", "end": "2024-05-20 12:30"}, {"device": "屏幕", "start": "2024-05-20 14:00", "end": "2024-05-20 18:15"}, {"device": "WiFi", "start": "2024-05-20 07:45", "end": "2024-05-20 19:00"}, {"device": "蓝牙", "start": "2024-05-20 09:10", "end": "2024-05-20 10:30"}, {"device": "蓝牙", "start": "2024-05-20 15:40", "end": "2024-05-20 17:20"}, ] # 数据预处理 df = pd.DataFrame(device_data) df["start"] = pd.to_datetime(df["start"]) df["end"] = pd.to_datetime(df["end"]) df["duration"] = df["end"] - df["start"] # 创建图表对象 fig = go.Figure() # 为每个设备添加独立trace for device in df["device"].unique(): filtered_data = df[df["device"] == device] fig.add_trace(go.Bar( x=filtered_data["duration"], y=[device] * len(filtered_data), base=filtered_data["start"], # 条形的起始时间点 orientation="h", name=device, width=0.8, # 调整条形宽度,避免不同时段重叠 hovertemplate="设备: %{y}<br>开启时间: %{base}<br>关闭时间: %{base}+%{x}<extra></extra>" )) # 布局配置 fig.update_layout( title="设备开关状态随时间变化", xaxis_title="时间", yaxis_title="设备", barmode="overlay", xaxis=dict(type="date"), showlegend=True ) fig.show()
方法二:使用带填充的散点图(Scatter Trace with Fill)
如果想要更接近“实心矩形块”的视觉效果,可以用散点图绘制矩形路径并填充,同样每个设备对应一个trace。
示例代码
import plotly.graph_objects as go import pandas as pd # 复用上面的device_data df = pd.DataFrame(device_data) df["start"] = pd.to_datetime(df["start"]) df["end"] = pd.to_datetime(df["end"]) fig = go.Figure() # 为每个设备分配y轴位置 device_y_pos = {dev: idx for idx, dev in enumerate(df["device"].unique())} for device in df["device"].unique(): filtered_data = df[df["device"] == device] y = device_y_pos[device] # 为每个开启时段绘制填充矩形 for _, row in filtered_data.iterrows(): fig.add_trace(go.Scatter( x=[row["start"], row["start"], row["end"], row["end"], row["start"]], y=[y-0.3, y+0.3, y+0.3, y-0.3, y-0.3], fill="toself", name=device, hovertemplate="设备: %{name}<br>开启时间: %{x[0]}<br>关闭时间: %{x[2]}<extra></extra>", showlegend=(device == df["device"].unique()[0]) # 避免图例重复显示 )) # 布局配置 fig.update_layout( title="设备开关状态随时间变化", xaxis_title="时间", yaxis=dict( tickmode="array", tickvals=list(device_y_pos.values()), ticktext=list(device_y_pos.keys()) ), xaxis=dict(type="date"), showlegend=True ) fig.show()
方案优势
- 完美支持图例交互:点击图例中的设备名称,即可单独显示/隐藏该设备的所有开启时段。
- 数据驱动更灵活:新增设备或时段只需要更新数据源,无需手动调整shape。
- 自带hover提示,方便查看每个时段的具体时间信息。
内容的提问来源于stack exchange,提问作者Arshiyan Alam
相关产品推荐
相关产品推荐

