python之动态柱状图

全球GDP

用什么结构,不看你画什么图,而看你"按什么查找数据"。

按顺序查 → 列表;按键查 → 字典。

场景 用列表还是字典 根本原因
普通柱状图 列表 只用一次,不需要"按某字段查找"
折线图 列表 年份天然有序,直接按位置索引即可
动态柱状图 字典 需要 按年份快速获取一组数据
动态折线图 字典 需要 按国家快速获取一条时间线

关键:看你要"按什么查"

  • 列表:适合"顺序访问"(第1个、第2个......)

  • 字典 :适合"按键查找"(给我 key=1960 的数据)

动态柱状图要 按年份取 8 个国家 ,所以 key=年份,字典最合适。

1.读取 CSV 文件

python

复制代码
f = open("1960-2019全球GDP数据.csv", "r", encoding="utf-8")
data_lines = f.readlines()
f.close()
data_lines.pop(0)
代码 解释
open("...csv", "r", encoding="utf-8") 只读模式 打开 CSV 文件,用 UTF-8 编码(确保中文不乱码)
f.readlines() 读取文件的所有行,返回一个字符串列表,每行是一个元素
f.close() 关闭文件,释放资源(好习惯)
data_lines.pop(0) 删除列表的第一个元素 (表头 year,country,gdp),只保留数据行

举例:

python

复制代码
# 原始 data_lines:
["year,country,gdp", "1960,美国,543300000000", "1960,中国,59716467625", ...]

# pop(0) 之后:
["1960,美国,543300000000", "1960,中国,59716467625", ...]

2.将数据整理成字典

python

复制代码
data_dict = {}
for line in data_lines:
    parts = line.strip().split(",")
    year = int(parts[0])
    country = parts[1]
    gdp = float(parts[2])
    if year not in data_dict:
        data_dict[year] = []
    data_dict[year].append([country, gdp])

字典结构

python

复制代码
data_dict = {
1960: \[\["美国", 5433\], \["中国", 597\], \["日本", 443\], ...\],
1961: \[\["美国", 5633\], \["中国", 500\], \["日本", 535\], ...\],
1962: \[\["美国", 5800\], \["中国", 520\], \["日本", 600\], ...\],
...
}

这是程序运行后data_dict 在内存中呈现的样子。

代码 解释
data_dict = {} 创建空字典,格式:{年份: [[国家, GDP], [国家, GDP], ...]}
for line in data_lines: 遍历每一行数据
line.strip().split(",") 去掉首尾空格/换行,然后按逗号分割成列表
year = int(parts[0]) 第一列是年份,转成整数
country = parts[1] 第二列是国家名
gdp = float(parts[2]) 第三列是 GDP 数值,转成浮点数
if year not in data_dict: 如果这个年份还没在字典里
data_dict[year] = [] 创建一个空列表
data_dict[year].append([country, gdp]) [国家, GDP] 添加到该年份的列表中

最终 data_dict 结构示例:

text

复制代码
CSV 文件内容:
┌──────────────────────────────────────┐
│ 1960,美国,543300000000 │ ← 第1行
│ 1960,中国,59716467625 │ ← 第2行
│ 1960,日本,44307342950 │ ← 第3行
│ 1961,美国,563300000000 │ ← 第4行
│ 1961,中国,50000000000 │ ← 第5行
└──────────────────────────────────────┘
↓ 逐行处理
第1行: year=1960, country="美国", gdp=543300000000
→ data_dict 中没有 1960 → 创建 data_dict\[1960\] = \[\]
→ data_dict\[1960\].append(\["美国", 543300000000\])
第2行: year=1960, country="中国", gdp=59716467625
→ data_dict 中已有 1960 → 直接追加
→ data_dict\[1960\].append(\["中国", 59716467625\])
第3行: year=1960, country="日本", gdp=44307342950
→ data_dict\[1960\].append(\["日本", 44307342950\])
第4行: year=1961, country="美国", gdp=563300000000
→ data_dict 中没有 1961 → 创建 data_dict\[1961\] = \[\]
→ data_dict\[1961\].append(\["美国", 563300000000\])
第5行: year=1961, country="中国", gdp=50000000000
→ data_dict\[1961\].append(\["中国", 50000000000\])
↓ 最终结果
data_dict = {
1960: \[\["美国", 543300000000\], \["中国", 59716467625\], \["日本", 44307342950\]\],
1961: \[\["美国", 563300000000\], \["中国", 50000000000\]\],
...
}

先按照逗号进行分割,然后如果没有年份的键值,那么就创建一个新的然后在添加元素,组成新的字典。字典里的键值是年份,元素是列表,包括 GDP 和国家。

原始 CSV 一行: "1960,美国,543300000000"

↓ split(",") 分割

"1960", "美国", "543300000000"

↓ 提取

year = 1960, country = "美国", gdp = 543300000000

↓ 检查字典

if year not in data_dict: ← 1960 在字典里吗?
data_dictyear = \[\] ← 不在 → 创建一个空列表

↓ 添加

data_dict1960.append("美国", 543300000000)

国家, GDP

把文档里的三个值(年份、国家、GDP),用年份作为键,用国家+GDP 作为值。值是一个列表,里面可以包含很多个 [国家, GDP]。因为年份不重复,所以用年份当键很合适。

为什么用年份当键?

类比帮助理解

想象一个文件柜

text

复制代码
文件柜(字典 data_dict)
    ├── 抽屉 1960(键) →  里面放了一叠文件(列表)
    │                       ├── 文件1: ["美国", 5433]
    │                       ├── 文件2: ["中国", 597]
    │                       └── 文件3: ["日本", 443]
    ├── 抽屉 1961(键) →  里面放了一叠文件(列表)
    │                       ├── 文件1: ["美国", 5633]
    │                       └── 文件2: ["中国", 500]
    └── 抽屉 1962(键) →  里面放了一叠文件(列表)
                            ├── 文件1: ["美国", 5800]
                            ├── 文件2: ["中国", 520]
                            └── 文件3: ["日本", 600]
  • 文件柜 = 字典 data_dict

  • 抽屉标签 = 键(年份)

  • 抽屉里的文件 = 值(列表)

原因 说明
不重复 1960 年只有一个,不会出现两个 1960
唯一标识 每个年份代表一组数据
快速查找 想取 1960 年的数据,直接 data_dict[1960] 就行

3.获取所有年份并排序

从为年份开始排序,按照gdp从大到小排序,然后只排序前8个,x轴为国家,y轴为GDP**。**

python

复制代码
# 1. 排序年份
sorted_year_list = sorted(data_dict.keys())
# 2. 遍历每一年
for year in sorted_year_list:
# 3. 对该年的数据按 GDP 降序排序(从大到小),lambda
data_dict\[year\].sort(key=lambda element: element\[1\], reverse=True)
# 4. 取前8名
year_data = data_dict\[year\]\[0:8\]
# 5. 准备 x 轴(国家)和 y 轴(GDP)数据
x_data = \[\]
y_data = \[\]
for country_gdp in year_data:
x_data.append(country_gdp\[0\]) # 国家名
y_data.append(country_gdp\[1\] / 100000000) # GDP 转亿

总结

代码 作用
sorted(data_dict.keys()) 年份排序(1960→2019)
data_dict[year].sort(key=lambda x: x[1], reverse=True) 该年数据按 GDP 从大到小排序
data_dict[year][0:8] 取前 8 名
x_data.append(country_gdp[0]) 提取国家名
y_data.append(country_gdp[1] / 100000000) 提取 GDP 并转成"亿"

核心总结

代码 作用
line.strip() 去掉换行符 \n 和首尾空格
.split(",") 按逗号切成列表
int(parts[0]) 年份转整数(为了排序)
parts[1] 国家名(保持文本)
float(parts[2]) GDP 转浮点数(为了计算)

python

复制代码
sorted_years = sorted(data_dict.keys())
代码 解释
data_dict.keys() 获取字典的所有键(年份)
sorted() 对年份升序排序(1960, 1961, 1962...)

反转 xy 轴就是让国家名在 y 轴、GDP 数值在 x 轴,变成横向柱状图。然后在排序年份之前创建时间线对象,排序好之后,以年份为时间线对象,就得到了动态柱状图。

但是还缺了排序,也就是GDP应该从高到低,高的应该排在最上边。

复制代码
# ===== 关键1:反转数据,让最大的显示在最上面 =====
top8 = list(reversed(top8))# 准备数据(用反转后的 top8) x_data = \[item\[0\] for item in top8\] y_data = \[round(item\[1\] / 100000000, 2) for item in top8\]

等价于

**x_data = \[\]
y_data = \[\]
for country_gdp in year_data:

country_gdp = "中国", 2888198000000

x_data.append(country_gdp0) # → "中国"
y_data.append(country_gdp1 / 100000000) # → 28881.98

循环完所有国家,结果一样**

加上此代码,就可改变。



补充:

这段代码是列表推导式 ,用来从 top8 中提取国家名和 GDP 数据。


第一步:提取国家名

python

复制代码
x_data = [item[0] for item in top8]
部分 含义
top8 一个列表,里面包含 8 个 [国家, GDP] 的小列表
for item in top8 遍历 top8,每次取出一个小列表赋值给 item
item[0] 取小列表的第 1 个元素(国家名)
[ ... ] 把所有结果放在一个新列表中

执行过程:

python

复制代码
# 假设 top8 是这样的:
top8 = \[
\["中国", 2888198000000\],
\["印度", 1453931000000\],
\["美国", 1086106000000\],
\["俄罗斯", 917687000000\],
\["巴西", 621887000000\],
\["德国", 563854000000\],
\["日本", 480920000000\],
\["西班牙", 373293000000\]
\]
# 循环过程:
# 第1次:item = \["中国", 2888198000000\] → item\[0\] = "中国"
# 第2次:item = \["印度", 1453931000000\] → item\[0\] = "印度"
# 第3次:item = \["美国", 1086106000000\] → item\[0\] = "美国"
# ...
# 最终结果:
x_data = \["中国", "印度", "美国", "俄罗斯", "巴西", "德国", "日本", "西班牙"\]

第二步:提取 GDP 并转换单位

python

复制代码
y_data = [round(item[1] / 100000000, 2) for item in top8]
部分 含义
item[1] 取小列表的第 2 个元素(GDP 原始数值,单位是"美元")
/ 100000000 除以 1 亿,把"美元"转成"亿美元"
round(..., 2) 四舍五入,保留 2 位小数
[ ... ] 把所有结果放在一个新列表中

执行过程:

python

复制代码
# 假设 top8 是这样的:
top8 = \[
\["中国", 2888198000000\],
\["印度", 1453931000000\],
...
\]
# 循环过程:
# 第1次:item = \["中国", 2888198000000\]
# item\[1\] = 2888198000000
# item\[1\] / 100000000 = 28881.98
# round(28881.98, 2) = 28881.98
# → y_data 添加 28881.98
# 第2次:item = \["印度", 1453931000000\]
# item\[1\] / 100000000 = 14539.31
# → y_data 添加 14539.31
# 最终结果:
y_data = \[28881.98, 14539.31, 10861.06, 9176.87, 6218.87, 5638.54, 4809.20, 3732.93\]


注意:

|------------------------------------|------------|----------------|
| year_data = list(reversed(top8)) | 反转数据顺序 | 数据本身(列表里的元素顺序) |
| bar.reversal_axis() | 反转坐标轴 | 图表的 x 轴和 y 轴 |

第一步变成这样:

第二步变成这样:

最后总结:

第一步:读取 CSV 数据

第二步:发现数据是按"行"存储的,但我们需要按"年份"取数据 → 用字典整理

第三步:整理成字典 → {年份: \[国家, GDP, 国家, GDP, ...]}

第四步:字典的键(年份)是无序的,需要排序 → sorted(data_dict.keys())

第五步:需要按年份动态播放 → 创建 Timeline 时间线对象

第六步:进入循环,处理每一年

第七步:取出该年的数据后,按 GDP 从大到小排序

第八步:取前 8 名

第九步:【关键1】反转数据 → top8 = list(reversed(top8))
因为排序后是 中国, 印度, 美国, ...,反转后变成 ..., 美国, 印度, 中国
反转轴之后,列表最后一个会显示在最上面,所以中国就在最上面了

第十步:准备 x 轴数据(国家名)和 y 轴数据(GDP 转亿)

第十一步:创建 Bar 柱状图对象,添加数据

第十二步:【关键2】反转坐标轴 → bar.reversal_axis()
把纵向柱状图变成横向柱状图

第十三步:设置标题(年份动态变化)

第十四步:添加到时间线

第十五步:所有年份都添加完后,设置时间线自动播放

第十六步:生成 HTML 文件

复制代码
from pyecharts.charts import Bar, Timeline
from pyecharts import options as opts
from pyecharts.globals import ThemeType

# ========== 1. 读取数据,整理成字典 ==========
f = open("1960-2019全球GDP数据.csv", "r", encoding="utf-8")
data_lines = f.readlines()
f.close()
data_lines.pop(0)  # 删除表头

data_dict = {}
for line in data_lines:
    parts = line.strip().split(",")
    year = int(parts[0])
    country = parts[1]
    gdp = float(parts[2])
    if year not in data_dict:
        data_dict[year] = []
    data_dict[year].append([country, gdp])

# ========== 2. 排序年份 ==========
sorted_years = sorted(data_dict.keys())

# ========== 3. 创建时间线对象 ==========
timeline = Timeline(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))

# ========== 4. 循环每一年 ==========
for year in sorted_years:
    # 4.1 按 GDP 从大到小排序
    data_dict[year].sort(key=lambda x: x[1], reverse=True)
    # 取前 8 名
    top8 = data_dict[year][:8]

    # ===== 加上这一行,就反转了 =====
    year_data = list(reversed(top8))  # ← 就加了这一行!

    # 5. 准备数据
    x_data = []
    y_data = []
    for country_gdp in year_data:
        x_data.append(country_gdp[0])
        y_data.append(country_gdp[1] / 100000000)

    # # ===== 关键:反转数据,让最大的显示在最上面 =====
    # # 因为 reversal_axis() 后,列表第一个显示在底部
    # top8 = list(reversed(top8))  # ← 直接覆盖 top8,不用新变量
    #
    # # 准备数据(用反转后的 top8)
    # x_data = [item[0] for item in top8]
    # y_data = [round(item[1] / 100000000, 2) for item in top8]

    bar = Bar()
    bar.add_xaxis(x_data)
    bar.add_yaxis(
        "GDP(亿)",
        y_data,
        label_opts=opts.LabelOpts(position="right")
    )

    # 4.5 反转 xy 轴(变成横向)
    bar.reversal_axis()

    # 4.6 设置标题
    bar.set_global_opts(
        title_opts=opts.TitleOpts(title=f"{year}年全球GDP前8国家")
    )

    # 4.7 添加到时间线
    timeline.add(bar, str(year))

# ========== 5. 设置自动播放 ==========
timeline.add_schema(
    play_interval=1000,
    is_timeline_show=True,
    is_auto_play=True,
    is_loop_play=False
)

# ========== 6. 生成 HTML ==========
timeline.render("GDP动态柱状图1.html")
print("🎉 生成成功!")