文章简介

动态排序柱状图(Bar Chart Race)是 B 站、抖音等平台超火的数据可视化形式,直观又有视觉冲击力。本文基于 Python + Pandas + Pyecharts,从零完成1960-2024 世界各国人口动态轮播图开发,包含数据清洗、宽表转长表、黑名单过滤无效数据、B 站风格深色美化、时间轴轮播、交互优化全流程,代码可直接运行,最终产出可在浏览器打开的 HTML 动态图表。

技术栈:Python3 + Pandas + Pyecharts5 + ECharts实现效果:自动轮播、暂停 / 播放、年份手动切换、悬浮数据提示、动态彩色圆角柱状图、纯主权国家数据(剔除大洲 / 地区 / 非主权领地)

一、项目背景与目标

1.1 项目背景

动态排序轮播图(Bar Chart Race)是数据可视化经典样式,在 B 站数据类视频中出镜率极高。本次项目使用世界人口数据集,还原 1960-2024 年全球各国人口排名变迁,复刻 B 站同款视觉风格。

1.2 核心实现目标

  1. 数据纯净:过滤大洲、收入分组、海外领地、地区汇总等无效数据,仅保留 195 个主权国家;
  2. 动态排序:按年份自动轮播,人口越多国家排名越靠前;
  3. 全交互:悬浮查看人口数值、播放 / 暂停、手动切换年份;
  4. 视觉美化:深色 B 站风格、动态配色、圆角柱状图、流畅轮播动画。

二、环境与依赖准备

2.1 安装依赖库

运行代码前,先安装所需第三方库:

bash

运行

pip install pandas pyecharts

2.2 核心技术说明

  • Pandas:数据读取、空值处理、黑名单过滤、宽表转长表数据重塑;
  • Pyecharts:绘制横向柱状图 + Timeline 时间轴实现轮播;
  • JsCode:实现柱子动态循环配色;
  • CDN 配置:修复国内网络 ECharts 加载空白问题(关键配置)。

三、完整代码实现(分步讲解)

3.1 整体导入库 + 修复国内 CDN(必加)

国内网络直接运行 Pyecharts 极易出现 HTML 空白,必须替换 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,解决HTML图表空白问题(重中之重)
CurrentConfig.ONLINE_HOST = "https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/"

3.2 数据加载与初步探查

数据集:世界人口数据-中文版(1960-2024).csv,文件编码为gbk,放置在./data/目录下。

python

运行

# 读取CSV人口数据
df = pd.read_csv('./data/世界人口数据-中文版(1960-2024).csv', encoding='gbk')

# 查看数据前5行
print(df.head())
# 查看数据结构、缺失值
print(df.info())

原始数据说明

  • 共 266 行数据,包含国家、大洲、地区汇总等内容;
  • 70 列:4 列基础属性(国家名称、代码等)+ 1960-2025 年份列;
  • 2025 年数据全部为空,有效年份:1960-2024。

3.3 核心数据清洗(剔除无效数据)

原始数据包含大量大洲、收入分组、非主权领地、地区汇总,需要通过黑名单精准过滤。

3.3.1 划分属性列与年份列

python

运行

# 提取固定属性列(国家名称、编码等)
attr_cols = ["Country Name", "Country Code", "Indicator Name", "Indicator Code"]
# 提取所有年份列(纯数字列)
year_cols = [col for col in df.columns if str(col).isdigit()]
3.3.2 黑名单过滤(剔除非主权国家)

定义黑名单,过滤全球汇总、大洲、收入等级、海外领地、港澳台地区等无效数据:

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())

3.4 宽表转长表(数据重塑)

原始数据是宽格式(一行国家,多年份并列),时序轮播图需要长格式(一行 = 一个国家 + 一个年份 + 人口),使用pd.melt()完成转换:

python

运行

# 宽表转为长表,生成 Year、Population 字段
df_long = pd.melt(
    df,
    id_vars=attr_cols,
    value_vars=year_cols,
    var_name="Year",    # 拆分后的年份列名
    value_name="Population"  # 拆分后的人口列名
)

# 数据类型转换 + 清除空值
df_long["Year"] = df_long["Year"].astype(int)
df_long["Population"] = pd.to_numeric(df_long["Population"], errors="coerce")
# 删除人口为空的行(剔除2025无效数据)
df_clean = df_long.dropna(subset=["Population"])

# 只保留核心三列:国家、年份、人口
df_clean = df_clean[["Country Name", "Year", "Population"]]
print("清洗后有效数据条数:", len(df_clean))

3.5 核心可视化:B 站风格动态轮播图

包含动态配色、圆角柱子、深色背景、时间轴轮播、交互优化全套美化配置。

3.5.1 定义动态配色 JS 代码

实现柱子循环变色,提升视觉效果:

python

运行

# 动态颜色配置:不同排名柱子自动切换颜色
color_js = JsCode("""
function(params){
    let c = ['#ff4757','#ffa502','#fffa65','#2ed573','#1e90ff','#3742fa','#a55eea','#48dbfb'];
    return c[params.dataIndex % c.length];
}
""")
3.5.2 初始化时间轴 + 画布样式

采用 B 站同款极深黑背景 + 深色主题,设置大屏尺寸:

python

运行

# 初始化时间轴组件(整体画布)
timeline_final = Timeline(
    init_opts=opts.InitOpts(
        width="1600px",
        height="850px",
        theme=ThemeType.DARK,
        bg_color="#080808"  # 极深黑色背景,B站风格
    )
)

# 获取所有年份列表
year_list = df_clean["Year"].unique().tolist()
3.5.3 遍历年份,逐一生成柱状图

关键排序逻辑:数据升序 + 坐标轴反转,保证人口最多的国家显示在图表最上方。

python

运行

for year in year_list:
    # 筛选当前年份数据,取人口Top20
    year_data = df_clean[df_clean["Year"] == year].sort_values("Population", ascending=False).head(20)
    # 反转排序:配合reversal_axis实现从上到下降序
    year_data = year_data.sort_values("Population", ascending=True)
    
    # 拆分数据
    country_list = year_data["Country Name"].tolist()
    pop_list = year_data["Population"].tolist()

    # 构建单年份横向柱状图
    bar = (
        Bar()
        .add_xaxis(country_list)
        .add_yaxis(
            series_name="人口数量",
            y_axis=pop_list,
            itemstyle_opts=opts.ItemStyleOpts(
                color=color_js,     # 动态配色
                opacity=0.85,       # 透明度
                border_radius=7     # 圆角柱子
            ),
            label_opts=opts.LabelOpts(
                is_show=True,
                position="right",
                color="#ffffff",
                font_size=11,
                formatter="{c} 人"
            )
        )
        .reversal_axis()  # 反转坐标轴(横向柱状图核心)
        .set_global_opts(
            title_opts=opts.TitleOpts(
                title=f"{year}年 世界各国人口Top20",
                title_textstyle_opts=opts.TextStyleOpts(color="#ffffff", font_size=20)
            ),
            tooltip_opts=opts.TooltipOpts(
                trigger="axis",
                formatter="<b>{b}</b><br>人口:{c}"
            ),
            legend_opts=opts.LegendOpts(is_show=False)
        )
    )
    # 将当年图表加入时间轴
    timeline_final.add(bar, str(year))
3.5.4 时间轴轮播样式配置

设置自动播放、循环、切换速度、时间轴美化:

python

运行

# 时间轴全局配置
timeline_final.add_schema(
    is_auto_play=True,        # 自动播放
    is_loop_play=True,        # 循环播放
    play_interval=600,        # 切换间隔600ms,丝滑轮播
    is_timeline_show=True,
    pos_left="center",
    width="95%",
    # 时间轴文字样式
    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)
)
3.5.5 导出 HTML 文件

python

运行

# 生成HTML文件,浏览器直接打开运行
timeline_final.render("B站同款人口动态排行.html")
# Jupyter Notebook 内直接预览(可选)
# timeline_final.render_notebook()

四、运行效果与功能说明

4.1 输出文件

运行代码后,目录下会生成 B站同款人口动态排行.html,使用任意浏览器打开即可查看。

4.2 核心功能

  1. 自动轮播:从 1960 年自动切换至 2024 年,循环播放;
  2. 手动控制:点击底部时间轴可自由切换年份,支持暂停 / 播放;
  3. 悬浮提示:鼠标放在柱子上,显示国家名称 + 精确人口数;
  4. 视觉效果:深色 B 站风格、彩色圆角柱状图、排名动态变化;
  5. 数据纯净:仅展示 195 个主权国家,无大洲、地区汇总干扰。

五、项目重难点总结

  1. CDN 配置:国内网络必须替换 ECharts CDN,否则 HTML 图表空白;
  2. 数据清洗:黑名单过滤是核心,剔除汇总数据才能保证排名准确;
  3. 宽表转长表pd.melt() 是时序可视化的常用数据重塑方法;
  4. 排序 + 坐标轴反转:先升序再反转 Y 轴,实现 “人口越多越靠上” 的展示逻辑;
  5. 动态配色:通过 JsCode 结合索引取色,实现柱子循环变色。

六、项目拓展优化方向

  1. 添加国旗:在国家标签中嵌入 Base64 国旗图片,提升视觉体验;
  2. 多指标切换:扩展下拉框,支持人口增长率、GDP 等数据切换轮播;
  3. 性能优化:大数据量下预计算每年 Top20 数据,减少前端渲染压力;
  4. 单位优化:人口数值改为 “万人 / 亿人” 单位,数字展示更简洁。

七、完整项目总结

本项目完整走完数据读取→数据探查→清洗过滤→数据重塑→可视化美化全流程,是 Pandas + Pyecharts 综合实战的经典案例。复刻出的动态轮播图完美对标 B 站主流数据视频风格,代码注释完整、可直接复用,适合 Python 数据分析、数据可视化入门学习。


配套文件说明

  1. 数据集:世界人口数据-中文版(1960-2024).csv(放置在./data/文件夹);
  2. 运行环境:Python3.7+;
  3. 运行方式:PyCharm / Jupyter / VS Code 均可。

更多推荐