如果你的电脑里还躺着 pyecharts 0.5.11 这个版本号,这篇文章就是为你准备的。我手上有几个数据看板项目,代码就是用这套旧版 API 写的,一直跑得好好的,所以也没急着升级。0.5.11 看起来不够“新”,但它在实际业务里非常能打:启动快、依赖少、入门曲线短,很多毕业设计、报表系统的代码到现在还在线上跑。这篇文章把 pyecharts 0.5.11 的图表生成拆开来讲,从安装、数据格式、常用图表到底层渲染原理、常见坑都会说到。不管你是刚接触想快速出图,还是接手旧项目需要维护,都能从中找到能直接用的东西。
需要提醒一句,如果你是从 1.x 版本切过来看这篇内容的,先放下手里的链式调用习惯。pyecharts 0.5.x 和 1.x 虽然都叫 pyecharts,但 API 风格完全是两代人。0.5.11 用的是老式实例化加 add 方法传参的写法,1.x 改成 x 轴 y 轴分开配置、统一用 options 管理。只有先把认知切回旧版本的语境,下面这些代码才不会让你觉得莫名其妙。
1. 为什么还在用 pyecharts 0.5.11,以及它的 API 边界
1.1 0.5.x 与 1.x 的差异到底有多大
很多朋友会问,既然 1.x 都出了那么久,为什么还守着 0.5.11 不放。答案很简单:存量代码太多,而且 0.5.11 在简单报表场景里真的是够用。0.5.11 的核心用法一句话就能讲完——初始化一个图表对象,然后用 add 方法把数据塞进去,最后 render 成 HTML 文件。
对比一下 0.5.11 和 1.x 的写法:
# pyecharts 0.5.11 的经典写法 from pyecharts import Bar bar = Bar("近30天销量", "示例数据") bar.add("订单量", ["1日", "2日", "3日"], [120, 200, 150]) bar.render("bar.html")# pyecharts 1.x 的写法 from pyecharts.charts import Bar from pyecharts import options as opts bar = ( Bar() .add_xaxis(["1日", "2日", "3日"]) .add_yaxis("订单量", [120, 200, 150]) .set_global_opts(title_opts=opts.TitleOpts(title="近30天销量")) ) bar.render("bar.html")看到区别了吗?0.5.11 把标题、数据系列、坐标轴都放在不同的传参位置里;1.x 则把配置项收敛成独立的 options 体系。从代码可读性来说,1.x 确实更清晰,但它引入的链式调用和大量 opts 类,会让接手旧项目的人感觉像换了一门语言。我见过不少人因为升级 1.x 时改坏了几十个图表,最后又把代码回滚到 0.5.11 继续跑,因为老板只关心图表能不能按时出来。
1.2 0.5.11 能画什么,不能画什么
0.5.11 支持的图表类型覆盖了绝大多数业务场景,常用的有 Bar 柱状图、Line 折线图、Pie 饼图、Scatter 散点图、KLine 蜡烛图、HeatMap 热力图、Map 地图、Geo 地理坐标图、Radar 雷达图、Funnel 漏斗图、Gauge 仪表盘、WordCloud 词云。这些类都在 pyecharts 包顶层,直接 import 就能用。
它明显弱于 1.x 的地方主要在两个点。第一,0.5.11 没有 1.x 那种统一且严格的 options 校验机制,参数名写错了很可能不报错,图表只是悄悄少一个功能,排查起来非常费劲。第二,0.5.11 对响应式布局的支持比较弱,移动端看图表经常会出现文字挤压、图例错位的问题。所以如果你的项目是移动端优先,我不建议新代码再用 0.5.11;但如果是 PC 端后台看板,那它完全够用。
还有一个容易踩的认知误区:0.5.11 虽然会把 echarts 的前端库打包进 HTML,但它的模板默认引用的是在线 CDN 地址。换句话说,render 出来的 HTML 必须在能访问外网的环境里才能完整渲染。这个问题留到后面“离线运行”部分细说。
2. 环境准备:装对版本、装对地图包,后面才能少踩坑
2.1 安装 Python3 环境与 pyecharts 0.5.11
如果你用的是虚拟环境,直接一条命令:
pip install pyecharts==0.5.11这里强烈建议在虚拟环境里装,不要用全局环境。因为很多人的机器上已经装了 1.x 或者其他依赖,混装之后 import 的类名可能完全对不上。0.5.11 安装时会自动带上一些运行必需的依赖,比如模板渲染需要的那几个包,不用额外操心。装完之后验证一下版本:
pip show pyecharts然后跑一个最简单的例子,确认能生成 HTML 文件:
from pyecharts import Bar bar = Bar("验证环境", "示例") bar.add("系列", ["A", "B"], [1, 2]) bar.render("smoke_test.html")如果这一步成功,会在当前目录生成一个 smoke_test.html,打开后能看到一个带标题的柱状图。看不到就先把浏览器控制台的报错截图留下来,后面排查章节会提到最常见的原因。
2.2 地图扩展包怎么装才算装对
0.5.11 的 Map 和 Geo 图表比较特殊,地图的 GeoJSON 数据不在 pyecharts 主包里,而是拆成了独立扩展包。画中国地图需要装的是这几个:
pip install echarts-countries-pypkg pip install echarts-china-provinces-pypkg pip install echarts-china-cities-pypkg pip install echarts-china-misc-pypkg这几个包体积不算小,装的时候要耐心等。第一次使用地图时,pyecharts 会把对应的地理数据加载进模板,所以第一个地图页面渲染可能会明显变慢,这是正常现象。
有一个容易困惑的点:这些包装完之后,代码里不需要显式 import 它们。你只要正确传了 maptype 参数,比如 maptype="china",pyecharts 会自己去注册表里找。如果渲染出来的地图只有轮廓、没有区域颜色,十有八九是这些扩展包没装上,或者装到了另一个 Python 环境里。
2.3 验证一个地图图表
from pyecharts import Map data = [("北京", 120), ("上海", 160), ("广东", 200), ("浙江", 300)] map_chart = Map("各省销量", width=800, height=400) map_chart.add("销量", data, maptype="china", is_visualmap=True, visual_range=[0, 300]) map_chart.render("map_test.html")这里命名为 map_chart 而不是 map,是因为 Python 内置函数 map 很容易被覆盖,一旦覆盖了,后面再写 list(map(...)) 就会报错。这是旧版 pyecharts 教程里经常会忽略的细节,我自己在这上面吃过亏。
3. 核心图表实操:从柱状图到地图的数据格式与参数
3.1 柱状图与折线图:先把手感练出来
柱状图是所有 pyecharts 项目里出现频率最高的图。0.5.11 的 Bar 初始化参数直接传标题,add 方法的第一个参数是系列名,第二个是 x 轴数据,第三个是 y 轴数据。多个系列就多次调用 add,柱状图默认并排显示。
from pyecharts import Bar bar = Bar( title="上半年销售额", subtitle="单位:万元", width=900, height=450, ) bar.add("华东", ["1月", "2月", "3月", "4月"], [12, 19, 28, 32]) bar.add("华南", ["1月", "2月", "3月", "4月"], [8, 15, 22, 27], is_stack=True) bar.add("华北", ["1月", "2月", "3月", "4月"], [5, 10, 14, 18], is_stack=True) bar.render("bar_demo.html")is_stack=True 会把同一 x 轴下的多个系列堆叠起来,适合展示整体构成。要注意堆叠的细节:如果你对第一个系列不传、第二个系列传了 is_stack=True,那么堆叠关系是从第二个系列开始的,视觉上会出现一半堆叠一半并排的混乱效果。同类参数里,is_label_show=True 会在柱子上直接显示数值,mark_point=["max", "min"] 可以标出最高最低点。
折线图和柱状图的套路基本一样,只是更值得关注的是曲线平滑和端点符号:
from pyecharts import Line line = Line("访问趋势", "近7日") line.add( "UV", ["周一", "周二", "周三", "周四", "周五", "周六", "周日"], [1200, 1500, 1300, 1800, 2100, 2600, 2400], is_smooth=True, is_symbol_show=False, mark_line=["average"], ) line.render("line_demo.html")is_smooth=True 会把折线变成曲线,is_symbol_show=False 能去掉数据点的小圆点,mark_line=["average"] 会在图上画一条平均值虚线。这几个参数在多指标趋势图里特别常用。折线图的数据点如果太多,可以在 add 里加一句 is_datazoom_show=True,自动出现一个拖拽缩放的滚动条,这个在时间序列数据里几乎是标配。
3.2 饼图与环形图:数据格式是最大的坑
饼图在 0.5.11 里的数据格式和其他图完全不一样。柱状图是 x 轴一个列表、y 轴一个列表,而饼图要的是成对数据,格式是 [(类别, 数值), (类别, 数值)]。很多从 1.x 转过来的人在这里翻车,因为 1.x 的 Pie 也是分 x 轴和 y 轴传的。
from pyecharts import Pie source = [ ("直接访问", 32), ("搜索引擎", 45), ("广告投放", 18), ("社交媒体", 5), ] pie = Pie("流量来源分析", width=800, height=400) pie.add( "占比", source, radius=[40, 65], # 内径和外径,不传就是实心饼 is_label_show=True, label_pos="outside", legend_orient="vertical", legend_pos="left", ) pie.render("pie_demo.html")radius=[40, 65] 是饼图做成环形图的关键。理解也很简单,40 是内半径,65 是外半径,内半径大于 0 时中间就是空的。legend_orient 和 legend_pos 控制图例的位置,饼图右侧空间不够时,把图例放左边或者下边会舒服很多。
饼图数据传入时还有一个小细节:pyecharts 在渲染之前会把数据转成 JSON,元组会被转成数组,所以在业务代码里用字典转列表即可。如果你直接把一个 dict 传进去,遍历顺序可能不是你想要的,建议显式生成 list:
data = [{"name": k, "value": v} for k, v in source]这个格式不是 0.5.11 的饼图要求的,它是底层 echarts 的格式。0.5.11 的 add 方法接受的仍是 list of tuple,但转成字典列表在排查 JSON 输出时会更直观,算是个人经验。
3.3 热力图与散点图:二维数据场景
热力图在业务里常用于时间序列和类目交叉分析,比如一周七天、每小时一段的活跃度变化。0.5.11 的 HeatMap 数据格式比较特殊,它要求一个由三个元素组成的列表:
from pyecharts import HeatMap x_labels = ["周一", "周二", "周三", "周四", "周五"] y_labels = ["8点", "10点", "12点", "14点"] data = [ [0, 0, 12], # 周一 8 点 [0, 1, 20], # 周一 10 点 [0, 2, 35], # 周一 12 点 [1, 0, 18], [1, 1, 26], # ... ] heatmap = HeatMap("时段活跃度") heatmap.add( "活跃度", x_labels, y_labels, data, is_visualmap=True, visual_text_color="#000", ) heatmap.render("heatmap_demo.html")data 里的三个数字分别是 x 轴索引、y 轴索引和数值。x 轴索引从 0 开始,对应 x_labels 里的第几个;y 轴索引同理。传反了图表不会报错,但会画出一个数据完全错位的图,排查时特别隐蔽。
散点图相对直觉,传入两个等长的列表即可:
from pyecharts import Scatter scatter = Scatter("身高体重分布") scatter.add( "样本", [165, 170, 172, 175, 180, 182], # x 轴数据 [55, 60, 68, 72, 80, 85], # y 轴数据 is_visualmap=True, symbol_size=12, ) scatter.render("scatter_demo.html")在 0.5.11 中,Scatter 也支持 is_visualmap=True,这会给图表加上渐变色彩条,等于同时看到第三维的信息,适合展示分布密度。
3.4 地图与词云:最容易出效果也最容易出问题
地图类图表是 0.5.11 的亮点,真要做好,需要先确认扩展包装好了。Map 的数据格式和饼图一样,也是成对列表:
from pyecharts import Map province_data = [ ("北京", 120), ("上海", 160), ("广东", 200), ("浙江", 300), ("四川", 220), ] map_chart = Map("区域指标分布", width=900, height=500) map_chart.add( "指标", province_data, maptype="china", is_visualmap=True, visual_range=[0, 300], ) map_chart.render("map_demo.html")在绘制具体某个省份的地图时,比如 maptype="广东",需要确认安装的是 echarts-china-provinces-pypkg,并且省份名要和扩展包内置名称一致,差一个字都可能画不出来。有人会碰到地图上区域颜色全部停留在最低色值,这通常不是数据问题,而是 visual_range 的范围没设好,导致最大值和最小值差距过大,视觉上拉不开层次。
词云是活动运营和分析报告里的常客。0.5.11 的 WordCloud 用起来也不复杂:
from pyecharts import WordCloud words = [ ("数据分析", 100), ("Python", 88), ("可视化", 76), ("报表", 60), ("自动化", 52), ] wordcloud = WordCloud("关键词分布", width=800, height=400) wordcloud.add("", words, shape="diamond", word_size_range=[20, 100]) wordcloud.render("wordcloud_demo.html")shape 参数支持 circle、cardioid、diamond、triangle 等形状。词云不显示的大部分原因是模板里额外引用的 echarts-wordcloud 扩展 JS 没加载成功,特别是在离线环境下。这个和普通图表只依赖 echarts.min.js 不一样,需要单独处理,后面离线运行章节会详细讲。
4. 多图组合与页面布局:让图表形成一份报告
4.1 Grid 多图拼合:一页展示多张图
单独画一张图不难,难的是把多张图组合在一页上,形成一份可读的看板。0.5.11 里最常用的组合容器是 Grid,它可以把不同图表拼到同一个 HTML 页面里。
from pyecharts import Grid, Bar, Line bar = Bar("销售与增长趋势") bar.add("销售额", ["1月", "2月", "3月"], [10, 20, 15]) line = Line() line.add("增长率", ["1月", "2月", "3月"], [5, 12, 8], is_smooth=True) grid = Grid(width=900, height=450) grid.add(bar, grid_left="10%", grid_right="55%") grid.add(line, grid_left="55%", grid_right="10%") grid.render("grid_demo.html")Grid 的 add 方法里,grid_left、grid_right 这些参数控制每个子图在页面中的占位。注意 Grid 自身的 title 参数不会直接作用在子图上,想让子图带标题,必须保证传入的子图对象自己有 title。所以上面代码里 bar 初始化时传了标题,而 line 就没有传,最终页面上只显示柱状图的标题。这种细节不调几次是不会发现的。
4.2 Overlap 双轴图层:柱线叠加的看板经典场景
柱状图和折线图叠加是销售分析里最经典的双轴图,左边柱是销量,右边线是增长率。在 0.5.11 里,用的是 Overlap 容器:
from pyecharts import Overlap, Bar, Line bar = Bar("销量与增长率") bar.add("销量", ["Q1", "Q2", "Q3", "Q4"], [320, 480, 620, 780]) line = Line() line.add("增长率", ["Q1", "Q2", "Q3", "Q4"], [8.2, 11.5, 14.8, 17.3]) overlap = Overlap() overlap.add(bar) overlap.add(line, is_new_axis=True) # 关键参数:让折线使用新的 y 轴 overlap.render("overlap_demo.html")is_new_axis=True 是双轴能够实现的核心,它会给折线图单独分配一个 y 轴。如果你忘记加这个参数,折线会和柱状图共用同一个 y 轴,数值范围相差过大时,折线会几乎被压成一条直线。
4.3 Timeline 时间轴:动态看数据变化
时间轴容器适合展示同一指标在不同时间段的变化,比如各年销售额。它自带一个可以拖拽或自动播放的时间条,交互感很强。
from pyecharts import Timeline, Bar timeline = Timeline("年度销售趋势", width=800, height=400) for year in range(2020, 2024): bar = Bar() bar.add( "销售额", ["Q1", "Q2", "Q3", "Q4"], [100 + year * 10, 120 + year * 10, 90 + year * 10, 130 + year * 10], ) timeline.add(bar, "{}年".format(year)) timeline.render("timeline_demo.html")timeline.add 的第二个参数是时间轴上的标签文本,格式自己定义。0.5.11 的时间轴默认是纵向排列还是横向排列,取决于 echarts 模板的默认行为,通常不需要额外配置。如果你希望自动循环播放,可以在 Timeline 初始化时透传一些事件参数,但我在实际项目里很少开自动播放,因为用户一般要自己盯着某个年份看。
4.4 Page 多图连续展示:五张图从上到下一张报告
Page 和 Grid 的区别很好理解,Page 是简单的纵向叠加,每个图表独占一行,适合生成一份长截图式的报告;Grid 则是把多个图表放在同一区域,适合做一个“总览仪表盘”。
from pyecharts import Page page = Page("数据看板", width=900) page.add(bar) page.add(line) page.add(pie) page.render("page_demo.html")Page 的主要坑在于图表的宽度。如果你不给图表显式设置宽度,Page 页面里的图表可能会按各自初始化时的宽度排列,导致页面参差不齐。建议每个子图在初始化时就统一 width,比如都传 width=900。另外 Page 没有 Grid 那么复杂的位置控制,所以做简单汇报材料时最省心。
4.5 主题切换与全局配置
0.5.11 自带几套主题,内置的常见主题包括 dark、macaron、shine、roma 等。主题调用方式是在图表对象上调用 use_theme:
bar = Bar("深色主题", width=800, height=400) bar.use_theme("dark") bar.add("数据", ["A", "B"], [10, 20]) bar.render("dark_demo.html")use_theme 必须要在 render 之前调用,而且切换主题之后,最好看一遍所有图表,因为某些主题下的文字颜色会成为浅色,如果恰好图表背景也是浅色,会出现文字看不见的情况。这个在深色主题下最容易犯,我通常会给图表手动加一个深色背景,或者干脆只对特定图表使用深色主题。
5. 输出与上手:HTML、Notebook、图片导出和离线运行
5.1 HTML 渲染与 JS 依赖加载原理
0.5.11 的 render 方法最终做的是:把图表配置转成 JSON,塞进 pyecharts 自带的 HTML 模板,生成一个独立 HTML 文件。这个文件本质上是 echarts 在前端把 JSON 配置渲染成 SVG 或 Canvas。
理解这个原理对排查空白页面特别有帮助。当你打开 HTML 发现整个页面是空白时,第一步不是看数据,而是按 F12 看浏览器控制台里有没有红色报错。最常见的是 echarts.min.js 加载失败,因为默认模板引用的是在线 CDN。解决方向有两个:一是让运行环境能访问外网,二是把 JS 文件下载到本地,并把模板里的引用地址改成相对路径。
离线内网环境部署是我踩过最多的坑。做法很简单,先在有网环境生成 HTML,打开 HTML 源码,找到 script 标签里引用的 echarts.min.js 地址,下载到本地 static 目录,然后替换 HTML 里的引用路径。词云图还要额外找 echarts-wordcloud.min.js,地图图表则要把地图数据的 JS 一并下载。
0.5.11 还有一个比较实用的方法叫 render_embed,它不生成文件,而是返回包含图表的 HTML 代码片段。这个在 Flask 后端返回图表时很常用:
html_code = bar.render_embed() # 把 html_code 嵌入自己的页面模板即可5.2 Notebook 中渲染的正确姿势
如果你在 Jupyter Notebook 里做数据分析,用 render_notebook 会比 render 方便很多:
from pyecharts import Bar bar = Bar("Notebook 内嵌图") bar.add("数据", ["A", "B"], [10, 20]) bar.render_notebook()render_notebook 会把图表以 HTML 片段的形式输出到 cell 里。这里有两个经验:第一,同一次 kernel 会话中反复执行 render_notebook 不会覆盖之前的输出,而是会追加,时间久了 Notebook 文件变大;第二,如果图表无法显示,先确认使用了新版 Jupyter 而不是只装了 ipython,因为 pyecharts 需要前端支持 HTML 渲染。
5.3 图片导出:phantomjs 方案与降级方案
pyecharts 0.5.11 本身不输出 png,它生成的是 HTML。如果业务上必须要图片,最正统的方式是用 pyecharts-snapshot 配合 phantomjs。
pip install pyecharts-snapshot snapshot bar.html bar.png这条命令会把 bar.html 渲染成 bar.png。phantomjs 的安装是真正的痛点,老版本 phantomjs 在新系统上经常缺少依赖。我的建议是:如果只是偶尔要图,直接用浏览器打开 HTML,然后手动截图;如果是批量生成图片,考虑用 selenium 驱动 Chrome 无头模式,稳定度和可维护性都比 phantomjs 好不少。脚本书写量大一点,但至少不会因为环境依赖折腾半天。
5.4 图表嵌入到 Web 项目时的注意事项
把 0.5.11 图表塞进 Django 或 Flask 页面时,最省事的方案就是前面提到的 render_embed,它返回 HTML 片段,直接拼接进模板字符串。这里有一个安全注意点:render_embed 输出的内容不经过义,如果图表的标题、系列名来自用户输入,务必先做 HTML 转义,防止脚本注入问题。我自己在做一个内部报表系统时,把用户填的标题直接塞进了图表标题,结果页面渲染异常,排查几小时才发现是特殊字符把属性值截断了。
6. 高频问题排查速查与避坑经验
6.1 版本错乱:pip 列表里出现多个 pyecharts
这是最常遇到的环境问题,特别是电脑里同时装过 0.5.x 和 1.x 的时候,缺的类在 import 时直接报 ModuleNotFoundError。处理方式很直接:
pip uninstall pyecharts -y pip install pyecharts==0.5.11但要注意,卸载 1.x 时它不会删掉缓存目录,pip 有时会解析出旧版本残留。建议在虚拟环境里操作,一旦环境乱了直接删掉重建,比排查依赖树快得多。
6.2 渲染出来是空白页面或图表不显示
空白页面分两种。第一种是整个页面只有标题文字,图表区域空荡荡,这种通常是 JS 没加载成功。第二种是页面有图表轮廓,但坐标轴和柱状条都不显示,这种通常是数据没传进去。
排查方法:打开浏览器开发者工具,看 Console 和 Network。Console 里有红色报错,先把报错截图,绝大多数是 JS 引用失败。Network 里可以看到 HTML 文件本身是否加载了外部资源,如果 script 请求失败,就按离线部署方案处理。数据问题就在业务代码里用 print 看 add 方法收到的数据,确认类型和长度。柱状图两个列表长度不一致时,echarts 通常不会报错,只会少画几个柱子,不要忽略这种静默失败。
6.3 图表上中文变成乱码
中文乱码一般有三个来源。一是 HTML 文件本身的字符集不是 UTF-8,用记事本打开再保存很容易改变编码,建议统一用编辑器保存为 UTF-8。二是模板里没有 charset 声明,不过 0.5.11 默认模板是有的,如果你手动拼接了 HTML 片段,就要自己补<meta charset="utf-8">。三是用 phantomjs 截图时,系统缺少中文字体,导致图片上的中文全是方块,这种情况要在系统里安装中文字体包。
在 Linux 服务器上因为缺字体导致报表截图全是方块,是很多运维同学会遇到的经典问题。解决后要重新生成图片,别用旧缓存。
6.4 数据量大导致页面卡顿
0.5.11 底层依赖的 echarts 版本比较早,数据点超过几千个时交互会明显变卡。我在处理一年多的分钟级监控数据时,直接把两万多个点塞进折线图,结果浏览器直接掉了两帧。
两种处理思路:一是后端先聚合,比如把分钟级数据聚合成小时级或天级;二是在前端开 datazoom,让用户自己选择区间,减少一次性渲染压力。如果你要展示的是长周期趋势,聚合不会损失太多信息,反而更好读。
6.5 饼图小数据项标签挤成一团
当饼图某个分类占比太小,它的 label 会和其他标签挤在一起,特别丑。常见解法是设置 label_pos 为 outside,同时调整 label_line_width,或者把占比低于某个阈值的分类合并成“其他”。后者在 0.5.11 里没有现成参数,需要自己业务代码合并,但这样做出来的图会专业很多。
6.6 add 方法参数名写错却不报错
因为 0.5.11 的 add 方法参数是通过 kwargs 传给底层图表的,写错了不报错,只是对应效果不生效。我常用的排查技巧是调用 show_config 方法:
bar.show_config()它会打印出图表当前保存的配置 JSON,逐一核对你要的参数是否真的生效。这个方法在旧版 pyecharts 调试里比看文档还管用。
结尾
最后讲一个我自己的习惯。接手任何 0.5.11 的旧项目时,我会先把里面所有图表对象的 show_config() 输出收集一遍,统一看一眼数据格式和参数项,这样可以避开很多隐藏的坑点。pyecharts 0.5.11 的 API 虽然旧,但只要把数据格式的约定理清楚,后面写任何图表都是套模板的事。整理模板的时候,我会把柱状图、折线图、饼图、地图这四类最常见图表各存一个函数,输入参数只有数据和标题,返回一个渲染好的图表对象,业务代码瞬间清爽很多。如果你也要长期维护这类项目,尽早做封装,后面会少加很多班。