ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

用Python打造销售数据可视化看板:Pandas+PyEcharts+Flask实战

用Python打造销售数据可视化看板:Pandas+PyEcharts+Flask实战 简介围绕python制作销售数据可视化看板这一实战项目资源面向Python开发者、数据分析爱好者以及需要搭建业务看板的运营人员旨在演示如何借助Pandas、Matplotlib、Seaborn和Plotly/Dash等常用工具完成销售数据的导入、清洗、聚合与可视化分析并最终构建一个具备筛选、缩放等交互能力的动态看板帮助读者快速掌握从原始数据到商业展示的完整流程。压缩包共3个文件包含1个Python主脚本、1个Excel示例数据集和1个依赖说明文本整体仅122KB小巧但结构完整便于直接运行与二次修改。已有1852人学习下载适合作为入门到进阶的练习项目。透过源码与数据读者可以对照学习数据预处理、图表选型、看板布局设计以及交互回调的写法理解如何将静态图表一步步升级为可筛选、可缩放的动态看板还可参考项目中的依赖清单快速复现运行环境并在此基础上替换为自己的销售数据完成个性化看板开发。对希望提升Python数据分析能力和商业报表制作效率的人来说是一份可复现、易扩展的参考实现。1. 销售看板不是画几张图先想清楚为什么用 Python 做销售主管每周一上午催周报我从 ERP 导出订单明细在 Excel 里做透视表画柱状图、折线图、饼图再截图贴进 PPT整个流程一个半小时起步。数据一变下周又得重来一遍。后来我把这套流程拆成了用 Python 制作销售数据可视化看板Pandas 处理订单明细、PyEcharts 生成交互图表、Flask 打包成 Web 页面浏览器打开就是当周的销售额、环比、品类排名和区域分布。这个方案适合销售运营、数据分析师以及所有想用 Python 做数据可视化看板但不想碰前端复杂框架的开发者。跟 Grafana、FineReport 这类商业工具比它的优势是不依赖外部平台数据口径完全自己掌控改一个指标只动一处代码。2. 技术选型为什么是 Pandas PyEcharts Flask2.1 图表库选型PyEcharts 比 Matplotlib 和 Plotly 更适合看板很多人一提到 Python 可视化第一反应是 Matplotlib。但 Matplotlib 出的是静态图做成 Web 看板后没法悬浮看数值、没法缩放和平滑切换交互层面几乎是零。Plotly 交互性不错但离线包体积大和 Flask 配合时序列化逻辑略重对新手不算友好。PyEcharts 是百度 ECharts 的 Python 封装图表类型极全中文文档完善最关键的是它能把图表配置直接序列化成 JSON由前端 ECharts 实例渲染这恰好是 Flask 模板最擅长的活。选型时要注意版本。网上大量教程用的是 pyecharts v2也就是pip install pyecharts默认装到的 2.x 版。但新版 pyecharts v1 改了不少 API比如LabelOpts的导入路径从pyecharts.options变成了pyecharts.commons.commons下的LabelOpts包装add_yaxis的参数名也有调整。我的建议是写项目时直接用 v2 语法并锁定版本pip install pyecharts2.2.0跑通后再决定要不要升。2.2 看板整体架构数据层、计算层、展示层一套能落地的销售看板架构上分三层和写脚本画图有本质区别。第一层是数据层。数据来源通常是 Excel、CSV 或业务数据库。这一层只负责把原始订单明细读进 Pandas DataFrame不做任何业务计算。第二层是计算层。所有销售指标在这一层产出月销售额、订单量、客单价、环比增长率、品类排名、区域占比。计算层输出的是一张或多张干净的汇总表。第三层是展示层。把汇总表里的数据映射到 PyEcharts 图表对象上再把图表对象的配置序列化成 JSON交给 Flask 渲染出的 HTML 模板由前端的 ECharts 库绘制。层级职责核心工具数据层读取订单明细、处理脏数据Pandas read_excel / read_csv计算层聚合统计、指标计算Pandas groupby / agg / pct_change展示层图表生成、页面渲染PyEcharts Flask ECharts JS这个分层最大的价值是隔离。数据源从 Excel 换成数据库只改第一层想新增一个指标只动第二层想换图表样式只改第三层。看板要长期维护这个隔离比代码本身更重要。2.3 销售数据长什么样字段设计与清洗规则做看板之前先搞清楚原始数据长什么样。我在实际项目里最常见的销售订单表字段至少包含下面几项字段名类型说明订单日期日期/字符串下单时间经常是文本格式订单号字符串每个订单唯一品类字符串商品分类大小写混用区域字符串华东、华北、华南等有空值销售额数值订单金额可能存在负值退款成本数值商品成本用于利润计算真实数据永远比理想模型脏。日期列经常是2024-01-05 14:32:00这样的字符串也有可能是2024/1/5销售额里混着退款导致的负值品类字段大小写不统一手机和手机带尾随空格都能被 groupby 拆成两个组区域字段直接是空值。这些坑在第一次跑通流程前就要预防清洗规则先定好后面所有指标才站得住。3. 从数据到图表Pandas 清洗、指标计算与图表配置实战3.1 数据读取与清洗read_excel 和 to_datetime 的关键参数看板的第一步是读数据。用pd.read_excel读订单明细有两个参数一定要用parse_dates和dtype。前者把日期列直接解析成 datetime 类型后者把订单号强制指定为字符串避免长数字被读成科学计数法。import pandas as pd # 读取订单明细日期列解析成 datetime订单号按字符串读入 df pd.read_excel( sales_data.xlsx, parse_dates[订单日期], dtype{订单号: string} ) # 丢弃关键字段为空的行防止后续聚合出现 NaN df df.dropna(subset[销售额, 订单日期]) # 过滤掉退款和异常负值只保留正常销售 df df[df[销售额] 0] # 统一品类字段去掉首尾空格转大写避免同一品类拆成多个组 df[品类] df[品类].str.strip().str.upper() # 生成 YYYY-MM 格式的月份列后续按月聚合用 df[月份] df[订单日期].dt.to_period(M) print(df.head()) print(df.dtypes)这里有几个参数值得展开说。parse_dates接收一个列名列表Pandas 会在读取时自动调用日期解析器比事后用pd.to_datetime再覆盖回去省一步。dtype{订单号: string}用 pandas 2.0 引入的 StringDtype比传统的object类型更规范字符串方法也更高效。dt.to_period(M)生成的是 Period 对象打印出来是2024-01这种格式按月分组时比dt.strftime(%Y-%m)更友好因为它保留了时间顺序信息排序不会乱。3.2 核心指标计算groupby agg 的命名聚合写法销售看板最核心的指标就三个销售额、订单量、客单价外加一个环比。用 Pandas 的命名聚合Named Aggregation可以一次算完不用写多个 groupby。# 按月聚合销售额求和、订单量计数、客单价取均值 monthly df.groupby(月份, as_indexFalse).agg( 销售额(销售额, sum), 订单量(订单号, count), 客单价(销售额, mean) ) # 环比增长率当前月相对上一个月的百分比变化 monthly[环比] monthly[销售额].pct_change() * 100 # 月份是 Period 类型转成字符串方便后续传给图表 monthly[月份] monthly[月份].astype(str) print(monthly)命名聚合的语法是新列名(原始列名, 聚合函数)一次 groupby 同时算出三个指标比groupby().sum()再groupby().count()这种链式调用高效得多。客单价这里用mean计算的是平均每笔订单的销售额严格来说应该是销售额总和除以订单量但在一张明细表里mean(销售额)和sum(销售额)/count(订单号)结果一致。如果订单表里一单有多行明细一个订单拆多个商品那客单价就必须用sum/count的方式单独算否则会算成商品均价口径完全不对。pct_change()计算环比是隐藏技巧它自动用当前行除以上一行再减 1第一行因为没有上一行会得到 NaN后续展示时注意处理。3.3 图表配置折线图、柱状图、饼图的参数细节指标算完进入图表环节。先看月度销售趋势的折线图。from pyecharts import options as opts from pyecharts.charts import Line line ( Line(init_optsopts.InitOpts(width100%, height400px)) .add_xaxis(monthly[月份].tolist()) .add_yaxis( 销售额, monthly[销售额].round(0).tolist(), is_smoothTrue, # 曲线平滑视觉更舒服 label_optsopts.LabelOpts(is_showFalse) # 不显示数值标签避免重叠 ) .set_global_opts( title_optsopts.TitleOpts(title月度销售趋势), tooltip_optsopts.TooltipOpts(triggeraxis), # 悬浮显示整列数值 yaxis_optsopts.AxisOpts(name销售额元), ) )折线图的参数里有三个值得注意。is_smoothTrue把折线变成平滑曲线数据点多时不会显得生硬。LabelOpts(is_showFalse)关闭柱子顶端的数值标签因为月度数据有十几条全显示会糊成一片悬浮看值更干净。TooltipOpts(triggeraxis)设置悬浮提示触发方式为坐标轴鼠标划过时同时显示当月所有系列的数据这是看板类图表的标配。再看品类 TOP10 的柱状图和区域占比的饼图。from pyecharts.charts import Bar, Pie # 品类销售额 Top10 category_sales ( df.groupby(品类)[销售额] .sum() .nlargest(10) # 取销售额最大的10个品类 .sort_values() # 重新升序排列让柱状图从下往上增长 ) bar ( Bar(init_optsopts.InitOpts(width100%, height400px)) .add_xaxis(category_sales.index.tolist()) .add_yaxis( 销售额, category_sales.values.round(0).tolist(), label_optsopts.LabelOpts(is_showFalse) ) .set_global_opts( title_optsopts.TitleOpts(title品类销售额 TOP10), xaxis_optsopts.AxisOpts(axislabel_optsopts.LabelOpts(rotate30)), yaxis_optsopts.AxisOpts(name销售额元), ) ) # 区域销售占比 region_sales df.groupby(区域)[销售额].sum() pie ( Pie(init_optsopts.InitOpts(width100%, height400px)) .add( , [list(z) for z in zip(region_sales.index.tolist(), region_sales.tolist())], radius[35%, 60%], # 环形饼图内径35%外径60% label_optsopts.LabelOpts(formatter{b}: {d}%) # 显示区域名和占比 ) .set_global_opts( title_optsopts.TitleOpts(title区域销售占比), ) )柱状图里的nlargest(10)取前十大品类再用sort_values()升序排列这样 ECharts 绘制时柱状图会从下往上增长视觉效果更符合阅读习惯。rotate30是品类名太长时的常见处理旋转 30 度避免字体重叠。饼图用radius[35%, 60%]配置成环形中间留白可以放总销售额标注formatter{b}: {d}%是 ECharts 的模板字符串{b}是区域名{d}是百分比悬浮和标签直接显示占比不需要额外计算。3.4 Flask 整合把图表 JSON 变成 Web 页面图表对象生成后把配置序列化成 JSON传给 Flask 模板。这一步是看板能否从脚本变成 Web 应用的关键。from flask import Flask, render_template import json app Flask(__name__) app.route(/) def dashboard(): # 图表配置序列化成 JSON 字符串 line_json line.dump_options_with_quotes() bar_json bar.dump_options_with_quotes() pie_json pie.dump_options_with_quotes() return render_template( dashboard.html, line_jsonline_json, bar_jsonbar_json, pie_jsonpie_json ) if __name__ __main__: # 监听所有网卡端口8000方便局域网内其他同事访问 app.run(host0.0.0.0, port8000, debugFalse)对应的templates/dashboard.html模板核心部分!DOCTYPE html html langzh-CN head meta charsetUTF-8 title销售数据看板/title script srchttps://cdn.jsdelivr.net/npm/echarts5/dist/echarts.min.js/script /head body div idline stylewidth:100%;height:400px;/div div idbar stylewidth:100%;height:400px;/div div idpie stylewidth:100%;height:400px;/div script var lineChart echarts.init(document.getElementById(line)); lineChart.setOption({{ line_json | safe }}); var barChart echarts.init(document.getElementById(bar)); barChart.setOption({{ bar_json | safe }}); var pieChart echarts.init(document.getElementById(pie)); pieChart.setOption({{ pie_json | safe }}); /script /body /html这里用dump_options_with_quotes()而不是dump_options()原因是后者生成的 JSON 里键值不带引号直接放进 JavaScript 会在低版本浏览器里报错。Flask 的 Jinja2 模板里必须加| safe过滤器否则模板引擎会把 JSON 字符串里的引号转义成实体字符前端拿到就是语法错误。图表初始化和setOption是 ECharts 的标准用法每个 div 对应一个图表实例宽度高度由 div 的 style 控制。4. 常见问题与避坑指南五个翻车现场还原4.1 图表区域空白浏览器控制台报 ECharts is not defined现象页面打开后三个图表区域全是空白按 F12 打开控制台报错ECharts is not defined。原因CDN 的 echarts.min.js 没有被加载进来。要么是内网环境访问不了 jsdelivr 等公共 CDN要么是网络波动导致脚本下载失败。脚本加载失败后echarts这个全局变量不存在echarts.init自然执行不了。解决把 echarts.min.js 下载到本地放在项目的static/js/目录下模板里改用本地引用script src{{ url_for(static, filenamejs/echarts.min.js) }}/script我一般会把 echarts.min.js 随项目一起维护不依赖外网内网部署也不受影响。4.2 图表标题和图例中文全部变成方块现象图表能显示但标题、图例、悬浮提示里的中文全是方块或乱码。原因HTML 页面缺少字符集声明或者 PyEcharts 生成的 JSON 里中文字符被模板转义。更隐蔽的情况是读取的 Excel 数据本身就是乱码比如用pd.read_csv读 GBK 编码的文件没指定encoding。解决模板 head 里加meta charsetUTF-8这是最容易漏的一步。读 CSV 时显式指定编码df pd.read_csv(sales_data.csv, encodinggbk)4.3 按月份分组后顺序全乱现象月度折线图的 X 轴顺序是 2024-01、2024-10、2024-02、2024-11完全乱序。原因月份列被转成了字符串groupby 按字典序排序2024-10排在2024-02前面。如果之前用df[月份] df[订单日期].dt.to_period(M)生成的是 Period 类型groupby 后顺序是对的但一旦astype(str)再排序就会乱。解决先按月排序再转字符串monthly monthly.sort_values(月份) # 此时月份还是 Period 类型按时间顺序排 monthly[月份] monthly[月份].astype(str)或者干脆在图表传入 X 轴之前用pd.Categorical指定顺序。4.4 数据量大时页面加载卡死现象订单明细几十万行按以上方式直接把月销售额传给折线图浏览器打开页面要等十几秒滚动和悬浮都卡。原因前端一次性渲染了太多数据点。几十万行明细如果没做聚合就传给图表ECharts 要处理十万量级的坐标点canvas 扛不住。另一个常见情况是 X 轴是按天粒度传了几百个点图表本身没问题但配合折线平滑计算就会变慢。解决在数据层就完成聚合只把汇总结果传给前端。按天数据可以先聚合成周或月必须保留明细粒度的场景用datazoom让图表只渲染可视区域。最直接的方案是在groupby里把订单日期dt.to_period(D)聚合成天再在图表配置里加 datazoom 组件控制缩放。4.5 pyecharts 新老版本 API 报错现象照着网上教程写from pyecharts import options as opts运行时报ImportError或者LabelOpts的is_show参数报错unexpected keyword argument。原因网上大量教程是 pyecharts v22.x的写法但新装了 pyecharts v11.x之后部分类和参数的导入路径、命名都变了。v1 是重写版本官方文档也更新了但存量教程还停留在 v2照着抄必然翻车。解决项目里用虚拟环境锁版本pip install pyecharts2.2.0。如果已经在用 v1就把add_yaxis里的label_opts换成 v1 的LabelOpts写法并检查import路径。我的习惯是每个看板项目独立建虚拟环境版本锁定写在 requirements.txt 里避免不同项目互相污染。5. 让看板真正落地定时刷新、参数筛选与数据核对看板做出来只是第一步能稳定每天自动更新才算落地。我常用 APScheduler 的 BackgroundScheduler 做定时任务每天早上数据同步完成后自动重算指标、重新生成图表 JSON。from apscheduler.schedulers.background import BackgroundScheduler def refresh_dashboard(): 重新读取数据、重算指标、更新全局图表 JSON global line_json, bar_json, pie_json df load_and_clean() # 第一步读取并清洗 monthly calc_monthly_metrics(df) # 第二步计算月度指标 category_top10 calc_category_top10(df) # 品类TOP10 region_dist calc_region_dist(df) # 区域分布 line_json build_line(monthly).dump_options_with_quotes() bar_json build_bar(category_top10).dump_options_with_quotes() pie_json build_pie(region_dist).dump_options_with_quotes() print(看板数据已刷新:, datetime.now()) # 每个工作日早上 8:30 刷新一次 scheduler BackgroundScheduler() scheduler.add_job(refresh_dashboard, cron, day_of_weekmon-fri, hour8, minute30) scheduler.start()参数筛选也值得做。Flask 的request.args.get(region)可以接收 URL 查询参数比如http://localhost:8000/?region华东后端按区域过滤数据再生成图表这样销售总监只看自己大区的数据不用开全量看板。注意过滤要在load_and_clean()之后、指标计算之前保证清洗逻辑只跑一遍。数据核对是每次交付前最容易被跳过的一步。我的习惯是月末对账时把看板上的月销售额和 ERP 系统导出数、财务月报数三方交叉验证。看板只是一个工具数字口径错了反而误导决策。从那以后我每次交付看板前都会强制走一遍核对清单先和 SQL 汇总数对总数再随机抽两天的明细订单人工核对金额确认无误才发布。这套流程救了我好几次希望帮到你。本文还有配套的精品资源点击获取
返回列表