找回密码
 立即注册
首页 业界区 业界 『Plotly实战指南』--布局进阶篇

『Plotly实战指南』--布局进阶篇

靳谷雪 4 天前
在数据可视化领域,Plotly的子图布局是打造专业级仪表盘的核心武器。
当面对多维数据集时,合理的子图布局能显著提升多数据关联分析效率,让数据的呈现更加直观和美观。
本文将深入探讨Plotly中子图布局技巧,并结合代码实现与实际场景案例,介绍多子图组织方法的技巧。
多子图布局

网格布局

网格布局是Plotly中实现多子图排列的一种常见方式,通过make_subplots函数,我们可以轻松创建行列对齐的子图。
例如,设置rows=2, cols=3,就可以生成一个2行3列的子图网格,这种方式的好处是子图的尺寸会自动分配,
而且我们还可以通过horizontal_spacing和vertical_spacing参数来调整子图之间的水平和垂直间距,从而让整个布局更加紧凑和美观。
  1. from plotly.subplots import make_subplots
  2. import plotly.graph_objects as go
  3. fig = make_subplots(rows=2, cols=3, horizontal_spacing=0.2, vertical_spacing=0.2)
  4. fig.add_trace(go.Scatter(x=[1, 2, 3], y=[4, 5, 6]), row=1, col=1)
  5. fig.add_trace(go.Scatter(x=[20, 30, 40], y=[50, 60, 70]), row=1, col=2)
  6. fig.add_trace(go.Scatter(x=[300, 400, 500], y=[600, 700, 800]), row=1, col=3)
  7. fig.add_trace(go.Scatter(x=[4000, 5000, 6000], y=[7000, 8000, 9000]), row=2, col=1)
  8. fig.add_trace(
  9.     go.Scatter(x=[50000, 60000, 70000], y=[80000, 90000, 100000]), row=2, col=2
  10. )
  11. fig.add_trace(
  12.     go.Scatter(x=[600000, 700000, 800000], y=[900000, 1000000, 1100000]), row=2, col=3
  13. )
  14. fig.show()
复制代码
1.png

自由布局

自由布局则给予了我们更大的灵活性,通过domain参数,我们可以手动设置子图的位置,即指定子图在图表中的x和y坐标范围。
这种方式非常适合实现一些非对齐排列的子图布局,比如主图与缩略图的组合。
我们可以将主图放在较大的区域,而将缩略图放在角落,通过这种方式来辅助展示数据的局部细节。
  1. # 自由布局
  2. import plotly.graph_objects as go
  3. # 自由布局示例
  4. fig_free = go.Figure()
  5. # 添加第一个子图
  6. fig_free.add_trace(go.Scatter(x=[1, 2, 3], y=[4, 5, 6], name="Trace 1"))
  7. # 添加第二个子图
  8. fig_free.add_trace(go.Scatter(x=[20, 30, 40], y=[50, 60, 70], name="Trace 2"))
  9. # 更新布局,定义每个子图的domain
  10. fig_free.update_layout(
  11.     xaxis=dict(domain=[0, 0.7]),  # 第一个子图占据左侧70%
  12.     yaxis=dict(domain=[0, 1]),    # 第一个子图占据整个高度
  13.     xaxis2=dict(domain=[0.7, 1], anchor="y2"),  # 第二个子图占据右侧30%
  14.     yaxis2=dict(domain=[0.5, 1], anchor="x2")   # 第二个子图在右侧上方
  15. )
  16. # 更新每个trace的坐标轴引用
  17. fig_free.update_traces(xaxis="x1", yaxis="y1", selector={"name": "Trace 1"})
  18. fig_free.update_traces(xaxis="x2", yaxis="y2", selector={"name": "Trace 2"})
  19. fig_free.show()
复制代码
2.png

子图共享坐标轴

在多子图的情况下,共享坐标轴是一个非常实用的功能,通过设置shared_xaxes=True或shared_yaxes=True,可以让多个子图在同一个坐标轴上进行联动。
这样,当我们在一个子图上进行缩放或平移操作时,其他共享相同坐标轴的子图也会同步更新,从而方便我们进行多数据的对比分析。
此外,当遇到不同量纲的数据时,我们还可以通过secondary_y=True来独立控制次坐标轴,避免因量纲冲突而导致图表显示不清晰。
  1. fig = make_subplots(
  2.     rows=2,
  3.     cols=1,
  4.     shared_xaxes=True,
  5.     specs=[[{"secondary_y": True}], [{}]],
  6. )
  7. fig.add_trace(go.Scatter(x=[1, 2, 3], y=[4, 5, 6]), row=1, col=1)
  8. fig.add_trace(go.Scatter(x=[1, 2, 3], y=[40, 50, 60]), row=1, col=1, secondary_y=True)
  9. fig.add_trace(go.Scatter(x=[1, 2, 3], y=[7, 8, 9]), row=2, col=1)
  10. fig.show()
复制代码
3.gif

实战案例

下面两个案例根据实际情况简化而来,主要演示布局如何在实际项目中使用。
股票多指标分析仪表盘

示例中先构造一些模拟数据,然后采用3行1列的布局模式来显示不同的股票信息。
  1. import plotly.graph_objects as go
  2. from plotly.subplots import make_subplots
  3. import pandas as pd
  4. # 模拟股票数据
  5. df = pd.DataFrame(
  6.     {
  7.         "date": pd.date_range(start="2023-01-01", periods=100),
  8.         "price": [100 + i * 0.5 + (i % 10) * 2 for i in range(100)],
  9.         "volume": [5000 + i * 10 + abs(i % 20 - 10) * 100 for i in range(100)],
  10.         "rsi": [50 + i % 15 - 7.5 for i in range(100)],
  11.     }
  12. )
  13. # 1. 创建3行1列的子图布局
  14. fig = make_subplots(
  15.     rows=3,
  16.     cols=1,
  17.     shared_xaxes=True,  # 共享x轴
  18.     vertical_spacing=0.05,  # 子图间距
  19.     subplot_titles=("价格趋势", "成交量", "RSI 指标"),
  20. )
  21. # 2. 添加价格走势图
  22. fig.add_trace(
  23.     go.Candlestick(
  24.         x=df["date"],
  25.         open=df["price"] * 0.99,
  26.         high=df["price"] * 1.02,
  27.         low=df["price"] * 0.98,
  28.         close=df["price"],
  29.         name="股票价格",
  30.     ),
  31.     row=1,
  32.     col=1,
  33. )
  34. # 3. 添加成交量柱状图
  35. fig.add_trace(
  36.     go.Bar(x=df["date"], y=df["volume"], name="成交量", marker_color="lightgray"),
  37.     row=2,
  38.     col=1,
  39. )
  40. # 4. 添加RSI指标图
  41. fig.add_trace(
  42.     go.Scatter(x=df["date"], y=df["rsi"], name="RSI", line=dict(color="blue")),
  43.     row=3,
  44.     col=1,
  45. )
  46. # 5. 更新布局设置
  47. fig.update_layout(
  48.     title_text="多指标仪表盘",
  49.     height=800,
  50.     margin=dict(l=20, r=20, t=80, b=20),
  51.     # 主图坐标轴配置
  52.     xaxis=dict(domain=[0, 1], rangeslider_visible=False),
  53.     # 成交量图坐标轴
  54.     xaxis2=dict(domain=[0, 1], matches="x"),
  55.     # RSI图坐标轴
  56.     xaxis3=dict(domain=[0, 1], matches="x"),
  57.     # 公共y轴配置
  58.     yaxis=dict(domain=[0.7, 1], showticklabels=False),
  59.     yaxis2=dict(domain=[0.35, 0.65], showticklabels=False),
  60.     yaxis3=dict(domain=[0, 0.3], tickformat=".0%"),
  61. )
  62. # 6. 添加形状标注
  63. fig.add_shape(
  64.     type="line",
  65.     x0="2023-01-01",
  66.     y0=30,
  67.     x1="2023-04-10",
  68.     y1=70,
  69.     line=dict(color="red", width=2, dash="dash"),
  70. )
  71. fig.show()
复制代码
4.png

这是单列的布局,如果指标多的话,可以用多列的网格布局方式来布局。
物联网设备状态监控

这个示例采用自由布局实现主监控图+4个状态指标环绕。
  1. import plotly.graph_objects as go
  2. from plotly.subplots import make_subplots
  3. import numpy as np
  4. # 模拟设备数据
  5. np.random.seed(42)
  6. device_data = {
  7.     "timestamp": np.arange(100),
  8.     "temp": 25 + np.random.normal(0, 2, 100),
  9.     "pressure": 100 + np.random.normal(0, 5, 100),
  10.     "vibration": np.random.exponential(0.5, 100),
  11. }
  12. # 1. 创建2行2列的子图布局
  13. fig = make_subplots(
  14.     rows=2,
  15.     cols=2,
  16.     subplot_titles=("温度", "压力", "振动", "状态"),
  17.     specs=[
  18.         [{"type": "scatter"}, {"type": "scatter"}],
  19.         [{"type": "scatter"}, {"type": "indicator"}],
  20.     ],
  21. )
  22. # 2. 添加温度折线图
  23. fig.add_trace(
  24.     go.Scatter(
  25.         x=device_data["timestamp"],
  26.         y=device_data["temp"],
  27.         mode="lines+markers",
  28.         name="温度 (°C)",
  29.         line=dict(color="firebrick"),
  30.     ),
  31.     row=1,
  32.     col=1,
  33. )
  34. # 3. 添加压力散点图
  35. fig.add_trace(
  36.     go.Scatter(
  37.         x=device_data["timestamp"],
  38.         y=device_data["pressure"],
  39.         mode="markers",
  40.         name="压力 (kPa)",
  41.         marker=dict(size=8, color="royalblue", opacity=0.7),
  42.     ),
  43.     row=1,
  44.     col=2,
  45. )
  46. # 4. 添加振动频谱图
  47. fig.add_trace(
  48.     go.Scatter(
  49.         x=device_data["timestamp"],
  50.         y=device_data["vibration"],
  51.         mode="lines",
  52.         name="振动 (g)",
  53.         line=dict(color="forestgreen"),
  54.     ),
  55.     row=2,
  56.     col=1,
  57. )
  58. # 5. 添加状态指示器
  59. fig.add_trace(
  60.     go.Indicator(
  61.         mode="gauge+number",
  62.         value=85,
  63.         domain={"x": [0, 1], "y": [0, 1]},
  64.         title="系统健康 (%)",
  65.         gauge={
  66.             "axis": {"range": [0, 100]},
  67.             "bar": {"color": "gold"},
  68.             "steps": [
  69.                 {"range": [0, 70], "color": "red"},
  70.                 {"range": [70, 90], "color": "orange"},
  71.                 {"range": [90, 100], "color": "green"},
  72.             ],
  73.         },
  74.     ),
  75.     row=2,
  76.     col=2,
  77. )
  78. # 6. 更新布局设置
  79. fig.update_layout(
  80.     title_text="IoT 设备监控仪表盘",
  81.     height=600,
  82.     margin=dict(l=20, r=20, t=80, b=20),
  83.     showlegend=False,
  84.     # 温度图坐标轴
  85.     xaxis=dict(domain=[0, 0.45], showgrid=False),
  86.     yaxis=dict(domain=[0.55, 1], showgrid=False),
  87.     # 压力图坐标轴
  88.     xaxis2=dict(domain=[0.55, 1], showgrid=False),
  89.     yaxis2=dict(domain=[0.55, 1], showgrid=False),
  90.     # 振动图坐标轴
  91.     xaxis3=dict(domain=[0, 0.45], showgrid=False),
  92.     yaxis3=dict(domain=[0, 0.45], showgrid=False),
  93. )
  94. fig.show()
复制代码
5.png

总结

在Plotly中,子图布局对于创建高质量的数据可视化作品至关重要。
在实际应用中,对于复杂的仪表盘项目,优先采用网格布局可以保证子图之间的对齐和一致性;而对于一些创意性的场景,自由布局则能够更好地发挥我们的想象力。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册