
pyecharts 学习系列写到第六篇终于要啃硬骨头了多 x 轴、多 y 轴、多图叠加。这三个功能单独分开看都不算难可一旦组合起来轴对应关系、图例位置、坐标范围全搅在一起翻车概率直线上升。这篇内容不是把官方文档复述一遍而是从一类很常见的业务需求出发把完整实现过程走一遍代码可以直接抄坑我也顺手标出来。适合已经在用 pyecharts 画过基础柱状图、折线图现在想进一步做复杂报表的人。如果你连 add_xaxis、add_yaxis 都还没摸熟建议先回去补一下基础再来啃这一篇。1. 内容整体设计与思路拆解1.1 为什么会有“双轴”这种需求业务里经常出现两个指标要放在同一张图里观察相关性的场景。比如一个电商团队要看销售额和环比增长率销售额是几千块、几万块这种绝对值增长率却是百分之十几的小数。如果两者画在同一个 y 轴上增长率曲线会被销售额直接“压扁”成一条贴地的直线什么趋势都看不出来。多 y 轴的本质是给两个不同量级的数据各配一把“尺子”。销售额用左侧轴增长率用右侧轴两条数据线才能在同一张图里各自舒展。听起来很合理但代价是读图的人需要同时看两把刻度容易误读。所以我的经验是不是所有指标都值得做双轴。如果两个指标量级接近一根轴反而更直观只有量级明显拉开或者单位完全不同时才值得引入双轴。多 x 轴的需求相对少一些但也不是没有。比如底部 x 轴放产品分类顶部 x 轴放销售区域希望在同一张图上同时呈现两种维度的对比。这种图如果不用双 x 轴就只能硬拆成两张图。多 x 轴本质上是通过“不同的分类标签挂在同一个图表的不同轴上”让系列各有各的刻度参照。1.2 多图叠加解决的是信息割裂问题很多看板类需求不是单张图能解决的。老板打开一个页面最好一眼看完“销售额趋势怎么样、增长率高不高、哪个区域没达标”。如果这些信息散落在三个 HTML 文件里每次都要来回切换看板的意义就没了。多图叠加就是把这些相关图表组织到同一个画布里。pyecharts 里的“叠加”要分两层理解第一层是在同一个坐标系内叠加多个系列比如柱状图上面叠一条折线第二层是把多个坐标系排列在同一张画布上比如上半部分放销售额趋势下半部分放区域完成率互不干扰又彼此呼应。前者对应 Overlap 或 chart.overlap后者对应 Grid。实际项目里这两种需求经常同时出现。先解决单个图内部的叠加再把这些已经处理好的图用 Grid 摆进同一个页面。这个顺序不能反否则坐标轴和网格区域会乱成一锅粥。1.3 三件套的分工pyecharts 里和轴、叠加相关的核心工具我用一张表说清楚工具作用典型场景extend_axis为当前图表增加额外的 x 轴或 y 轴双 y 轴、双 x 轴Overlap / chart.overlap把多个图表系列叠加到同一个坐标系柱状图 折线图Grid把多个图表布局在同一画布的不同区域上下看板、左右对比这三者的关系不是替代而是分层协作。先 extend_axis 配好双轴再用 overlap 把柱和线叠起来最后扔进 Grid 和其他图一起排版。你如果一上来就 Grid 套 Overlap可能代码也能跑但出了问题很难定位而且后续调整坐标轴会很痛苦。2. 核心概念拆解pyecharts 里的轴与系列到底怎么关联2.1 默认轴、新增轴、axis_index 的关系没做过复杂图表的人可能不会注意到 pyecharts 里每一张图默认都有一根 x 轴和一根 y 轴索引都是 0。你平时用 add_xaxis、add_yaxis实际上就是在往这两个默认轴上添加数据。调用 extend_axis 之后新增加的轴会按添加顺序获得新的索引值。第一次 extend_axis 加的轴无论它是 x 轴还是 y 轴索引一定是 1如果再调用一次索引就是 2。系列通过 xaxis_index、yaxis_index 两个参数绑定到具体轴。不指定的话默认都是 0也就是全部挂在最初的轴上。很多人轴“跑偏”原因就在这里。你以为 extend_axis 加了一根右侧轴数据就会自动画到右侧去实际上 pyecharts 不会自动感知。你必须在新系列那里明确写 yaxis_index1它才知道“哦我该用右侧那把尺子”。这个概念不理解后面所有双轴图都会写得一塌糊涂。我建议可以做一个类比图表是舞台坐标轴是舞台两侧的投影幕布。系列是演讲的人讲哪段数据就要告诉他站到哪块幕布前面。axis_index 就是那块幕布的编号。2.2 轴类型与数据长度最容易翻车的地方pyecharts 里的 x 轴主要有两类category 和 value。直接用 add_xaxis 传入一个列表时默认会按 category 处理列表里的字符串或数字会变成一个个分类刻度。如果第二根 x 轴想显示另外一组分类需要在 extend_axis 里说明 AxisOpts(type_category, data[...])。数据长度不匹配是新手选错轴之后最常见的翻车点。一个系列要画在顶部 x 轴上而顶部 x 轴有 5 个分类这个系列却只给了 3 个数据点图表不会报错但后几个点会凭空消失或者错位挂到别的地方。实际开发里这个问题特别隐蔽因为图表能渲染出来只是看起来哪里不太对。我的习惯是在写代码前先把每个轴对应的数据列成一张对应表。哪组数据用哪个轴、长度是多少、单位是什么全部列清楚再开始写。这样可以省掉很多来回调试的时间。数据长度不一致不一定错但必须是有意为之不能是手滑。2.3 Overlap 和 Grid 的本质区别Overlap 解决的是“同一个坐标系内多个系列怎么叠”的问题。柱状图和折线图叠在一起共享同一组 x 轴和 y 轴配置这样叠加出来的图视觉上是前后层次关系数据点可以互相参照。Grid 解决的是“同一个画布上多个坐标系怎么排”的问题。每个子图都有自己独立的轴、图例、标题它们只是共享一个平台。Grid 组合图表时更像是在做拼版每张子图拥有自己的空间范围互不干扰。所以判断用哪个只有一个标准系列是否需要共用坐标系。需要共用的用 Overlap需要独立坐标系的用 Grid。如果把不该共用轴的图强行用 Overlap 叠在一起会出现一个坐标系里同时混入两套毫不相关的分类数据那画面会非常乱。3. 实操案例做一个经营看板3.1 准备环境和数据先确认 pyecharts 版本。我这里使用的是 2.x 版本的 API和 1.x 差异很大如果你的环境还是老版本建议直接升级pip install -U pyechartspyecharts 默认渲染结果是 HTML 文件浏览器直接打开就能看到交互图表不依赖网络。如果你想在 Notebook 里直接展示可以改成 notebook 模式但本文所有示例都用 render 输出 HTML方便对照。下面用一个虚构公司上半年的经营数据做例子。数据很简单就是每个月销售额、环比增长率以及各区域的销售情况。这里不用真实业务数据反正核心是看怎么把轴和图表组合起来。months [1月, 2月, 3月, 4月, 5月, 6月] sales [3200, 4300, 3900, 5100, 6800, 7400] growth [12.5, 18.3, 10.2, 23.1, 28.7, 25.4]sales 是销售额单位是万元数值都是四位数growth 是环比增长率单位是百分号。这两个数据量级相差几十倍正是双 y 轴的典型场景。3.2 第一步双 y 轴柱线图先做柱状图展示销售额柱子用默认蓝色。然后用 extend_axis 加一根右侧 y 轴用来展示增长率。折线图通过 yaxis_index1 绑定到右侧轴最后把折线合并到柱状图里。from pyecharts import options as opts from pyecharts.charts import Bar, Line bar_chart ( Bar() .add_xaxis(months) .add_yaxis( 销售额, sales, color#4C84FF, label_optsopts.LabelOpts(is_showFalse), ) .extend_axis( yaxisopts.AxisOpts( name增长率, type_value, positionright, axislabel_optsopts.LabelOpts(formatter{value}%), ) ) .set_global_opts( title_optsopts.TitleOpts(title上半年销售额与环比增长率), tooltip_optsopts.TooltipOpts(triggeraxis), legend_optsopts.LegendOpts(pos_toptop), xaxis_optsopts.AxisOpts(name月份), yaxis_optsopts.AxisOpts(name销售额), ) ) line_chart ( Line() .add_xaxis(months) .add_yaxis( 增长率, growth, yaxis_index1, color#F56C6C, symbolcircle, symbol_size8, label_optsopts.LabelOpts(is_showFalse), ) ) bar_chart.overlap(line_chart) bar_chart.render(dual_y_axis.html)这段代码里的几个细节值得讲一下。第一set_global_opts 里的 yaxis_opts 只能控制最左边的默认 y 轴右侧 y 轴的配置写在 extend_axis 的 AxisOpts 里。第二折线图 add_yaxis 时如果不写 yaxis_index1它就会被画到左侧默认轴也就是销售额那把尺子上结果就是增长率曲线被压成一条直线。第三柱子和折线我都关了数据标签因为两个系列的数字叠在一起会非常乱而且双轴图的信息本身已经够多再标数字就是噪音。如果 pyecharts 版本不支持 bar_chart.overlap(line_chart) 这种写法也可以用 Overlap 类from pyecharts.charts import Overlap overlap_chart Overlap() overlap_chart.add(bar_chart) overlap_chart.add(line_chart) overlap_chart.render(dual_y_axis_overlap.html)两种方式结果等价看个人习惯。我自己更常用 chart.overlap因为代码链式调用比较紧凑。3.3 第二步双 x 轴加双 y 轴组合接下来做个更复杂的例子。底部 x 轴放产品分类顶部 x 轴放区域同时右侧再挂一个 y 轴放区域利润。这样一张图里既能看各产品销量又能看各区域利润水平。regions [华东, 华北, 华南, 西南, 东北] region_profit [210, 175, 198, 132, 150] multi_axis_chart ( Bar() .add_xaxis([食品, 饮料, 日化, 家电]) .add_yaxis( 产品销量, [430, 510, 390, 470], label_optsopts.LabelOpts(is_showFalse), ) .extend_axis( xaxisopts.AxisOpts( type_category, dataregions, positiontop, ) ) .extend_axis( yaxisopts.AxisOpts( name利润, type_value, positionright, ) ) .add_yaxis( 区域利润, region_profit, xaxis_index1, yaxis_index1, color#67C23A, label_optsopts.LabelOpts(is_showFalse), ) .set_global_opts( title_optsopts.TitleOpts(title产品销量与区域利润对照), tooltip_optsopts.TooltipOpts(triggeraxis), legend_optsopts.LegendOpts(pos_toptop), ) ) multi_axis_chart.render(multi_axis.html)这里有两个地方一定要对应好。第一个系列“产品销量”没写 xaxis_index默认用 xaxis_index0也就是底部产品分类轴“区域利润”写 xaxis_index1、yaxis_index1明确告诉它挂到顶部 x 轴和右侧 y 轴。第二个注意点顶部 x 轴有 5 个区域分类所以区域利润数据必须是 5 个。万一少给一个图表不会报红但画面会少一根柱子真实原因很难一眼看出来。这种图形好看是好看但信息层次多。建议只在需要同时强调两个维度时使用不要滥用。一旦两个 x 轴的分类标签都很长刻度会互相挤那就要做标签旋转或者隐藏后面我会单独说。3.4 第三步用 Grid 把多张图叠加到同一画布现在把前面两张图放进同一个看板。上半部分放销售额与增长率双轴图下半部分放产品销量与区域利润对照图。Grid 最关键的是 grid_opts它控制每个子图在画布上的位置和大小。from pyecharts.charts import Grid grid_chart Grid(init_optsopts.InitOpts(width1600px, height900px)) grid_chart.add( bar_chart, grid_optsopts.GridOpts( pos_left12%, pos_right12%, pos_top8%, pos_bottom55%, ), ) grid_chart.add( multi_axis_chart, grid_optsopts.GridOpts( pos_left12%, pos_right12%, pos_top60%, pos_bottom5%, ), ) grid_chart.render(business_dashboard.html)grid_opts 里的 pos_left、pos_right、pos_top、pos_bottom 表示子图相对画布边界的距离。我这里上半部分 pos_bottom 留了 55%意思是从底部往上 55% 的位置作为上半部分图的边界所以上半部分图占了顶部到中间偏下的区域下半部分 pos_top 设成 60%留出 5% 的缝隙两张图不会贴在一起。Grid 组合时有一个常见错误给每个子图都设置了一个全局标题结果两个标题在画布上重叠。正确做法是子图内部不要放标题最多只给整个看板设一个总标题或者在子图内部用图例和轴名称来区分。我上面的代码里 bar_chart 已经带了标题如果放进 Grid 后出现标题压住画面就把子图 set_global_opts 里的 title_opts 删掉只保留 tooltip 和 legend。3.5 一些值得调教的细节轴名称的单位要写清楚。右侧增长率轴我加了 formatter{value}% 这样刻度上会带百分号左侧销售额轴的名字也写了“销售额”但没有写单位。实际项目中建议改成“销售额万元”否则看图的人还要猜单位。x 轴分类文字过长时可以在 AxisOpts 里加 axislabel_optsopts.LabelOpts(rotate15)让文字斜着排避免互相覆盖。顶部 x 轴的标签如果和底部 x 轴标签语义重叠也可以直接开一个开关把顶部轴标签隐藏只保留轴本身的位置含义。tooltip 的 trigger 建议统一设置为 axis。多轴图的数据点是按 x 轴位置对齐的item 模式容易变成鼠标指到哪一行就只显示那一行里的某一个系列不够直观。改成 axis 模式后鼠标移动时会同时显示同一 x 位置上的多个系列数值看双轴图尤其方便。4. 常见问题与排查技巧4.1 轴和数据“对不上”这是我在项目里遇到最多的一类问题表现形式多种多样。最常见的是柱状图在左轴折线图也在左轴增长率被压平。原因很简单折线图的 add_yaxis 里没写 yaxis_index1。排查时不要靠猜直接看系列和轴的对应关系。pyecharts 生成的图表对象里保存了配置结构你可以打印出来检查 series 里每个 dataIndex 对应的 yAxisIndex 到底是什么。如果发现某个系列该用右侧轴却用了默认轴改对应参数的索引即可。多 x 轴场景还有一个类似的问题第二个系列忘了写 xaxis_index1结果它跑到了底部分类轴上。如果两个轴的数据长度不一致图表会按照默认轴的分类去匹配第二个系列的数据丢弃多余的数据点。这个问题隐蔽就隐蔽在不报错你只看到少了一根柱子但不一定想到是轴绑错了。4.2 图例和 tooltip 表现怪异图例重复是一个高频问题。每到 Grid 组合图每个子图都有自己的一套图例如果每个子图都显示图例画布顶部就会挤满两排甚至三排文字。解决方案也很简单子图内部不要全部开图例只保留最重要的一个或者把图例位置放进子图内部而不是全局顶部。Grid 整体上也可以设置全局图例但需要控制好位置。tooltip 失效的话优先检查是不是某个子图里的 series 被 Overlap 合并后tooltip 配置没有跟着全局设置一起生效。我在实际操作中遇到过柱状图有 tooltip折线图却什么都不弹的情况。后来把 tooltip 配置移到 set_global_opts 里统一设置问题就消失了。只要所有系列都归属于同一个全局配置tooltip 通常不会单独失效。4.3 刻度标签挤成一团双 x 轴图底部和顶部各有一排分类标签如果分类名长度超过四五个字很容易粘在一起。另一种情况是 Grid 上下两个子图的 x 轴标签都很长pos_bottom 和 pos_top 留白不够标签被截断或者和下一张图重叠。解法有两个方向。一是旋转标签让文字倾斜 15 到 45 度牺牲一点可读性换空间。二是隐藏不重要的轴标签只保留轴本身的刻度位置在 AxisOpts 里设置 axislabel_optsopts.LabelOpts(is_showFalse)。如果两个 x 轴分类完全一致甚至可以只保留其中一个轴的标签另一个轴只画轴线不画文字画面会清爽很多。4.4 快速排查对照表有些问题是几句话说不完的我整理成一张速查表方便你遇到问题直接对着找现象可能原因处理方式折线被压在底部几乎变成水平线没有绑定右侧 y 轴在 add_yaxis 中指定 yaxis_index1柱子数量变少数据点丢失系列绑定了错误的 x 轴索引检查 xaxis_index 是否与轴数据长度匹配右上角出现多余的空白坐标系extend_axis 加了轴但没有系列使用移除无用轴或给对应系列绑定索引Grid 里两个图重叠grid_opts 的 pos_top 与 pos_bottom 设置不合理为每个子图明确设置独立区域图例重复多份每个子图单独开启了 legend只保留一个全局图例或单独控制子图图例鼠标悬浮没有十字提示tooltip trigger 不是 axis在 set_global_opts 中配置 TooltipOpts(triggeraxis)右侧轴刻度没有单位AxisOpts 的 axislabel_opts 未配置 formatter增加 formatter{value}%等格式化这张表是我实际写代码时查得最勤的一张表建议收藏或者记在自己的笔记里。5. 我在实际项目里的配置心得5.1 左右轴上的“尺子”怎么调才不骗人双 y 轴最怕的就是左右两把尺子的刻度范围设置不当导致趋势被放大或缩小让看图的人产生错误判断。比如销售额那根轴从 0 到 8000增长率那根轴也从 0 到 30两边的比例是分开计算的本来没有问题。但如果右侧增长率轴只显示 10 到 25那增长率曲线上下波动的幅度就会被夸大。我的原则是业务汇报用的图右轴尽量不手动调 min 和 max让 pyecharts 根据数据自动计算。自动计算虽然不一定完美但至少不会刻意误导。唯一需要手动干预的场景是增长率会出现负数而自动计算把负值以下的空间留太大这时候可以在 AxisOpts 里设 min_-5 这种业务上合理的值让负数区域不至于空太多。还有一点右侧轴如果数值范围很大比如百分比从 0 到 100需要在 AxisOpts 里设置 axislabel_opts 的 formatter保证刻度上显示的是百分号而不是单纯的小数。单位不标清楚双轴图就像一把没刻度的尺子看了也白看。5.2 多图叠加的配色和可读性多图叠加上去之后信息量会成倍增长配色就变得非常重要。我的习惯是主指标用深色比如深蓝、藏青辅助指标用亮色比如橙红、青绿。柱状图如果是面积型主导透明度可以调到 0.7 左右避免柱子太实把后面的折线完全挡住。折线的线宽我一般设成 2 到 3点用 circle 类型symbol_size 控制在 6 到 10。太宽的线会盖住柱子细节太窄的点在交互悬浮时不容易点击。Grid 上下图之间可以用 pos_top 和 pos_bottom 留出足够空隙至少 5% 到 10% 的余量视觉上没有压迫感。字体方面轴名称和标题的字号我都习惯保持一致颜色比主文字浅一个灰度。这样整个看板看上去统一不会被某个刺眼的颜色抢走注意力。颜色数量控制在三四种以内超过五种人眼就很难快速定位重点了。5.3 给新手的练习建议如果你正在学多 x 轴和多 y 轴我建议不要一上来就挑战 Grid 套 Overlap 的组合拳。先单独做一张双 y 轴图把 yaxis_index 掰扯清楚再做一张双 x 轴图把 xaxis_index 弄明白然后单独用 Grid 摆两张普通图表确认 grid_opts 的定位方式最后再把这些能力组合起来。练习时最好用自己编的假数据这样不用对业务是否合理负责可以大胆试错。每次改完一个参数就把渲染出来的 HTML 打开看一遍重点看轴位置、图例、tooltip 是否正常。pyecharts 的好处就是渲染成本极低改一次看一眼整个过程非常快。我个人在实际操作中最深的体会是多轴图的所有问题几乎都是“轴和系列是否一一对应”的问题。先把每个系列该用哪根轴想清楚再写代码就不会陷入边写边猜的循环。这个思路养成了双轴、多图叠加这些功能就只是套模板而已。