Python+pandas+pyecharts|B 站风世界人口动态轮播柱状图(1960-2024 完整实战)
·
一、项目概述
1.1 项目背景
动态排序轮播图(Bar Chart Race)是 B 站数据区热门可视化形式,直观展示指标随年份变迁的排名变化。本项目基于世界银行全球人口数据集,用 Python 完成数据清洗→宽长格式转换→动态可视化全流程,复刻高质量世界各国人口年度动态排名图,时间跨度 1960~2024 年。
1.2 项目目标
- 数据纯净:剔除全球汇总、各大洲、收入分组、海外领地等非主权数据,最终留存 195 个独立主权国家;
- 动态轮播:图表自动按年份轮播,人口数值越高,国家在图表越靠上;
- 交互完整:悬浮查看人口、暂停 / 自动播放、手动切换指定年份;
- 视觉美化:深色 B 站风格、循环配色圆角柱状、自定义标签格式。
1.3 技术栈
- 编程语言:Python3.x
- 数据处理:
pandas(数据读取、清洗、宽表转长表) - 可视化:
pyecharts(Bar 柱状图 + Timeline 时间轴轮播) - 辅助:
JsCode实现柱子动态循环配色、自定义 CDN 解决国内 HTML 加载空白
二、环境准备与依赖安装
2.1 安装依赖包
python运行
# 安装pyecharts可视化库
!pip install pyecharts
2.2 导入全部依赖 + 配置国内 CDN(关键,解决网页白屏)
python运行
import pandas as pd
from pyecharts.charts import Bar, Timeline
from pyecharts import options as opts
from pyecharts.globals import ThemeType, CurrentConfig
from pyecharts.commons.utils import JsCode
# 配置国内可用CDN,解决echarts国内加载失败空白问题
CurrentConfig.ONLINE_HOST = "https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/"
三、数据加载与原始数据探查
3.1 读取 CSV 数据集
数据集:世界人口数据-中文版(1960-2024).csv,原始为宽表结构,年份横向平铺为列,编码使用gbk适配中文。
python运行
# 读取本地csv文件
df = pd.read_csv('D:\wxl\code\数据查询\世界人口数据-中文版(1960-2024) (1).csv',encoding="gbk")
# 预览前5行数据
df.head()
原始数据结构说明:
- 属性固定列:
Country Name(国家名称)、Country Code(国家代码)、Indicator Name、Indicator Code - 年份列:1960~2025 共 66 列,2025 年全为空值,有效数据 1960-2024
- 原始 266 行数据:包含全球汇总、各大洲、非主权地区、主权国家,需要过滤无效行
3.2 数据基本信息查看
python运行
df.info()
总数据:266 行 ×70 列;object 类型 4 列(属性),float64 类型 66 列(年份人口)。
四、数据清洗与预处理(核心步骤)
4.1 筛选属性列、年份列
python运行
# 筛选列名全为数字的年份字段
year_cols = [i for i in df.columns if i.isdigit()]
# 固定属性字段列表
attr_cols = ['Country Name','Country Code','Indicator Code','Indicator Name']
4.2 清洗无效数据:空值删除 + 黑名单过滤
① 删除国家名称为空的行(大洲汇总行大多无国家名)
python运行
# 删除Country Name为空的汇总数据
df = df.dropna(subset=['Country Name'])
② 黑名单剔除非主权地区(全球 / 大洲 / 收入分组 / 海外领地)
python运行
# 黑名单:汇总区域、非主权地区、收入分类
black_list = [
"世界", "北美", "东亚与太平洋地区", "欧洲与中亚地区",
"东亚与太平洋地区(不包括高收入)", "欧洲与中亚地区(不包括高收入)",
"拉丁美洲与加勒比海地区", "拉丁美洲与加勒比海地区(不包括高收入)",
"中东、北非、阿富汗与巴基斯坦", "中东与北非地区(不包括高收入)",
"撒哈拉以南非洲地区", "撒哈拉以南非洲地区(不包括高收入)",
"南亚", "小国", "加勒比小国", "太平洋岛国", "其他小国",
"未分类国家", "阿拉伯联盟国家", "欧洲联盟", "欧洲货币联盟",
"经合组织成员", "重债穷国 (HIPC)", "脆弱和受衝突影響的情況下", "高收入国家",
"低收入国家", "中等收入国家","中高等收入国家", "中低等收入国家", "中低收入国家",
"早人口紅利", "後期人口紅利", "預人口紅利", "人口紅利之後", "最不发达国家:联合国分类",
"IBRD与IDA", "只有IBRD", "只有IDA", "IDA總", "IDA混合",
"东亚与太平洋地区 (IBRD与IDA)","欧洲与中亚地区 (IBRD与IDA)",
"拉丁美洲与加勒比海地区 (IBRD与IDA)","中东与北非地区 (IBRD与IDA)",
"南亚 (IBRD与IDA)","撒哈拉以南非洲地区 (IBRD与IDA)",
"阿鲁巴", "美属萨摩亚", "百慕大", "库拉索", "开曼群岛","海峡群岛",
"法罗群岛", "直布罗陀", "格陵兰", "关岛","中国香港特别行政区",
"中国澳门特别行政区", "圣马丁(法属)","圣马丁(荷属)","北马里亚纳群岛",
"新喀里多尼亚", "波多黎各", "约旦河西岸和加沙","法属波利尼西亚",
"特克斯科斯群岛","英屬維爾京群島", "美属维京群岛", "科索沃"
]
# 反向过滤黑名单数据
df = df[~df["Country Name"].isin(black_list)].reset_index(drop=True)
print("过滤后剩余主权国家数量:",df["Country Name"].nunique())
# 输出:过滤后剩余主权国家数量:195
4.3 宽表转长表(pd.melt 重塑数据,绘图必备)
原始数据一行一个国家、多列年份(宽表),melt拆分后变成一行 = 一个国家单年人口(长表)
python运行
# 宽表转长格式
df_long = pd.melt(
df,
id_vars=attr_cols, # 保留不变的属性列
value_vars=year_cols, # 需要拆分的所有年份列
var_name="Year", # 拆分后新列:年份
value_name="Population" # 拆分后新列:人口数值
)
4.4 字段精简 + 类型转换 + 缺失值清理
python运行
# 仅保留绘图需要三列:国家、年份、人口
df_clean = df_long[["Country Name","Year","Population"]].copy()
# 年份转为整型
df_clean["Year"] = df_clean["Year"].astype(int)
# 人口转为数值格式
df_clean["Population"] = pd.to_numeric(df_clean["Population"])
# 删除空值(剔除2025全空数据)
df_clean.dropna(inplace=True)
# 预览清洗后数据
df_clean.head()
五、可视化分步实现
5.1 单年份柱状测试(1990 年 TOP20 人口,验证绘图逻辑)
python运行
# 筛选1990年数据,人口降序取前20
test_df = df_clean[df_clean['Year']==1990]
test_df = test_df.sort_values(by='Population',ascending=False).head(20)
# 绘制横向柱状图
bar_test = (
Bar(init_opts=opts.InitOpts(theme=ThemeType.DARK))
.add_xaxis(test_df["Country Name"].tolist())
.add_yaxis("人口",test_df["Population"].tolist())
.reversal_axis() # 坐标轴反转→横向柱状
.set_global_opts(
title_opts=opts.TitleOpts(title="1990年世界人口排名",pos_left="center"),
legend_opts=opts.LegendOpts(is_show=False)
)
.set_series_opts(label_opts=opts.LabelOpts(position="right"))
)
# Jupyter预览
bar_test.render_notebook()
# 导出HTML文件
# bar_test.render("1990人口柱状.html")
5.2 基础版 Timeline 年份轮播图
python运行
# 获取全部有效年份列表
year_list = df_clean["Year"].unique().tolist()
# 初始化时间轴
timeline = Timeline(init_opts=opts.InitOpts(width="1500px",height="820px",theme=ThemeType.DARK))
# 循环每个年份生成柱状图
for year in year_list:
# 筛选当年数据、降序取前20、升序排序适配横向反转
data_df = df_clean[df_clean["Year"]==year]
data_df = data_df.sort_values(by="Population",ascending=False).head(20)
data_df = data_df.sort_values(by="Population",ascending=True)
country = data_df["Country Name"].tolist()
population = data_df["Population"].tolist()
bar = (
Bar(init_opts=opts.InitOpts(theme=ThemeType.DARK))
.add_xaxis(country)
.add_yaxis("人口",population)
.reversal_axis()
.set_global_opts(
title_opts=opts.TitleOpts(title=f"{year}年世界人口排名",pos_left="center"),
legend_opts=opts.LegendOpts(is_show=False)
)
.set_series_opts(label_opts=opts.LabelOpts(position="right"))
)
timeline.add(bar,str(year))
# 配置轮播参数:自动播放、间隔600ms、循环播放
timeline.add_schema(is_auto_play=True,play_interval=600,is_loop_play=True)
# 渲染输出
timeline.render("基础轮播图.html")
# timeline.render_notebook()
5.3 B 站美化完整版(动态变色 + 圆角柱子 + 自定义时间轴样式)
python运行
# JS代码:实现柱子循环随机配色
color_js = JsCode("""
function(params){
let c =['#ff4757','#ffa502','#fffa65','#2ed573','#1e90ff','#3742fa','#a55eea'];
return c[params.dataIndex % c.length];
}
""")
year_list = df_clean["Year"].unique().tolist()
# 初始化深色背景时间轴
timeline = Timeline(init_opts=opts.InitOpts(
width="1600px",
height="850px",
theme=ThemeType.DARK,
bg_color="#080808" # 极深黑背景,B站风格
))
# 遍历年份绘图
for year in year_list:
data_df = df_clean[df_clean["Year"]==year]
data_df = data_df.sort_values(by="Population",ascending=False).head(20)
data_df = data_df.sort_values(by="Population",ascending=True)
country = data_df["Country Name"].tolist()
population = data_df["Population"].tolist()
bar = (
Bar(init_opts=opts.InitOpts(theme=ThemeType.DARK))
.add_xaxis(country)
# 配置圆角、透明度、动态颜色、右侧标签格式
.add_yaxis("人口",population,
itemstyle_opts=opts.ItemStyleOpts(color=color_js,opacity=0.85,border_radius=7),
label_opts=opts.LabelOpts(position="right",formatter="{c}人",font_size=11)
)
.reversal_axis()
.set_global_opts(
title_opts=opts.TitleOpts(title=f"{year}年世界人口TOP20排名",pos_left="center"),
legend_opts=opts.LegendOpts(is_show=False)
)
)
timeline.add(bar,str(year))
# 自定义时间轴样式:蓝色轴线、白色加粗年份文字
timeline.add_schema(
is_auto_play=True,
play_interval=600,
is_loop_play=True,
label_opts=opts.LabelOpts(color="#ffffff", font_size=11,font_weight="bold"),
linestyle_opts=opts.LineStyleOpts(color="#00a1ff", width=4),
itemstyle_opts=opts.ItemStyleOpts(color="#00a1ff",border_color="#ffffff", border_width=2)
)
# 最终生成成品HTML
timeline.render("B站风人口动态轮播图.html")
# timeline.render_notebook()
六、项目成果与效果说明
- 数据成果:原始 266 条数据过滤后保留195 个主权国家,时间跨度 1960-2024,共 195×65=12675 条有效人口数据;
- 可视化效果:
- 自动轮播:600ms 切换一个年份,支持暂停 / 手动点击年份跳转;
- 视觉:深色黑底、7 色循环圆角柱状、人口数值标注在柱子右侧;
- 交互:鼠标悬浮可查看对应国家人口详情;
- 拓展方向:可筛选单个大洲、单独分析中印人口增速、导出 GIF 动态图。
七、踩坑总结
- HTML 页面空白:必须配置
CurrentConfig.ONLINE_HOST国内 CDN 地址,默认国外 CDN 国内无法加载; - KeyError 列名报错:原始数据列名中英文 / 空格不一致,提前用
df.columns核对字段; - 排序颠倒:先
降序取TOP20再升序+reversal_axis(),实现人口越多越在图表上方; - SettingWithCopyWarning:字段赋值使用
.copy()生成新 DataFrame 消除警告。
更多推荐
所有评论(0)