☰
七普年龄结构分析:Pandas清洗与pyecharts可视化实战模板
2026/10/8 2:42:33 网站建设 项目流程

简介:一份聚焦第七次全国人口普查年龄结构的Python数据分析与可视化实践资源,全流程约550行代码,依托Pandas、NumPy与Pyecharts完成从数据清洗、统计计算到交互式图表生成的全过程,适合社会科学研究者、数据分析初学者及高校相关课程作业参考。压缩包共14个文件,包含10个csv原始数据表、1个Python脚本、2个Jupyter Notebook进度记录以及1个HTML可视化报告;csv数据覆盖全国及分地区人口年龄构成、城乡分布、性别结构、受教育程度等主题,脚本与Notebook则展示具体分析步骤和可视化逻辑,包体仅有335KB,轻量易获取。目前已吸引168人浏览学习。借助该资源,读者能清晰看到从原始表格到分析结论的完整路径,包括不同年龄段占比、人口年龄金字塔及地区差异的Pyecharts画法,并可直接修改参数复用到自己的数据上;同时有助于理解我国人口年龄结构现状与未来趋势,为课程论文、课题研究或政策解读提供数据支撑与可视化素材,在社会科学量化分析场景中相当实用。

1. 为什么建议你直接拆这份七普年龄结构分析:550 行代码背后的数据链路

如果你手头正要做人口年龄结构分析,又不想从零开始搭数据链路,这份资源是那种“拿到就能跑”的典型。它用第七次人口普查公开数据,把全国和各地区的年龄构成整理成可以直接出图的样子:Pandas 负责清洗和聚合,pyecharts 负责最终可视化。约 550 行的代码量非常适合当模板,不算大而全,但足够覆盖数据读取、年龄分组、地区对比和 HTML 交互图导出。这个案例的适用面很具体:社科方向做课程设计或者调研报告的人,需要一个标准的人口结构可视化过程;已经会用 DataFrame 基础操作,却不知道怎么把官方 CSV 变成一张拿得出手的年龄结构图。它不会教你怎么做学术级人口学建模,但一定能帮你把官方普查数据变成能放进报告的图表。

2. 资源包拆包:先搞清楚这 10 张 CSV 怎么互相 join

拿到压缩包第一件事,不是双击打开 ipynb,而是先把 data 目录下的文件当数据库表看。这个包里的 CSV 不是同一类数据:有全国口径的,有分省面板的,还有 2000 年第五次普查遗留数据。如果不先分清主键和粒度,后面做 merge 时很容易出现人口数翻倍或者全是 NaN 的局面。

2.1 解压后的目录结构:主文件、附属数据、缓存目录要分清楚

先看整体结构:

七普年龄结构分析/ ├── 关于第七次人口普查数据的探索.ipynb ├── 关于第七次人口普查数据的探索.py ├── 关于第七次人口普查数据的探索.html ├── data/ │ ├── 全国人口年龄构成.csv │ ├── 各地区人口.csv │ ├── 2000年中国各地区城乡人口分布.csv │ ├── 各地区人口年龄构成.csv │ ├── 分省年度GDP数据.csv │ ├── 2005-2019年中国各地区城镇人口数量.csv │ ├── 2001-2020中国各地区常住人口数量.csv │ ├── 各地区每10万人口中拥有的各类受教育程度人数.csv │ ├── 各地区15岁及以上人口平均受教育年限.csv │ └── 各地区性别构成.csv └── .ipynb_checkpoints/ └── 关于第七次人口普查数据的探索-checkpoint.ipynb

主入口是.ipynb和.py,内容基本等价,.html是已经跑出来的可视化成品。.ipynb_checkpoints是 Jupyter 自动保存的草稿缓存,不是数据,可以忽略。

data 目录下看起来有 10 张表,但真正参与年龄结构分析核心计算的,其实只有两张:全国人口年龄构成.csv和各地区人口年龄构成.csv。其余 8 张表的作用是扩展分析,比如把年龄结构和常住人口趋势放一起,或者看不同受教育程度地区的人口年龄差异。把它们全部装进同一个 DataFrame 是不现实的,也不应该那么做。

2.2 用地区还是用年份作为连接键:几张表的粒度和关联方式

我习惯先做一张粒度表,别凭感觉合并:

数据文件时间粒度空间粒度在年龄结构分析里的角色
全国人口年龄构成.csv普查时点全国核心表,做总体年龄画像
各地区人口年龄构成.csv普查时点省级核心表,做地区对比
2000年中国各地区城乡人口分布.csv2000 年省级跨普查对比
分省年度GDP数据.csv2001-2020 逐年省级关联经济变量
2005-2019年中国各地区城镇人口数量.csv各年度省级城镇化趋势
2001-2020中国各地区常住人口数量.csv各年度省级人口规模变化
各地区每10万人口中拥有的各类受教育程度人数.csv普查时点省级受教育结构
各地区15岁及以上人口平均受教育年限.csv普查时点省级受教育水平
各地区性别构成.csv普查时点省级性别结构
各地区人口.csv普查时点省级基础人口总量

这里最关键的一点是:除了全国那张表之外,其余九张表几乎都能按“地区”字段连接,也就是省级行政区名称,比如“北京市”“河北省”这种口径。时间序列表之间则要按“年份”连接,分省年度GDP数据.csv和2001-2020中国各地区常住人口数量.csv可以并肩使用,但两张表的年份覆盖范围必须核对一下,不要默认完全一致。

我在实际项目里的做法是:年龄结构分析先用两张核心表单独完成,得出“全国年龄结构”和“各地区年龄结构”两个中间结果。如果需要看“老龄化程度是否和地区 GDP 有关”,再把地区年龄结构聚合得到的老年人口占比,与分省年度GDP数据.csv按地区合并。这样每一步都只有一种粒度,不会出现同一行里一边是全国总量、一边是省级数据的错配。

2.3 数据边界:这份资源能支撑哪些分析,哪些不能硬套

既然是省级汇总数据,就不要指望用它做微观层面的个体分析。比如“某个区县的年龄结构”“特定家庭户的人口特征”这类问题,这份资源完全回答不了。它能支撑的分析包括:全国及各省 0-14 岁、15-59 岁、60 岁及以上三个常见年龄段的比例;用 2000 年城乡人口数据做跨普查比较;把年龄结构与常住人口、GDP 这类省级经济社会指标做粗粒度相关分析。

做分析前先把边界划定,能省掉后面很多返工。比如想在结论里说“某省份老龄化程度排第几”,那么只要用各地区人口年龄构成.csv算 60 岁及以上占比再排序即可;如果想说“老龄化在加速”,那就得去找对应年份的数据,这个包里只有普查时点的横截面,无法单独支撑完整时间趋势,除非你再补外部数据。这也是我看这个资源时最想提醒你的一点:文件多不代表指标全,先用表格梳理清楚“能回答什么”,再开始写代码。

3. 主代码拆解:从 Pandas 清洗到 pyecharts 出图的 550 行

这份资源的主体是.py和.ipynb里的约 550 行代码。我拆过之后发现它的结构非常像一条规整的数据流水线:先把原始 CSV 读进来,用 Pandas 做字段清洗,提炼年龄段,再写各种聚合结果,最后用 pyecharts 渲染成 HTML。这不是一个算法项目,而是一个数据分析和可视化实践项目,价值在于能直接复制这套流程去处理其他人口数据。

3.1 代码整体结构:四个阶段和它们的分工

拿到代码先看大框架,我就按我认为最合理的组织方式把它分成四段:

import pandas as pd from pyecharts.charts import Bar, Pie, Line from pyecharts import options as opts # 阶段 1:读取数据 df = pd.read_csv("data/全国人口年龄构成.csv", encoding="utf-8") # 阶段 2:清洗年龄段字段,拆出数值型下限 # 阶段 3:按 0-14 岁、15-59 岁、60 岁及以上聚合 # 阶段 4:pyecharts 渲染并保存成 HTML

第一阶段负责把 CSV 读进来。encoding="utf-8"看起来不起眼,但在 Windows 默认 GBK 环境下非常关键。不写这个参数,很多机器上会直接报UnicodeDecodeError。第二阶段是这套代码里最值得学的地方,后面我单独展开。第三阶段做分组聚合,结果是一个按年龄段排好序的小表。第四阶段调用 pyecharts 的Bar、Pie等组件生成图表,核心入口一般是render()方法,它会输出一个自带 JavaScript 的 html 文件。

如果你只看 550 行这个数字,可能会觉得很多,但实际上里面有大量代码是在设置图表标题、颜色、坐标轴标签、提示框这类展示参数。真正在算数据的逻辑可能只占三分之一。所以拆的时候不要把注意力全放在绘图参数上,先看数据是怎么洗的。

3.2 年龄分段的具体写法:不要直接 sort 字符串

人口年龄构成 CSV 里的年龄段是字符串,比如“0-4岁”“5-9岁”“60-64岁”,还有“100岁及以上”。如果直接对字符串排序,结果会变成按字典序排,而不是按人的年龄从小到大排。这几乎是每个初用这份数据的人都会踩的坑。

我处理这种情况下一般会先给每个年龄段提取一个最小年龄,作为排序和分组的依据:

import pandas as pd raw = pd.read_csv("data/全国人口年龄构成.csv", encoding="utf-8") # 先处理"100岁及以上"这类没有区间下限的写法 raw["年龄段_clean"] = ( raw["年龄段"].astype(str).str.replace("岁及以上", "-100", regex=False) ) # 提取"0-4"两个数字中的下限 raw["年龄下限"] = raw["年龄段_clean"].str.extract(r"(\d+)-(\d+)", expand=True)[0] raw["年龄下限"] = pd.to_numeric(raw["年龄下限"], errors="coerce") # 按业务口径重新分大组 bins = [0, 14, 60, 200] labels = ["0-14岁", "15-59岁", "60岁及以上"] raw["大组"] = pd.cut(raw["年龄下限"], bins=bins, labels=labels, right=False) grouped = raw.groupby("大组", observed=True)["人口数"].sum()

这里str.extract(r"(\d+)-(\d+)", expand=True)是从“0-4”这种结构里一次性提出前后两个数字。expand=True让它返回 DataFrame,我们取第 0 列作为年龄下限。pd.to_numeric和errors="coerce"的作用是把“100+”这种被我用前面命令替换成的数字转成数值型,转不了的变成NaN,后续用dropna处理。

分组参数bins=[0, 14, 60, 200]表示分箱边界。right=False表示左边闭、右边开,也就是 0 岁算进“0-14岁”,14 岁也算进“0-14岁”,15 岁开始才算“15-59岁”。60 岁及以上这个区间,上界我习惯写 200,而不是 100。因为 100 岁及以上的人也算 60 岁以上,如果你用 100 作为上界,最后那群“100岁及以上”会被丢掉,导致人口总数对不上。

3.3 pyecharts 的两种输出:HTML 文件与 Notebook 内联

数据聚合完之后就是画图。pyecharts 有render和render_notebook两个常用方法,很多人混用之后发现结果不显示,其实只是选错了入口。

from pyecharts.charts import Bar from pyecharts import options as opts bar = ( Bar(init_opts=opts.InitOpts(width="900px", height="500px")) .add_xaxis(grouped.index.tolist()) .add_yaxis("人口数", grouped.values.tolist()) .set_global_opts( title_opts=opts.TitleOpts(title="第七次全国人口普查年龄结构") ) ) bar.render("output/age_structure.html")

在.py脚本里,render()会把图表写成一个独立的 HTML 文件,路径由参数指定。在 Jupyter Notebook 里,如果想让人物直接显示在单元格下方,应该用render_notebook(),而不是render()。grouped.index.tolist()这一步也需要注意:Pandas 的groupby结果索引是标签类型,直接用index列参与绘图可以,但如果索引是层级结构,就要先reset_index(drop=True)再取列。最稳妥的写法是先把grouped转成普通 DataFrame,再取列传入图表。

4. 复现实验:跑通七普年龄结构分析的完整步骤与参数清单

光看懂逻辑不算完,我建议你按下面的步骤自己跑一遍。这套流程我多次用在类似的人口数据结构上,只要依赖和路径不出问题,半小时内能从解压走到图表生成。

4.1 环境准备:建议锁定 pandas 1.x 与 pyecharts 1.x 的同一系列

首先建一个干净虚拟环境,避免和你日常的 Python 环境冲突。用 pyecharts 时最怕的就是版本不统一,很多旧教程用的是 0.5.x 的接口,和现在主流的 1.x 完全不兼容。

python -m venv venv source venv/bin/activate pip install "pandas>=1.5,<2.5" "pyecharts>=1.9,<2" jupyter

Windows 下第二行要改成venv\Scripts\activate。版本范围我给的是一个比较稳的区间,pandas 1.x 的pd.cut表现稳定,pyecharts 1.9 及以上对应的是from pyecharts.charts import Bar的写法。如果你之前装过旧版 pyecharts,建议先pip uninstall pyecharts再装新的,避免接口错位。

4.2 三种运行方式:Notebook、命令行执行、直接跑脚本

第一个方式是交互式运行:

jupyter notebook 关于第七次人口普查数据的探索.ipynb

进入 Notebook 后逐格执行,可以按自己的想法改参数。

第二个方式是命令行无头执行整个 Notebook,适合复现和交付:

jupyter nbconvert --to notebook --execute --inplace "关于第七次人口普查数据的探索.ipynb"

--inplace表示把执行结果写回原文件。如果某一步报错但你又想看到完整输出,可以加--allow-errors,不过正式复现时我不建议这么做,它会掩盖错误。

第三个方式是直接运行纯脚本:

python 关于第七次人口普查数据的探索.py

如果脚本里用了相对路径data/xxx.csv,那么终端当前工作目录必须位于项目根目录,否则会抛FileNotFoundError。我从别的位置执行时习惯先cd进去再跑,这是最简单也最不会错的。

4.3 用公开公报数字反推结果:从图表反查脚本是否有误

跑通之后不要只看图漂不漂亮,先做一个总人口校验。七普公报发布过全国总人口数据,你可以把全国人口年龄构成.csv里各年龄段人口数加总,看看是否与公报中的总人口基本一致。这一步能同时暴露两个问题:年龄段字段有没有解析丢值,以及pd.cut分箱时有没有把某个年龄组排除掉。

import pandas as pd df = pd.read_csv("data/全国人口年龄构成.csv", encoding="utf-8") print(df["人口数"].sum())

如果这个数明显偏小,优先检查“100岁及以上”这类非标准写法,再用我前面提到的方法把它统一转成上限区间。如果脚本里按性别拆分了,也要检查“男 + 女”是否等于“合计”。我见过不少因为 age 字段解析错误,最后画出来“60 岁及以上人口占比只有百分之零点几”的翻车案例,根源都是把末尾年龄段丢掉了。

4.4 参数微调:把标题、颜色和输出路径改成自己的

复现完成之后,最常见的需求是把标题和输出位置改掉。这一部分不用动核心逻辑,只需要改set_global_opts和render路径:

bar.set_global_opts( title_opts=opts.TitleOpts( title="省级人口年龄结构对比", subtitle="基于第七次全国人口普查数据", ), toolbox_opts=opts.ToolboxOpts(), )

toolbox_opts开启的是 pyecharts 右上角工具栏,可以保存图片、刷新视图,对做报告的人比较实用。render()的路径参数要注意,如果目录不存在,pyecharts 会直接报错,不会自动帮你建目录。建议先os.makedirs("output", exist_ok=True),再调用 render。

5. 避坑指南:七普数据和 pyecharts 的六类典型翻车现场

这部分是我拆数据项目时最想提前告诉你的。下面每一条都是真实发生过的问题,按“现象 → 原因 → 解决”给你列清楚。

5.1 读进来的数据对不上:编码、字段类型和年龄上限问题

第一条:pd.read_csv直接报UnicodeDecodeError。
现象是文件明明存在,读进来却乱码或报错。原因是 Windows 系统下 Pandas 默认使用 GBK 解码,而这个包里的 CSV 大概率是 UTF-8 编码。解决方法是显式指定encoding="utf-8"。如果发现列名开头有看不到的字符,那一般是 UTF-8 BOM 导致,改用encoding="utf-8-sig"。

第二条:读进来之后,年龄人口列显示成字符串,没法求和。
现象是df["人口数"].dtype显示为object。原因是 CSV 里某些缺失值被读成空字符串,导致整列被推断为文本类型。解决办法是pd.to_numeric(df["人口数"], errors="coerce"),再按需填充或丢弃缺失值。

第三条:pd.cut报“Bins must increase monotonically”,或者分组后人数总和差一大截。
现象是分组结果严重偏小,或者直接报错。原因出在“100岁及以上”这种特殊值处理上。如果你的年龄下限没有提取出来,pd.cut会把年龄下限为 NaN 的行丢弃。解决方法是先做一步str.replace("岁及以上", "-100", regex=False),把开口区间改写成闭口区间,再提取下限。

5.2 分组和出图有问题:年龄排序、空白 HTML、合并 NaN

第四条:图表 X 轴顺序显示成“0-14岁、100岁及以上、15-59岁”。
现象是明明看着像年龄,但排序完全不对。原因是对字符串列直接排序,Python 是按字符编码顺序来的。解决办法是前面提过的提取“年龄下限”数值列作为排序键,或者干脆用pd.cut生成有序 Categorical 类型。用pd.cut生成的索引天然按区间顺序排列,自带排序属性,能避免这个坑。

第五条:打开生成的 HTML,图表区域是空白,或者提示找不到某个图表组件。
现象是 HTML 文件存在,但浏览器里只有工具栏没有图。原因通常是 pyecharts 版本不一致,导致图表注册组件缺失。解决方法是先检查代码里是否有from pyecharts.charts import Bar,再确认当前环境是 pyecharts 1.x 而不是 0.5.x。如果还不行,卸载重装依赖再加import pyecharts打印版本号排查。

第六条:用地区字段合并两个 CSV 后,人口列全是 NaN。
现象是合并成功,但目标列的值全空。原因是两张表里的地区名不一致,比如一张写“北京”,另一张写“北京市”,或者一张写“新疆”,另一张写“新疆维吾尔自治区”。解决方法是先做名称归一化,我一般会定义一个简短的映射字典,把常见缩写统一成完整官方名称,再执行合并。做任何合并之前,用set(表A["地区"]) - set(表B["地区"])找差异是最快的。

6. 进阶用法:把年龄结构脚本参数化成模板,后续任务半小时出图

这份资源最有价值的部分不是某一张图,而是整个年龄段处理思路。我后来在遇到全国人口年龄构成、地区人口年龄构成这类数据时,不再把分析步骤散落在多个单元格里,而是把核心流程包成一个函数,只暴露数据路径、分箱边界和输出路径三个参数。这样无论换 2000 年数据还是换某省数据,都能在半小时内出一版新图。

import pandas as pd from pyecharts.charts import Bar from pyecharts import options as opts def age_structure(csv_path, bins, labels, title, out_html): raw = pd.read_csv(csv_path, encoding="utf-8") raw["年龄段_clean"] = ( raw["年龄段"].astype(str).str.replace("岁及以上", "-100", regex=False) ) raw["年龄下限"] = raw["年龄段_clean"].str.extract(r"(\d+)-(\d+)", expand=True)[0] raw["年龄下限"] = pd.to_numeric(raw["年龄下限"], errors="coerce") raw["大组"] = pd.cut(raw["年龄下限"], bins=bins, labels=labels, right=False) grouped = raw.groupby("大组", observed=True)["人口数"].sum().reset_index() bar = ( Bar() .add_xaxis(grouped["大组"].tolist()) .add_yaxis("人口数", grouped["人口数"].tolist()) .set_global_opts(title_opts=opts.TitleOpts(title=title)) ) bar.render(out_html) return out_html

调用时只需要写一行:

age_structure( "data/各地区人口年龄构成.csv", bins=[0, 14, 60, 200], labels=["0-14岁", "15-59岁", "60岁及以上"], title="省级年龄结构", out_html="output/province_age.html", )

这里把grouped用reset_index()转成了普通 DataFrame,所以后面取大组列不会遇到索引类型问题。如果你需要对比 2000 年和 2020 年的城乡人口年龄分布,只要换csv_path,并把分箱边界保持口径一致,两张图就能放在同一份报告里互相解释。这也是我会推荐的最小可用模板:不要为了改一次数据就复制整个 Notebook,用函数把“读入、清洗、聚合、渲染”固定下来,剩下的交给参数。

在人口数据这类和社会现象直接挂钩的分析上,我吃过一次亏:当时没有先校验总人口,直接把图表交了出去,最后发现“100岁及以上”那一行在整个分析里被静默丢掉了。从那以后,我每次跑完脚本第一件事永远是打印一次总和,先和公开总量对齐,再谈可视化。这个习惯看起来笨,却替我挡掉了绝大部分返工。希望它能帮到你。

本文还有配套的精品资源,点击获取

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

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

立即咨询