Python pyecharts 实战:手把手教你绘制专业帕累托图
2026/9/12 19:13:55 网站建设 项目流程

1. 从数据报表到决策洞察:为什么我们需要帕累托图?

如果你经常和数据打交道,无论是产品运营、质量分析还是项目管理,大概率都听过“二八法则”——80%的问题往往由20%的原因造成,80%的销售额可能来自20%的客户。这个道理听起来简单,但如何在一堆杂乱的数据中,直观地找出那关键的“20%”,并说服你的同事或老板呢?这时候,帕累托图(Pareto Chart)就派上用场了。

帕累托图本质上是一个组合图表:它将柱状图和折线图叠加在一起。柱状图按降序排列,展示各个类别的频数或金额;折线图则是这些类别的累积百分比。图表上通常会画一条80%的参考线。一眼望去,你就能看到哪些类别是“头部问题”,它们累积贡献了多少比例。这比单纯看一个排序的表格要直观得多,因为它把“重要性排序”和“累积影响”两个维度同时呈现了出来,是进行问题优先级排序、资源聚焦分配的绝佳可视化工具。

过去,制作这样的专业图表可能需要依赖Excel的复杂操作,或者更专业的BI工具。但对于数据分析师和开发者来说,如果能在Python的数据分析流程中,直接生成高质量、可交互的帕累托图,无疑会极大提升报告效率和自动化水平。pyecharts这个基于ECharts的Python可视化库,以其丰富的图表类型、优雅的交互效果和灵活的配置选项,成为了一个非常不错的选择。它生成的图表是网页格式,可以无缝嵌入到Web应用或离线报告中,视觉效果和专业度都相当出色。

今天,我就结合一次实际的数据分析需求,来手把手带你用pyecharts从零绘制一个专业的帕累托图。我们会从数据模拟与准备开始,一步步拆解图表构建的每一个环节,并深入探讨那些官方文档可能不会细说,但在实际应用中至关重要的细节和“坑点”。

2. 环境准备与数据模拟:构建一个贴近实战的分析场景

在开始画图之前,我们得先把“舞台”搭好。这里包括两件事:确保你的Python环境里有必要的工具,以及准备一份用于演示的、贴近真实业务逻辑的数据。

2.1 安装与导入核心库

首先,你需要安装pyecharts。我强烈建议使用最新版本(本文基于1.x版本,其API设计与早期版本有较大不同,更清晰易用)。通过pip安装非常简单:

pip install pyecharts

如果你需要将图表保存为静态图片(比如PNG格式)以便插入PPT或Word,还需要安装额外的渲染器。pyecharts本身主要生成HTML,保存为图片需要依赖seleniumphantomjs,配置起来稍显麻烦。一个更便捷的选择是安装pyecharts-snapshot插件,并配合phantomjs

pip install pyecharts-snapshot # 然后需要下载 phantomjs 的可执行文件,并将其路径加入系统环境变量。

不过,对于大多数分析报告场景,生成一个独立的、可交互的HTML文件已经足够,我们本次就以生成HTML为例。

接下来,在Python脚本或Jupyter Notebook中,我们需要导入必要的模块:

import pandas as pd import numpy as np from pyecharts import options as opts from pyecharts.charts import Bar, Line from pyecharts.commons.utils import JsCode

这里解释一下为什么这样导入:

  • pandasnumpy是数据处理的基础,我们用来构造和整理数据。
  • pyecharts.charts导入BarLine,因为帕累托图是两者的组合。
  • opts模块包含了所有图表配置项,用于精细化控制图表的每一个视觉元素。
  • JsCode是一个非常有用的工具,它允许我们注入自定义的JavaScript代码到图表中,实现一些高级的、预设配置无法满足的效果,后面我们会用到它来格式化坐标轴标签。

2.2. 模拟一份产品缺陷分析数据

为了演示,我们虚构一个场景:某App上线后,通过用户反馈和测试,收集到了一周内各类缺陷的报错次数。我们的目标是找出最影响用户体验的“关键少数”缺陷类型。

# 模拟数据:缺陷类型及其发生次数 data = { ‘缺陷类型‘: [‘界面卡顿‘, ‘支付失败‘, ‘图片加载慢‘, ‘消息推送延迟‘, ‘注册流程复杂‘, ‘视频播放卡顿‘, ‘搜索无结果‘, ‘账号登录异常‘, ‘字体显示模糊‘, ‘夜间模式闪退‘], ‘发生次数‘: [156, 89, 72, 64, 58, 47, 35, 28, 19, 12] } # 转换为DataFrame df = pd.DataFrame(data) # 按‘发生次数‘降序排列,这是绘制帕累托图的第一步 df_sorted = df.sort_values(by=‘发生次数‘, ascending=False).reset_index(drop=True) print(df_sorted)

运行后,你会得到一份已经排序好的数据:

缺陷类型 发生次数 0 界面卡顿 156 1 支付失败 89 2 图片加载慢 72 3 消息推送延迟 64 4 注册流程复杂 58 5 视频播放卡顿 47 6 搜索无结果 35 7 账号登录异常 28 8 字体显示模糊 19 9 夜间模式闪退 12

关键点解析:为什么必须先排序?帕累托图的柱状图部分要求数据按照度量值(这里是发生次数)从大到小排列,这是其核心逻辑——让最重要的项排在左边。如果数据是乱序的,那么累积百分比折线将失去意义,无法清晰展示“少数关键因素”的累积效应。

2.3. 计算累积百分比

这是绘制折线图的数据基础。累积百分比表示:到当前缺陷类型为止,所有缺陷发生次数占总次数的比例。

# 计算总次数 total = df_sorted[‘发生次数‘].sum() # 计算累积次数 df_sorted[‘累积次数‘] = df_sorted[‘发生次数‘].cumsum() # 计算累积百分比 df_sorted[‘累积百分比‘] = df_sorted[‘累积次数‘] / total * 100 # 格式化一下,保留两位小数,方便查看 df_sorted[‘累积百分比‘] = df_sorted[‘累积百分比‘].round(2) print(df_sorted[[‘缺陷类型‘, ‘发生次数‘, ‘累积百分比‘]])

现在,数据准备就绪了。我们有了类别(缺陷类型)、柱状图数据(发生次数)和折线图数据(累积百分比)。接下来进入核心的图表构建环节。

3. 核心图表构建:组合Bar与Line的技法与陷阱

pyecharts绘制组合图表的精髓在于使用overlap方法,或者在一个Grid布局中叠加。对于帕累托图这种X轴完全共享的图表,使用overlap更为简单直接。我们的思路是:先绘制柱状图(Bar),再在其基础上叠加折线图(Line)。

3.1. 绘制基础柱状图

首先,我们初始化一个柱状图对象,并设置基本的X轴和Y轴数据。

# 初始化柱状图 bar = ( Bar(init_opts=opts.InitOpts(width=“900px“, height=“500px“)) # 设置画布大小 .add_xaxis(df_sorted[‘缺陷类型‘].tolist()) # X轴:缺陷类型 .add_yaxis( series_name=“发生次数“, # 系列名称,会显示在图例中 y_axis=df_sorted[‘发生次数‘].tolist(), # Y轴:发生次数 category_gap=“50%“, # 柱子之间的间隙,设为50%看起来比较舒适 color=“#5470c6“, # 自定义柱子颜色,ECharts经典蓝色 ) .set_global_opts( title_opts=opts.TitleOpts(title=“产品缺陷帕累托分析“, subtitle=“基于发生次数的优先级排序“), tooltip_opts=opts.TooltipOpts( trigger=“axis“, # 触发方式:坐标轴触发,鼠标移到轴上任意位置都会显示提示框 axis_pointer_type=“shadow“ # 坐标轴指示器类型为阴影,效果更好看 ), ) )

经验之谈一:关于category_gap。这个参数控制同一系列中不同柱子之间的间距。如果设置得太小(比如“0%”),柱子会挤在一起,影响阅读;设置得太大,又会显得稀疏。对于帕累托图这种需要清晰区分每个类别的图表,“40%”到“60%”是一个比较合适的范围。你可以根据类别的多少进行调整。

经验之谈二:关于tooltiptrigger。这里设置为“axis“有一个巨大好处:当鼠标在图表上横向移动时,会有一条竖线跟随,并同时显示该X坐标位置下所有系列(柱子和折线)的具体数值。这对于对比某个类别下的原始次数和累积百分比非常方便。如果设为默认的“item“,则只会显示鼠标悬停的那个具体图形元素(如单个柱子)的信息。

3.2. 绘制累积百分比折线图

接下来,我们在同一个坐标系上添加折线图。这里有几个关键配置:

  1. Y轴对齐:折线图的数据(百分比)和柱状图数据(次数)量纲不同,必须使用独立的Y轴。
  2. 折线样式:需要将其与柱状图明显区分,通常使用红色,并加上数据点标记。
  3. 标签格式化:我们希望折线图的数据点标签显示为“xx%”的格式。
# 初始化折线图 line = ( Line() .add_xaxis(df_sorted[‘缺陷类型‘].tolist()) # 共享同一个X轴 .add_yaxis( series_name=“累积百分比“, y_axis=df_sorted[‘累积百分比‘].tolist(), yaxis_index=1, # 关键!指定使用第二个Y轴(索引为1) color=“#d14a61“, # 红色,与蓝色柱子形成对比 label_opts=opts.LabelOpts( is_show=True, # 显示数据标签 formatter=JsCode(“function(params){return params.value + ‘%‘;}“) # 使用JS代码格式化标签为百分比 ), linestyle_opts=opts.LineStyleOpts(width=3), # 设置线宽 symbol=“circle“, # 数据点标记为圆形 symbol_size=8, # 标记大小 ) )

核心细节解析:yaxis_index=1。这是实现双Y轴的核心。在pyecharts中,第一个Y轴(索引0)是默认的、为柱状图服务的左侧Y轴。通过设置yaxis_index=1,我们告诉折线图:“你的数据应该参考第二个Y轴来绘制。” 接下来,我们就需要创建并配置这第二个Y轴。

3.3. 组合图表与配置双Y轴

现在,将折线图叠加到柱状图上,并完成全局的坐标轴配置。

# 将折线图叠加到柱状图上 pareto_chart = bar.overlap(line) # 配置全局选项,重点是第二个Y轴 pareto_chart.set_global_opts( # 配置第一个Y轴(左侧,用于柱状图) yaxis_opts=opts.AxisOpts( name=“发生次数“, type_=“value“, axislabel_opts=opts.LabelOpts(formatter=“{value} 次“), # 格式化标签 ), # 配置第二个Y轴(右侧,用于折线图) yaxis_opts=opts.AxisOpts( name=“累积百分比“, type_=“value“, position=“right“, # 位置在右侧 axislabel_opts=opts.LabelOpts(formatter=“{value}%“), # 格式化标签为百分比 max_=100, # 最大值固定为100% interval=20, # 刻度间隔为20% splitline_opts=opts.SplitLineOpts( # 设置网格线,便于观察 is_show=True, linestyle_opts=opts.LineStyleOpts(type_=“dashed“, opacity=0.5) ), ), # 配置X轴 xaxis_opts=opts.AxisOpts( axislabel_opts=opts.LabelOpts(rotate=-30), # 标签旋转-30度,防止重叠 name=“缺陷类型“, ), # 图例配置 legend_opts=opts.LegendOpts(pos_top=“10%“), # 将图例放在顶部,避免遮挡标题 # 工具箱,提供保存图片等功能 toolbox_opts=opts.ToolboxOpts( is_show=True, feature={ “saveAsImage“: {}, # 保存为图片 “dataView“: {“readOnly“: True}, # 数据视图 } ), )

踩坑记录:Y轴配置的覆盖问题set_global_opts中的yaxis_opts默认只配置第一个Y轴。当你需要配置第二个Y轴时,必须像上面代码一样,传入一个opts.AxisOpts列表。第一个元素对应索引0的Y轴,第二个元素对应索引1的Y轴。如果只传一个配置对象,那么第二个Y轴会使用默认设置,可能导致刻度不对齐、标签格式错误等问题。

实用技巧:max_=100interval=20。对于累积百分比折线,将其Y轴最大值固定为100%,并以20%为间隔划分网格线,这是一个行业惯例。它使得80%的参考线(我们下一步会添加)能清晰地落在网格上,让图表更易读。

4. 画龙点睛:添加80%参考线与高级视觉优化

一个专业的帕累托图,80%参考线几乎是标配。这条线能帮助读者快速定位“关键少数”的边界。在pyecharts中,我们可以通过MarkLine组件轻松实现。

4.1. 为折线图添加80%参考线

我们直接在折线图系列上添加一条贯穿整个图表的水平标记线。

# 在之前的line配置中,扩展add_yaxis的配置,加入markline line = ( Line() .add_xaxis(df_sorted[‘缺陷类型‘].tolist()) .add_yaxis( series_name=“累积百分比“, y_axis=df_sorted[‘累积百分比‘].tolist(), yaxis_index=1, color=“#d14a61“, label_opts=opts.LabelOpts(is_show=True, formatter=JsCode(“function(params){return params.value + ‘%‘;}“)), linestyle_opts=opts.LineStyleOpts(width=3), symbol=“circle“, symbol_size=8, # 添加标记线 markline_opts=opts.MarkLineOpts( data=[opts.MarkLineItem(y=80, name=“80%参考线“)], # 在Y轴值为80的位置画线 linestyle_opts=opts.LineStyleOpts(type_=“dashed“, color=“#999“, width=2), # 虚线,灰色 label_opts=opts.LabelOpts( # 标签配置 position=“insideEndTop“, # 标签在线段内部末端顶部 formatter=“{b}“, # 显示数据项的名称,即‘80%参考线‘ color=“#333“ ) ) ) )

重要提示MarkLine是添加在Line系列的add_yaxis方法里的,而不是在全局配置中。这意味着这条线是折线图的一部分,其Y坐标值遵循折线图所在的Y轴(即我们设置的第二个Y轴,索引1)。因此,y=80表示的是80%的位置,完全正确。

4.2. 优化数据标签与提示框

为了让图表信息更清晰,我们可以进一步优化提示框(Tooltip)的显示内容。默认的提示框可能会同时显示“发生次数”和“累积百分比”,但格式是数字。我们可以自定义其格式,使其更友好。

# 更新全局配置中的tooltip_opts pareto_chart.set_global_opts( # ... 保留之前所有的yaxis_opts, xaxis_opts等配置 ... tooltip_opts=opts.TooltipOpts( trigger=“axis“, axis_pointer_type=“shadow“, # 自定义格式化函数 formatter=JsCode(“““ function (params) { let result = params[0].name + ‘<br/>‘; for (let i=0; i<params.length; i++) { if (params[i].seriesName === ‘发生次数‘) { result += params[i].marker + params[i].seriesName + ‘: ‘ + params[i].value + ‘ 次<br/>‘; } else if (params[i].seriesName === ‘累积百分比‘) { result += params[i].marker + params[i].seriesName + ‘: ‘ + params[i].value.toFixed(1) + ‘%<br/>‘; } } return result; } “““) ), )

这段JavaScript代码做了以下几件事:

  1. 获取当前X轴坐标(即缺陷类型)作为标题。
  2. 遍历所有系列(params数组,包含柱子和折线的数据点)。
  3. 根据系列名称,分别格式化其值:次数后面加“次”,百分比保留一位小数并加“%”。
  4. 使用<br/>进行换行,使显示更整齐。

4.3. 渲染与保存图表

最后,将图表渲染为HTML文件,这样就可以在浏览器中打开并交互了。

# 渲染图表到HTML文件 pareto_chart.render(“defect_pareto_analysis.html“)

打开生成的defect_pareto_analysis.html文件,你将看到一个完整的、交互式的帕累托图。鼠标悬停可以查看详细数据,可以点击图例隐藏/显示某个系列,还可以利用工具箱保存为图片。

5. 结果解读与实战进阶:从图表到行动方案

现在,让我们来解读一下我们亲手绘制的这张帕累托图。

直观观察:柱状图从左到右依次降低,清晰地展示了“界面卡顿”是发生次数最多的缺陷。折线图快速上升,在前几个类别就达到了很高的累积百分比。

关键分析:找到累积百分比折线首次穿过80%参考线的点。从我们的模拟数据图上看,这个点大约在“注册流程复杂”和“视频播放卡顿”之间。这意味着,排名前五的缺陷(界面卡顿、支付失败、图片加载慢、消息推送延迟、注册流程复杂)其累积发生次数已经超过了总次数的80%。这五类缺陷就是我们应当优先投入资源进行修复的“关键少数”。

行动建议

  1. 立即聚焦:开发团队应优先解决“界面卡顿”和“支付失败”这两个头部问题,它们贡献了最大的问题量。
  2. 制定计划:针对前五类缺陷,制定详细的排查和修复计划。
  3. 持续监控:在修复之后,应重新收集数据,绘制新的帕累托图,以验证修复效果,并发现新的“关键少数”。

5.1. 常见问题与进阶处理

在实际项目中,你可能会遇到一些特殊情况,需要调整我们的基础代码:

情况一:数据类别过多,X轴标签重叠当缺陷类型超过15个时,即使旋转标签也会显得拥挤。解决方案是使用X轴的axislabel间隔显示,或者将过长的类别名截断。

xaxis_opts=opts.AxisOpts( axislabel_opts=opts.LabelOpts( rotate=-45, interval=0, # 0表示显示所有标签,可以设为1隔一个显示一个 formatter=JsCode(“““ function(value) { // 如果标签名太长,截取前6个字符并加... if (value.length > 6) { return value.substr(0, 6) + ‘...‘; } else { return value; } } “““) ), )

情况二:数据量纲差异巨大如果“发生次数”的数值很大(如几万),而“累积百分比”是小数,会导致柱状图几乎顶到天花板,折线图却紧贴底部。这时需要对双Y轴的刻度进行更精细的配置,确保两个系列都能在图表中清晰展示。通常,pyecharts会自动计算合适的刻度范围,但如果自动效果不佳,可以手动指定min_max_参数。

情况三:需要动态数据或集成到Web应用pyecharts生成的HTML文件是独立的。如果你需要将图表集成到Flask、Django等Web框架中,可以使用render_embed()方法生成不包含完整HTML结构的图表组件,然后将其嵌入到你的模板中。对于动态数据,你需要在后端准备好数据,调用相同的绘图逻辑生成图表配置,然后传递给前端渲染。

5.2. 封装成可复用的函数

为了提高效率,我们可以将整个绘图过程封装成一个函数,以后只需传入数据和少量参数即可生成帕累托图。

def create_pareto_chart(categories, values, title=“帕累托图“, xaxis_name=“类别“, yaxis_name=“数值“): “““ 创建帕累托图 Args: categories: list, 类别列表 values: list, 数值列表,与categories一一对应 title: str, 图表标题 xaxis_name: str, X轴名称 yaxis_name: str, 主Y轴(柱状图)名称 Returns: pyecharts.charts.composite_charts.Overlap: 组合图表对象 “““ # 1. 数据准备 df = pd.DataFrame({‘category‘: categories, ‘value‘: values}) df_sorted = df.sort_values(by=‘value‘, ascending=False).reset_index(drop=True) total = df_sorted[‘value‘].sum() df_sorted[‘cum_pct‘] = (df_sorted[‘value‘].cumsum() / total * 100).round(2) # 2. 绘制柱状图 bar = ( Bar(init_opts=opts.InitOpts(width=“1000px“, height=“600px“)) .add_xaxis(df_sorted[‘category‘].tolist()) .add_yaxis(yaxis_name, df_sorted[‘value‘].tolist(), color=“#5470c6“, gap=“50%“) .set_global_opts( title_opts=opts.TitleOpts(title=title), xaxis_opts=opts.AxisOpts(name=xaxis_name, axislabel_opts=opts.LabelOpts(rotate=-30)), yaxis_opts=opts.AxisOpts(name=yaxis_name), tooltip_opts=opts.TooltipOpts(trigger=“axis“, axis_pointer_type=“shadow“), ) ) # 3. 绘制折线图 line = ( Line() .add_xaxis(df_sorted[‘category‘].tolist()) .add_yaxis( “累积百分比“, df_sorted[‘cum_pct‘].tolist(), yaxis_index=1, color=“#d14a61“, label_opts=opts.LabelOpts(is_show=True, formatter=JsCode(“function(p){return p.value+‘%‘;}“)), markline_opts=opts.MarkLineOpts( data=[opts.MarkLineItem(y=80, name=“80%线“)], linestyle_opts=opts.LineStyleOpts(type_=“dashed“, color=“#999“) ) ) ) # 4. 组合与最终配置 chart = bar.overlap(line) chart.set_global_opts( yaxis_opts=[ opts.AxisOpts(name=yaxis_name), opts.AxisOpts( name=“累积百分比“, position=“right“, max_=100, interval=20, axislabel_opts=opts.LabelOpts(formatter=“{value}%“), ) ], legend_opts=opts.LegendOpts(pos_top=“10%“), toolbox_opts=opts.ToolboxOpts(is_show=True), ) return chart # 使用函数 categories = df[‘缺陷类型‘].tolist() values = df[‘发生次数‘].tolist() chart = create_pareto_chart(categories, values, “产品缺陷帕累托分析“, “缺陷类型“, “发生次数“) chart.render(“pareto_by_function.html“)

通过这样的封装,你可以轻松地将帕累托分析应用到销售数据(客户贡献分析)、库存数据(SKU价值分析)、客服数据(问题分类分析)等任何符合“二八分布”的场景中。图表不仅是一种展示工具,更是驱动决策的视觉语言。掌握用代码灵活生成它的能力,能让你的数据分析报告更具说服力和专业性。

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

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

立即咨询