基于改进2SFCA的医疗设施可达性分析:Python实战与参数调优

发布时间:2026/10/10 15:02:03
基于改进2SFCA的医疗设施可达性分析:Python实战与参数调优
简介本资源为《基于改进2SFCA上海中心城区医疗设施可达性研究》的PDF学术论文面向地理信息、城市规划、公共卫生及智慧医疗领域的研究人员与从业者可用于医疗设施可达性评价、资源布局优化等课题的参考文献与专业指导。压缩包内共1个PDF文件大小约1.35MB内容完整呈现论文正文、图表与参考文献便于直接阅读与引用。论文引入幂函数距离衰减函数改进传统两步移动搜索法2SFCA结合GIS技术以上海中心城区为对象考虑人口规模特征对不同级别医疗设施的空间可达性进行评价分析。读者可从中获取改进2SFCA模型的构建思路、医疗设施分级可达性差异、内环内高可达与中外环间低可达的空间格局以及面向医疗资源规划与合理布局的参考依据适合作为相关研究的方法借鉴与实证案例。目前已有497人学习下载。1. 医疗可达性算不准多半栽在“供需匹配”这一步做过城市公共服务设施可达性评估的人大概都经历过这种场景辛辛苦苦把某市中心城区的医院点位、常住人口网格、路网数据都凑齐了跑完一遍两步移动搜索法2SFCA结果出来一看核心区某些街道的可达性指数居然比郊区还低。第一反应是数据错了反复检查坐标、投影、人口字段都没问题。问题往往出在2SFCA本身的一个隐含假设上——它把搜索半径内的所有供给和所有需求一视同仁既不区分医院等级也不考虑人口跨区就医的真实意愿。基于改进2SFCA的某市中心城区医疗设施可达性研究讲的正是这件事在经典两步移动搜索法框架上做改进让可达性结果更贴近真实就医行为。它解决的不是“有没有医院”这种粗粒度问题而是“某街道居民在合理出行范围内能实际竞争到多少医疗资源”。适合做城市规划、公共卫生资源配置、GIS空间分析方向的从业者也适合需要拿可达性结果支撑设施选址报告的人。下面把我自己复现这套方法时踩过的路、调过的参数、翻过的车按能照着做的顺序讲一遍。2. 经典2SFCA为什么在中心城区会失真三个假设与改进方向2.1 经典2SFCA的两步逻辑与它的隐含前提2SFCA的核心就两步。第一步对每个供给点j以它为中心画一个搜索半径d0把半径内所有需求点k的人口按距离衰减加权求和得到该供给点的潜在服务人口再用供给量除以这个人口得到供需比Rj。第二步对每个需求点i同样以d0为半径把半径内所有供给点的Rj按距离衰减加权求和得到需求点i的可达性Ai。公式不复杂但里面藏了三个假设第一搜索半径内所有供给点对需求者的吸引力相同不分医院等级第二搜索半径内所有人口对供给点的竞争是等权的不考虑人口自身特征第三需求者只会在半径内就医半径外完全不考虑。在中心城区医院密集、人口密度高、路网复杂这三个假设同时失效的概率很高。2.2 中心城区让2SFCA失真的三个具体机制第一个机制是供给端同质化。某市中心城区三甲医院和社区卫生服务中心在2SFCA里被当成同等供给只要在半径内就参与竞争。但真实情况是居民小病去社区、大病去三甲两者的服务腹地完全不同。把三甲和社区混在一起算核心区反而会因为三甲拉高了供需比、社区拉低了供需比出现可达性指数被平均掉的假象。第二个机制是需求端跨区竞争。中心城区人口密度高一个三甲医院的服务半径内可能覆盖几十万人口但这些人口并不都去这家医院。经典2SFCA把半径内所有人口都算作竞争者导致供给点的潜在服务人口被高估供需比被压低最终可达性偏低。第三个机制是距离衰减函数的玄学。经典2SFCA常用的是二值衰减——半径内权重为1半径外为0。这等于说距离医院499米和距离医院1米的人竞争权重完全一样。中心城区路网密集实际出行距离和直线距离差异大二值衰减会把可达性算得忽高忽低。2.3 改进方向分级供给、竞争权重与多半径针对上面三个机制常见的改进思路有三条。一是供给分级把医院按等级赋予不同权重三甲权重高、社区权重低让供需比反映真实吸引力差异。二是需求竞争加权引入人口竞争系数比如老年人口对医疗资源的竞争权重高于青壮年或者用实际就医流向数据反推竞争强度。三是多半径或连续衰减用高斯函数或幂函数替代二值衰减让距离的影响平滑过渡。这三条改进可以叠加使用。我一般会先做供给分级和连续衰减这两条对结果影响最直接数据要求也不高。竞争加权需要额外的人口结构数据或就医流向数据如果拿不到可以用年龄结构做代理变量。注意改进不是越多越好。每加一个改进项就多一组需要标定的参数。如果参数没有数据支撑改进反而会引入新的主观偏差。3. 用Python复现改进2SFCA从数据准备到可达性出图3.1 数据准备三类输入与坐标系统一复现改进2SFCA需要三类数据供给点数据医院点位、等级、床位数或医生数、需求点数据人口网格或街道人口、中心点坐标、路网数据用于计算实际出行距离可选。数据格式上供给点和需求点用带经纬度的CSV或GeoJSON人口网格用栅格或矢量面。坐标系统一是个容易翻车的点。如果直接用经纬度算距离得到的是度不是米。我一般会先把所有数据投影到适合该城市的投影坐标系比如某市中心城区常用的高斯-克吕格投影单位是米。投影参数根据城市所在经度带确定不要随便套用。import geopandas as gpd import pandas as pd import numpy as np from scipy.spatial import cKDTree # 读取供给点医院和需求点人口网格中心 supply gpd.read_file(hospitals.shp) # 字段name, level, beds, geometry demand gpd.read_file(population_grid.shp) # 字段pop, geometry # 统一投影到米制坐标系这里以某市中心城区适用的投影为例 crs_meter EPSG:32651 # UTM 51N实际按城市经度带调整 supply supply.to_crs(crs_meter) demand demand.to_crs(crs_meter) # 提取坐标数组 supply_coords np.array([(geom.x, geom.y) for geom in supply.geometry]) demand_coords np.array([(geom.x, geom.y) for geom in demand.geometry]) supply_beds supply[beds].values.astype(float) demand_pop demand[pop].values.astype(float)这段代码做了三件事读数据、转投影、提坐标。关键参数是crs_meter必须根据城市实际经度带选选错了距离计算会整体偏移。supply_beds是供给量也可以用医生数或床位数看数据可得性。demand_pop是需求量如果是栅格人口需要先转成点或面。3.2 供给分级给不同等级医院赋权重供给分级是改进2SFCA的第一步。常见做法是按医院等级赋权重三甲1.0、三乙0.8、二甲0.6、社区0.3具体数值可以根据当地就医流向调查调整。如果没有调查数据可以用床位数或卫技人员数做连续权重避免主观分级。# 按医院等级赋权重等级字段为level level_weight { 三甲: 1.0, 三乙: 0.8, 二甲: 0.6, 社区: 0.3 } supply[weight] supply[level].map(level_weight) # 如果等级字段缺失用床位数做连续权重 if supply[weight].isna().any(): supply[weight] supply[beds] / supply[beds].max() # 加权供给量 supply_weighted supply_beds * supply[weight].values这里level_weight是分级权重字典supply_weighted是加权后的供给量。如果等级数据不全用床位数归一化做连续权重是更稳妥的退路。注意权重不要全设为1否则就退化成经典2SFCA了。3.3 连续距离衰减高斯函数替代二值衰减距离衰减函数决定搜索半径内权重怎么随距离变化。经典二值衰减是半径内全1、半径外全0改进版常用高斯函数或幂函数。高斯函数的优势是平滑不会在半径边界产生突变。def gaussian_decay(distance, radius, sigmaNone): 高斯距离衰减distance和radius单位均为米 if sigma is None: sigma radius / 2.0 # 默认带宽为半径的一半 weight np.exp(-0.5 * (distance / sigma) ** 2) weight[distance radius] 0 # 超出半径权重为0 return weight def power_decay(distance, radius, beta1.5): 幂函数衰减 weight np.zeros_like(distance, dtypefloat) mask distance radius weight[mask] (1 - distance[mask] / radius) ** beta return weightgaussian_decay里sigma控制衰减速度越小衰减越快。power_decay里beta控制曲线形状越大衰减越剧烈。我一般先用高斯sigma取半径的一半跑出来结果比较稳。如果发现核心区可达性还是偏低可以调小sigma让近距离权重更突出。3.4 两步计算供需比与可达性指数有了供给权重和衰减函数就可以跑两步计算了。第一步算每个供给点的供需比第二步算每个需求点的可达性。# 参数设置 radius 3000 # 搜索半径单位米中心城区一般2000-5000 sigma radius / 2.0 # 构建KDTree加速邻域搜索 supply_tree cKDTree(supply_coords) demand_tree cKDTree(demand_coords) # 第一步计算每个供给点的供需比Rj Rj np.zeros(len(supply_coords)) for j, s_coord in enumerate(supply_coords): # 找出半径内的需求点 idx demand_tree.query_ball_point(s_coord, radius) if len(idx) 0: Rj[j] 0 continue d np.linalg.norm(demand_coords[idx] - s_coord, axis1) w gaussian_decay(d, radius, sigma) potential_pop np.sum(demand_pop[idx] * w) Rj[j] supply_weighted[j] / potential_pop if potential_pop 0 else 0 # 第二步计算每个需求点的可达性Ai Ai np.zeros(len(demand_coords)) for i, d_coord in enumerate(demand_coords): idx supply_tree.query_ball_point(d_coord, radius) if len(idx) 0: Ai[i] 0 continue d np.linalg.norm(supply_coords[idx] - d_coord, axis1) w gaussian_decay(d, radius, sigma) Ai[i] np.sum(Rj[idx] * w) # 把结果写回需求点 demand[accessibility] Ai demand.to_file(accessibility_result.shp, encodingutf-8)这段代码是核心。radius是搜索半径中心城区我一般从3000米起步跑完看结果分布再调。query_ball_point是KDTree的邻域查询比暴力循环快很多。第一步里potential_pop是供给点半径内的加权人口Rj是供需比。第二步里Ai是需求点的可达性本质是半径内供需比的加权和。参数说明radius决定搜索范围太小会导致很多需求点可达性为0太大会把远距离供给也算进来。sigma决定衰减速度越小越强调近距离。supply_weighted是加权供给量如果没做供给分级直接用原始供给量也行但结果会退化成经典2SFCA。3.5 结果可视化与分级出图算完可达性一般要出图看空间分布。用GeoPandas加Matplotlib可以快速出分级设色图。import matplotlib.pyplot as plt fig, ax plt.subplots(1, 1, figsize(10, 10)) demand.plot(columnaccessibility, cmapRdYlGn, legendTrue, schemequantiles, k5, axax) supply.plot(axax, colorblack, markersize5, label医院) ax.set_title(某市中心城区医疗设施可达性改进2SFCA) ax.axis(off) plt.savefig(accessibility_map.png, dpi300, bbox_inchestight)schemequantiles是分位数分级k5是分五级。分位数分级能保证每级数量相近适合看相对高低。如果想看绝对差异可以用schemefisher_jenks做自然断点分级。出图后重点看核心区是否出现可达性洼地如果有回去检查供给权重和衰减参数。4. 参数标定与结果验证让改进2SFCA的结果站得住4.1 搜索半径怎么定从出行意愿反推搜索半径是2SFCA最敏感的参数。定太大远距离医院也参与竞争可达性被平均定太小很多需求点算不出可达性。常见做法是参考当地居民就医出行调查看大多数人能接受的出行时间再按平均车速折算成距离。如果没有调查数据可以用公交或驾车等时圈做代理。我一般会跑三组半径做敏感性分析2000米、3000米、5000米。如果三组结果的空间格局一致说明结论稳健如果格局差异大说明半径选择对结论影响大需要在报告里说明。中心城区路网密、医院多3000米通常是个合理起点。4.2 衰减参数怎么调看结果分布偏度高斯衰减的sigma和幂衰减的beta控制衰减速度。调参时看可达性结果的分布偏度如果大部分需求点可达性接近0说明衰减太快调大sigma或调小beta如果可达性分布过于均匀说明衰减太慢调小sigma或调大beta。一个实操技巧是先把sigma设为半径的一半跑一遍然后看可达性最高的10%需求点和最低的10%需求点差距。如果差距在5倍以内说明衰减可能太慢如果差距超过50倍说明衰减太快。这个范围不是绝对的但能帮你快速判断参数是否离谱。4.3 验证方法与真实就医流向对比改进2SFCA的结果好不好最终要看能不能解释真实就医行为。如果有医院实际就诊患者居住地分布数据可以把可达性指数和实际就诊概率做相关分析。如果可达性高的地方就诊概率也高说明模型合理如果两者不相关甚至负相关说明模型设定有问题。没有就医流向数据时可以用医院等级和可达性的空间匹配做定性验证。三甲医院周边可达性应该明显高于社区医院周边如果结果相反回去检查供给权重是否设反了。提示验证不是为了证明模型对而是为了找出模型在哪些地方不对。可达性研究里没有绝对正确的模型只有相对合理的近似。5. 避坑与排查改进2SFCA最容易翻车的五个地方5.1 投影选错导致距离整体偏移现象可达性结果整体偏高或偏低但空间格局看起来正常。原因投影坐标系选错距离计算有系统偏差。解决用城市所在经度带对应的UTM投影或高斯-克吕格投影跑之前先用已知两点距离验证投影是否正确。5.2 供给权重设反导致核心区可达性洼地现象三甲医院密集的核心区可达性反而低于郊区。原因供给权重设反了或者供需比计算时分子分母搞反了。解决检查Rj supply_weighted / potential_pop供给量在分子、潜在人口在分母。如果核心区可达性低先看是不是三甲权重被设成了低值。5.3 人口数据精度不够导致供需比失真现象可达性结果在人口网格边界处出现突变。原因人口数据精度太粗网格太大供需比在网格内部被平均。解决尽量用细粒度人口网格或者用街道级人口做面插值。如果只有街道人口至少把街道中心点作为需求点不要用街道面直接算。5.4 搜索半径内无供给导致可达性为0现象部分需求点可达性为0但这些点周边明明有医院。原因搜索半径太小或者医院点位坐标有误。解决先检查医院点位是否落在路网外再逐步增大半径看可达性为0的点是否减少。如果增大半径后仍为0检查坐标是否投影错误。5.5 衰减函数参数极端导致结果两极化现象可达性结果要么全高要么全低几乎没有中间值。原因衰减函数参数设置极端比如sigma太小导致只有极近距离有权重或者beta太大导致衰减过快。解决把sigma设为半径的0.3到0.7倍之间试跑看结果分布是否合理。beta一般取1.0到2.0之间。6. 从可达性指数到选址建议一个可复用的分析习惯改进2SFCA跑出可达性指数只是第一步真正有价值的是把指数翻译成可执行的建议。我一般会做三件事识别可达性洼地、叠加人口密度找重点区域、对比不同改进方案的排序差异。识别可达性洼地时不要只看可达性最低的点要看低值是否成片。如果低值集中在某个街道说明这个街道的医疗资源配置确实不足如果低值零散分布可能是数据噪声或参数问题。叠加人口密度后优先关注高人口密度加低可达性的区域这些是资源缺口最大的地方。对比不同改进方案的排序差异是判断改进是否值得的关键。如果经典2SFCA和改进2SFCA的可达性排序高度一致说明改进没带来实质变化可能白折腾如果排序差异大说明改进确实改变了结论需要在报告里说明为什么改进版更可信。# 对比经典2SFCA和改进2SFCA的排序差异 demand[rank_classic] demand[accessibility_classic].rank() demand[rank_improved] demand[accessibility].rank() demand[rank_diff] demand[rank_improved] - demand[rank_classic] # 找出排序变化最大的需求点 top_diff demand.reindex(demand[rank_diff].abs().sort_values(ascendingFalse).index) print(top_diff[[pop, accessibility_classic, accessibility, rank_diff]].head(10))这段代码算排序差异rank_diff绝对值大的点就是改进影响最大的地方。如果这些点集中在核心区说明改进对中心城区的影响确实显著。我一般会把排序差异图和可达性图叠在一起看差异大的地方往往就是经典2SFCA失真最严重的地方。最后说个血泪经验改进2SFCA的参数没有标准答案只有针对特定城市、特定数据、特定问题的合理区间。我见过有人把参数调得极其精细结果换一个城市就完全不能用。与其追求参数最优不如把参数选择的逻辑写清楚让结果可复现、可质疑、可调整。可达性研究是给决策做参考的不是给论文凑图的。希望帮到你。本文还有配套的精品资源点击获取