第19节:地理空间分析——使用 Geopandas 绘制热力地图

目录

    • [第19节:地理空间分析------使用 Geopandas 绘制热力地图](#第19节:地理空间分析——使用 Geopandas 绘制热力地图)
    • 本节学习目标
    • 为什么学这个
    • 核心知识点讲解
      • 一、地理空间数据基础
        • [1.1 什么是地理空间数据?](#1.1 什么是地理空间数据?)
        • [1.2 常见地理数据格式](#1.2 常见地理数据格式)
        • [1.3 坐标系(CRS)](#1.3 坐标系(CRS))
      • [二、GeoDataFrame 入门](#二、GeoDataFrame 入门)
        • [2.1 创建 GeoDataFrame](#2.1 创建 GeoDataFrame)
        • [2.2 从多边形创建区域](#2.2 从多边形创建区域)
      • [三、读取 Shapefile 数据](#三、读取 Shapefile 数据)
      • 四、Choropleth(分级设色地图)
        • [4.1 基础 Choropleth](#4.1 基础 Choropleth)
        • [4.2 Choropleth 分级的选择](#4.2 Choropleth 分级的选择)
        • [4.3 将业务数据与地图合并](#4.3 将业务数据与地图合并)
      • [五、Folium 交互式地图](#五、Folium 交互式地图)
        • [5.1 基础 Folium 地图](#5.1 基础 Folium 地图)
        • [5.2 热力图(Heatmap)](#5.2 热力图(Heatmap))
        • [5.3 气泡地图](#5.3 气泡地图)
      • 六、综合实战:中国省份销售热力地图
    • 实战练习
    • 本节总结
    • 下一节预告

专栏导读

🌸 欢迎来到Python办公自动化专栏---Python处理办公问题,解放您的双手
🏳️‍🌈 个人博客主页:请点击------> 个人的博客主页 求收藏
🏳️‍🌈 Github主页:请点击------> Github主页 求Star⭐
🏳️‍🌈 知乎主页:请点击------> 知乎主页 求关注
🏳️‍🌈 CSDN博客主页:请点击------> CSDN的博客主页 求关注
👍 该系列文章专栏:请点击------>Python办公自动化专栏 求订阅
🕷 此外还有爬虫专栏:请点击------>Python爬虫基础专栏 求订阅
📕 此外还有python基础专栏:请点击------>Python基础学习专栏 求订阅
文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
❤️ 欢迎各位佬关注! ❤️

第19节:地理空间分析------使用 Geopandas 绘制热力地图

本节学习目标

完成本节学习后,你将能够:

  1. 理解地理空间数据的基本概念(坐标系、投影、Shapefile)
  2. 掌握 GeoDataFrame 的数据结构与基本操作
  3. 读取和写入 Shapefile、GeoJSON 等地理数据格式
  4. 绘制分级设色地图(Choropleth)
  5. 使用 Geopandas 进行空间数据的基本处理
  6. 利用 Folium 创建交互式在线地图
  7. 将业务数据与地理数据结合进行空间可视化分析

为什么学这个

想象你是某连锁零售品牌的数据分析师。老板问了一个问题:"我们的门店在全国的分布合理吗?哪些省份需要新开?哪些省份已经饱和?"

你可以拿出一张包含各省份门店数量和销售额的表格------但老板看了半天也看不出规律。

你也可以画一张中国地图,用颜色深浅表示各省销售额,用气泡大小表示门店数量------一眼就能看出:"东部密集,西部稀疏;华南销售额最高,西北最低。"

这就是地理空间可视化的力量。人类的大脑天生就对空间位置敏感------一张地图比一百行数字更有说服力。

在数据分析中,地理空间分析常用于:

  • 市场分布分析:门店、客户、竞争对手的地理分布
  • 区域业绩对比:各省市的销售、利润、增长率地图
  • 物流路径优化:仓库选址、配送路线规划
  • 人口与市场分析:人口密度、收入水平、消费能力分布

核心知识点讲解

一、地理空间数据基础

1.1 什么是地理空间数据?

地理空间数据 = 普通数据 + 位置信息。位置信息可以是:

  • 矢量数据:点(经纬度坐标)、线(道路、河流)、面(省份边界、区域)
  • 栅格数据:卫星影像、高程图(每个像素对应一个地理位置)

在数据分析中,最常用的是矢量数据

1.2 常见地理数据格式
格式 扩展名 说明
Shapefile .shp, .shx, .dbf 等 ESRI 标准格式,最常用
GeoJSON .geojson 基于 JSON,适合 Web 应用
GeoPackage .gpkg SQLite 封装,单文件格式
KML/KMZ .kml, .kmz Google Earth 格式

Shapefile 说明:一个 Shapefile 实际上是一组文件(.shp 存储几何形状、.shx 存储索引、.dbf 存储属性数据、.prj 存储投影信息)。使用时只需指定 .shp 文件,Geopandas 会自动读取其他相关文件。

1.3 坐标系(CRS)

坐标系定义了地球表面的点如何映射到二维平面。常见的有:

  • EPSG:4326(WGS84):经纬度坐标系,GPS 使用的标准
  • EPSG:3857:Web Mercator,Google 地图、百度地图使用的投影

打个比方:坐标系就像"翻译器",把三维地球上的位置"翻译"成平面地图上的坐标。选错了翻译器,地图就会变形。

二、GeoDataFrame 入门

GeoDataFrame 是 Geopandas 的核心数据结构,继承自 Pandas 的 DataFrame,多了一个 geometry 列。

python 复制代码
import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt

# 从内置数据集加载(Geopandas 自带世界地图数据)
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))

# 查看基本信息
print(f"数据类型: {type(world)}")
print(f"形状: {world.shape}")
print(f"\n列名: {world.columns.tolist()}")
print(f"\n前3行:")
print(world[['name', 'continent', 'pop_est', 'geometry']].head(3))

# geometry 列存储的是几何对象(多边形、点等)
print(f"\ngeometry 列的示例:")
print(world.loc[0, 'geometry'])
2.1 创建 GeoDataFrame
python 复制代码
import geopandas as gpd
import pandas as pd
from shapely.geometry import Point, Polygon

# 方法一:从点数据创建
df = pd.DataFrame({
    '城市': ['北京', '上海', '广州', '深圳'],
    '经度': [116.40, 121.47, 113.26, 114.06],
    '纬度': [39.90, 31.23, 23.13, 22.54],
    '人口(万)': [2154, 2424, 1530, 1302]
})

# 将经纬度转为 Point 对象
geometry = [Point(xy) for xy in zip(df['经度'], df['纬度'])]

# 创建 GeoDataFrame
gdf = gpd.GeoDataFrame(df, geometry=geometry, crs='EPSG:4326')

print(gdf)

# 直接绘图
fig, ax = plt.subplots(figsize=(8, 6))
gdf.plot(ax=ax, markersize=gdf['人口(万)']/50, color='red', alpha=0.7)

# 添加城市标签
for idx, row in gdf.iterrows():
    ax.annotate(row['城市'], (row['经度'], row['纬度']), 
                fontsize=10, ha='center')

ax.set_title('中国主要城市分布', fontsize=14, fontweight='bold')
ax.set_xlabel('经度')
ax.set_ylabel('纬度')
plt.tight_layout()
plt.show()
2.2 从多边形创建区域
python 复制代码
import geopandas as gpd
from shapely.geometry import Polygon

# 创建几个模拟的区域(矩形)
polygons = [
    Polygon([(0, 0), (2, 0), (2, 2), (0, 2)]),
    Polygon([(2, 0), (4, 0), (4, 2), (2, 2)]),
    Polygon([(0, 2), (2, 2), (2, 4), (0, 4)]),
    Polygon([(2, 2), (4, 2), (4, 4), (2, 4)])
]

gdf = gpd.GeoDataFrame({
    '区域': ['A区', 'B区', 'C区', 'D区'],
    '销售额': [150, 230, 180, 290],
    'geometry': polygons
}, crs='EPSG:4326')

# 绘制面图
fig, ax = plt.subplots(figsize=(8, 6))
gdf.plot(column='销售额', ax=ax, legend=True, 
         cmap='YlOrRd', edgecolor='black',
         legend_kwds={'label': '销售额(万元)'})

for idx, row in gdf.iterrows():
    centroid = row['geometry'].centroid
    ax.annotate(row['区域'], (centroid.x, centroid.y), 
                fontsize=14, ha='center', va='center', fontweight='bold')

ax.set_title('模拟区域销售分布', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()

三、读取 Shapefile 数据

实际工作中,地图数据通常来自外部文件。以下以中国省级地图数据为例。

python 复制代码
import geopandas as gpd
import matplotlib.pyplot as plt

# 读取中国省级地图 Shapefile
# (实际使用时替换为你本地的 Shapefile 路径)
# china_map = gpd.read_file('中国省级地图/china-provinces.shp')

# 这里用世界地图作为演示
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))

# 筛选亚洲国家
asia = world[world['continent'] == 'Asia']

fig, ax = plt.subplots(figsize=(14, 8))

# 基础地图绘制
asia.plot(ax=ax, color='lightgray', edgecolor='black', linewidth=0.5)

# 为每个国家标注名称
for idx, row in asia.iterrows():
    centroid = row['geometry'].centroid
    ax.annotate(row['name'], (centroid.x, centroid.y), 
                fontsize=7, ha='center', va='center', alpha=0.7)

ax.set_title('亚洲国家分布', fontsize=16, fontweight='bold')
ax.set_xlim(60, 150)
ax.set_ylim(5, 55)
ax.set_xlabel('经度')
ax.set_ylabel('纬度')

plt.tight_layout()
plt.show()

四、Choropleth(分级设色地图)

Choropleth 地图是最常用的地理可视化方式------用颜色深浅表示数值大小。

4.1 基础 Choropleth
python 复制代码
import geopandas as gpd
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

# 使用世界地图 + 模拟GDP数据
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))

# 模拟GDP数据(实际应用中使用真实数据)
np.random.seed(42)
world['GDP_模拟'] = np.random.lognormal(10, 2, len(world))

# 排除南极洲
world = world[world['name'] != 'Antarctica']

fig, axes = plt.subplots(1, 2, figsize=(20, 8))

# 子图1:原始 Choropleth
world.plot(column='GDP_模拟', ax=axes[0], cmap='viridis',
           legend=True, edgecolor='white', linewidth=0.3,
           legend_kwds={'label': '模拟GDP', 'orientation': 'horizontal'})
axes[0].set_title('世界GDP分布(线性刻度)', fontsize=14, fontweight='bold')
axes[0].axis('off')

# 子图2:对数刻度(更适合差距大的数据)
world.plot(column='GDP_模拟', ax=axes[1], cmap='viridis',
           scheme='quantiles', k=7,  # 分位数分7级
           legend=True, edgecolor='white', linewidth=0.3,
           legend_kwds={'label': '模拟GDP', 'orientation': 'horizontal'})
axes[1].set_title('世界GDP分布(分位数分级)', fontsize=14, fontweight='bold')
axes[1].axis('off')

plt.tight_layout()
plt.show()
4.2 Choropleth 分级的选择

颜色分级的方法会影响地图的视觉效果和数据传达:

python 复制代码
import geopandas as gpd
import matplotlib.pyplot as plt
import numpy as np

world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
np.random.seed(42)
world['value'] = np.random.lognormal(8, 1.5, len(world))
world = world[world['name'] != 'Antarctica']

schemes = ['quantiles', 'equal_interval', 'natural_breaks']
titles = ['分位数分级 (Quantiles)', '等距分级 (Equal Interval)', '自然断点分级 (Natural Breaks)']

fig, axes = plt.subplots(1, 3, figsize=(24, 7))

for i, (scheme, title) in enumerate(zip(schemes, titles)):
    try:
        world.plot(column='value', ax=axes[i], cmap='YlOrRd',
                   scheme=scheme, k=6,
                   legend=True, edgecolor='white', linewidth=0.3,
                   legend_kwds={'label': '数值', 'orientation': 'horizontal',
                               'shrink': 0.6})
    except:
        # 如果缺少 mapclassify 库,使用默认分级
        world.plot(column='value', ax=axes[i], cmap='YlOrRd',
                   legend=True, edgecolor='white', linewidth=0.3)
    
    axes[i].set_title(title, fontsize=14, fontweight='bold')
    axes[i].axis('off')

plt.tight_layout()
plt.show()

# 分级方法说明:
# - quantiles: 每个颜色等级包含相同数量的区域(推荐通用场景)
# - equal_interval: 数值范围等分(适合均匀分布数据)
# - natural_breaks: 基于数据自然聚集点分界(适合有聚类特征的数据)
4.3 将业务数据与地图合并
python 复制代码
import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# 基础地图
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
world = world[world['name'] != 'Antarctica']

# 模拟业务数据(各国家的销售额和门店数)
np.random.seed(42)
business_data = pd.DataFrame({
    'name': world['name'].values,
    'sales': np.random.randint(50, 500, len(world)),
    'stores': np.random.randint(5, 100, len(world)),
    'growth': np.random.uniform(-10, 30, len(world))
})

# 合并地图数据和业务数据(类似 Pandas 的 merge)
merged = world.merge(business_data, on='name', how='left')

fig, axes = plt.subplots(1, 2, figsize=(20, 7))

# 左图:销售额
merged.plot(column='sales', ax=axes[0], cmap='YlOrRd',
            scheme='quantiles', k=6,
            legend=True, edgecolor='white', linewidth=0.3,
            legend_kwds={'label': '销售额(万元)', 'orientation': 'horizontal'})
axes[0].set_title('全球销售额分布', fontsize=14, fontweight='bold')
axes[0].axis('off')

# 右图:增长率(发散型颜色)
merged.plot(column='growth', ax=axes[1], cmap='RdBu_r',
            scheme='quantiles', k=7,
            legend=True, edgecolor='white', linewidth=0.3,
            legend_kwds={'label': '增长率(%)', 'orientation': 'horizontal'})
axes[0].set_title('全球销售额分布', fontsize=14, fontweight='bold')
axes[0].axis('off')

axes[1].set_title('全球增长率分布(正负分化)', fontsize=14, fontweight='bold')
axes[1].axis('off')

plt.tight_layout()
plt.show()

五、Folium 交互式地图

Folium 基于 Leaflet.js 创建可在浏览器中交互的在线地图。

5.1 基础 Folium 地图
python 复制代码
import folium
import pandas as pd
import numpy as np

# 创建基础地图(以中国为中心)
m = folium.Map(
    location=[35.0, 105.0],  # 中心点(纬度,经度)
    zoom_start=4,             # 初始缩放级别
    tiles='OpenStreetMap'     # 地图样式
)

# 添加标记点
cities = pd.DataFrame({
    '城市': ['北京', '上海', '广州', '深圳', '成都', '杭州'],
    '纬度': [39.90, 31.23, 23.13, 22.54, 30.57, 30.27],
    '经度': [116.40, 121.47, 113.26, 114.06, 104.07, 120.15],
    '人口(万)': [2154, 2424, 1530, 1302, 1633, 1194]
})

for _, row in cities.iterrows():
    folium.Marker(
        location=[row['纬度'], row['经度']],
        popup=folium.Popup(
            f"<b>{row['城市']}</b><br>人口: {row['人口(万)']}万",
            max_width=200
        ),
        icon=folium.Icon(color='blue', icon='info-sign')
    ).add_to(m)

# 保存为 HTML 文件
m.save('interactive_map.html')
print("交互式地图已保存为: interactive_map.html")
print("用浏览器打开即可查看")

# 在 Jupyter 中直接显示 m
# m
5.2 热力图(Heatmap)
python 复制代码
from folium.plugins import HeatMap
import numpy as np

# 创建基础地图
m = folium.Map(location=[35.0, 105.0], zoom_start=4, tiles='OpenStreetMap')

# 模拟客户位置数据
np.random.seed(42)
n_points = 1000
latitudes = np.random.normal(35, 10, n_points)
longitudes = np.random.normal(105, 15, n_points)

# 创建热力图数据:[纬度, 经度, 权重]
heat_data = [[lat, lon, np.random.uniform(0.5, 1.5)] 
             for lat, lon in zip(latitudes, longitudes)]

# 添加热力图层
HeatMap(heat_data, radius=20, blur=15, max_zoom=1).add_to(m)

m.save('customer_heatmap.html')
print("客户分布热力图已保存为: customer_heatmap.html")
5.3 气泡地图
python 复制代码
from folium.plugins import MarkerCluster
import pandas as pd

# 模拟门店数据
np.random.seed(42)
stores = pd.DataFrame({
    '店名': [f'门店{i}' for i in range(1, 31)],
    '纬度': np.random.normal(32, 8, 30),
    '经度': np.random.normal(110, 12, 30),
    '月营收': np.random.randint(20, 150, 30),
    '评级': np.random.choice(['A', 'B', 'C'], 30, p=[0.3, 0.5, 0.2])
})

# 评级对应颜色
color_map = {'A': 'green', 'B': 'blue', 'C': 'red'}

m = folium.Map(location=[35.0, 105.0], zoom_start=4)

# 使用 MarkerCluster 实现缩放聚合
marker_cluster = MarkerCluster().add_to(m)

for _, row in stores.iterrows():
    folium.CircleMarker(
        location=[row['纬度'], row['经度']],
        radius=row['月营收'] / 10,  # 气泡大小与营收相关
        color=color_map[row['评级']],
        fill=True,
        fill_color=color_map[row['评级']],
        fill_opacity=0.6,
        popup=folium.Popup(
            f"<b>{row['店名']}</b><br>"
            f"月营收: {row['月营收']}万元<br>"
            f"评级: {row['评级']}",
            max_width=200
        )
    ).add_to(marker_cluster)

m.save('store_bubble_map.html')
print("门店气泡地图已保存为: store_bubble_map.html")

六、综合实战:中国省份销售热力地图

python 复制代码
import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# 模拟数据:中国各省份销售
np.random.seed(42)
provinces_data = pd.DataFrame({
    '省份': ['北京', '上海', '广东', '江苏', '浙江', '山东', '河南', 
            '四川', '湖北', '湖南', '福建', '安徽', '河北', '辽宁', 
            '陕西', '重庆', '天津', '江西', '云南', '广西', 
            '山西', '贵州', '吉林', '甘肃', '海南', '宁夏', 
            '青海', '西藏', '新疆', '内蒙古', '黑龙江'],
    '销售额': np.random.randint(50, 500, 31),
    '增长率': np.random.uniform(-5, 25, 31),
    '门店数': np.random.randint(5, 80, 31)
})

# 注意:实际工作中需要读取真实的中国地图 Shapefile
# 这里用世界地图中的中国区域作为演示

world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
china = world[world['name'] == 'China']

fig, axes = plt.subplots(1, 2, figsize=(16, 6))

# 绘制中国地图(简化版)
china.plot(ax=axes[0], color='lightblue', edgecolor='black', linewidth=1)
axes[0].set_title('中国地图(示意)', fontsize=14, fontweight='bold')
axes[0].axis('off')

# 业务数据可视化
colors = plt.cm.YlOrRd(np.linspace(0.2, 0.9, 31))
scatter = axes[1].scatter(
    provinces_data['销售额'],
    provinces_data['增长率'],
    s=provinces_data['门店数'] * 15,
    c=provinces_data['销售额'],
    cmap='YlOrRd',
    alpha=0.7,
    edgecolors='black',
    linewidth=0.5
)

# 标注部分省份
for idx, row in provinces_data.head(10).iterrows():
    axes[1].annotate(row['省份'], (row['销售额'], row['增长率']),
                    fontsize=8, ha='center')

cbar = plt.colorbar(scatter, ax=axes[1])
cbar.set_label('销售额(万元)', fontsize=11)

axes[1].set_title('各省份销售分析(气泡=门店数)', fontsize=14, fontweight='bold')
axes[1].set_xlabel('销售额(万元)')
axes[1].set_ylabel('增长率(%)')
axes[1].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

实战练习

练习一:门店选址分析

假设你正在为一家连锁咖啡店分析新门店选址。现有数据包括:各商圈的人流量、平均收入、竞争门店数量、租金水平。请:

  1. 在地图上标注现有门店位置(用不同颜色区分业绩等级)
  2. 创建一个模拟的区域热力图,显示各商圈的"开店指数"
  3. 标注推荐的新店选址

参考答案

python 复制代码
import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from shapely.geometry import Point, Polygon

# 模拟商圈数据
np.random.seed(42)
districts = pd.DataFrame({
    '商圈': ['商圈A', '商圈B', '商圈C', '商圈D', '商圈E', 
            '商圈F', '商圈G', '商圈H', '商圈I', '商圈J'],
    '中心经度': np.random.uniform(115, 122, 10),
    '中心纬度': np.random.uniform(30, 42, 10),
    '人流量': np.random.randint(5000, 50000, 10),
    '平均收入': np.random.randint(8000, 25000, 10),
    '竞争店数': np.random.randint(0, 15, 10),
    '月租金': np.random.randint(200, 1000, 10)
})

# 计算开店指数 = 人流量/竞争店数 * 收入水平 / 租金
districts['开店指数'] = (districts['人流量'] / (districts['竞争店数'] + 1) * 
                       districts['平均收入'] / districts['月租金'])

# 现有门店
stores = pd.DataFrame({
    '店名': ['门店1', '门店2', '门店3', '门店4', '门店5'],
    '经度': districts['中心经度'].head(5).values,
    '纬度': districts['中心纬度'].head(5).values,
    '月业绩': [85, 62, 91, 45, 73]
})
stores['等级'] = pd.cut(stores['月业绩'], bins=[0, 50, 70, 100], 
                        labels=['C', 'B', 'A'])

fig, axes = plt.subplots(1, 2, figsize=(16, 6))

# 左图:现有门店分布
colors_map = {'A': 'green', 'B': 'orange', 'C': 'red'}
for _, store in stores.iterrows():
    axes[0].scatter(store['经度'], store['纬度'], 
                    s=store['月业绩'] * 15, 
                    c=colors_map[store['等级']], 
                    alpha=0.7, edgecolors='black', linewidth=1, zorder=5)
    axes[0].annotate(store['店名'], (store['经度'], store['纬度']),
                    fontsize=8, ha='center')

axes[0].set_title('现有门店分布(绿=A级,橙=B,红=C)', fontsize=13)
axes[0].grid(True, alpha=0.3)

# 右图:商圈开店指数
bars = axes[1].barh(districts['商圈'], districts['开店指数'],
                    color=plt.cm.YlOrRd(np.linspace(0.3, 0.9, 10)))
axes[1].set_title('各商圈开店指数排行', fontsize=13)
axes[1].set_xlabel('开店指数(越大越推荐)')

# 标注推荐选址
top3 = districts.nlargest(3, '开店指数')
for _, d in top3.iterrows():
    axes[1].annotate(f'推荐!', (d['开店指数'], d['商圈']),
                    fontsize=9, color='red', fontweight='bold')

plt.tight_layout()
plt.show()

print("推荐开店商圈(按开店指数):")
print(districts[['商圈', '开店指数']].sort_values('开店指数', ascending=False).head(3))

练习二:用 Folium 创建客户分布地图

利用 Folium 创建一个交互式的客户分布地图,包含热力图层和标记点。

参考答案

python 复制代码
import folium
from folium.plugins import HeatMap, MarkerCluster
import numpy as np
import pandas as pd

np.random.seed(42)

# 模拟客户数据(以中国主要城市为中心聚集)
city_centers = [
    [39.90, 116.40, '北京'],   # 纬度, 经度, 城市名
    [31.23, 121.47, '上海'],
    [23.13, 113.26, '广州'],
    [30.57, 104.07, '成都'],
    [22.54, 114.06, '深圳']
]

customers = []
for lat, lon, city in city_centers:
    n = np.random.randint(50, 150)
    for _ in range(n):
        customers.append([
            lat + np.random.normal(0, 2),
            lon + np.random.normal(0, 2),
            city
        ])

df = pd.DataFrame(customers, columns=['lat', 'lon', '城市'])

# 创建基础地图
m = folium.Map(location=[33, 110], zoom_start=5)

# 添加热力图
heat_data = [[row['lat'], row['lon']] for _, row in df.iterrows()]
HeatMap(heat_data, radius=15, blur=10, max_zoom=8).add_to(m)

# 添加城市标记
for lat, lon, city in city_centers:
    count = len(df[df['城市'] == city])
    folium.CircleMarker(
        location=[lat, lon],
        radius=count / 10,
        color='blue',
        fill=True,
        fill_color='blue',
        fill_opacity=0.5,
        popup=f'{city}: {count} 位客户'
    ).add_to(m)

m.save('customer_distribution.html')
print("客户分布地图已保存为: customer_distribution.html")

本节总结

本节我们学习了地理空间分析的核心内容:

  1. 地理数据基础:矢量数据、常见格式(Shapefile/GeoJSON)、坐标系概念
  2. GeoDataFrame:Pandas DataFrame 的"地理扩展版",增加了 geometry 列
  3. 数据读取:从 Shapefile 等文件加载地理数据,与业务数据合并
  4. Choropleth 地图:用颜色深浅表示数值大小,注意分级方法的选择
  5. Folium 交互地图:创建可在浏览器中交互的在线地图,支持热力图、气泡图、标记点
  6. 空间可视化策略:根据分析目的选择合适的可视化方式

核心认知:地图可视化的本质是把数字"翻译"为空间模式。选择合适的颜色映射、分级方法和投影方式,才能确保翻译的"准确性"。记住:一张好的地图,读者应该在 3 秒内发现数据的关键模式。


下一节预告

第20节我们将学习 探索性分析(EDA)------如何通过图表发现数据背后的规律。前面我们学了很多绘图工具,但"画什么图"和"怎么画"同样重要。EDA 是一套系统化的数据分析流程,帮助你在建模之前全面理解数据。我们将学习 EDA 的完整流程,包括数据概况、分布分析、异常值检测、多变量关系,以及如何生成一份完整的 EDA 报告。

结尾

希望对初学者有帮助;致力于办公自动化的小小程序员一枚
希望能得到大家的【❤️一个免费关注❤️】感谢!
求个 🤞 关注 🤞 +❤️ 喜欢 ❤️ +👍 收藏 👍
此外还有办公自动化专栏,欢迎大家订阅:Python办公自动化专栏
此外还有爬虫专栏,欢迎大家订阅:Python爬虫基础专栏
此外还有Python基础专栏,欢迎大家订阅:Python基础学习专栏
相关推荐
Byte Wizard2 小时前
C语言文件操作
c语言·开发语言
Wonderful U2 小时前
基于Python+Django+MySQL构建个人任务管理系统:告别零散记录,实现高效日程管理
python·mysql·django
scan7242 小时前
SystemMessage,HumanMessage,AIMessage,ToolMessage
开发语言·前端·javascript
TickDB2 小时前
支持 MCP 的金融行情数据源怎么选:实时行情、财务数据和交易 API 的工程边界
python·websocket·mcp·行情数据 api
郝学胜-神的一滴2 小时前
干货版《算法导论》08:哈希——重构集合数据结构的速度魔法
数据结构·python·程序人生·算法·重构·软件构建·哈希算法
怣疯knight2 小时前
电脑多版本Python安装+切换全方案(分Windows / Mac/Linux,3种常用方法)
python
小科先生2 小时前
配置java环境变量
java·开发语言
meilindehuzi_a2 小时前
撕开 JS 的 Class 面具:从构造函数的 new 降生到顶层原型链的终极通关
开发语言·javascript·ecmascript
李可以量化2 小时前
QMT 实战:自定义绘制专属 K 线(下篇)—— 国产库与高性能库全解析
python·信息可视化·数据分析·量化·qmt·ptrade