如何为伦敦MSOA贫困率Choropleth地图添加清晰区域标识?
问题描述
我正在用英国ONS发布的家庭贫困数据和MSOA地理边界数据制作伦敦地区的分级统计图(Choropleth Map),目前遇到以下问题:
- 用Matplotlib生成的静态地图因区域粒度太细,无法识别对应区域,难以读取有效信息;尝试添加区域首字母标签又会遮挡地图。
- 用Plotly制作交互式Choroplethmapbox时,内存占用过大导致电脑卡顿。
- 用Folium时无法匹配坐标。
另外,我想添加带有地方当局名称的彩色点图例,但不知如何实现。现请教:如何为该分级统计图整洁地添加地方当局信息?
现有代码
Matplotlib 静态地图代码
import pandas as pd import matplotlib.pyplot as plt import geopandas as gpd from mpl_toolkits.axes_grid1 import make_axes_locatable import folium import plotly.express as px import pyproj plt.style.use('ggplot') import json # 数据来源:ONS家庭贫困数据 populationData=pd.read_csv("householdsinpoverty2014.csv") # 数据来源:ONS MSOA地理边界数据 mapData= gpd.read_file("MSOA.shp") mapData.drop(columns=['Shape__Are', 'Shape__Len','GlobalID','OBJECTID'],inplace=True) populationData.drop(columns=['Unnamed: 9'],inplace=True) populationData2=populationData.iloc[3:].copy() populationData2.columns=populationData2.iloc[0] populationDataClean=populationData2.iloc[1:].copy() populationDataClean.reset_index(inplace=True) populationDataClean.dropna(inplace=True) populationDataClean.drop(columns=['MSOA name', 'Local authority code','Percentage of Households Below 60% of the Median Income; (after housing costs); 95% Confidence Interval Lower Limit','Percentage of Households Below 60% of the Median Income; (after housing costs); 95% Confidence Interval Upper Limit'],inplace=True) populationDataClean.sort_values(by=['Percentage of Households Below 60% of the Median Income; (after housing costs)'],inplace=True) populationDataClean['PercentBelowMedIncome']=populationDataClean['Percentage of Households Below 60% of the Median Income; (after housing costs)'].astype(float) # 筛选伦敦地区数据并合并 mapStats=mapData.merge(populationDataClean[populationDataClean['Region name']=='London'],left_on='MSOA21CD',right_on='MSOA code') fig, ax = plt.subplots(1, figsize=(8, 8)) plt.xticks(rotation=90) mapStats.plot(column="PercentBelowMedIncome", cmap="Blues", linewidth=0.4, ax=ax, edgecolor=".4",scheme='equalinterval',missing_kwds={ "color": "lightgrey", "edgecolor": "red", "hatch": "///", "label": "Missing values", }) ax.set_title('% of Households Below 60% \n of the Median Income (London)', fontdict={'fontsize': '25', 'fontweight' : '3'}) ax.axis("off") sm = plt.cm.ScalarMappable(cmap='Blues', norm=plt.Normalize(vmin=6.7, vmax=50)) sm._A = [] cbar = fig.colorbar(sm)
Plotly 交互式地图代码
from plotly import graph_objects as go fig = go.Figure( go.Choroplethmapbox( geojson=json, featureidkey="properties.MSOA21CD", locations=mapStats["MSOA21CD"], z=mapStats['PercentBelowMedIncome'], zauto=True, colorscale='Reds', showscale=True ) ) fig.update_layout(mapbox_style='carto-positron', mapbox_zoom=5, mapbox_center_lon=-2.057852, mapbox_center_lat=53.404854, height=700, width=700) fig.show()
解决方案
针对你的核心需求,以下是几种不同工具下的实现方案:
一、Matplotlib 静态地图:添加地方当局边界+中心标注
通过叠加地方当局边界、在区域中心标注名称的方式,既不遮挡MSOA级别的贫困数据,又能清晰识别地方当局范围:
import pandas as pd import matplotlib.pyplot as plt import geopandas as gpd # 提取伦敦地方当局的合并边界(MSOA编码以E09开头为伦敦区域) la_data = mapData[mapData['MSOA21CD'].str.startswith('E09')].dissolve(by='LAD21NM') # 绘制MSOA贫困分级图 fig, ax = plt.subplots(1, figsize=(12, 12)) mapStats.plot(column="PercentBelowMedIncome", cmap="Blues", linewidth=0.2, ax=ax, edgecolor=".4", scheme='equalinterval', missing_kwds={ "color": "lightgrey", "edgecolor": "red", "hatch": "///", "label": "缺失值", }) # 叠加地方当局粗边界,区分区域 la_data.boundary.plot(ax=ax, linewidth=1.5, color='black', zorder=3) # 在每个地方当局几何中心添加名称标注,用半透明白底避免遮挡 for idx, row in la_data.iterrows(): centroid = row.geometry.centroid ax.text(centroid.x, centroid.y, idx, fontsize=8, ha='center', va='center', bbox=dict(facecolor='white', alpha=0.7, pad=1, edgecolor='none'), zorder=4) # 优化标题和图例 ax.set_title('伦敦地区家庭收入低于中位数60%的比例', fontdict={'fontsize':16, 'fontweight':'bold'}) ax.axis('off') # 配置颜色条 sm = plt.cm.ScalarMappable(cmap='Blues', norm=plt.Normalize(vmin=mapStats['PercentBelowMedIncome'].min(), vmax=mapStats['PercentBelowMedIncome'].max())) sm._A = [] cbar = fig.colorbar(sm, ax=ax, fraction=0.03, pad=0.02) cbar.set_label('贫困家庭比例(%)', fontsize=12) # 添加缺失值图例 ax.legend(handles=[plt.Rectangle((0,0),1,1, color='lightgrey', edgecolor='red', hatch='///')], labels=['缺失值'], loc='lower right', fontsize=10) plt.tight_layout() plt.show()
二、Plotly 交互式地图:解决内存问题+添加地方当局信息
内存优化方案:
- 只保留伦敦地区的MSOA数据,不要加载全英范围
- 简化MSOA几何形状(降低坐标精度)
添加地方当局信息:
- 在hover提示框中显示地方当局名称
- 可选叠加地方当局边界层
import plotly.express as px import geopandas as gpd # 简化MSOA几何形状,减少内存占用(tolerance值越大,简化程度越高) mapStats_simplified = mapStats.copy() mapStats_simplified['geometry'] = mapStats_simplified['geometry'].simplify(tolerance=100) # 转换为GeoJSON格式 london_geojson = mapStats_simplified.__geo_interface__ # 绘制交互式地图,hover显示地方当局名称(假设mapStats包含LAD21NM字段) fig = px.choropleth_mapbox( mapStats_simplified, geojson=london_geojson, locations='MSOA21CD', featureidkey='properties.MSOA21CD', color='PercentBelowMedIncome', color_continuous_scale='Blues', mapbox_style='carto-positron', zoom=10, center={'lat': 51.5074, 'lon': -0.1278}, # 伦敦中心坐标 hover_data={ 'MSOA21CD': False, 'PercentBelowMedIncome': ':,.1f', 'LAD21NM': True # 显示地方当局名称 }, title='伦敦地区家庭贫困比例' ) # 可选:添加地方当局边界层 # la_data_simplified = la_data.copy() # la_data_simplified['geometry'] = la_data_simplified['geometry'].simplify(tolerance=200) # la_geojson = la_data_simplified.__geo_interface__ # fig.add_trace(px.choropleth_mapbox( # la_data_simplified, # geojson=la_geojson, # locations=la_data_simplified.index, # featureidkey='properties.LAD21NM', # color_discrete_sequence=['black'], # showscale=False, # hover_data={'LAD21NM': True}, # marker_line_width=1.5 # ).data[0]) fig.update_layout(height=800, width=800) fig.show()
三、Folium 地图:匹配坐标+添加地方当局信息
Folium默认使用WGS84坐标系(EPSG:4326),需要将你的GeoDataFrame转换到该坐标系,再添加地方当局边界和tooltip:
import folium import geopandas as gpd # 将数据转换为WGS84坐标系(Folium默认) mapStats_wgs84 = mapStats.to_crs(epsg=4326) la_data_wgs84 = la_data.to_crs(epsg=4326) # 创建地图实例,以伦敦为中心 m = folium.Map(location=[51.5074, -0.1278], zoom_start=10, tiles='CartoDB Positron') # 添加MSOA贫困分级图层 folium.Choropleth( geo_data=mapStats_wgs84, name='贫困家庭比例', data=mapStats_wgs84, columns=['MSOA21CD', 'PercentBelowMedIncome'], key_on='feature.properties.MSOA21CD', fill_color='Blues', fill_opacity=0.7, line_opacity=0.2, legend_name='贫困家庭比例(%)' ).add_to(m) # 添加地方当局边界图层,hover显示名称 folium.GeoJson( la_data_wgs84, name='地方当局边界', style_function=lambda x: {'color': 'black', 'weight': 1.5, 'fillOpacity': 0}, tooltip=folium.GeoJsonTooltip(fields=['LAD21NM'], aliases=['地方当局名称']) ).add_to(m) # 添加图层控制器,切换显示 folium.LayerControl().add_to(m) # 保存为HTML交互式地图 m.save('london_poverty_map.html')
四、地方当局彩色点图例实现
如果需要用彩色点标记地方当局并添加图例,可以在Matplotlib中实现:
import matplotlib.patches as Patch # 给每个地方当局分配唯一颜色 la_colors = plt.cm.tab20.colors[:len(la_data)] la_color_map = dict(zip(la_data.index, la_colors)) # 在地方当局中心绘制彩色点 for idx, row in la_data.iterrows(): centroid = row.geometry.centroid ax.scatter(centroid.x, centroid.y, color=la_color_map[idx], s=50, zorder=5) # 创建图例元素 legend_elements = [Patch.Patch(facecolor=la_color_map[la], label=la) for la in la_data.index] # 将图例放在图外,避免遮挡 ax.legend(handles=legend_elements, loc='lower left', bbox_to_anchor=(1, 0), fontsize=6, title='地方当局') plt.tight_layout() plt.show()
内容的提问来源于stack exchange,提问作者user13948
相关产品推荐
相关产品推荐

