如何用Matplotlib FuncAnimation实现Pandas年度数据的GeoPandas动态可视化
美国各州百分比变化动态地图实现问题
问题说明
已将美国各州百分比变化数据集与美国州界shapefile合并,希望通过Matplotlib的FuncAnimation按年份循环展示动态地图,但遇到以下问题:
- 将
shape.plot()放入animate函数或循环时,会丢失shape.boundary.plot()设置的边界样式 - 不清楚如何动态切换
shape.plot()的column参数实现年份数据更新 - 想了解是否可通过getter/setter修改属性,或让边界持久化后叠加年份数据
数据样例
df.head() Year 2017 2018 2019 2020 2021 2022 2023 State Alabama 0.00 0.00 0.00 0.00 0.00 0.00 0.00 Arizona 0.24 0.00 0.03 -0.15 0.56 -0.36 0.21 Arkansas 0.35 -0.06 -0.03 0.03 -0.00 -0.13 -0.02 California 0.13 0.07 -0.03 0.04 0.21 -0.10 0.03 Colorado 0.81 -0.18 -0.01 -0.05 0.10 -0.03 -0.51
尝试代码
shape = gpd.read_file(shapefile) years = dfc.columns # dfc = % change df tspan = len(dfc.columns) """ merge map with dataframe on state name column """ shape = pd.merge( left=shape, right=dfc, left_on='NAME', right_on='State', how='right' ) """ init pyplot 'OO method' """ fig, ax = plt.subplots(figsize=(10, 5)) """ draw shape boundary """ ax = shape.boundary.plot( ax=ax, edgecolor='black', linewidth=0.3, ) """ plot shape """ ax = shape.plot( ax=ax, column=year, # what I need access to legend=True, cmap='RdBu_r', legend_kwds={'shrink': 0.3, 'orientation': 'horizontal', 'format': '%.0f'}) """ cycle through columns -- not operable yet """ def animate(year): ax.clear() ax.shape.column(year) animation = FuncAnimation(states, animate, frames=(dfc.columns[0], dfc.columns[tspan] + 1, 1), repeat=True, interval=1000)
可行解决方案
核心思路
- 提前绘制州界并持久化,避免每次动画更新时清除边界
- 在
animate函数中仅更新年份对应的填充图层,不清除整个坐标轴 - 固定颜色映射范围,避免图例随年份数据波动
完整实现代码
import geopandas as gpd import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation import pandas as pd # 读取并合并数据 shapefile = "cb_2018_us_state_500k.shp" # 替换为你的文件路径 shape = gpd.read_file(shapefile) dfc = pd.read_csv("your_percent_change_data.csv", index_col="State") # 替换为你的数据路径 shape = pd.merge( left=shape, right=dfc, left_on='NAME', right_on='State', how='right' ) # 初始化画布和坐标轴 fig, ax = plt.subplots(figsize=(12, 6)) ax.set_aspect('equal') # 提前绘制州界(持久化) shape.boundary.plot(ax=ax, edgecolor='black', linewidth=0.3) # 固定颜色映射范围(基于所有年份数据的极值) vmin = dfc.min().min() vmax = dfc.max().max() # 初始化颜色条(仅创建一次) sm = plt.cm.ScalarMappable(cmap='RdBu_r', norm=plt.Normalize(vmin=vmin, vmax=vmax)) sm.set_array([]) cbar = fig.colorbar(sm, ax=ax, shrink=0.3, orientation='horizontal', format='%.2f') cbar.set_label('百分比变化') # 定义动画函数 def animate(year): # 清除之前的填充图层(保留边界和颜色条) for artist in ax.collections: artist.remove() # 绘制当前年份的填充数据 shape.plot( ax=ax, column=year, cmap='RdBu_r', vmin=vmin, vmax=vmax, legend=False # 已提前创建颜色条,无需重复生成 ) # 更新标题显示当前年份 ax.set_title(f"美国各州百分比变化 - {year}") # 创建动画 animation = FuncAnimation( fig, animate, frames=dfc.columns, repeat=True, interval=1000 # 每帧间隔1秒 ) # 可选:保存动画为MP4(需要ffmpeg支持) # animation.save('state_percent_change.mp4', writer='ffmpeg', dpi=150) plt.show()
关键说明
- 边界持久化:先绘制州界,之后在
animate中仅清除ax.collections中的填充图层,保留边界线条(边界属于ax.lines,不会被移除) - 颜色映射固定:通过
vmin和vmax基于所有年份数据的极值设置颜色范围,确保不同年份的颜色刻度一致,避免图例跳动 - 高效更新:直接移除旧的填充集合再绘制新的,比清除整个坐标轴更高效,且保留了边界和颜色条
- 图例优化:提前创建颜色条,避免每次动画帧都重新生成图例,提升流畅度
内容的提问来源于stack exchange,提问作者Avery Freeman
相关产品推荐
相关产品推荐

