第8章 8.5 综合实战:可视化数据大屏
「上一章我们学会了用 Canvas 画布配合 ECharts,把折线图、柱状图、饼图都跑起来了。这一章我们要把它组合起来,做一个能让你在老板面前装 X 的完整数据大屏——就跟那种科幻电影里指挥中心的大屏幕一样酷。」
🎯 开场 3 分钟:为什么要学这个?
你有没有见过那种「数据大屏」?就是新闻里经常出现的,领导人盯着背后那块大屏幕,上面密密麻麻全是图表、数字、地图。智慧城市大屏、双十一销售大屏、工厂车间监控大屏……
痛点 1:你以为这种大屏都是专业团队用很贵的商业软件做的,其实用 Python + ECharts 半小时就能撸一个原型。
痛点 2:网上教程都是单独讲图表怎么画,没有一篇文章教你怎么把多个图表组合成一个完整大屏。
学完本文你能:用 Python 的 pyecharts 库做出一个多图表组合的数据大屏,支持折线图、柱状图、饼图、地图等多种图表类型,还能导出成 HTML 文件直接用浏览器打开。
🧱 基础 25 分钟:核心概念(小白视角\n\n
\n\n
\n\n)
8.5.1 什么是数据大屏?
生活类比:数据大屏就像是你家的「家庭仪表盘」——冰箱上贴的全家日程表、手机上的健康数据汇总。它不是给你看单一数据,而是把多个相关数据放在一起,让你一眼就能看清全貌。
为什么要用:当你有 5 个图表要同时看的时候,一个个大屏外分开,脑子里要做信息整合很累。大屏就是把多个图表「拼」在一起,就像拼图一样,让相关数据互相邻居,方便对比和发现规律。
怎么用(最简代码):
from pyecharts.charts import Bar, Line, Pie
from pyecharts import options as opts
# 创建一个柱状图
bar = Bar()
bar.add_xaxis(["一月", "二月", "三月"])
bar.add_yaxis("销售额", [120, 200, 150])
# 保存成 HTML 文件,用浏览器打开就是"大屏"
bar.render("my_dashboard.html")
这 5 行代码生成了一个柱状图,保存为 HTML。双击用浏览器打开,你就拥有了一个「数据网页」。
8.5.2 页面布局:大屏的「户型图」
生活类比:大屏不是把图表随便往上一摆就完事了,得像装修一样规划布局。常见的布局有「上下布局」(上面大图下面小图)、「左右布局」(左边一个占一半,右边两个摞起来)、「网格布局」(像九宫格一样均匀分布)。
为什么要用:布局乱的大屏看起来像杂货铺,布局好的大屏看起来像指挥中心。合理布局能让你看数据的时候「视线有引导」,先看重点再看细节。
怎么用(最简代码):
from pyecharts.charts import Grid, Bar, Line
# 先创建两个图表
bar = Bar()
bar.add_xaxis(["北京", "上海", "广州", "深圳"])
bar.add_yaxis("人口(万)", [2154, 2428, 1530, 1253])
line = Line()
line.add_xaxis(["2018", "2019", "2020", "2021"])
line.add_yaxis("GDP增长率(%)", [6.6, 6.1, 2.3, 8.1])
# Grid 是网格布局,init_opts 指定页面大小
grid = Grid(init_opts=opts.InitOpts(width="1200px", height="600px"))
# add_schema 调整 xaxis_index 和 yaxis_index 可以控制位置
grid.add(bar, grid_opts=opts.GridOpts(pos_left="10%", pos_right="50%", pos_top="50%", pos_bottom="50%"))
grid.add(line, grid_opts=opts.GridOpts(pos_left="55%", pos_right="5%", pos_top="50%", pos_bottom="50%"))
grid.render("layout_demo.html")
8.5.3 数据格式:图表的「食材」
生活类比:如果你把图表比作一道菜,数据就是食材。做番茄炒蛋需要番茄和鸡蛋,做柱状图需要「分类标签」和「数值」。pyecharts 对数据格式有严格要求,格式不对就像用面粉做火锅——不是不行,但很别扭。
为什么要用:新手最常犯的错误就是数据格式不对,导致图表画不出来。搞清楚数据格式,就搞定了大屏一半的问题。
怎么用(最简代码):
# 柱状图的数据格式:[ ["分类1", 数值1], ["分类2", 数值2], ... ]
bar_data = [
["北京", 2154],
["上海", 2428],
["广州", 1530],
["深圳", 1253]
]
# 饼图的数据格式:[ ["分类", 数值], ... ]
pie_data = [
["手机", 45],
["电脑", 30],
["平板", 15],
["其他", 10]
]
# 折线图的数据格式:x轴是一个列表,y轴是一个列表
x_data = ["2018", "2019", "2020", "2021"]
y_data = [6.6, 6.1, 2.3, 8.1]
8.5.4 主题风格:让大屏更「专业」
生活类比:同样的食材,炒出来可以是小餐馆的味道,也可以是米其林餐厅的味道。图表的「主题」就是调味料,决定了你的大屏是「朴实无华」还是「bling bling」。
为什么要用:大屏通常是给人看的,总得体面点吧?选个好看的主题,瞬间提升档次。
怎么用(最简代码):
from pyecharts.globals import ThemeType
# 内置主题有很多:light, dark, purple, vanilla, roma, wonderland, chimei ...
bar = Bar(init_opts=opts.InitOpts(theme=ThemeType.DARK))
bar.add_xaxis(["一月", "二月", "三月"])
bar.add_yaxis("销售额", [120, 200, 150])
bar.render("dark_dashboard.html")
ThemeType.DARK 是大屏最常用的主题,黑色背景配彩色图表,看起来就像真的指挥中心。
🔥 实战 35 分钟:3 个递进的小项目
项目 1(5 分钟):天气数据看板
场景:你想做一个「最近 7 天天气趋势看板」,展示每天的最高温度和最低温度。
完整可运行代码:
from pyecharts import options as opts
from pyecharts.charts import Line
# 模拟一周的天气数据
days = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
high_temp = [28, 29, 27, 31, 30, 32, 29] # 最高温
low_temp = [22, 23, 21, 25, 24, 26, 23] # 最低温
line = Line(init_opts=opts.InitOpts(width="800px", height="400px"))
line.add_xaxis(days)
# add_yaxis 的series_name是图例名称,y_axis是数据
line.add_yaxis("最高温(°C)", high_temp, linestyle_opts=opts.LineStyleOpts(width=3))
line.add_yaxis("最低温(°C)", low_temp, linestyle_opts=opts.LineStyleOpts(width=3))
# 设置全局配置项
line.set_global_opts(
title_opts=opts.TitleOpts(title="未来7天天气预报", subtitle="仅供参考~"),
tooltip_opts=opts.TooltipOpts(trigger="axis"), # 鼠标悬停显示数值
xaxis_opts=opts.AxisOpts(name="星期"),
yaxis_opts=opts.AxisOpts(name="温度(°C)")
)
line.render("weather_dashboard.html")
print("天气看板已生成:weather_dashboard.html")
预期输出:运行后在当前文件夹生成一个 HTML 文件,用浏览器打开是一条折线图,有两条线(最高温/最低温),标题是「未来7天天气预报」。
一句话解释:add_yaxis 的 linestyle_opts 控制线条粗细,set_global_opts 设置标题、提示框等全局配置。
项目 2(15 分钟):公司销售数据大屏
场景:你是公司运营,需要给老板看季度销售报告。数据从 CSV 文件读取,要展示:1)每月销售额柱状图,2)各产品占比饼图,3)季度趋势折线图。
完整可运行代码:
from pyecharts import options as opts
from pyecharts.charts import Bar, Pie, Line, Grid, Page
import csv
# 模拟从 CSV 读取数据(实际项目中替换成真实文件路径)
def read_csv_data(filename):
"""读取CSV文件,返回列表"""
data = []
with open(filename, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
data.append(row)
return data
# 为了演示,这里直接定义数据(实际项目用上面的函数读取)
sales_data = [
{"月份": "1月", "销售额": 125000, "产品": "手机"},
{"月份": "2月", "销售额": 158000, "产品": "电脑"},
{"月份": "3月", "销售额": 142000, "产品": "平板"},
{"月份": "4月", "销售额": 189000, "产品": "手机"},
{"月份": "5月", "销售额": 201000, "产品": "电脑"},
{"月份": "6月", "销售额": 178000, "产品": "平板"},
]
# 1. 柱状图:每月销售额
bar = Bar(init_opts=opts.InitOpts(theme="dark"))
bar.add_xaxis([d["月份"] for d in sales_data])
bar.add_yaxis("销售额(元)", [d["销售额"] for d in sales_data])
bar.set_global_opts(
title_opts=opts.TitleOpts(title="2024年上半年销售概况", pos_left="center"),
xaxis_opts=opts.AxisOpts(name="月份"),
yaxis_opts=opts.AxisOpts(name="销售额(元)")
)
# 2. 饼图:产品占比
product_sales = {}
for d in sales_data:
product = d["产品"]
product_sales[product] = product_sales.get(product, 0) + d["销售额"]
pie = Pie(init_opts=opts.InitOpts(theme="dark"))
pie.add(
"产品销量",
[list(z) for z in zip(product_sales.keys(), product_sales.values())],
radius=["30%", "60%"], # 环形饼图
label_opts=opts.LabelOpts(formatter="{b}: {d}%") # 显示名称和百分比
)
# 3. 折线图:季度趋势
line = Line(init_opts=opts.InitOpts(theme="dark"))
line.add_xaxis([d["月份"] for d in sales_data])
line.add_yaxis("销售额(元)", [d["销售额"] for d in sales_data])
line.set_global_opts(
yaxis_opts=opts.AxisOpts(name="销售额(元)")
)
# 4. 布局组装
grid = Grid(init_opts=opts.InitOpts(width="1200px", height="700px", theme="dark"))
# pos_left="5%" 表示距离左边 5%,pos_right="55%" 表示右边结束位置
grid.add(bar, grid_opts=opts.GridOpts(pos_left="5%", pos_right="55%", pos_top="10%", pos_bottom="55%"))
grid.add(pie, grid_opts=opts.GridOpts(pos_left="55%", pos_right="5%", pos_top="10%", pos_bottom="55%"))
grid.add(line, grid_opts=opts.GridOpts(pos_left="5%", pos_right="5%", pos_top="60%", pos_bottom="10%"))
grid.render("sales_dashboard.html")
print("销售大屏已生成:sales_dashboard.html")
预期输出:生成一个 HTML 文件,浏览器打开后是一个三栏布局的大屏:上面左侧是柱状图(每月销售额),右侧是饼图(产品占比),下面是折线图(趋势)。
一句话解释:Grid 的 pos_* 参数控制每个图表的位置,用百分比而非像素,好处是窗口缩放时布局会自动调整。
项目 3(15 分钟):个人健康数据监控小工具
场景:你戴智能手环,记录了一周的运动数据。想做一个「个人健康仪表盘」,自动从 JSON 文件读取数据,生成一个完整大屏,还能一键导出分享。
完整可运行代码:
from pyecharts import options as opts
from pyecharts.charts import Bar, Line, Gauge, Page
import json
from datetime import datetime
# 健康数据(实际项目从 JSON 文件读取:json.load(open('health.json')))
health_data = {
"user": "小明",
"date": "2024-06-20",
"week_data": {
"days": ["周一", "周二", "周三", "周四", "周五", "周六", "周日"],
"steps": [8500, 10200, 7800, 12000, 6500, 15000, 9000],
"calories": [320, 410, 290, 480, 250, 580, 350],
"sleep_hours": [7.2, 6.8, 7.5, 6.5, 7.8, 8.0, 7.3]
},
"today_summary": {
"steps": 15000,
"goal_steps": 10000,
"calories": 580,
"sleep_score": 85
}
}
# 创建 Page 可以把多个图表放在同一个 HTML 里,每个独立存在
page = Page(page_title=f"{health_data['user']}的健康仪表盘", layout=Page.DraggablePageLayout)
# 1. 步数柱状图
bar_steps = Bar(init_opts=opts.InitOpts(theme="dark", width="350px", height="250px"))
bar_steps.add_xaxis(health_data["week_data"]["days"])
bar_steps.add_yaxis("步数", health_data["week_data"]["steps"])
bar_steps.set_global_opts(
title_opts=opts.TitleOpts(title="📊 周步数统计", pos_left="center"),
yaxis_opts=opts.AxisOpts(name="步数")
)
# 2. 卡路里折线图
line_cal = Line(init_opts=opts.InitOpts(theme="dark", width="350px", height="250px"))
line_cal.add_xaxis(health_data["week_data"]["days"])
line_cal.add_yaxis("卡路里", health_data["week_data"]["calories"],
areastyle_opts=opts.AreaStyleOpts(opacity=0.3)) # 填充面积
line_cal.set_global_opts(
title_opts=opts.TitleOpts(title="🔥 卡路里消耗", pos_left="center"),
yaxis_opts=opts.AxisOpts(name="千卡")
)
# 3. 步数目标完成率仪表盘
completion_rate = int(health_data["today_summary"]["steps"] / health_data["today_summary"]["goal_steps"] * 100)
gauge = Gauge(init_opts=opts.InitOpts(theme="dark", width="350px", height="250px"))
gauge.add("目标完成率", [("完成率", completion_rate)], radius="75%",
detail_opts=opts.GaugeDetailOpts(formatter="{value}%"))
gauge.set_global_opts(title_opts=opts.TitleOpts(title="🎯 今日目标", pos_left="center"))
# 4. 睡眠质量折线图
line_sleep = Line(init_opts=opts.InitOpts(theme="dark", width="350px", height="250px"))
line_sleep.add_xaxis(health_data["week_data"]["days"])
line_sleep.add_yaxis("睡眠时长", health_data["week_data"]["sleep_hours"])
line_sleep.set_global_opts(
title_opts=opts.TitleOpts(title="😴 睡眠时长", pos_left="center"),
yaxis_opts=opts.AxisOpts(name="小时")
)
# 组装到 Page
page.add(bar_steps, line_cal, gauge, line_sleep)
page.render("health_dashboard.html")
print(f"✅ 健康仪表盘已生成!")
print(f"👤 用户:{health_data['user']}")
print(f"📅 数据日期:{health_data['date']}")
print(f"🎯 今日步数:{health_data['today_summary']['steps']} / 目标 {health_data['today_summary']['goal_steps']}")
print(f"💤 今日睡眠得分:{health_data['today_summary']['sleep_score']}分")
预期输出:
✅ 健康仪表盘已生成!
👤 用户:小明
📅 数据日期:2024-06-20
🎯 今日步数:15000 / 目标 10000
💤 今日睡眠得分:85分
生成了一个 HTML 文件,包含 4 个独立图表,可以用鼠标拖拽调整位置(Page.DraggablePageLayout)。
一句话解释:Page 和 Grid 的区别是 Page 里每个图表是独立的,可以用鼠标拖动;Grid 里图表位置是固定的。
💪 进阶 20 分钟:常见坑 + 性能小贴士
❌ 坑 1:中文显示成方块
错误示例:
bar.add_xaxis(["一月", "二月", "三月"]) # 乱码警告
正确示例:
from pyecharts.globals import Symbol
# 方法1:设置全局字体
bar.set_global_opts(
title_opts=opts.TitleOpts(title="销售报表", title_textstyle_opts=opts.TextStyleOpts(font_family="Microsoft YaHei"))
)
# 方法2:最简单——确保系统有中文字体,pyecharts 大部分版本已内置
# 如果还有问题,手动指定编码
import sys
sys.stdout.reconfigure(encoding='utf-8')
❌ 坑 2:数据格式不对,图表画不出来
错误示例:
# 饼图数据写成这样是错的
pie.add("占比", ["北京", "上海", "广州"], [2154, 2428, 1530])
正确示例:
# 饼图需要 [ ["名称", 数值], ... ] 的嵌套列表格式
pie.add("占比", [
["北京", 2154],
["上海", 2428],
["广州", 1530]
])
❌ 坑 3:Grid 布局图表重叠
错误示例:
grid.add(bar, grid_opts=opts.GridOpts(pos_left="10%", pos_right="50%", pos_top="10%", pos_bottom="50%"))
grid.add(line, grid_opts=opts.GridOpts(pos_left="20%", pos_right="40%", pos_top="20%", pos_bottom="40%"))
# 两个图表位置重叠了!
正确示例:
# 确保 pos_* 参数不重叠,留点间隙
grid.add(bar, grid_opts=opts.GridOpts(pos_left="5%", pos_right="55%", pos_top="5%", pos_bottom="50%"))
grid.add(line, grid_opts=opts.GridOpts(pos_left="55%", pos_right="5%", pos_top="5%", pos_bottom="50%"))
❌ 坑 4:大数据量渲染慢
错误示例:
# 10万条数据直接渲染,浏览器会卡死
bar.add_yaxis("数据", [random.randint(1,1000) for _ in range(100000)])
正确示例:
# 1. 数据采样,只显示关键点
sampled_data = original_data[::100] # 每100个取1个
# 2. 或者用 DataZoom 组件,让用户缩放查看
line.add_yaxis("数据", sampled_data)
line.set_global_opts(
datazoom_opts=opts.DataZoomOpts(range_start=0, range_end=100) # 可滚轮缩放
)
💡 性能小技巧
# 生成大屏前,可以用这个技巧减少 HTML 文件体积
# render 之后,pyecharts 会嵌入完整的 ECharts 库(约 1MB)
# 如果部署到服务器,可以用 CDN 外链
page.render("dashboard.html")
# 或者直接生成 echarts CDN 版本(文件更小)
from pyecharts.globals import CurrentConfig
CurrentConfig.ONLINE_HOST = "https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/"
# 下次 render 的 HTML 会从 CDN 加载 ECharts,本地文件只有几十 KB
🔧 调试技巧
# 最朴素的调试:print 大法
print("数据读取成功:", sales_data)
print("图表数量:", len(page.charts))
# 如果图表没显示,先用最简代码验证环境
from pyecharts.charts import Bar
bar = Bar()
bar.add_xaxis(["A", "B", "C"])
bar.add_yaxis("数字", [1, 2, 3])
bar.render("test.html")
print("测试图表已生成,打开 test.html 验证环境是否正常")
✏️ 练习题 + 作业题
练习题(10 分钟)
练习 1(1 分钟):改标题
- 输入:把项目 1 的标题从「未来7天天气预报」改成「北京7日天气」
- 预期输出:HTML 里标题变了
- 提示:找 title_opts 相关代码
练习 2(2 分钟):加判断
- 输入:在项目 1 里,如果最高温超过 35°C,提示「高温预警」
- 预期输出:控制台打印「⚠️ 周三高温预警:38°C」
- 提示:用 for 循环遍历 high_temp 列表
练习 3(3 分钟):新数据源
- 输入:用项目 2 的方法,读取以下班级的成绩数据:{"语文": [85, 90, 78], "数学": [92, 88, 95]}
- 预期输出:一个柱状图展示两科成绩
- 提示:数据结构是 {科目: [成绩列表]},需要换个方式处理
练习 4(4 分钟):串项目 2 + 3
- 输入:把项目 2 的销售数据加上项目 3 的页面布局,生成一个「销售健康度」综合大屏
- 预期输出:包含销售额柱状图 + 饼图 + 一个 Gauge 仪表盘
- 提示:参考项目 3 的 Gauge 用法
练习 5(挑战题,5 分钟):报错分析
- 输入:运行以下代码:
from pyecharts import options as opts
from pyecharts.charts import Pie
pie = Pie()
pie.add("占比", ["北京", "上海", 2154, 2428])
pie.render("test.html")
- 预期输出:报错或图表不正确
- 提示:检查
add方法的参数格式
作业题(30 分钟 - 2 小时)
作业:做一个「自习室使用情况监控大屏」
你是图书馆管理员,想做一个大屏实时展示自习室的使用情况:
- 需求描述:展示一天内各个时段(8:00-22:00)自习室的占用率
- 功能点:
1. 折线图展示 8:00-22:00 每小时占用率(0-100%)
2. 饼图展示「已用」「空闲」「预约」三种状态占比
3. 仪表盘展示当前整体占用率
4. 数据从 Python 字典定义(不用读文件) - 加分项:
1. 用 DataZoom 让折线图可缩放
2. 添加当前时间显示(用 datetime) - 验收标准:能跑起来 + 用浏览器打开看到三个图表 + 代码有注释
- 提交方式:评论区贴代码或 GitHub 链接
📚 总结 + 资源
本文学到的 3 个核心点:
1. Grid 布局把多个图表组装成一个大屏,位置用百分比控制
2. Page 布局让图表独立存在,支持拖拽调整
3. 数据格式是 pyecharts 的核心——柱状图用 [ ["分类", 数值] ],饼图用同样的格式
延伸学习资源:
- pyecharts 官方文档:所有图表类型和配置项的完整参考
- ECharts 官方示例:150+ 个可交互示例,找灵感
- 《Python 数据可视化之美》:进阶配色和图表美学
互动钩子:你在公司做过数据看板吗?是给老板看的还是给客户看的?用的什么技术?评论区聊聊,老粉优先回复!
「学完这章,你已经能做出一个像模像样的数据大屏了。但这些数据都是我们手动输入的或者模拟的。下一章我们要解决一个实际问题——怎么让大屏的数据自动从网上获取,不用每次手动更新。剧透:跟 uniCloud 云开发有关,敬请期待。」

评论(0)