2. 选址布局问题的数据基础与预处理

选址布局模型算得准不准,往往不取决于求解器多先进,而取决于输入数据是否干净、口径是否一致。一个典型的设施选址问题,输入无非三类:需求点(谁需要服务、需求量多大)、候选点(可以在哪里建、容量与固定成本多少)、距离或成本矩阵(两点之间走多远、花多少钱)。本节围绕这三类数据的获取、清洗、生成与可视化展开,目标是让读者拿到一份可以直接喂给模型的、可追溯的数据集。

一、空间数据获取与清洗

空间数据的来源大致分三层:底图与路网(如 OpenStreetMap、天地图、商业地图 API)、业务台账(订单地址、门店坐标、客户档案)、以及统计资料(行政区人口、用地性质、地价)。实操中最常见的做法是"地址转坐标":把台账里的文字地址批量地理编码为经纬度。以国内某城市配送网络为例,假设有 1200 条客户地址,通过地图 API 批量地理编码后,通常会有 5%~15% 的记录返回失败或精度不足(只匹配到区县中心)。这类记录必须单独标记,不能直接丢弃,也不能默认填充为城市中心点,否则会人为制造出一批"伪需求点"。

清洗环节要重点处理四类问题:坐标越界(经纬度写反、落在研究范围外)、重复点(同一客户多条记录、坐标完全相同)、精度不足(只到行政区级别)、属性缺失(需求量、服务时限为空)。建议在数据表中增加 geo_level(门牌/道路/区县)、is_valid、clean_note 三个字段,把每一次修正的原因写清楚。这样后续模型结果异常时,能快速回溯到是哪一批数据出了问题。

坐标参照系统一:若底图使用 WGS84,而业务坐标来自 GCJ-02(国内地图常见偏移坐标系),必须先统一转换,否则同一位置会出现数百米偏差,直接影响距离矩阵。

二、需求点与候选点生成

需求点通常有两种粒度:原始点粒度(每个客户一个点,适合点数在几千以内)和聚合粒度(按网格、街道或聚类中心聚合,适合大规模问题)。聚合时要保留"权重"——即该聚合单元内的总需求量,否则聚合会扭曲目标函数。常用做法是按 500m×500m 网格聚合,把落在同一网格的需求量求和,坐标取需求加权质心。

候选点的生成更考验业务判断。常见来源包括:现有设施位置(扩建/保留决策)、路网交叉口与主干道沿线、用地规划中的可建设地块、以及随机采样点。候选点不是越多越好——候选点数量直接决定模型规模,一个 200 需求点 × 500 候选点的 p-中值问题,变量规模已相当可观。实践中可先用"服务半径过滤"剔除明显不可行的候选点:若某候选点到最近需求点的路网距离已超过最大服务半径,则直接排除。

三、距离与成本矩阵构建

距离矩阵是选址模型的计算核心。三种口径要分清:欧氏距离(直线,适合粗略估算与理论分析)、曼哈顿距离(网格路网近似)、路网距离(真实最短路,最准确但计算量大)。对城市配送类问题,建议使用路网距离;对区域级设施布局,欧氏距离乘以 1.2~1.4 的迂回系数也是可接受的近似。

成本矩阵则是在距离基础上叠加单位成本:运输成本 = 距离 × 单位距离费率 × 需求量。若涉及多车型或多时段,费率应分档。下面是一段用 Python 构建距离矩阵的示意代码(示例参数,非实测结果):

import numpy as np
from scipy.spatial.distance import cdist

# demand_xy: (n,2) 需求点坐标; cand_xy: (m,2) 候选点坐标
D = cdist(demand_xy, cand_xy, metric='euclidean') * 1.3  # 迂回系数
rate = 2.5          # 元/公里·吨(示例)
demand = demand_qty # 各需求点需求量
cost = D * rate * demand[:, None]   # (n,m) 成本矩阵
print(cost.shape, cost.min(), cost.max())

构建完成后必须做一致性检查:矩阵是否对称(若对称距离)、对角是否为零、是否存在异常大值(往往是坐标错误导致)、单位是否统一(公里 vs 米是最常见的坑)。

选址数据预处理流程与三类核心输入 底图与路网 OSM / 地图API 业务台账 地址 / 坐标 / 需求量 统计资料 人口 / 用地 / 地价 数据清洗 坐标越界 / 写反 重复点去重 精度分级标记 坐标系统一 属性缺失补全 需求点生成 原始点 / 网格聚合 保留需求量权重 候选点生成 现有设施 / 路口 服务半径过滤 距离/成本 矩阵 欧氏 / 曼哈顿 路网最短路 × 费率 × 需求 对称性检查 单位一致性 输出:可追溯的建模数据集 需求点表 + 候选点表 + 距离矩阵 + 成本矩阵 + 清洗日志

四、数据可视化与探索性分析

在把数据交给模型之前,至少要做三张图。第一张是需求点分布图,用点的大小或颜色深浅表示需求量,能一眼看出需求是集中在老城区还是分散在郊区。第二张是候选点与需求点的叠加图,重点观察候选点是否覆盖了需求密集区,是否存在大片"服务盲区"。第三张是距离分布直方图,如果距离矩阵中出现大量超过 50km 的值,而业务上最大服务半径只有 30km,说明坐标或口径出了问题。

探索性分析还要关注几个量化指标:需求点的空间聚集度(可用最近邻指数)、候选点的覆盖冗余度(每个需求点被多少个候选点在服务半径内覆盖)、以及距离矩阵的偏度。覆盖冗余度尤其有用——如果某个需求点只被 1 个候选点覆盖,那么该候选点一旦因用地或成本原因被排除,这个需求点就会失去服务,模型必须对此有约束或备选方案。

常见陷阱:把"直线距离"和"路网距离"混用。若需求点与候选点分别用不同口径计算,矩阵将不再满足三角不等式,后续基于距离的聚类和分配结果都会失真。务必在数据字典中写明每一列距离的口径与单位。

完成上述四步后,建议交付一份数据字典,至少包含:字段名、含义、单位、来源、清洗规则、缺失处理方式。这份字典是模型可复现的基础,也是后续结果解释与敏感性分析的依据。数据基础打牢了,选址模型才谈得上"算得准、说得清"。