DuckDB 地理空间分析实战:从坐标到商业洞察
为什么现在需要地理空间分析?
在电商、物流、零售行业,地理位置就是钱。
- 外卖平台需要根据用户位置推荐最近的商家
- 连锁品牌选址要看周边 3 公里内的人流和竞争
- 物流公司要计算最优配送路径
- 金融风控需要识别异常交易地点
传统方案是什么?PostGIS + PostgreSQL,或者 Python 的 GeoPandas + Shapely。但这些都是重型工具——安装复杂、运维成本高、学习曲线陡峭。
DuckDB 的出现改变了这一切。内置的 ST_ 系列空间函数让你能在纯 SQL 中完成 90% 的地理空间分析需求,而且速度极快。
核心能力一览
DuckDB 的空间扩展提供了完整的 OGC Simple Features 标准支持:
| 功能类别 | 常用函数 | 典型场景 |
|---|---|---|
| 距离计算 | ST_Distance, ST_DWithin | 查找附近门店 |
| 缓冲区 | ST_Buffer | 划定服务半径 |
| 包含关系 | ST_Contains, ST_Within | 判断是否在区域内 |
| 相交检测 | ST_Intersects | 碰撞检测 |
| 中心点 | ST_Centroid | 区域中心计算 |
| 面积周长 | ST_Area, ST_Perimeter | 地块分析 |
| 最近邻 | ST_NearestPoint | 最优匹配 |
第一步:启用空间扩展
-- 加载空间扩展(首次需要下载)
LOAD spatial;
INSTALL spatial;
-- 验证安装
SELECT version();
第二步:创建地理空间数据集
假设你是一家连锁咖啡店的运营分析师,手头有:
- 全国门店列表(经纬度)
- 竞品门店列表
- 商圈 POI 数据
LOAD spatial;
-- 创建门店表
CREATE TABLE my_stores AS
SELECT * FROM (VALUES
('北京三里屯店', 116.4510, 39.9320),
('上海南京路店', 121.4730, 31.2300),
('广州天河路店', 113.3250, 23.1300),
('深圳华强北路', 114.0650, 22.5400),
('成都春熙路店', 104.0800, 30.6600),
('杭州西湖路店', 120.1500, 30.2800),
('武汉江汉路店', 114.2800, 30.5800),
('重庆解放碑店', 106.5800, 29.5600)
) AS t(name, longitude, latitude);
-- 创建竞品门店表
CREATE TABLE competitor_stores AS
SELECT * FROM (VALUES
('星巴克三里屯', 116.4530, 39.9340),
('瑞幸三里屯', 116.4490, 39.9310),
('星巴克南京路', 121.4750, 31.2320),
('瑞幸南京路', 121.4710, 31.2280),
('Manner天河路', 113.3270, 23.1320),
('瑞幸华强北', 114.0670, 22.5420)
) AS t(name, longitude, latitude);
-- 将经纬度转换为 WKT 几何对象
ALTER TABLE my_stores ADD COLUMN geom GEOMETRY;
UPDATE my_stores SET geom = ST_SetSRID(ST_MakePoint(longitude, latitude), 4326);
ALTER TABLE competitor_stores ADD COLUMN geom GEOMETRY;
UPDATE competitor_stores SET geom = ST_SetSRID(ST_MakePoint(longitude, latitude), 4326);
第三步:距离分析 —— 找到你的"安全距离"
最基础也最有价值的分析:我的门店和竞品之间隔多远?
-- 计算每对门店之间的距离(单位:米)
SELECT
m.name AS my_store,
c.name AS competitor,
ROUND(
ST_Distance(
ST_Transform(m.geom, 3857), -- 转为 Web Mercator(米制坐标系)
ST_Transform(c.geom, 3857)
)::INTEGER
) AS distance_meters,
CASE
WHEN ST_Distance(
ST_Transform(m.geom, 3857),
ST_Transform(c.geom, 3857)
) < 500 THEN '⚠️ 直接竞争'
WHEN ST_Distance(
ST_Transform(m.geom, 3857),
ST_Transform(c.geom, 3857)
) < 1000 THEN '📊 潜在竞争'
ELSE '✅ 安全距离'
END AS competition_level
FROM my_stores m
CROSS JOIN competitor_stores c
WHERE ST_DWithin(
ST_Transform(m.geom, 3857),
ST_Transform(c.geom, 3857),
1000 -- 1000米范围内
)
ORDER BY distance_meters;
业务洞察:
- 北京三里屯店距离两家竞品都小于 500 米 → 激烈竞争区,需要差异化策略
- 成都春熙路店没有竞品在 1km 内 → 蓝海市场,可考虑加大投放
第四步:缓冲区分析 —— 划定你的"辐射圈"
-- 为每家门店创建 500 米服务半径缓冲区
CREATE TABLE store_buffers AS
SELECT
name,
ST_Buffer(ST_Transform(geom, 3857), 500) AS buffer_geom
FROM my_stores;
-- 查询每个缓冲区内有多少家竞品
SELECT
s.name AS store,
COUNT(c.name) AS nearby_competitors
FROM store_buffers s
LEFT JOIN competitor_stores c
ON ST_Contains(s.buffer_geom, ST_Transform(c.geom, 3857))
GROUP BY s.name
ORDER BY nearby_competitors DESC;
变现建议: 这个分析可以直接做成 SaaS 产品——面向连锁品牌的"选址助手"。输入地址,输出竞品密度热力图,月费 299 元起。
第五步:最近邻搜索 —— 智能派单
-- 给定一个新订单位置,找到最近的 3 家门店
WITH order_location AS (
SELECT ST_SetSRID(ST_MakePoint(116.4550, 39.9350), 4326)::GEOMETRY AS geom
)
SELECT
m.name,
ROUND(ST_Distance(
ST_Transform(m.geom, 3857),
ST_Transform(o.geom, 3857)
)) AS distance_m
FROM my_stores m, order_location o
ORDER BY distance_m
LIMIT 3;
第六步:从外部数据源加载真实地理信息
DuckDB 可以直接读取 GeoJSON 文件:
-- 加载城市商圈 GeoJSON
CREATE TABLE districts AS
SELECT
feature->>'name' AS district_name,
feature->>'population' AS population,
st_geomfromjson(feature->'geometry') AS geom
FROM read_geojson('https://example.com/china-districts.geojson');
-- 统计每个商圈内有多少门店
SELECT
d.district_name,
d.population,
COUNT(m.name) AS store_count,
ROUND(d.population::DOUBLE / NULLIF(COUNT(m.name), 0), 0) AS per_store_population
FROM districts d
LEFT JOIN my_stores m ON ST_Contains(d.geom, m.geom)
GROUP BY d.district_name, d.population
ORDER BY per_store_population;
与传统方案对比
| 维度 | PostGIS + PostgreSQL | GeoPandas (Python) | DuckDB Spatial |
|---|---|---|---|
| 安装难度 | 高(需编译) | 中 | 低(一行 SQL) |
| 学习成本 | 高 | 中 | 低(SQL 语法) |
| 大数据量 | 好 | 差(内存限制) | 优秀(列式存储) |
| 交互式分析 | 一般 | 好 | 极好 |
| 部署运维 | 重 | 轻 | 极轻 |
| 适合场景 | 生产级 GIS | 地理可视化 | 快速分析/原型 |
变现建议
地理空间分析是高价值 B 端服务的核心能力:
选址 SaaS:面向连锁品牌,输入候选地址,自动输出竞品密度、人流预测、租金性价比评分。参考定价:500-2000 元/月/企业。
物流优化 API:为电商平台提供"最近仓库/门店"查询接口,按调用量收费。单次查询成本几乎为零。
房地产估值辅助:结合房价数据和 POI 距离(地铁站、学校、商场),生成区域价值评分报告。
营销精准投放:基于用户 GPS 轨迹,分析消费偏好区域,为品牌方提供定向广告投放建议。
关键优势:DuckDB 的空间分析可以在单机上处理千万级地理要素,不需要分布式集群。这意味着你可以用极低的硬件成本(甚至树莓派)搭建完整的服务。
总结
DuckDB 的空间扩展让地理空间分析变得前所未有的简单:
- 零安装:
LOAD spatial即可使用 - 标准 SQL:所有空间函数遵循 OGC 标准
- 高性能:列式存储 + 向量化执行,比 Pandas 快 10-50 倍
- 可扩展:支持 GeoJSON、WKT、WKB 等多种格式互转
对于数据分析师来说,这意味着你可以在同一个工作流中完成:数据采集 → 地理分析 → 报表生成 → 可视化导出,全部用 SQL 搞定。
💡 更多 DuckDB 地理空间分析案例和商业应用 → duckdblab.org
