You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 11:01:45