You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于指定半径统计酒店周边POI数量及ID列表的技术问题

问题:酒店指定半径内POI统计偏差(半径高估1.5倍、数量统计错误)

我们需要统计指定半径(200米)内,每个酒店周边各类型POI的数量及对应ID,但现有方案经Tableau验证不符合预期:

  • POI数量被高估
  • 实际搜索半径约为设定值的1.5倍

现有数据集:

  • 酒店数据集(df_hotels):7万行50列,包含唯一酒店ID、经纬度、名称及其他属性
  • POI数据集(df_poi):2.5万行9列,包含唯一场所ID、经纬度、名称、设施类型(如restaurant、bar)等属性

由于数据量较大,无法用嵌套循环计算距离,计划用六边形网格优化效率,但当前用圆形搜索的代码存在偏差,需排查投影或取整问题。


现有代码的核心问题分析

  1. 半径转换逻辑错误

    • meters_to_degrees函数混用两种转换方式,最终仅使用公式计算结果,且创建buffer时错误只传入lon_deg作为半径,导致buffer是基于经度度数的圆形,而非对应200米的实际地理半径。
    • EPSG:4326是地理坐标系(经纬度),直接在此坐标系下用度数做buffer完全错误——经纬度的度数距离随纬度变化,且不是等距的,这是半径偏差的核心原因。
  2. 候选过滤缺失实际距离校验

    • R-tree的intersection仅返回buffer外接矩形内的POI,后续未校验这些POI到酒店的实际地理距离,导致矩形内超出圆形半径的POI也被统计,造成数量高估。
  3. 其他笔误

    • 代码最后修改的是airbnb_test而非目标数据集df_hotels。

修正后的解决方案(正确投影+距离校验)

核心思路:将地理坐标系(EPSG:4326)转换为局部UTM投影(以米为单位,保证距离计算准确),在投影坐标系下做buffer和距离计算,彻底避免经纬度的距离偏差。

import geopandas as gpd
import pyproj

# 输入参数
radius_meters = 200

# 1. 转换为GeoDataFrame并设置地理坐标系
hotels_geo = gpd.GeoDataFrame(
    df_hotels,
    geometry=gpd.points_from_xy(df_hotels["longitude"], df_hotels["latitude"]),
    crs="EPSG:4326"
)

poi_geo = gpd.GeoDataFrame(
    df_poi,
    geometry=gpd.points_from_xy(df_poi["longitude"], df_poi["latitude"]),
    crs="EPSG:4326"
)

# 2. 自动匹配区域UTM投影(以米为单位,保证距离计算准确)
def get_utm_crs(gdf):
    lon_center = gdf.geometry.x.mean()
    utm_zone = int((lon_center + 180) / 6) + 1
    is_northern = gdf.geometry.y.mean() > 0
    epsg_code = 32600 + utm_zone if is_northern else 32700 + utm_zone
    return pyproj.CRS(f"EPSG:{epsg_code}")

utm_crs = get_utm_crs(hotels_geo)
hotels_utm = hotels_geo.to_crs(utm_crs)
poi_utm = poi_geo.to_crs(utm_crs)

# 3. 创建POI的R-tree空间索引
poi_sindex = poi_utm.sindex

# 4. 提前初始化所有设施类型列,避免赋值报错
amenity_types = poi_utm["amenity"].unique()
for amenity in amenity_types:
    df_hotels[amenity] = 0

# 5. 遍历酒店统计周边POI
for idx, hotel_row in hotels_utm.iterrows():
    # 在UTM投影下创建200米半径的buffer
    hotel_buffer = hotel_row.geometry.buffer(radius_meters)
    # 用R-tree获取buffer外接矩形内的候选POI
    candidate_ids = list(poi_sindex.intersection(hotel_buffer.bounds))
    candidates = poi_utm.iloc[candidate_ids]
    
    # 精确筛选在圆形buffer内的POI(二次校验,排除矩形内超出圆形的点)
    valid_pois = candidates[candidates.geometry.within(hotel_buffer)]
    
    # 更新统计结果到原酒店数据集
    if not valid_pois.empty:
        counts = valid_pois.groupby("amenity").size()
        for amenity, count in counts.items():
            df_hotels.at[idx, amenity] = count
    
    # 进度打印
    if idx % 10000 == 0:
        print(f"已处理 {idx} 条酒店数据")

# 填充遗漏的NaN值为0
df_hotels.fillna(0, inplace=True)

六边形网格优化方案(可选,提升大数量级数据效率)

若数据量进一步增大,可通过六边形网格预分块减少查询范围:

  1. 用geopandas.GeoDataFrame.hexbin生成覆盖所有POI的六边形网格(网格边长对应200米半径的等效覆盖范围)
  2. 为每个POI标记所属的六边形ID
  3. 为每个酒店找到其所在及相邻的六边形,仅在这些六边形内搜索POI,避免全量索引查询

内容的提问来源于stack exchange,提问作者Chef Koch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 13:53:12