ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Python区域建设缺口分析:数据清洗与Folium可视化

Python区域建设缺口分析:数据清洗与Folium可视化 刚看到“Almost nowhere in California is building enough”这类新闻时很多人的第一反应是争论政策。但如果你是一名数据分析师更自然的反应应该是这个“nowhere”到底是怎么算出来的统计口径是哪个部门定的比较的基准是每个城市自己的规划目标还是全州统一的标准数据落到哪个地理粒度——城市、县还是人口普查区块说实话“整个州几乎没有地方建够了”是一个过于宏观的结论。真正有价值的分析恰恰发生在你看不见的地方数据分散在不同部门口径不统一城市名时写“LA”时写“Los Angeles”有的地方没有规划目标有的时间序列还是空的。把这些乱七八糟的原始数据整理成一张能说明问题的缺口地图才是数据分析师的日常工作也才是很多人觉得“这种新闻离技术很远”背后真正值得展开的部分。本文不评价任何政策目标定得是否合理只做一件事演示一套可复用的区域建设缺口数据分析流程。从原始建设数据出发经过清洗、合并、计算缺口指标最后用地理可视化输出一张交互式缺口分布图。读完你能掌握数据清洗的通用套路、缺口计算的指标设计、以及用 Folium 快速生成地图报表的完整方法。1. 这篇文章真正要解决的问题1.1 数据分析师遇到的现实问题当新闻里出现“某地区整体建设不足”这类结论时大多数普通读者只关心结果。但作为技术人你要关心的第一件事永远是这个结论能不能被复现复现难在三个地方。第一建设数据来自多个数据源常见的是美国人口普查局的 Building Permits Survey它记录各城市新建住宅许可数量但这份数据的地理覆盖和更新频率有局限第二规划目标往往来自地方规划部门或州一级的住房分配数据和普查数据是两套体系字段名、单位、时间区间都不一致第三一旦要做空间分析还需要把城市名称转成经纬度这一步看着简单实际会踩很多坑比如重名城市、拼写差异、经纬度缺失。这些问题单独看都不难叠在一起就会让一个看起来五分钟能搞定的分析拖上半天。1.2 为什么直接看汇总数据不够如果只看全州总量你很可能会得出“确实不够”的结论。但这个结论没有任何粒度信息你回答不了更关键的问题缺口主要集中在沿海大城市还是内陆中小城市是普遍性不足还是少数极端值拉低了整体在哪个区域按什么比例增加建设最有效要回答这些问题必须把数据下钻到城市或县一级。下钻之后你会遇到典型的长尾效应少数几个大城市的建设量占了全州很大比例而绝大多数中小城市的建设量极低。如果用同一个可视化比例尺小城市会在地图上完全不可见。如何处理这种量纲差异是实际分析中必须解决的问题。1.3 本文的技术边界与读者收益本文的技术边界很明确不讨论目标设定是否科学不讨论政策执行效果只讲数据分析链路。你会得到一套可以直接跑通的 Python 代码完成从原始表到缺口地图的完整流程。适合以下读者需要处理城市级面板数据的数据分析师想学地理可视化但不熟悉 GIS 的 Python 使用者以及准备做类似社会数据项目的学生。2. 核心概念与数据处理思路2.1 建设量与规划目标建设量衡量的是一个地区在统计周期内实际新建或许可建设的住宅单元数。规划目标则是某个部门预先设定的期望值。这两者口径差异很大比如规划目标可能针对五年周期而建设许可是按年统计规划目标可能包含不同类型住宅而建设量数据可能只覆盖某类许可。做分析前最优先的事就是把两者对齐到同一个时间窗口和同一个地理范围。如果不确认口径算出来的缺口分分钟是错的。2.2 数据粒度数据粒度指一条记录对应多大的地理范围。常见粒度从细到粗是人口普查区块、街区、城市、县、州。粒度越细分析越精确但数据缺失、噪声和隐私脱敏问题也越严重。本文以城市级粒度演示因为这是公开数据中比较容易获取且信息量适中的粒度。需要注意规划目标文件里的“城市”边界和人口普查数据里的“城市”并不总是同一个东西合并前要核对行政区划编码。2.3 地理编码地理编码是将城市名或地址转换为经纬度坐标的过程。简单做法是用地理数据库做匹配但城市名匹配经常遇到问题同名的城市在不同州存在、官方名称和惯用简称不一致、数据表里混入了拼写错误。更稳妥的做法是先通过行政区划代码关联坐标城市名只作为辅助校验字段。这个思路可以大幅减少“匹配到了错误位置”这类隐蔽问题。3. 数据获取与准备工作3.1 常见公开数据源真实项目中数据获取是最不性感的环节但也是决定后续分析上限的环节。针对加州住房建设分析这类场景至少有两条公开数据来源可以参考一是美国人口普查局的 Building Permits Survey提供城市级别的新建住宅许可数量。它的问题是按年度发布对实时性有要求的场景需要关注发布时间滞后。二是加州住房与社区发展部门发布的区域住房需求分配数据它给出了各地区在一定周期内需要完成建设的规划目标。这份数据也是按区域划分和城市级建设数据对接时要先确认区域边界与城市列表的映射关系。本文为了演示完整流程会直接用一份模拟的演示数据。真实项目中请替换成你确认过口径的官方数据源并记录来源和下载日期这是数据报告的基本素养。3.2 环境准备本文使用 Python 3.8 以上版本依赖库如下pandas数据清洗和表格计算folium交互式地图可视化jinja2folium 依赖的模板库一般会随 folium 自动安装安装命令pip install pandas folium如果网络环境较慢可以使用国内镜像源pip install pandas folium -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后在命令行执行python -c import pandas, folium; print(ok)能输出 ok 就说明环境就绪。这是保证后续代码可复现的第一步也是排查问题时的底线。4. 数据清洗与缺口指标计算4.1 构造演示数据先做一份用于演练的演示数据包含城市名、年份、建设量、规划目标、经纬度。注意这里的数据是我手动构造的模拟值仅用于演示计算流程不代表任何真实统计结论。文件路径build_demo_data.pyimport pandas as pd demo pd.DataFrame({ city: [ Los Angeles, San Francisco, San Diego, San Jose, Sacramento, Fresno, Oakland ], year: [2023, 2023, 2023, 2023, 2023, 2023, 2023], units_built: [15000, 3600, 8200, 5100, 3300, 2800, 2600], target: [22000, 6500, 11500, 9500, 5500, 4500, 5500], lat: [34.0522, 37.7749, 32.7157, 37.3382, 38.5816, 36.7378, 37.8044], lon: [-118.2437, -122.4194, -117.1611, -121.8863, -121.4944, -119.7871, -122.2712] }) demo.to_csv(housing_data.csv, indexFalse) print(demo data saved)这段代码做了三件事创建包含核心字段的 DataFrame、写出 CSV 文件、打印提示信息。真实项目中这一步通常是从官方数据源下载后另存为 CSV而不是手动构造。4.2 清洗逻辑文件路径clean_data.pyimport pandas as pd def load_housing_data(csv_path): df pd.read_csv(csv_path) return df def clean_housing_data(df): # 去掉关键字段缺失的记录 df df.dropna(subset[units_built, target]) # 统一城市名称格式去除首尾空格并转为首字母大写 df[city] df[city].str.strip().str.title() # 过滤掉目标值为空的年份 df df[df[target] 0] return df if __name__ __main__: raw load_housing_data(housing_data.csv) cleaned clean_housing_data(raw) cleaned.to_csv(housing_cleaned.csv, indexFalse) print(cleaned)这个清洗脚本里最容易被忽略的是target 0这个过滤条件。如果规划目标为 0 或负值会造成后续完成率计算出无穷大或负数直接污染结果。另一个容易踩坑的是城市名标准化如果直接使用原始名称做合并los angeles和Los Angeles会被当成两个城市这种问题在真实数据里非常常见。4.3 缺口指标文件路径calc_gap.pyimport pandas as pd def calc_gap(df): df df.copy() df[gap] df[target] - df[units_built] df[completion_rate] df[units_built] / df[target] df[status] df[completion_rate].apply( lambda r: 达标 if r 1 else (接近 if r 0.8 else 缺口明显) ) return df if __name__ __main__: cleaned pd.read_csv(housing_cleaned.csv) result calc_gap(cleaned) result.to_csv(housing_result.csv, indexFalse) print(result.groupby(status)[city].count()) print(result)这里设计了三个指标。gap反映绝对缺口数量适合回答“缺多少”completion_rate反映完成比例适合回答“完成得怎么样”status是一个用于快速分类的派生字段。三个指标侧重点不同可视化时可以根据需要选择。这里的核心判断是缺口分析不能只看绝对值。一万套缺口在大城市可能只是零头在小城市可能是几年都完不成的任务。所以实际报告中应该同时展示绝对缺口和完成率否则很容易被个别大城市的数值带偏。5. 地理空间分析与可视化5.1 合并城市坐标在多数真实项目中建设数据表只有城市名没有经纬度。坐标通常来自另一张地理信息表可以通过城市代码来关联。为了保持演示流程的完整性前置的坐标字段已经写入 CSV。真实场景中你可以用以下代码完成合并import pandas as pd result pd.read_csv(housing_result.csv) geo_df pd.read_csv(city_geo.csv) merged pd.merge(result, geo_df, oncity, howleft) missing merged[merged[lat].isna()] if len(missing): print(以下城市缺少坐标) print(missing[city].tolist())这里使用howleft含义是保留左侧建设数据的所有记录坐标匹配不上就补空值。这样你可以显式看到哪些城市没有坐标而不是被 merge 静默丢弃。这是数据处理中必须养成的一个习惯合并之后一定要检查缺失值。5.2 用 Folium 生成缺口地图文件路径make_map.pyimport pandas as pd import folium merged pd.read_csv(housing_merged.csv) m folium.Map(location[36.5, -119.5], zoom_start6, tilesCartoDB positron) for _, row in merged.iterrows(): if pd.isna(row[lat]) or pd.isna(row[lon]): continue if row[completion_rate] 1: color green elif row[completion_rate] 0.8: color orange else: color red radius min(30, max(6, abs(row[gap]) ** 0.5 * 2)) folium.CircleMarker( location[row[lat], row[lon]], radiusradius, colorcolor, fillTrue, fill_opacity0.6, popup( f{row[city]}br f完成率: {row[completion_rate]:.1%}br f缺口: {row[gap]:.0f} 套 ) ).add_to(m) m.save(housing_gap_map.html) print(已生成 housing_gap_map.html)这段代码有几个值得留意的设计。第一颜色编码明确区分达标、接近、缺口明显看图的人不需要额外解释。第二圆点半径使用abs(gap) ** 0.5做开方缩放避免缺口差距过大导致小城市完全被大城市遮住。第三使用popup展示关键指标让地图既能总览、又能下钻到个体城市。辐射半径用开方而不是线性比例是一个比较实用的可视化技巧。线性比例下如果 A 城市缺口是 B 城市的 100 倍圆点直径也是 100 倍B 城市基本会变成一个看不见的小点。开方缩放能把比例压缩到 10 倍主体差异仍然保留但小城市也能被看到。5.3 交互式输出的价值.html格式的好处是可以直接在浏览器中打开无需启动任何服务。你可以缩放、拖动、点击圆点查看详情这种交互式体验在汇报场景中的价值远高于静态图片。以下命令用于启动一个本地服务方便局域网内其他人访问python -m http.server 8000启动后在浏览器中访问http://localhost:8000/housing_gap_map.html即可查看地图。如果你的数据文件很大圆点数超过几千页面交互可能会变卡这时建议改用静态聚合图或后端地图服务而不是继续堆 CircleMarker。6. 运行结果与效果验证6.1 运行命令与预期输出按顺序执行以下命令python build_demo_data.py python clean_data.py python calc_gap.py python merge_geo.py python make_map.py在演示数据集上最终会输出一个housing_gap_map.html文件。控制台上会看到类似这样的分组计数缺口明显 4 接近 2 达标 1同时housing_result.csv中可以看到每个城市独立一行包含gap、completion_rate、status字段。手工核对一遍这些数字能很快确认计算逻辑是否符合预期。6.2 如何判断结果是否可信跑通流程只是第一步结果可信是更重要的标准。建议按以下顺序检查检查completion_rate最大值和最小值如果出现负数或超过 100 的异常倍数优先怀疑规划目标单位不一致。检查status为“达标”的城市数量一个地区全州都不达标很正常但如果全部达标或全部不达标要怀疑规划目标的统计口径是不是有问题。用地图层面对比打开地图后点几个大城市的圆点看popup里的数字和表格是否一致排除坐标关联错误。如果第一步就发现明显异常不要急着调可视化先回到清洗脚本检查字段类型和过滤条件。地图上的错误往往不是空间问题而是数据问题。7. 常见问题与排查方法问题现象可能原因排查方式解决方案运行脚本时 Chinese 字符乱码CSV 编码不是 UTF-8查看文件头部十六进制在pd.read_csv中指定encodingutf-8或encodinggbk地图上某些城市不显示经纬度字段缺失或类型为字符串print(df[df[lat].isna()])检查缺失再df.dtypes检查类型填充坐标或把字符串列用pd.to_numeric转成数值完成率出现负数建设量或目标值有负值执行df.describe()查看最小值清洗时过滤掉units_built 0或target 0合并后城市行数变多坐标表存在一城多行用df[city].value_counts()检查重复按城市编码去重或先聚合坐标表地图打开后是白屏生成 html 时没有实际加载地图底图检查控制台是否有报错确认网络能访问底图服务改用离线瓦片或更换tiles参数这些坑在真实项目中出现的概率非常高尤其是 CSV 编码和坐标缺失。排查时记住一个原则先确认数据再怀疑代码。大部分异常只要打印出关键列的dtype、范围、缺失值数量就能快速定位。8. 最佳实践与工程建议8.1 数据口径统一数据分析最怕口径不统一。项目开始前先做一个数据字典明确每个字段的含义、单位、时间口径。建设量是“许可数”还是“开工数”规划目标是“年度目标”还是“周期目标”这些差异会直接决定分析结论是否成立。写清楚数据字典之后团队协作和后续复查都会省很多事。另外所有下载的原始数据要保留副本不要直接在原始文件上做清洗。建议目录结构按raw/、cleaned/、result/分层存放每层文件加日期后缀。这与版本管理的思路一致可以在结论被质疑时快速回溯到是哪个环节出了问题。8.2 代码工程化脚本不要全是面条式的pd.read_csv加to_csv。建议把每个步骤封装成函数就像前面示例里的load_housing_data、clean_housing_data、calc_gap一样。每个函数只做一件事输入输出都通过参数显式传递。这样后续调参、加数据源、换口径都只需要改一个函数不会影响整条链路。脚本之间用命令行依次执行在真实项目中更推荐用 Makefile 或 shell 脚本串联。只要新增规则清晰就能做到一键全量重算避免“改了第三步忘了跑第四步”这种低级事故。8.3 自动化更新如果这类分析要定期运行建议写一个调度任务。对于一次性分析手动执行没问题对于需要月度或季度更新的项目调度脚本能避免大量重复劳动。自动化之外至少要留一个 README写清楚每条命令的作用和运行顺序。三个月后你自己回头看项目一定会感谢这份文档。安全提醒如果你使用的数据包含个人隐私或敏感信息务必先脱敏再分析。用于工程决策的数据最好有明确的数据来源、下载日期和负责人信息保证任何环节出问题都能追溯到人。9. 总结与后续学习方向这篇文章从一个宏观新闻结论切入拆解了如何用 Python 完成区域建设缺口的数据分析。核心链路是准备数据、清洗字段、计算缺口指标、按城市合并坐标、用 Folium 生成交互式地图。你不仅学到了代码还理解了每个步骤背后的设计理由——为什么要先检查缺失值、为什么坐标合并要用left join、为什么地图的圆点半径要做开方缩放。下一步如果想深入可以从这几个方向继续。一是学习 GeoPandas它能把地理空间数据分析和 pandas 语法结合起来适合处理具体到人口普查区块的更细粒度分析。二是学习更复杂的面板数据处理方法因为住房数据通常是多年多城市的纵向数据时间维度的加入会让分析复杂度上升一个层级。三是研究更专业的可视化库比如 Pydeck 或 Kepler.gl它们在处理海量空间点时会比 Folium 更顺手。最后给你一个做这类项目的实用提醒先花时间找对数据源确认口径再写第一行代码。绝大部分城市级数据项目的返工都不是代码写错了而是数据口径从一开始就没对齐。把这个问题放在最前面解决你的分析结果会可靠得多。
返回列表