如何让Plotly Express动态Choropleth地图每年展示多个国家?
我在2020款M1芯片MacBook Pro上,使用Python 3.11.5版本的Jupyter Notebook运行代码,尝试用Plotly Express制作动态Choropleth地图,展示1460-1985年间各国的殖民情况。测试数据集包含4个国家(阿尔及利亚、安哥拉、澳大利亚、伯利兹),记录了对应殖民国家(法国、英国、葡萄牙、西班牙、未被殖民)及各年份的殖民状态,包含country、colonizer、year三列,数据子集如下:
| country | colonizer | year |
|---|---|---|
| Algeria | France | 1844 |
| Algeria | France | 1845 |
| Algeria | France | 1846 |
| Algeria | France | 1847 |
| Algeria | France | 1848 |
| Algeria | France | 1849 |
| Algeria | France | 1850 |
| Algeria | France | 1851 |
| Algeria | France | 1852 |
| Algeria | France | 1853 |
| Algeria | France | 1854 |
| Algeria | France | 1855 |
| Algeria | France | 1856 |
| Algeria | France | 1857 |
| Algeria | France | 1858 |
| Angola | Portugal | 1844 |
| Angola | Portugal | 1845 |
| Angola | Portugal | 1846 |
| Angola | Portugal | 1847 |
| Angola | Portugal | 1848 |
| Angola | Portugal | 1849 |
| Angola | Portugal | 1850 |
| Angola | Portugal | 1851 |
| Angola | Portugal | 1852 |
| Angola | Portugal | 1853 |
| Angola | Portugal | 1854 |
| Angola | Portugal | 1855 |
| Belize | Spain | 1845 |
| Belize | Spain | 1846 |
| Belize | Spain | 1847 |
| Belize | Spain | 1848 |
| Belize | Spain | 1849 |
| Belize | Spain | 1850 |
| Belize | Spain | 1851 |
| Belize | Spain | 1852 |
| Belize | Spain | 1853 |
| Belize | Spain | 1854 |
| Belize | Spain | 1855 |
| Australia | Britain | 1839 |
| Australia | Britain | 1840 |
| Australia | Britain | 1841 |
| Australia | Britain | 1842 |
| Australia | Britain | 1843 |
| Australia | Britain | 1844 |
| Australia | Britain | 1845 |
| Australia | Britain | 1846 |
| Australia | Britain | 1847 |
| Australia | Britain | 1848 |
| Australia | Britain | 1849 |
| Australia | Not Colonized | 1850 |
| Australia | Not Colonized | 1851 |
| Australia | Not Colonized | 1852 |
| Australia | Not Colonized | 1853 |
我按年份对数据集排序col_sort_df = colonized_df.sort_values(by = ['Year']),期望地图在年份重叠时显示所有4个国家,并按当年殖民国家着色,但实际每次仅显示一个国家和对应的殖民国家。使用的代码如下:
import pandas as pd import plotly.express as px # Add a color mapping for each colonizer colonizer_color = { 'Britain': 'blue', 'France': 'red', 'Spain': 'yellow', 'Portugal': 'green', 'Belgium': 'purple', 'Germany': 'gray', 'Italy': 'orange', 'Not Colonized': 'black' } # Create the choropleth map fig = px.choropleth( col_sort_df, locations="country", # Column name for countries locationmode="country names", color="colonizer", # Column name for the color mapping hover_name="country", animation_frame="year", # Column name for the year to animate color_discrete_map=colonizer_color, projection="natural earth", # Projection style for the map title="Colonization by England and France (1800s-Present)" ) # Update layout for a better appearance fig.update_layout( geo=dict( showframe=False, showcoastlines=False ) ) # Show the figure fig.show()
问题原因
核心问题是数据中部分年份缺少部分国家的记录,导致Plotly在渲染对应年份的动画帧时,仅加载有数据的国家;同时,即使年份存在多个国家记录,也可能因国家名称匹配误差或数据分组异常,导致仅显示单个国家。
修复步骤
补全年份-国家的完整记录
确保每个年份下,所有4个国家都有对应的殖民状态记录,缺失的记录可填充为Not Colonized(根据实际历史调整):# 生成所有年份和国家的笛卡尔积 years = col_sort_df['year'].unique() countries = col_sort_df['country'].unique() full_index = pd.MultiIndex.from_product([years, countries], names=['year', 'country']) full_df = pd.DataFrame(index=full_index).reset_index() # 合并原数据,填充缺失的殖民状态 full_df = full_df.merge(col_sort_df, on=['year', 'country'], how='left') full_df['colonizer'] = full_df['colonizer'].fillna('Not Colonized')验证国家名称匹配
Plotly的locationmode="country names"对拼写要求严格,确保数据中的国家名称与Plotly识别的名称完全一致:- 确认
Belize、Algeria等名称无拼写错误 - 若
Britain匹配异常,可尝试替换为United Kingdom(同步修改颜色映射)
- 确认
调整数据排序与去重
确保数据按年份和国家正确排序,同时移除重复的年份-国家记录:full_df = full_df.sort_values(by=['year', 'country']) full_df.drop_duplicates(subset=['year', 'country'], inplace=True)
修改后的完整代码
import pandas as pd import plotly.express as px # 加载原始数据并排序 colonized_df = pd.read_csv('你的数据文件路径.csv') # 替换为实际数据加载方式 col_sort_df = colonized_df.sort_values(by=['year']) # 补全年份-国家完整记录 years = col_sort_df['year'].unique() countries = col_sort_df['country'].unique() full_index = pd.MultiIndex.from_product([years, countries], names=['year', 'country']) full_df = pd.DataFrame(index=full_index).reset_index() full_df = full_df.merge(col_sort_df, on=['year', 'country'], how='left') full_df['colonizer'] = full_df['colonizer'].fillna('Not Colonized') # 颜色映射 colonizer_color = { 'Britain': 'blue', 'France': 'red', 'Spain': 'yellow', 'Portugal': 'green', 'Belgium': 'purple', 'Germany': 'gray', 'Italy': 'orange', 'Not Colonized': 'black' } # 创建动态地图 fig = px.choropleth( full_df, locations="country", locationmode="country names", color="colonizer", hover_name="country", animation_frame="year", color_discrete_map=colonizer_color, projection="natural earth", title="1800年至今各国殖民状态变化" ) fig.update_layout( geo=dict( showframe=False, showcoastlines=False ) ) fig.show()
额外提示
如果补全数据后仍有匹配问题,可将locationmode改为"ISO-3",并在数据中添加国家的ISO 3位代码列(如阿尔及利亚为DZA),能大幅提升匹配准确率。
内容的提问来源于stack exchange,提问作者Brynė Hadnott

