You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于多期GeoPandas绘图生成动画/动态GIF展示人口时序演变

GeoDataFrame时间序列人口分布动态GIF实现方案

你之前的代码报错、动画无法渲染,核心是4个问题:

  • 初始化GeoDataFrame时mhg.set_index('month')没有重新赋值或加inplace=True,月份索引实际未生效
  • 帧更新函数中取了全量所有月份的几何数据,且传入绘图函数时用了未定义的population变量,没有使用当月筛选出的人口数据
  • FuncAnimation生成的动画对象如果没有被全局变量持有,会被Python自动垃圾回收,直接触发animation was deleted without rendering anything警告,渲染中断
  • 没有固定全时段色阶范围,每帧单独映射颜色会导致不同月份的颜色深浅无法横向对比,看不出人口变化趋势

下面给两种可直接运行的实现方案,适配不同数据量场景:


方案一:基于Matplotlib FuncAnimation实时渲染

适合月份数量在50以内、数据量不大的场景,代码简洁,可直接预览动画效果。

import geopandas as gpd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation, PillowWriter

# --------------- 数据初始化部分 ---------------
# 样例数据构造,真实使用时替换成你自己的mhg即可
data = [['2019-01-01', 'POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))', 1000], 
        ['2019-01-01', 'POLYGON((1 0, 2 0, 2 1, 1 1, 1 0))', 1500], 
        ['2019-01-01', 'POLYGON((2 0, 3 0, 3 1, 2 1, 2 0))', 1400], 
        ['2019-02-01', 'POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))', 1100], 
        ['2019-02-01', 'POLYGON((1 0, 2 0, 2 1, 1 1, 1 0))', 1600], 
        ['2019-02-01', 'POLYGON((2 0, 3 0, 3 1, 2 1, 2 0))', 1300]]
mhg = gpd.GeoDataFrame(data, columns=['month','geometry', 'population'], crs="EPSG:4326")
mhg = mhg.set_index('month') # 必须重新赋值,否则索引不生效
# 样例数据中geometry是字符串,需要转成几何对象;真实数据如果已经是几何类型可删掉下一行
mhg['geometry'] = gpd.GeoSeries.from_wkt(mhg['geometry'])

# --------------- 动画配置部分 ---------------
# 取排序后的所有月份
months = np.sort(mhg.index.unique())
# 固定全时段人口最值,保证所有帧色条范围一致,颜色具备可比性
vmin = mhg['population'].min()
vmax = mhg['population'].max()

# 初始化画布和第一帧
fig, ax = plt.subplots(figsize=(8, 4))
first_plot = mhg.loc[months[0]].plot(column='population', cmap='jet', vmin=vmin, vmax=vmax, ax=ax)
fig.colorbar(first_plot.get_children()[0], ax=ax, label='人口数')

def update(frame_month):
    ax.clear()
    # 绘制当前月份数据
    current_data = mhg.loc[frame_month]
    current_plot = current_data.plot(column='population', cmap='jet', vmin=vmin, vmax=vmax, ax=ax)
    ax.set_title(f'月份: {frame_month}')
    return current_plot,

# 生成动画,注意anim变量必须全局持有,不能被回收
anim = FuncAnimation(
    fig, 
    update, 
    frames=months,
    interval=1000, # 每帧停留1000毫秒
    repeat_delay=3000 # 循环结束后停留3秒再重播
)

# 本地预览
plt.show()

# 导出为GIF
writer = PillowWriter(fps=1)
anim.save('population_evolution.gif', writer=writer, dpi=100)

方案二:逐帧绘图后合并GIF

适合月份过百、单月几何数据量大的场景,逻辑和你最开始写的伪代码完全一致,内存占用稳定,排查问题方便。

import os
import tempfile
from PIL import Image
import geopandas as gpd
import numpy as np
import matplotlib.pyplot as plt

# 数据初始化部分和方案一一致,此处省略,直接使用已经处理好的mhg、months、vmin、vmax变量
# 新建临时目录存储单帧图片
temp_dir = tempfile.mkdtemp()
frame_paths = []

# 逐月份生成单帧图片
for idx, month in enumerate(months):
    fig, ax = plt.subplots(figsize=(8,4))
    current_data = mhg.loc[month]
    plot = current_data.plot(column='population', cmap='jet', vmin=vmin, vmax=vmax, ax=ax)
    ax.set_title(f'月份: {month}')
    plt.colorbar(plot.get_children()[0], ax=ax, label='人口数')
    # 保存单帧
    frame_path = os.path.join(temp_dir, f'frame_{idx}.png')
    plt.savefig(frame_path, dpi=100, bbox_inches='tight')
    plt.close() # 关闭画布释放内存
    frame_paths.append(frame_path)

# 合并所有帧为GIF
frames = [Image.open(f) for f in frame_paths]
frames[0].save(
    'population_evolution.gif',
    save_all=True,
    append_images=frames[1:],
    duration=1000, # 每帧停留1000毫秒
    loop=0, # 无限循环
    optimize=True
)

# 清理临时文件
for f in frame_paths:
    os.remove(f)
os.rmdir(temp_dir)

内容的提问来源于stack exchange,提问作者ElTitoFranki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 18:12:57