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

如何减小Plotly绘制的两个图表之间的间距?

Plotly双图紧凑对齐排布实现方法

要解决两个独立Plotly图表间距过大、X轴不对齐的问题,可根据使用场景选择对应方案实现,不需要反复调整无效边距参数。


核心实现思路

  • 纯Plotly本地渲染场景:使用内置子图接口创建统一布局,直接控制子图间距、强制X轴共享
  • Dash应用部署场景:不改动原有绘图逻辑,通过调整组件样式+图表边距+统一X轴范围快速实现效果

方案1:纯Plotly单图实现(子图合并)

直接将甘特图、分面折线图合并到同一个figure对象中,从布局层面消除间距、保证对齐。

完整可运行代码

import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd

# 甘特图数据集
df_fddevents = pd.DataFrame([
    dict(Event="On", EventStartTime='2022-01-01 08:00:00', EventEndTime='2022-01-01 16:45:00', Point="Switch1", width=0.5),
    dict(Event="Off", EventStartTime='2022-01-01 16:45:00', EventEndTime='2022-01-02 08:00:00', Point="Switch1", width=0.3),
    dict(Event="On", EventStartTime='2022-01-02 08:00:00', EventEndTime='2022-01-02 16:45:00', Point="Switch1", width=0.5),
    dict(Event="On", EventStartTime='2022-01-01 08:00:00', EventEndTime='2022-01-01 10:00:00', Point="Switch2", width=0.3),
    dict(Event="Off", EventStartTime='2022-01-01 10:00:00', EventEndTime='2022-01-01 13:45:00', Point="Switch2", width=0.5),
    dict(Event="On", EventStartTime='2022-01-02 08:00:00', EventEndTime='2022-01-02 16:00:00', Point="Switch2", width=0.3)
])
# 折线图数据集
df2 = pd.DataFrame([
    dict(DateTime='2022-01-01 00:00:00', Value=90, Point="A", Unit='%', DataType='Normal'),
    dict(DateTime='2022-01-01 00:15:00', Value=80, Point="A", Unit='%', DataType='Normal'),
    dict(DateTime='2022-01-01 00:30:00', Value=85, Point="A", Unit='%', DataType='Normal'),
    dict(DateTime='2022-01-01 00:45:00', Value=92, Point="A", Unit='%', DataType='Normal'),
    dict(DateTime='2022-01-01 01:00:00', Value=100, Point="A", Unit='%', DataType='Fault'),
    dict(DateTime='2022-01-01 01:15:00', Value=72, Point="A", Unit='%', DataType='Fault'),
    dict(DateTime='2022-01-01 00:00:00', Value=22, Point="B", Unit='°C', DataType='Normal'),
    dict(DateTime='2022-01-01 00:15:00', Value=22, Point="B", Unit='°C', DataType='Normal'),
    dict(DateTime='2022-01-01 00:30:00', Value=23, Point="B", Unit='°C', DataType='Normal'),
    dict(DateTime='2022-01-01 00:45:00', Value=20, Point="B", Unit='°C', DataType='Normal'),
    dict(DateTime='2022-01-01 01:00:00', Value=21, Point="B", Unit='°C', DataType='Fault'),
    dict(DateTime='2022-01-01 01:15:00', Value=23, Point="B", Unit='°C', DataType='Fault'),    
    dict(DateTime='2022-01-01 00:00:00', Value=24, Point="C", Unit='°C', DataType='Normal'),
    dict(DateTime='2022-01-01 00:15:00', Value=24, Point="C", Unit='°C', DataType='Normal'),
    dict(DateTime='2022-01-01 00:30:00', Value=24, Point="C", Unit='°C', DataType='Normal'),
    dict(DateTime='2022-01-01 00:45:00', Value=24, Point="C", Unit='°C', DataType='Normal'),
    dict(DateTime='2022-01-01 01:00:00', Value=24, Point="C", Unit='°C', DataType='Fault'),
    dict(DateTime='2022-01-01 01:15:00', Value=24, Point="C", Unit='°C', DataType='Fault'),
    dict(DateTime='2022-01-01 00:00:00', Value=60, Point="D", Unit='Pa', DataType='Normal'),
    dict(DateTime='2022-01-01 00:15:00', Value=58, Point="D", Unit='Pa', DataType='Normal'),
    dict(DateTime='2022-01-01 00:30:00', Value=62, Point="D", Unit='Pa', DataType='Normal'),
    dict(DateTime='2022-01-01 00:45:00', Value=61, Point="D", Unit='Pa', DataType='Normal'),
    dict(DateTime='2022-01-01 01:00:00', Value=64, Point="D", Unit='Pa', DataType='Fault'),
    dict(DateTime='2022-01-01 01:15:00', Value=59, Point="D", Unit='Pa', DataType='Fault'),
    dict(DateTime='2022-01-01 00:00:00', Value=0, Point="E", Unit='Binary', DataType='Normal'),
    dict(DateTime='2022-01-01 00:15:00', Value=0, Point="E", Unit='Binary', DataType='Normal'),
    dict(DateTime='2022-01-01 00:30:00', Value=1, Point="E", Unit='Binary', DataType='Normal'),
    dict(DateTime='2022-01-01 00:45:00', Value=1, Point="E", Unit='Binary', DataType='Normal'),
    dict(DateTime='2022-01-01 01:00:00', Value=1, Point="E", Unit='Binary', DataType='Fault'),
    dict(DateTime='2022-01-01 01:15:00', Value=1, Point="E", Unit='Binary', DataType='Fault'),
    dict(DateTime='2022-01-01 01:30:00', Value=0, Point="E", Unit='Binary', DataType='Normal'),
    dict(DateTime='2022-01-01 01:45:00', Value=0, Point="E", Unit='Binary', DataType='Normal'),
    dict(DateTime='2022-01-01 02:00:00', Value=1, Point="E", Unit='Binary', DataType='Normal'),
    dict(DateTime='2022-01-01 02:15:00', Value=1, Point="E", Unit='Binary', DataType='Normal'),
    dict(DateTime='2022-01-01 02:30:00', Value=1, Point="E", Unit='Binary', DataType='Normal'),
    dict(DateTime='2022-01-01 02:45:00', Value=1, Point="E", Unit='Binary', DataType='Normal'),
])

# 创建子图:共5行(1行甘特图+4行折线分面),共享X轴,设置极小垂直间距
fig = make_subplots(
    rows=5, cols=1,
    shared_xaxes=True,
    vertical_spacing=0.02,
    row_heights=[0.18, 0.205, 0.205, 0.205, 0.205]
)

# 生成甘特图并添加到第一行
fig_gantt = px.timeline(df_fddevents, x_start="EventStartTime", x_end="EventEndTime", y="Event", color="Point")
fig_gantt.update_yaxes(autorange="reversed")
for i, d in enumerate(fig_gantt.data):
    d.width = df_fddevents[df_fddevents['Point']==d.name]['width']
for trace in fig_gantt.data:
    fig.add_trace(trace, row=1, col=1)

# 生成分面折线图,按分面顺序添加到2-5行
figline = px.line(df2, x="DateTime", y="Value", color="Point", line_group="Unit", hover_name="Point", facet_row="Unit",
        line_shape="spline", render_mode="svg")
for idx, trace in enumerate(figline.data):
    fig.add_trace(trace, row=idx//len(df2['Point'].unique())+2, col=1)

# 统一配置坐标轴、布局
fig.update_xaxes(showgrid=True, gridwidth=1, gridcolor='red')
fig.update_yaxes(showgrid=True, gridwidth=1, gridcolor='red', autorange=True)
fig.update_yaxes(autorange="reversed", row=1, col=1)
fig.update_layout(
    hovermode="x unified",
    height=900,
    margin=dict(t=30, b=30, l=60, r=20),
    showlegend=False
)

fig.show()

关键参数说明

  • vertical_spacing:取值范围01,设置为0.020.05即可实现两图无大段空白的紧凑效果
  • shared_xaxes=True:强制所有子图共用同一X轴刻度、范围,实现完全对齐
  • margin:调整画布整体外边距,消除边缘多余空白
  • row_heights:按比例分配每行高度,适配甘特图内容少、折线分面多的高度需求

方案2:Dash应用快速适配(无需重构原有代码)

如果是在Dash项目中部署,不需要合并两个figure,直接调整样式即可实现效果:

  1. 给两个dcc.Graph组件设置样式,去掉上下外边距
  2. 给两个图表设置完全一致的X轴范围、左右边距,保证X轴对齐
  3. 把甘特图的下边距、折线图的上边距设为0,消除两图之间的空白

核心布局代码

import dash
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash(__name__)
# 此处保留原有的fig_gantt、figline生成逻辑不变

app.layout = html.Div([
    dcc.Graph(
        id='gantt-chart',
        figure=fig_gantt.update_layout(
            margin=dict(t=20, b=0, l=60, r=20),
            xaxis_range=['2022-01-01 00:00:00', '2022-01-02 20:00:00']
        ),
        style={'margin-bottom': '0 !important'}
    ),
    dcc.Graph(
        id='line-chart',
        figure=figline.update_layout(
            margin=dict(t=0, b=20, l=60, r=20),
            xaxis_range=['2022-01-01 00:00:00', '2022-01-02 20:00:00']
        ),
        style={'margin-top': '0 !important'}
    )
])

if __name__ == '__main__':
    app.run_server(debug=True)

两种方案都可以实现两图紧凑排布、X轴完全对齐的效果,同时保留原有的hover联动、颜色配置等功能。


内容的提问来源于stack exchange,提问作者Sartaj0111

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 17:09:30