Python数据可视化实战:2011—2025年区域经济社会发展多维分析(Pandas+Matplotlib+Seaborn+Pyecharts)
一、项目背景
期末数据可视化课程设计,我选择以 上海市2011—2025年国民经济与社会发展统计数据 为研究对象,使用 Python 完成从数据清洗、特征工程到静态图表、统计图表、交互式图表的完整可视化分析流程,最终从经济总量、人口、投资、消费、收入、外贸、财政、就业、物价、民生支出等维度综合评估区域经济社会发展状况。
二、实验环境
-
操作系统:Windows 11
-
编程语言:Python 3.12.9
-
开发环境:Jupyter Notebook
-
依赖库:
-
pandas 2.2.0:数据读取、清洗与预处理
-
numpy 1.26.0:数值计算
-
matplotlib 3.8.0:静态可视化绘图
-
seaborn 0.13.0:统计可视化
-
pyecharts 2.0.4:交互式可视化
-
三、数据集说明
本项目使用 2011—2025年上海市国民经济与社会发展统计数据,共 15 组年度数据,包含 12 项核心统计指标。数据来源于国家统计局官网及上海市统计局公开统计年鉴,经人工整理为 UTF-8 编码的 CSV 文件。
| 字段名 | 含义 | 单位 |
|---|---|---|
| year | 统计年份 | 年 |
| gdp | 地区生产总值 | 亿元 |
| population | 年末常住人口 | 万人 |
| fixed_assets | 固定资产投资 | 亿元 |
| retail_sales | 社会消费品零售总额 | 亿元 |
| urban_income | 城镇居民人均可支配收入 | 元 |
| rural_income | 农村居民人均可支配收入 | 元 |
| export | 出口总额 | 亿元 |
| finance | 地方财政收入 | 亿元 |
| employment | 城镇就业人数 | 万人 |
| cpi | 居民消费价格指数 | 无 |
| education | 教育经费支出 | 亿元 |
| medical | 医疗卫生支出 | 亿元 |
数据特点:时间跨度 15 年,指标覆盖经济总量、人口、投资、消费、收入、外贸、财政、就业、物价、民生等多个维度;部分指标(固定资产投资、城镇就业人数、教育经费、医疗卫生支出)存在年度缺失值,需要在预处理阶段处理。
四、项目整体设计
-
数据预处理:数据读取、缺失值处理、描述性统计、衍生指标计算;
-
Matplotlib 可视化:绘制 2 行 3 列共 6 张子图的组合大图,覆盖趋势折线图、分组柱状图、堆叠柱状图、双轴图等(课程设计重点);
-
Seaborn 统计可视化:绘制相关性热力图、线性拟合散点图、城乡收入箱线图;
-
Pyecharts 可视化:制作 GDP 与常住人口双轴交互式组合图;
-
综合数据分析:结合图表得出结论。
五、数据预处理
1. 数据读取
使用 pandas.read_csv() 读取 CSV 文件,得到 15 行 × 13 列数据。
2. 缺失值处理
对固定资产投资、城镇就业人数、教育经费、医疗卫生支出四类存在缺失的指标,采用四步组合填充策略:
-
线性插值:
interpolate(method="linear"),根据前后年份数据进行线性插值,适合时间序列; -
后向填充:
bfill(),用后续年份数据填充前面的缺失值; -
前向填充:
ffill(),用前面年份数据填充后续的缺失值; -
均值填充:
fillna(df.mean()),对仍存在的极端缺失值使用全列均值。
经过处理后,数据集中缺失值数量降为 0。
3. 衍生指标计算
基于原始数据计算四类衍生指标:
-
GDP增长率(gdp_growth):
pct_change()计算环比增长率; -
城乡收入比(income_ratio):城镇居民收入 ÷ 农村居民收入;
-
财政GDP占比(finance_ratio):地方财政收入 ÷ GDP × 100%;
-
民生总支出(people_exp):教育经费 + 医疗卫生支出。
预处理完成后将数据导出为 processed_data.csv,作为后续可视化的标准数据集。
六、Matplotlib 可视化:六子图组合大图
使用 plt.subplots(2, 3) 绘制 2 行 3 列共 6 张子图,画布尺寸 18×10 英寸,总标题为"区域2011-2025年经济社会发展多维指标总图",最终保存为 300dpi 高清 PNG。
子图1:GDP年度变化趋势折线图
带圆点标记的蓝色折线图,直观反映 2011—2025 年 GDP 总量的增长轨迹。
子图2:固定资产投资与社零总额分组柱状图
橙色代表固定资产投资,绿色代表社会消费品零售总额,对比投资与消费两大需求的规模变化。
子图3:民生财政支出堆叠柱状图
紫色为教育经费,棕色为医疗卫生支出,展示民生支出的结构与总量变化。
子图4:GDP与财政收入双轴对比图
左轴 GDP(蓝色),右轴地方财政收入(红色),解决两类指标量级差异问题,对比经济总量与财政收入的增长关系。
子图5:出口总额年度趋势折线图
红色折线搭配 0.2 透明度填充区域,展示出口贸易规模的年度波动趋势。
子图6:城镇就业人数柱状图
青色柱状图,展示城镇就业规模的年度变化。
七、Seaborn 统计可视化
1. 经济指标相关性热力图
基于 12 项核心指标计算皮尔逊相关系数矩阵,采用 RdBu_r 红蓝配色,中心值设为 0,格子内保留 2 位小数,可以快速识别高度相关的指标对。
2. GDP与常住人口线性拟合散点图
使用 sns.regplot(),x 轴为常住人口(万人),y 轴为 GDP(亿元),散点大小 80、透明度 0.7,红色拟合线附带 95% 置信区间,用于分析人口规模与经济总量的线性关系。
3. 城乡居民收入分布箱线图
使用 pd.concat() 将城乡收入转换为"分类 + 金额"长表格式,蓝色与橙色分别代表城镇和农村收入,对比收入中位数、四分位距与极值分布。
八、Pyecharts 交互式可视化
使用 pyecharts 制作 GDP与常住人口双轴交互式折线图,实现以下功能:
-
双坐标轴:左轴 GDP(亿元),右轴常住人口(万人);
-
平滑曲线:
is_smooth=True; -
峰值标记:自动标记两条曲线的年度最大值;
-
十字准星悬浮提示:鼠标悬浮时显示对应年份两项指标数值;
-
Slider 数据缩放:底部滑块支持筛选年份区间;
-
图例切换:点击图例可隐藏/显示数据系列。
画布尺寸 1200px×500px,canvas 渲染,JS 资源使用 jsdelivr 在线 CDN 加载 ECharts,最终输出离线 HTML 文件,可在浏览器中交互操作。
说明:受本地浏览器文件 JS 安全策略限制,离线 HTML 在部分浏览器直接打开时可能出现渲染问题,代码逻辑本身无误,更换运行环境后即可正常展示。
九、综合分析结论
1. 经济增长稳健,发展质量提升
GDP 从 2011 年的 20406.1 亿元增长至 2025 年的 56708.7 亿元,年均增长率约 7.2%。GDP 增长率在 2012—2013 年达到峰值后逐步回落并趋于平稳,反映经济从高速增长向高质量发展转型。GDP 与财政收入双轴图显示两者保持同步增长,财政 GDP 占比稳定在 15%—17%,财政收支结构健康。
2. 城乡差距缩小,民生保障加强
城镇居民人均可支配收入从 37079 元增长至 96842 元,农村居民人均可支配收入从 15737 元增长至 48122 元,城乡收入比从 2.35 降至 2.01,差距持续缩小。民生总支出从 2011 年的 900.66 亿元增长至约 2900 亿元,年均增速约 8.5%,高于 GDP 增速,民生保障力度不断加大。
3. 消费作用增强,外贸保持韧性
消费规模持续扩大,内需驱动特征更加明显;出口总额虽受外部环境影响存在年度波动,但整体维持在较高水平;城镇就业人数稳步增长,就业形势总体稳定。
4. 人口经济相关,动能加速转换
GDP 与常住人口的 相关系数达到 0.98,人口规模是经济发展的重要支撑。近年来常住人口稳定在 2480 万人左右,人口红利逐步减弱,经济发展更多依赖人均产出提升和产业结构优化,发展动能正从要素驱动向创新驱动转换。
十、完整源码
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
# 一、数据读取
df = pd.read_csv('2011-2025年经济数据统计表.csv', encoding='utf-8')
print(f"数据读取完成:{df.shape[0]}行 × {df.shape[1]}列")
# 二、数据预处理
numeric_cols = ['fixed_assets', 'employment', 'education', 'medical']
for col in numeric_cols:
df[col] = pd.to_numeric(df[col], errors='coerce')
df = df.interpolate(method="linear")
df = df.bfill()
df = df.ffill()
df = df.fillna(df.mean())
df['gdp_growth'] = df['gdp'].pct_change().fillna(0) * 100
df['income_ratio'] = df['urban_income'] / df['rural_income']
df['finance_ratio'] = df['finance'] / df['gdp'] * 100
df['people_exp'] = df['education'] + df['medical']
core_cols = ['gdp', 'population', 'fixed_assets', 'retail_sales',
'urban_income', 'rural_income', 'export', 'finance',
'employment', 'cpi', 'education', 'medical']
desc_stats = df[core_cols].describe().round(2)
df.to_csv("processed_data.csv", index=False, encoding="utf-8-sig")
print("数据预处理完成,清洗后数据集已导出:processed_data.csv")
# 三、Matplotlib可视化(2行3列6张子图)
fig, axes = plt.subplots(2, 3, figsize=(18, 10))
fig.suptitle('区域2011-2025年经济社会发展多维指标总图', fontsize=16, weight="bold")
years = df["year"].values
axes[0,0].plot(years, df["gdp"].values, marker="o", c="#1f77b4", lw=2)
axes[0,0].set_title("GDP年度变化趋势折线图")
axes[0,0].set_xlabel("年份")
axes[0,0].set_ylabel("GDP(亿元)")
axes[0,0].grid(alpha=0.3)
x = np.arange(len(years))
width = 0.35
axes[0,1].bar(x-width/2, df["fixed_assets"].values, width, label="固定资产投资", color="#ff7f0e")
axes[0,1].bar(x+width/2, df["retail_sales"].values, width, label="社会消费品零售总额", color="#2ca02c")
axes[0,1].set_title("固定资产投资 vs 社零总额分组柱状图")
axes[0,1].set_xticks(x[::2])
axes[0,1].set_xticklabels(years[::2])
axes[0,1].legend()
axes[0,1].grid(axis="y", alpha=0.3)
axes[0,2].bar(years, df["education"].values, label="教育经费", color="#9467bd")
axes[0,2].bar(years, df["medical"].values, bottom=df["education"].values, label="医疗卫生", color="#8c564b")
axes[0,2].set_title("民生财政支出堆叠柱状图")
axes[0,2].set_xlabel("年份")
axes[0,2].set_ylabel("支出(亿元)")
axes[0,2].legend()
axes[0,2].grid(axis="y", alpha=0.3)
ax1 = axes[1,0]
ax2 = ax1.twinx()
ax1.plot(years, df["gdp"].values, "b-o", label="GDP")
ax2.plot(years, df["finance"].values, "r-s", label="地方财政收入")
ax1.set_xlabel("年份")
ax1.set_ylabel("GDP(亿元)", c="b")
ax2.set_ylabel("财政收入(亿元)", c="r")
axes[1,0].set_title("GDP与财政收入双轴对比图")
lines1, lab1 = ax1.get_legend_handles_labels()
lines2, lab2 = ax2.get_legend_handles_labels()
ax1.legend(lines1+lines2, lab1+lab2, loc="upper left")
axes[1,1].plot(years, df["export"].values, marker="^", c="#d62728", lw=2)
axes[1,1].fill_between(years, df["export"].values, alpha=0.2, color="#d62728")
axes[1,1].set_title("出口总额年度趋势折线图")
axes[1,1].set_xlabel("年份")
axes[1,1].set_ylabel("出口总额(亿元)")
axes[1,1].grid(alpha=0.3)
axes[1,2].bar(years, df["employment"].values, color="#17becf")
axes[1,2].set_title("城镇就业人数柱状图")
axes[1,2].set_xlabel("年份")
axes[1,2].set_ylabel("就业人数(万人)")
axes[1,2].grid(axis="y", alpha=0.3)
plt.tight_layout()
plt.subplots_adjust(top=0.9)
plt.savefig("matplotlib_6subplots.png", dpi=300, bbox_inches="tight")
plt.show()
print("Matplotlib六张子组合图已保存:matplotlib_6subplots.png")
# 四、Seaborn统计可视化
plt.figure(figsize=(12,8))
sns.heatmap(df[core_cols].corr(), annot=True, cmap="RdBu_r", center=0, fmt=".2f", square=True)
plt.title("经济指标相关性热力图", fontsize=14, weight="bold")
plt.tight_layout()
plt.savefig("seaborn_heatmap.png", dpi=300, bbox_inches="tight")
plt.show()
plt.figure(figsize=(10,6))
sns.regplot(x="population", y="gdp", data=df,
scatter_kws={"s":80, "alpha":0.7, "color":"#1f77b4"},
line_kws={"linewidth":2, "color":"red"})
plt.title("GDP与常住人口线性拟合散点图", fontsize=14)
plt.xlabel("常住人口(万人)")
plt.ylabel("GDP(亿元)")
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig("seaborn_scatter.png", dpi=300, bbox_inches="tight")
plt.show()
plt.figure(figsize=(8,6))
income_data = pd.concat([
pd.DataFrame({"分类":"城镇收入","金额":df["urban_income"]}),
pd.DataFrame({"分类":"农村收入","金额":df["rural_income"]})
])
sns.boxplot(x="分类", y="金额", data=income_data, palette=["#1f77b4","#ff7f0e"])
plt.title("城乡居民收入分布箱线图", fontsize=14)
plt.grid(axis="y", alpha=0.3)
plt.tight_layout()
plt.savefig("seaborn_boxplot.png", dpi=300, bbox_inches="tight")
plt.show()
print("Seaborn三张统计图表已保存")
# 五、Pyecharts交互式可视化
try:
from pyecharts.charts import Line
from pyecharts import options as opts
from pyecharts.globals import CurrentConfig
CurrentConfig.ONLINE_HOST = "https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/"
line = (
Line(init_opts=opts.InitOpts(width="1200px", height="500px", renderer="canvas"))
.add_xaxis(df["year"].astype(str).tolist())
.add_yaxis(
"GDP(亿元)",
df["gdp"].round(2).tolist(),
yaxis_index=0,
is_smooth=True,
markpoint_opts=opts.MarkPointOpts(data=[opts.MarkPointItem(type_="max", name="GDP最大值")])
)
.add_yaxis(
"常住人口(万人)",
df["population"].round(2).tolist(),
yaxis_index=1,
is_smooth=True,
markpoint_opts=opts.MarkPointOpts(data=[opts.MarkPointItem(type_="max", name="人口最大值")])
)
.extend_axis(
yaxis=opts.AxisOpts(
name="常住人口(万人)",
type_="value",
position="right",
axislabel_opts=opts.LabelOpts(formatter="{value} 万人")
)
)
.set_global_opts(
title_opts=opts.TitleOpts(title="GDP与常住人口双轴交互图", subtitle="2011-2025年"),
tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="cross"),
legend_opts=opts.LegendOpts(pos_left="center", pos_top="5%"),
xaxis_opts=opts.AxisOpts(name="年份"),
yaxis_opts=opts.AxisOpts(name="GDP(亿元)", axislabel_opts=opts.LabelOpts(formatter="{value} 亿元")),
datazoom_opts=[opts.DataZoomOpts(type_="slider")]
)
)
line.render("gdp_pop_inter.html")
print("Pyecharts交互图已生成:gdp_pop_inter.html")
except ImportError:
print("未安装pyecharts库,交互图未生成")
print("\n全部代码执行完毕。")
更多推荐


所有评论(0)