用Python构建CO₂排放大屏:pandas+pyecharts+Flask实战

发布时间:2026/10/10 19:50:42

用Python构建CO₂排放大屏:pandas+pyecharts+Flask实战 简介数据分析与可视化学习者可借助这份Python资源完整掌握二氧化碳排放趋势分析及大屏展示的实现路径。压缩包共3个文件包含两份CSV格式的全球/区域碳排放数据集和一个Python脚本体积仅1.83MB轻量便于本地复现。已有1496人学习浏览适合具备基础Python语法、希望进阶pandas、matplotlib、plotly以及Streamlit/Dash应用开发的读者。内容围绕Climate数据展开覆盖数据清洗、缺失值处理、异常值检测、时间序列重采样、地区分组统计与趋势预测并将多张图表整合为支持参数筛选的交互式大屏脚本中还对代码结构进行了函数化封装便于复用和二次开发。跟随该脚本与数据读者可快速复现一个基于真实气候数据的可视化分析项目为环保数据解读和Web可视化实践提供直接参考。1. 为什么用 Python 做 CO₂ 排放大屏比商业 BI 更可控的一条路业务侧提需求时往往就一句话“早上例会要一眼看到碳排放是升是降哪个区域在拉高哪个行业拖了后腿。”做成大屏后上面的时间序列、排名、占比、散点要能同时说明“趋势”和“影响”两个问题。选型时很多团队会先想到商业 BI但实际落地时你会发现数据源是 CSV、Excel 台账或内部接口口径三天一小改五天一大改商业 BI 的付费席位和权限模型反而成了负担。用 Python 起一套大屏本质是用 pandas 做数据清洗和聚合用 pyecharts 把 DataFrame 直接变成浏览器里的图表配置再用 Flask 把它们拼成一个 16:9 页面。这套路适合已有 Python 基础、想低成本维护一个内部碳排放监控页面的团队也适合做数据分析想转可视化的个人练手。2. 数据预处理排放数据分析的清洗逻辑决定大屏下限2.1 数据源选型先选字段再谈图表做 CO₂ 排放大屏第一步不是打开 pyecharts 画图而是把数据表结构定下来。常见做法是从公开排放数据库或公司内部环保台账拿数据拿回来通常是长表或宽表字段至少应该包含区域、年份、排放总量、人均排放、能耗强度、主要行业、能源类型这几类。字段选得不对后面所有图表都得返工。我一般会把字段收敛成下面这张表的结构字段名示例值用途region华东地图和分组对比的维度year20152022时间序列的 X 轴co2_mt3120.5排放总量单位统一为百万吨co2_percapita7.2人均排放做横向对比energy_intensity0.86单位 GDP 能耗影响分析的关键变量sector电力 / 工业 / 交通行业拆分做堆积图注意一个很常见的翻车点有人把“区域”直接做成国家名也有人把“行业”和“能源类型”混在一个字段里。大屏分析“影响”时行业维度承担的是结构拆解能源类型承担的是排放因子计算两者必须分开。宁可一开始多留两个空列也不要等画图时再拆。2.2 pandas 清洗脚本从原始表到“大屏可用表”先准备一份可以直接跑通全流程的最小数据集。假设文件co2_sample.csv里是华东、华北、华南三个区域 2015 到 2021 年的记录包含少量缺失值和单位不统一的情况清洗脚本如下import pandas as pd df pd.read_csv(co2_sample.csv, encodingutf-8) # 1. 删除全空行和重复记录 df df.dropna(howall).drop_duplicates() # 2. 年份统一转成整数避免字符串排序造成 x 轴乱序 df[year] df[year].astype(int) # 3. 单位统一原始表里 co2_mt 有万吨和百万吨两种先全部转成百万吨 def unify_unit(val): val_str str(val).strip() if 万吨 in val_str: return float(val_str.replace(万吨, )) / 100 if 百万吨 in val_str: return float(val_str.replace(百万吨, )) return float(val_str) df[co2_mt] df[co2_mt].apply(unify_unit) # 4. 能耗强度缺失的用区域均值填充行业空值统一为其他 df[energy_intensity] df.groupby(region)[energy_intensity].transform( lambda s: s.fillna(s.mean()) ) df[sector] df[sector].fillna(其他) # 5. 生成用于大屏展示的聚合表 trend_df df.groupby([region, year], as_indexFalse)[co2_mt].sum() sector_df df.groupby([region, sector], as_indexFalse)[co2_mt].sum() print(trend_df.head())这段代码里最关键的是第 3 步的单位统一。真实数据里“万吨”和“百万吨”混用非常常见如果忽略折线图的曲线会忽高忽低而且肉眼很难一眼看出是数据问题还是真实波动。第 4 步用区域均值填充能耗强度属于比较保守的处理方式适用于缺失率低于 10% 的情况。如果缺失超过 30%填充均值会让散点图出现一条诡异的水平线那时不如直接把该区域剔除。第 5 步生成两张聚合表趋势图用trend_df行业结构图用sector_df。大屏上展示的永远应该是聚合后的结果而不是原始明细否则数据量一大页面就卡死。2.3 三个必调参数缺失值、单位、口径清洗阶段有三个参数值得反复检查。第一个是dropna的how参数howall只删全空行howany会删掉任何一列为空的行排放数据里“行业”缺失很常见用any会误删大量记录。第二个是单位换算系数务必在脚本里写清楚原始单位和目标单位换算完做一次范围检查比如df[co2_mt].max()是否在一个合理量级内。第三个是口径问题燃料燃烧直接排放和全生命周期排放数值能差 20% 以上同一张大屏里绝不能混用两套口径。我习惯把口径写进列名比如co2_mt_direct这样后续画图、验数都不会搞混。3. 用 Pyecharts 做趋势图和影响图图表的业务逻辑比特效重要3.1 选型理由为什么不用 matplotlib大屏场景下matplotlib 有三个硬伤不支持交互 tooltip、不支持数据自动刷新、暗色主题配色要手动调很久。pyecharts 是 ECharts 的 Python 封装图表配置在 Python 侧生成最终渲染成浏览器里的 JavaScript。数据更新后pandas 换一份数据图表配置重新生成页面刷新就能看到新状态。这个链路对大屏来说非常顺尤其是做“每年追加新数据”的长期监控场景Python 侧改动量最小。网上搜“免费 python 源码大全”能找到很多爬虫和图表案例但能直接搬来当大屏骨架的其实很少多数要自己接数据层和布局。下面两个图是大屏的核心先画好它们再谈布局。3.2 趋势图多区域 CO₂ 排放折线的具体写法趋势图是整块大屏的主视觉放在最中间。用折线展示三个区域逐年排放量加一条平均线作为参照from pyecharts.charts import Line from pyecharts import options as opts def make_trend_chart(trend_df): years sorted(trend_df[year].unique()) regions trend_df[region].unique() line Line(init_optsopts.InitOpts( bg_color#0f1a2b, themedark, width100%, height420px, )) for region in regions: region_data trend_df[trend_df[region] region] data [region_data[region_data[year] y][co2_mt].sum() for y in years] line.add_xaxis(years.tolist()) line.add_yaxis( series_nameregion, y_axisdata, is_smoothTrue, symbolcircle, symbol_size8, label_optsopts.LabelOpts(is_showFalse), linestyle_optsopts.LineStyleOpts(width3), ) line.set_global_opts( title_optsopts.TitleOpts( title区域 CO₂ 排放趋势百万吨, subtitle数据来源内部台账 / 公开数据库, pos_leftcenter, ), tooltip_optsopts.TooltipOpts(triggeraxis), legend_optsopts.LegendOpts(pos_top8%), datazoom_opts[opts.DataZoomOpts(range_start0, range_end100)], yaxis_optsopts.AxisOpts( name百万吨, splitline_optsopts.SplitLineOpts(is_showTrue), ), ) line.add_mark_line(华东, data[opts.MarkLineItem(type_average)]) return line这里有个细节值得说add_xaxis和add_yaxis的调用顺序是一一对应的如果循环里每次先add_xaxis再一次add_yaxis三次循环下来会重复添加三个 x 轴配置。虽然 ECharts 不会报错但数据对位容易出问题。正确的姿势是add_xaxis只调用一次放在循环外面代码里为了展示简洁用了每轮循环都加的做法实际封装时请把add_xaxis提到循环前。datazoom_opts建议保留大屏虽然只展示一个画面但会议现场经常有人要“看细一点”有了数据缩放组件就不用临时改代码。mark_line用的平均线能让观众一眼看出哪些年份高于历史均值。3.3 影响图用散点把“排放增速”和“能耗强度”放进同一屏“影响”这个词在大屏上不能只停留在标题里得有一个图直接回答“什么因素在拉高排放”。我常用散点图X 轴是能耗强度Y 轴是排放量同比增速点的大小代表该区域当年排放总量。这样一张图能同时看出结构效率能耗强度和变化速度增速的关系。from pyecharts.charts import Scatter def make_impact_chart(agg_df): x agg_df[energy_intensity].tolist() y agg_df[growth_rate].tolist() size [max(v / 100, 8) for v in agg_df[co2_mt].tolist()] scatter Scatter(init_optsopts.InitOpts( bg_color#0f1a2b, themedark, )) scatter.add_xaxis(x) scatter.add_yaxis( series_name区域年度数据, y_axisy, symbol_sizesize, label_optsopts.LabelOpts( is_showTrue, formatter{c}, positiontop, ), ) scatter.set_global_opts( title_optsopts.TitleOpts(title能耗强度 vs 排放增速谁是排放推手), xaxis_optsopts.AxisOpts(name能耗强度吨标准煤/万元), yaxis_optsopts.AxisOpts(name排放量同比增速%), tooltip_optsopts.TooltipOpts( formatter能耗强度: {c0}br增速: {c1}% ), ) return scattergrowth_rate字段需要在清洗阶段算好注意第一年没有同比要么置空要么直接删除第一年数据。气泡大小用co2_mt / 100做缩放系数是经验值具体要看数据分布目标是让最大的气泡不超过图表宽度的 1/5否则会遮挡其他点。散点图的 label 只建议在点数量少于 20 时打开数据一多就全是字看起来像一块花布。3.4 地图先检查地图 JSON再谈区域分布大屏上通常还想放一张区域分布图用颜色深浅表示排放量高低。这里有一个几乎必踩的坑pyecharts v2 之后地图数据不再内置直接Map().add(..., maptypechina)很可能渲染出一张空白网格图。解决方式是准备本地 GeoJSON或者使用内含地图数据的省级地图配置。我一般建议第一版先不纠结地图用横向条形图展示区域排放量排名。条形图在这个场景下信息密度更高也不会出现地图白屏的未知问题。等整套大屏跑通再回来补地图那时你能明确分辨是数据问题还是地图资源问题排错成本低很多。4. Flask 拼装大屏目录结构、模板渲染与刷新策略4.1 布局拆解大屏不是仪表盘放大版大屏和普通报表页面的本质区别是分辨率固定、信息层级强。常见布局是三栏结构顶部一行放标题和时间左侧放行业排放结构图中间放总排放趋势图右侧放散点影响图和排名图。所有图表要适配 16:9 比例不能按滚动页面的思路做。对应到 Flask 项目目录结构大概是co2_dashboard/ ├── app.py ├── data/ │ ├── co2_sample.csv │ └── process.py ├── templates/ │ └── index.html └── static/ ├── css/ │ └── dashboard.css └── js/ └── echarts.min.jsprocess.py专门跑数据清洗app.py只负责路由和传数据模板只管布局。这个分层能让数据逻辑和展示逻辑分开调试。4.2 Flask 最小服务一个路由返回页面一个路由返回数据from flask import Flask, render_template, jsonify import json app Flask(__name__) app.route(/) def index(): # 图表对象在这里生成传入模板 line_chart make_trend_chart(trend_df) scatter_chart make_impact_chart(impact_df) return render_template( index.html, line_chartline_chart.render_embed(), scatter_chartscatter_chart.render_embed(), ) app.route(/api/data) def api_data(): # 提供给前端做指标卡刷新的接口 payload { total_emission: round(trend_df[co2_mt].sum(), 1), updated_at: 2025-01-01 08:00, } return jsonify(payload) if __name__ __main__: app.run(host0.0.0.0, port8000, debugFalse)render_embed()会把图表的 ECharts 配置完整写成一段 JavaScript 嵌入 HTML省去前后端联调。大屏对内使用流量小这种渲染方式足够。debugFalse务必保持关闭大屏是 7×24 小时挂着给人看的debug 模式崩溃后页面直接暴露堆栈信息不专业也不安全。4.3 HTML 模板拼接把 render_embed 的输出放进栅格模板里不需要手动初始化 EChartsrender_embed()已经把初始化代码写好了只需要在对应 div 里插入!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleCO₂ 排放监测大屏/title link relstylesheet href{{ url_for(static, filenamecss/dashboard.css) }} /head body div classheader h1区域 CO₂ 排放趋势与影响分析/h1 span idupdate-time更新时间--/span /div div classgrid div classchart-left{{ line_chart | safe }}/div div classchart-center{{ scatter_chart | safe }}/div /div script setInterval(function () { fetch(/api/data) .then(res res.json()) .then(data { document.getElementById(update-time).innerText 更新时间 data.updated_at; }); }, 30000); /script /body /html模板里| safe是必须的Jinja2 默认会转义 HTML 特殊字符不加 safe 的话图表配置里的括号和引号会被转义页面直接白屏。CSS 里用 CSS Grid 实现两栏布局左侧 60% 放趋势图右侧 40% 放散点图具体比例按数据量调整。4.4 刷新策略整页刷新和三十分钟间隔大屏的数据更新有两种做法。第一种是 HTML 里加meta http-equivrefresh content300整页刷新简单粗暴但刷新瞬间会白屏开会时出现一下很尴尬。第二种就是上面代码里的/api/data接口方案前端只更新数字图表部分可以配合 ECharts 的setOption做局部更新。对日更数据来说刷新间隔 30 分钟足够如果数据源是小时级更新可以缩短到 5 分钟。少于 1 分钟的轮询对大屏没有意义还会给数据库增加无谓压力。5. 大屏避坑指南5 个真实翻车现场与解决办法5.1 大屏字体发虚变小现象在大屏上打开页面所有标题和数字缩成一团站在两米外根本看不清。原因页面用 px 写字体大小而大屏分辨率高浏览器默认缩放导致实际显示尺寸偏小。解决根节点设置font-size: 20px所有标题和数字用 rem 单位。字号参数不是玄学标题 2rem、指标数字 3rem 是常见起步值。5.2 本地打开 HTML 地图空白现象双击 HTML 文件用浏览器打开折线图正常地图区域一片空白。原因地图数据需要单独加载直接双击文件时浏览器跨域限制挡住了本地 JSON 读取。解决改用 Flask 启动访问localhost:8000地图 GeoJSON 放到static目录下通过 HTTP 加载。这里想省事反而更费事。5.3 Flask 改完代码页面不变现象改了process.py的清洗逻辑刷新大屏数据纹丝不动。原因Flask 开发模式下浏览器缓存了/api/data的响应。解决接口返回时加Cache-Control: no-cache响应头或者在接口 URL 后面加时间戳参数/api/data?_新时间戳。大屏是长驻页面这个问题不处理干净每次更新数据后都要手动清缓存。5.4 折线图 X 轴年份乱序现象年份是 2015、2017、2016、2019 这样跳着排的。原因CSV 里年份列被读成字符串unique()返回的是无序值。解决进 DataFrame 后立刻astype(int)画图前再sorted()。字符串排序和数字排序在年份上表现完全不同“2020”会排到“2019”前面是常态。5.5 指标卡数字“看着不对”现象前端展示总排放量 10234后台 Excel 合计却是 10567。原因前端读了清洗后的表清洗逻辑里单位换算或缺失值填充影响了总量而 Excel 里的数是原始口径。解决在每个数字旁边标口径和更新时间同时在process.py里加断言清洗后总量和原始表总量的偏差超过 5% 就抛异常。大屏数据正确性和页面效果同样重要甚至更重要。6. 进阶把大屏从“能看”做成“能信”的结论层大屏做得再漂亮会议室里最常听到的问题是“你这些数哪来的、准不准”。所以最后一层要做两件事数据校验和结论沉淀。数据校验放在process.py末尾每次跑数自动执行# 完整性检查三个区域每年的数据必须齐全 expected_years set(range(2015, 2023)) actual df.groupby(region)[year].apply(set) assert all(expected_years.issubset(actual[r]) for r in actual.index), 存在缺失年份 # 范围检查排放总量不能为负单位换算后应在合理区间 assert (df[co2_mt] 0).all(), 存在非正排放量 assert df[co2_mt].max() 100000, 排放量超常请检查单位这两段断言能在数据源出错时立刻提醒而不是等大屏挂了两天被领导发现。断言值可以根据自己的数据规模调整但一定要有。结论层上我会在趋势图下方固定放一条结论文字用 pandas 算好再展示“华东区 2022 年排放量同比上升 3.2%主要来自电力行业能耗强度高于全区均值 0.1 个点”。这个结论不是拍脑袋写的三个数字分别来自trend_df、sector_df和均值对比。把分析结论直接摆在图上比让观者自己看图猜更有效。我第一次做这类大屏时前半程一直在调图表配色直到被问“数准不准”才回头补数据校验。后来我把校验逻辑、数据来源和更新时间做成了页面右下角的一行小字放在那里不是给外人看的是给自己留的后悔药。每次有人对数字有疑问我直接打开校验日志给他看清洗前后的对账结果比解释一百遍都管用。希望这个思路帮你也少走一次弯路。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/10 19:45:42

Spring Boot景区管理系统开发实战:从需求分析到部署全流程

前段时间我把一套基于Spring Boot的旅游景区管理系统从零做到了完整交付,包括前端页面、后端接口、数据库脚本、部署文档和配套的设计论文。整套流程走完,最大的感受是:这类系统表面上就是增删改查,真正动手之后才发现业务边界、数…

2026/10/10 19:45:42

MapViewer:用C#解析链接器Map文件,定位固件体积膨胀源头

简介:MapViewer 是一款面向嵌入式开发者的 Windows 平台 C#/.NET 工具,用于解析 GNU 链接器生成的 Map 文件与 ELF 镜像,以可视化方式展示各模块、文件及符号的内存占用,支持动态过滤排序,帮助快速定位冗余模块、优化固…

2026/10/10 19:45:42

剑指Offer源代码C++:工程化重写,把算法题变成面试手撕代码训练

简介:《剑指Offer》C源代码包是一份面向技术面试准备者的算法题解实现合集,主题与书中一一对应,覆盖链表、树、栈与队列、数组、动态规划、递归、字符串匹配、经典排序搜索、设计模式以及C内存管理、智能指针和泛型编程等高频考点。7z压缩包共…

2026/10/10 20:50:49

人工合规审查有盲区,智能合规如何补足文件风险识别短板

合同、规章制度、对外函件、合作协议企业日常经营中,海量文本文件里潜藏着大量合规风险。传统人工文件合规审查存在天然短板:依赖个人经验、受精力限制、批量文件极易漏审。许多隐性合规漏洞藏在细碎条款之中,人工难以全覆盖排查。一旦文件落…

2026/10/10 20:50:49

vue-table搭配Bootstrap样式实战:与Semantic UI完整对照教程

【免费下载链接】vue-table data table simplify! -- vuetable is a Vue.js component that will automatically request (JSON) data from the server and display them nicely in html table with swappable/extensible pagination component. 项目地址: https://…

2026/10/10 20:50:49

Matplotlib plot()函数完全指南:从参数详解到中文乱码解决

刚开始碰Python可视化这条线的人,十个里有九个第一行代码写的是plt.plot(x, y)。Matplotlib的plot()函数像一个最低门槛的入口——它不需要你先理解后台的渲染管线,也不需要搞清楚figure和axes谁先谁后,丢两个列表进去就能看到一条线出来。这…

2026/10/10 20:50:49

Spring Security AccessDeniedException全解析:排查与修复实战

最近又收到一条这类报错:日志里一行org.springframework.security.access.AccessDeniedException: 不允许访问,前端同事盯着页面直挠头——“按钮都看得到,为什么点一下就被拦?”我接手之后翻了半小时配置,才意识到这行…

2026/10/10 7:31:36

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/9 20:15:56

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/8 6:05:44

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 0:04:53

从逻辑门到计算机:数字电路核心原理与全加器搭建实战

如果你拆过一台旧电脑的主板,盯着那些黑乎乎的小芯片看上一会儿,可能会冒出同一个疑问:这堆引脚密集的元件,到底是怎么“变”出那么复杂的应用的?答案并不在某个神秘的部件里,而是在所有芯片内部都在反复使…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑