一、项目概述

1.1 项目背景

动态排序轮播图(Bar Chart Race,条形竞赛图)是数据可视化热门形式,在 B 站数据分析类视频应用广泛。本项目依托1960-2024 全球各国人口数据集,通过 Python 数据分析 + Pyecharts 可视化完整复刻专业级动态人口排名图表,直观展现 64 年间全球各国人口数量变迁历程。

1.2 项目目标

  1. 数据纯净:过滤大洲汇总、收入分组、海外领地等无效数据,最终仅保留195 个主权国家数据;
  2. 动态展示:图表自动按年份轮播切换,每年按人口降序排序,人口越多国家图表位置越靠上;
  3. 全交互:生成 HTML 图表支持悬浮查看人口数值、手动切换年份、暂停 / 自动播放;
  4. 商用美化:深色暗黑主题、柱子动态随机配色、自定义时间轴样式,对标 B 站专业可视化效果。

二、技术栈与运行环境

表格

工具用途
Python3.x项目开发编程语言
pandas数据读取、清洗、宽长格式转换
pyecharts柱状图 + Timeline 时间轴动态绘图
JsCode实现柱子动态随机配色

环境安装

bash

运行

# 安装可视化依赖库
pip install pyecharts -i https://pypi.tuna.tsinghua.edu.cn/simple

若提示已安装代表环境就绪,Jupyter 环境安装完成后建议重启内核。

三、数据加载与探查

3.1 读取原始 CSV 数据

数据源:世界人口数据-中文版(1960-2024).csv,中文编码使用gbk,修改本地文件路径即可运行。

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

# 解决国内echarts CDN加载空白问题(关键配置)
CurrentConfig.ONLINE_HOST = "https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/"

# 读取csv,替换为你的本地文件路径
df = pd.read_csv('D:\LXY\pandas数据导入\世界人口数据-中文版(1960-2024).csv',encoding="gbk")
df.head()

原始数据预览:

表格

Country NameCountry CodeIndicator NameIndicator Code19601961
0阿鲁巴ABW人口,总数SP.POP.TOTL54922.055578.0
1NaNAFE人口,总数SP.POP.TOTL130075728.0133534923.0
2阿富汗AFG人口,总数SP.POP.TOTL9035043.09214083.0

3.2 数据结构说明

  • 原始数据共266 行、70 列
  • 属性字段 4 列:Country Name(国家名)、Country Code(国家编码)、Indicator Name、Indicator Code
  • 年份字段 66 列:1960~20252025 全为空值 NaN,有效年份 1960-2024
  • 数据问题:包含大洲汇总、收入分类、非主权领地数据,需要黑名单剔除。

四、数据清洗预处理(核心步骤)

4.1 筛选字段

python

运行

# 筛选年份数字列、基础属性列
year_cols=[i for i in df.columns if i.isdigit()]
attr_cols=['Country Name', 'Country Code','Indicator Name','Indicator Code']

# 删除国家名称为空的汇总行
df=df.dropna(subset=['Country Name'])

4.2 黑名单过滤(精准筛选 195 个主权国家)

黑名单包含:全球 / 大洲汇总、收入等级、人口红利分类、世行分组、海外非主权领地。

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" # 人口数值列
)
df_long.head()

转换后长表结构:

表格

Country NameCountry CodeIndicator NameIndicator CodeYearPopulation
阿富汗AFG人口,总数SP.POP.TOTL19609035043.0

4.4 数据类型转换 & 空值清洗

python

运行

# 只保留绘图需要三列
df_clean=df_long[["Country Name","Year","Population"]].copy()
# 年份转int
df_clean["Year"]=df_clean["Year"].astype(int)
# 人口转为数值,异常值转为空
df_clean["Population"]=pd.to_numeric(df_clean["Population"],errors="coerce")
# 删除空数据(剔除2025无效空值)
df_clean.dropna(inplace=True)
# 查看最终干净数据
df_clean.head()

使用.copy()规避 pandas SettingWithCopyWarning 警告。

五、可视化分步实现

5.1 单年份测试绘图(1990 年 TOP20 人口)

先单独绘制 1990 年横向柱状图,验证绘图逻辑:

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年世界人口TOP20",pos_left="center"),
        legend_opts=opts.LegendOpts(is_show=False),
    )
    .set_series_opts(label_opts=opts.LabelOpts(position="right"))
)
# 生成单图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:
    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}年世界人口排名TOP20",pos_left="center"),
            legend_opts=opts.LegendOpts(is_show=False),
        )
        .set_series_opts(label_opts=opts.LabelOpts(position="right"))
    )
    timeline.add(bar,str(year))

# 开启自动轮播
timeline.add_schema(is_auto_play=True,play_interval=600,is_loop_play=True)
timeline.render("基础人口轮播图.html")

5.3 B 站风格美化完整版(最终成品代码)

添加动态随机配色、自定义深色背景、美化时间轴样式,商用级可视化:

python

运行

# JS动态配色:不同排名柱子自动变色
color_js = JsCode("""
function(params){
let c = ['#ff4757','#ffa502','#fffa65','#2ed573','#1e90ff','#3742fa','#a55eea','#48dbfb']
return c[params.dataIndex % c.length];
}
""")

# 初始化美化版时间线
timeline_final = Timeline(init_opts=opts.InitOpts(
    width="1600px",
    height="850px",
    theme=ThemeType.DARK,
    bg_color="#080808" # 极深黑背景
))

year_list=df_clean["Year"].unique().tolist()
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))
        .reversal_axis()
        .set_global_opts(
            title_opts=opts.TitleOpts(title=f"{year}年全球人口TOP20排名",pos_left="center"),
            legend_opts=opts.LegendOpts(is_show=False),
        )
        .set_series_opts(label_opts=opts.LabelOpts(position="right"))
    )
    timeline_final.add(bar,str(year))

# 自定义时间轴样式:字体、轴线、节点配色
timeline_final.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_final.render("B站同款人口动态排行.html")
# Jupyter单元格内直接展示
timeline_final.render_notebook()

六、项目成果与总结

6.1 输出成果

  1. 清洗后数据集:仅 195 个主权国家、1960-2024 有效人口数据,合计 12675 条有效记录;
  2. 可视化文件:B站同款人口动态排行.html,浏览器打开即可交互:
    • 自动循环播放年份切换,间隔 600ms;
    • 鼠标悬浮柱子查看精确人口;
    • 手动点击底部年份快速跳转;
    • 柱子随排名自动变换颜色、深色沉浸主题。

6.2 项目知识点总结

  1. Pandas:数据空值删除、黑名单筛选、melt()宽转长核心用法;
  2. Pyecharts:横向柱状图reversal_axis()、Timeline 时间轴组件、JsCode 动态样式注入;
  3. 避坑点:配置ONLINE_HOST替换 CDN,解决国内 HTML 图表空白;使用.copy()消除切片警告。

七、拓展优化方向

  1. 新增人口增长率计算,增加折线副图;
  2. 筛选大洲单独绘图,如亚洲各国人口变迁;
  3. 导出图片 / GIF 动图,适配短视频素材。

更多推荐