Plotly.py 表格与图表组合子图:用 make_subplots 在 Python 中创建 Table + Scatter 混合布局
2026/9/21 15:38:09 网站建设 项目流程
  • 数据可视化
  • 数据分析

【免费下载链接】plotly.py

The interactive graphing library for Python :sparkles:

项目地址:https://gitcode.com/gh_mirrors/pl/plotly.py
点击查看免费下载

doc/python/table-subplots.md演示了 Plotly.py 的一项典型实战能力:通过plotly.subplots.make_subplotsspecs参数,在同一个画布中同时容纳go.Table表格 trace 与go.Scatter折线 trace,形成"顶部数据表格 + 下方趋势曲线"的数据看板式布局。读完本文,你将掌握make_subplotsspecs网格声明、row/col定向添加 trace、shared_xaxes轴共享以及表格样式(header/cells)的完整配置方法,并能直接从仓库源码层面理解表格为何被归类为domain子图类型。

使用场景:为什么需要表格与图表混排

在实际的数据分析工作中,经常需要同时呈现"明细数据"与"宏观趋势":表格负责展示精确数值,折线图负责呈现时间序列走势。Plotly.py 的make_subplots允许把二者编排进同一个Figure,共享同一套时间轴,形成信息互补的复合视图。原文给出的示例即为一幅"比特币挖矿 180 天统计看板":顶部是包含日期、交易量、市场价、哈希率等多列明细的表格,下方两张子图分别绘制挖矿收入(Mining revenue)与哈希率(Hash rate)的时间序列曲线。

完整示例:一张 3 行混合子图

以下是关联文档 doc/python/table-subplots.md 中的完整示例,它可直接复制运行(需要pandasre以及网络访问以下载公开数据集):

import plotly.graph_objects as go from plotly.subplots import make_subplots import pandas as pd import re df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/Mining-BTC-180.csv") for i, row in enumerate(df["Date"]): p = re.compile(" 00:00:00") datetime = p.split(df["Date"][i])[0] df.iloc[i, 1] = datetime fig = make_subplots( rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.03, specs=[[{"type": "table"}], [{"type": "scatter"}], [{"type": "scatter"}]] ) fig.add_trace( go.Scatter( x=df["Date"], y=df["Mining-revenue-USD"], mode="lines", name="mining revenue" ), row=3, col=1 ) fig.add_trace( go.Scatter( x=df["Date"], y=df["Hash-rate"], mode="lines", name="hash-rate-TH/s" ), row=2, col=1 ) fig.add_trace( go.Table( header=dict( values=["Date", "Number<br>Transactions", "Output<br>Volume (BTC)", "Market<br>Price", "Hash<br>Rate", "Cost per<br>trans-USD", "Mining<br>Revenue-USD", "Trasaction<br>fees-BTC"], font=dict(size=10), align="left" ), cells=dict( values=[df[k].tolist() for k in df.columns[1:]], align = "left") ), row=1, col=1 ) fig.update_layout( height=800, showlegend=False, title_text="Bitcoin mining stats for 180 days", ) fig.show()

逐步拆解:从数据清洗到布局声明

1. 数据准备与日期清洗

数据集Mining-BTC-180.csv中的Date列带有00:00:00时间后缀。示例用正则表达式将其剥离,只保留日期部分,保证表格与折线图的时间标签干净一致:

p = re.compile(" 00:00:00") datetime = p.split(df["Date"][i])[0] df.iloc[i, 1] = datetime

这一步对后续shared_xaxes=True的轴对齐至关重要——上下子图共享 x 轴时,刻度标签必须可比较。

2. specs:声明每个网格单元的子图类型

make_subplots的核心是specs:一个与网格一一对应的"列表的列表"。原文中rows=3, cols=1声明了 3 行 1 列网格,specs中每个元素声明该单元格的子图类型:

specs=[[{"type": "table"}], [{"type": "scatter"}], [{"type": "scatter"}]]
  • {"type": "table"}:将第 1 行声明为表格子图;
  • {"type": "scatter"}:将第 2、3 行声明为二维笛卡尔(xy)子图。

shared_xaxes=True让第 2、3 行散点子图共享 x 轴;vertical_spacing=0.03则把行间距压缩到 0.03(归一化坐标),给 800 像素高的画布留出尽可能多的表格显示空间。

3. add_trace:把 trace 定向填入指定网格单元

fig.add_trace(trace, row=..., col=...)是向子图填充内容的入口。原文将两个go.Scatter分别填入row=3row=2,把go.Table填入row=1。注意表格子图与散点子图在填充方式上的差异:

  • 散点 trace 通过row/col关联到网格中对应的xaxis/yaxis
  • 表格 trace 属于domain子图类型,row/col参数决定其domain(绘图区域)坐标,详见下文原理分析。

4. 表格样式:header 与 cells

go.Table的展示样式集中在headercells两个子对象中(可在 plotly/graph_objs/_table.py 的Table._valid_props中看到cellsheaderdomaincolumnordercolumnwidth等全部有效属性):

go.Table( header=dict( values=["Date", "Number<br>Transactions", "Output<br>Volume (BTC)", ...], font=dict(size=10), align="left" ), cells=dict( values=[df[k].tolist() for k in df.columns[1:]], align="left" ) )
  • header.values:表头文本列表,其中<br>是 Plotly 支持的换行标记,用于在窄列中把"Number Transactions"折成两行显示;
  • header.font.size=10:缩小表头字号,让 8 列内容在有限宽度内容纳;
  • cells.values:每个列表对应一列数据,示例用[df[k].tolist() for k in df.columns[1:]]把 DataFrame 中除第一列外的所有列批量转为列表;
  • align="left":统一左对齐,原文同时在headercells上设置。

最后通过fig.update_layout(height=800, showlegend=False, title_text=...)统一定制画布高度、图例开关与总标题。

深入原理:表格为何是 domain 子图类型

从源码看,make_subplots支持两类"占位"逻辑。在 plotly/_subplots.py 开头定义了子图类型常量:

_single_subplot_types = {"scene", "geo", "polar", "ternary", "map"} _subplot_types = set.union(_single_subplot_types, {"xy", "domain"})

tablepieparcoordsparcats等 trace 没有关联的坐标轴对象,而是通过自身的domain属性(x/y两个归一化区间)在画布上独立定位。_subplot_type_for_trace_type正是依据 trace 是否携带domain属性来判定:

trace = trace_validator.validate_coerce([{"type": trace_type}])[0] if "domain" in trace: return "domain" elif "xaxis" in trace and "yaxis" in trace: return "xy"

也就是说,当specs中写{"type": "table"}时,make_subplots会把它归一化为domain子图类型,再由_init_subplot_domain生成子图引用:

def _init_subplot_domain(x_domain, y_domain): # No change to layout since domain traces are labeled individually subplot_ref = SubplotRef( subplot_type="domain", layout_keys=(), trace_kwargs={"domain": {"x": tuple(x_domain), "y": tuple(y_domain)}}, ) return (subplot_ref,)

这段实现说明了两点:

  1. 不需要修改 layoutlayout_keys=()表示表格子图不创建任何xaxis/yaxis对象(散点子图则会创建xaxis/yaxis对,见同文件中SubplotXY命名元组);
  2. 坐标通过 trace_kwargs 注入:子图网格计算出的domain.x/domain.y会在添加 trace 时写入go.Table(domain=...),使表格精确占据网格中第 1 行对应的归一化区域。

相应地,make_subplotsspecs的合法性有严格校验,例如specs维度必须与rows/cols完全匹配、colspan/rowspan不能越界,这些行为在 tests/test_core/test_subplots/test_make_subplots.py 中有系统性测试(如test_specs_underspecifiedtest_specs_colspan_too_big)。

make_subplots 关键参数速查(源自源码文档)

在 plotly/subplots.py 的make_subplots签名与 docstring 中,与本例直接相关的参数及默认值如下:

参数默认值说明
rows/cols1/1子图网格的行数/列数,必须大于 0
shared_xaxesFalse布尔或字符串:True'columns'表示同列子图共享 x 轴;'rows'表示同行共享;'all'表示全部共享
shared_yaxesFalse同理控制 y 轴共享('columns'/True'rows'/'all'
horizontal_spacing0.2 / cols列间距(归一化坐标,0~1)
vertical_spacing0.3 / rows行间距(归一化坐标,0~1)
start_cell'top-left'网格编号起点,可选'bottom-left'
print_gridFalseTrue时打印网格字符串表示,也可用结果Figure.print_grid()
specsNone每个单元格的规格:type'xy'/'scene'/'polar'/'ternary'/'map'/'domain'/trace 类型)、secondary_ycolspanrowspanl/r/t/b内边距
subplot_titlesNone每个子图的标题(按行优先顺序的列表,空字符串表示该格无标题)
column_widths/row_heightsNone各列宽度/各行高度比例
insetsNone叠加在网格单元格上的内嵌子图规格

原文示例中vertical_spacing=0.03远小于默认的0.3 / 3 = 0.1,正是为了让表格区域获得更大展示高度;shared_xaxes=True则让两条折线共用同一时间轴,形成连贯的时间对比。

进阶调优:调试与排版

用 print_grid 验证网格结构

make_subplots中传入print_grid=True,或在生成 figure 后调用fig.print_grid(),会在控制台打印类似下面的网格示意图,帮助确认每个单元格的类型与归属:

This is the format of your plot grid: [ (1, 1) table ] [ (2, 1) xy ] [ (3, 1) xy ]

这是排查"trace 没有出现在预期位置"类问题的最快手段。

表格区域与图表区域的精细控制

  • 若希望表格列宽更合理,可在go.Table中设置columnwidth(以比例控制各列宽度)与columnorder(调整列渲染顺序),二者均为 plotly/graph_objs/_table.py 中Table声明的有效属性;
  • 若子图较多需要各自标题,使用subplot_titles=["Table", "Revenue", "Hash rate"]
  • 若希望网格中某些单元格留空,可将specs对应位置设为None(例如在 3 列布局中实现[{}, None, {}]的效果)。

参考与延伸阅读

  • 本文主题对应的原始文档:doc/python/table-subplots.md;
  • 纯表格场景的完整用法:doc/python/table.md;
  • 子图网格的整体编排、shared_xaxesspecs的通用说明:doc/python/subplots.md;
  • make_subplots的完整参数文档与实现:plotly/subplots.py;
  • 子图类型判定与 domain 布局的底层实现:plotly/_subplots.py;
  • go.Tabletrace 的属性定义:plotly/graph_objs/_table.py;
  • specs 校验与网格初始化测试:tests/test_core/test_subplots/test_make_subplots.py。

结合以上内容,你可以用同样的specs机制自由组合tablescatterbarpieheatmap等任意类型的子图,把"数据明细 + 可视化图表"整合到单张交互式画布中。

  • 数据可视化
  • 数据分析

【免费下载链接】plotly.py

The interactive graphing library for Python :sparkles:

项目地址:https://gitcode.com/gh_mirrors/pl/plotly.py
点击查看免费下载

相关推荐

上一篇:如何5步快速掌握COLMAP:从零到三维重建专家的终极指南
下一篇:三步搞定文本大小!Quill编辑器Size格式完全指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询