Python地图可视化:怎么用folium和matplotlib展示纬度分布
本文系统讲解如何使用Python中的folium与matplotlib两款库展示地理数据的纬度分布特征。涵盖数据清洗分箱、静态纬度统计图表构建、交互式Web地图纬线标注与热力渲染,并针对高纬度投影变形和数据聚合等常见误区提供切实可行的解决方案。
在空间数据分析中,分析某种现象或指标沿地球南北方向的变化规律是一种常见需求。展示纬度分布并非简单把经纬度散点丢到地图上,而是需要清晰回答“数据随纬度升高或降低呈现怎样的集中度与演变趋势”。本文将通过具体代码和几何原理,讲解如何结合使用 matplotlib 与 folium:前者专精于将连续空间沿纬度轴降维统计,后者专精于在可交互地图上直观映射地理坐标与纬线关联。
一、基础数据准备与纬度分箱思路
展示纬度分布的第一步是对原始点位数据进行科学的清洗与区间切分。地球纬度范围为 -90° 至 +90°,在常规业务数据中通常集中于特定范围。我们需要利用 pandas 对纬度字段建立规则分箱(Bins),以便提取各纬度带上的样本计数或指标均值。
安装所需依赖库的终端命令如下:
pip install matplotlib folium pandas numpy
随后编写数据生成与预处理逻辑。下面的代码构造了一组具有明显中高纬度偏向的样本点,作为本文后续演示的数据源:
import numpy as np
import pandas as pd
# 固定随机种子以保证结果可复现
np.random.seed(42)
n_samples = 800
# 模拟经纬度:集中在北纬 20° 至 50° 之间
lats = np.concatenate([
np.random.normal(loc=23.5, scale=3.5, size=250), # 低纬偏回归线附近
np.random.normal(loc=38.0, scale=4.0, size=350), # 中纬度密集带
np.random.normal(loc=52.0, scale=3.0, size=200) # 高纬度边缘区
])
lons = np.random.uniform(low=75.0, high=135.0, size=n_samples)
# 模拟随纬度增加而降低的温度指标
values = 35 - 0.45 * lats + np.random.normal(0, 2.5, size=n_samples)
df = pd.DataFrame({
'latitude': lats,
'longitude': lons,
'value': values
})
# 按 5 度为一个统计区间切分纬度带
lat_bins = np.arange(10, 65, 5)
df['lat_bin'] = pd.cut(df['latitude'], bins=lat_bins)
二、使用 Matplotlib 呈现纬度分布规律
Matplotlib 最适合用来展示“沿纬度方向的降维投影”。很多初学者习惯直接画散点图,但散点容易在密集处重叠,掩盖密度峰值。更有效的方法是将散点图与侧边“纬度边际分布直方图(Marginal Latitude Distribution)”或“纬度切片箱线图”结合排版。
下面给出生成“散点图与侧边纬度分布直方图联动”的完整代码:
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
fig = plt.figure(figsize=(10, 6))
# 划分 1 行 4 列的网格:前 3 列为主图,第 4 列为侧边纬度直方图
gs = GridSpec(1, 4, figure=fig, wspace=0.15)
ax_main = fig.add_subplot(gs[0, :3])
ax_hist = fig.add_subplot(gs[0, 3], sharey=ax_main)
# 1. 主散点图:用颜色映射观测指标
scatter = ax_main.scatter(
df['longitude'], df['latitude'],
c=df['value'], cmap='viridis',
alpha=0.7, edgecolors='none', s=25
)
ax_main.set_xlabel('经度 (Longitude °E)', fontsize=11)
ax_main.set_ylabel('纬度 (Latitude °N)', fontsize=11)
ax_main.set_title('数据空间分布与纬度特征', fontsize=12, pad=10)
ax_main.grid(True, linestyle='--', alpha=0.4)
# 2. 侧边水平直方图:orientation='horizontal' 让频数横向生长,Y 轴与主图对齐
ax_hist.hist(
df['latitude'], bins=25,
orientation='horizontal',
color='#8A9478', edgecolor='#464B48',
linewidth=0.8, alpha=0.85
)
ax_hist.set_xlabel('样本数量', fontsize=11)
# 隐藏副图的 Y 轴刻度,避免与主图重复
plt.setp(ax_hist.get_yticklabels(), visible=False)
ax_hist.grid(True, linestyle='--', alpha=0.4)
# 添加色条表示指标强弱
cbar = fig.colorbar(scatter, ax=ax_main, orientation='horizontal', pad=0.15, aspect=30)
cbar.set_label('观测指标数值', fontsize=10)
plt.savefig('lat_distribution_matplotlib.png', dpi=300, bbox_inches='tight')
plt.close()
该写法的核心在于 sharey=ax_main 与 orientation='horizontal'。它让读者在观察经纬度散布的同时,视线直接平移到右侧,立刻看懂哪个纬度区间最为集聚,克服了普通地图上散点视觉密度容易被点大小欺骗的弱点。
三、使用 Folium 构建交互式纬度空间地图
与 Matplotlib 偏重静态统计不同,Folium 的优势在于能够让读者缩放地图、确认样本与现实行政区划的地理相对位置。在 Folium 中展示纬度分布的关键技术点包括:
- 明确画出关键纬度基准线:使用
folium.PolyLine沿特定纬度绘制水平辅助虚线(例如北回归线 23.5°N、40°N),给读者建立视觉标尺。 - 基于纬度数值映射颜色:通过
branca.colormap对folium.CircleMarker赋色,使散点颜色随纬度产生梯度过渡。 - 添加纬度带聚合统计弹窗:点击点位或纬度带时显示当前点所在纬度区间及统计摘要。
完整可执行代码如下:
import folium
import branca.colormap as cm
# 1. 确定底图中心和缩放级别
center_lat = df['latitude'].median()
center_lon = df['longitude'].median()
m = folium.Map(
location=[center_lat, center_lon],
zoom_start=5,
tiles='CartoDB positron' # 使用柔和底图突出数据
)
# 2. 绘制纬度辅助线(北纬 20°、30°、40°、50°)
ref_latitudes = [20.0, 30.0, 40.0, 50.0]
for r_lat in ref_latitudes:
folium.PolyLine(
locations=[[r_lat, 70.0], [r_lat, 140.0]],
color='#464B48',
weight=1.2,
dash_array='5, 5',
opacity=0.6,
tooltip=f'基准纬线: {r_lat}°N'
).add_to(m)
# 3. 创建按纬度数值插值的连续色彩条
min_lat, max_lat = df['latitude'].min(), df['latitude'].max()
colormap = cm.LinearColormap(
colors=['#8A9478', '#D5CEBF', '#464B48'],
vmin=min_lat,
vmax=max_lat
)
colormap.caption = '纬度分级梯度 (°N)'
colormap.add_to(m)
# 4. 遍历点位添加 CircleMarker
for _, row in df.iterrows():
lat = row['latitude']
lon = row['longitude']
val = row['value']
folium.CircleMarker(
location=[lat, lon],
radius=4,
color=colormap(lat),
fill=True,
fill_color=colormap(lat),
fill_opacity=0.75,
weight=0.5,
popup=folium.Popup(
f"纬度: {lat:.2f}°N
经度: {lon:.2f}°E
观测值: {val:.1f}",
max_width=200
)
).add_to(m)
# 5. 保存为交互式 HTML 文档
m.save('latitude_distribution_map.html')
在上述代码中,folium.CircleMarker 的半径单位是屏幕像素而非真实地理米数。这保证了在用户进行大范围缩放地图时,标记圈不会发生不可控的视觉膨胀,始终保持对纬度带分布的清晰展示。
四、常见误区与高频陷阱辨析
在处理与展示纬度分布时,有几个容易混淆的常见误区值得注意:
常见误区一:认为“散点密度高”就等同于“纬度带总量多”。
因为地球表面经线在两极收敛,高纬度地区 1 个经度跨度对应的实际物理地表距离远短于赤道(例如北纬 60° 处的经线圈周长仅为赤道的一半)。如果直接按经纬度做简单的等面积网格切分,高纬度网格的实际地理面积比低纬度小得多。因此统计纬度分布时,若要评估单位地表强弱,需要乘以 \(\cos(\text{latitude})\) 进行面积权重校正。
常见误区二:误将 Web 墨卡托投影的视觉面积当做真实分布。
Folium 默认采用 Web 墨卡托投影(EPSG:3857)。该投影在靠近两极的区域存在严重的面积放大效应(高纬度区域被上下左右拉伸)。在 Folium 观察热力图时,视觉上很宽的斑块其真实物理分布并不一定密集。这也是为什么必须配合 Matplotlib 的一维直方图进行数值校核的原因。
五、总结与技术选型决策
综上所述,在 Python 生态中表达地理数据的纬度分布,最成熟的策略并非在 Folium 与 Matplotlib 之间“二选一”,而是让两者各司其职:
- 当需要向读者证明“数值在南北方向上呈现特定函数规律、极值或置信区间”时,优先采用 Matplotlib 进行一维边际直方图投影与带状拟合。
- 当需要向读者展示“这些纬度特征在地理底图上的具体分布地、所处行政区域及多点空间聚合”时,优先采用 Folium 绘制带虚线纬标、色彩分级 CircleMarker 的交互式地图。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









