2011到2025年,整整15年,覆盖全国两百多个地级市的二手房房价数据,还同时提供Excel和Shp两种格式——第一次凑齐这样一份数据的时候,我第一反应不是兴奋,反而是警惕。做数据分析的人都知道,越“完整”的数据,越可能暗藏各种口径和空间的坑。这份数据的价值不在于它“有没有”,而在于你拿到之后能不能把它用对、用透。
这篇文章我打算把这套数据的来龙去脉、字段结构、两种格式的适用场景,以及我从处理过程中踩出来的那些坑,一次性讲清楚。不管你是要做城市房价面板分析、写毕业论文,还是只想用Shp文件画一张像样的房价分布图,都应该能从里面找到直接能用的东西。
1. 数据制作前的关键决定:时间跨度、空间粒度和指标口径
1.1 为什么从2011年开始,而不是更早
很多人在找房价数据时,第一反应是“越早越好”。但真正把2010年之前的数据翻出来看一眼,会发现一个冷冰冰的现实:那时候大部分地级市的二手房交易样本量少得可怜,很多城市一个月成交就几十套,价格波动根本不是市场真实反映,而是少数几套房源的偶然结果。
2011年算是一个比较公认的“靠谱分水岭”。一方面,主流房产信息平台从那几年开始大范围积累线上挂牌和成交记录,数据覆盖的城市数量上来了;另一方面,二手房市场从那个时间点开始真正从“房改房主导”转向“商品房主导”,价格信号比之前要干净得多。所以把2011设为起点,不是随便拍脑袋,而是综合考虑了样本量、覆盖度和数据稳定性之后的选择。到2025年,正好一个完整的15年区间,做长周期趋势分析也够用了。
1.2 “二手房价格”到底取哪个数值才算准
这是整个数据集里最容易产生分歧的地方。二手房价格至少存在四种常见口径:挂牌均价、成交均价、挂牌中位数、成交中位数。不同平台、不同机构给的往往不是同一个东西,直接拿来横向比较,结果会非常离谱。
这套数据最终选择的是以年度为单位的成交均价作为主指标,同时附带了样本量和变异系数两个辅助字段。为什么选中位数?因为中位数对极端高总价房源不敏感。比如一个城市某年成交了300套二手房,其中有两套顶级豪宅,均价会被瞬间拉高8到10个百分点,但中位数几乎纹丝不动。做城市间横向对比和年份纵向演变时,稳健性比敏感性重要得多。
当然,成交均价也有意义,它和城市的产品结构强相关。所以数据里两个都保留了,主推中位数,均价作为参照字段。你在使用的时候,建议以中位数为主指标做分析,均价可以用来做交叉验证,或者研究城市内部的“高端房源溢价程度”。
1.3 地级市粒度的价值与局限
这套数据的空间粒度是地级市,也就是“市域”层面的汇总。相比区县粒度,它的优势在于稳定和可计算:全国多数区县的二手房成交样本在早期年份太少,根本无法形成有效价格;而地级市层面,即使2011年也能保证基本样本量。但代价是,市域均价会掩盖城市内部的巨大差异——比如某东南沿海城市,城区和代管县之间的房价可能差出三四倍,一个均值落在这中间,两边都不讨好。
所以在使用这类数据时,我个人的建议是:做省级对比、全国格局、城市分类、面板回归这类宏观分析,地级市粒度完全够用;但如果你的研究问题是“某个城市内部哪个板块涨得最快”,这份数据只能给你一个粗粒度的背景参考,你必须去找更细粒度的区县或板块数据。认清边界,数据才不会用错方向。
2. Excel与Shp字段结构拆解:拿到手先看懂这张表
2.1 Excel主表的字段设计与单位约定
Excel格式的表格是这套数据的基础层,也是绝大多数人先打开的东西。主表的字段结构是大宽表设计:每一行是一个城市在某一年的一条记录。核心字段包括:行政区划代码、城市名称、所属省份、年份、二手房成交均价、二手房成交中位数、样本套数、同比涨跌幅、经度、纬度。
这里单独提醒几个容易忽略的小约定。第一,价格字段的单位是“元/平方米”,没有做万元换算,所以读出来就是上万的四位数或五位数;第二,同比涨跌幅字段只在2012年及以后有值,2011年作为基期必然是空值,这不是数据缺陷,而是定义使然;第三,经度纬度采用的是GCJ-02坐标系的近似值,是基于城市政府驻地点位生成的,精度在公里级,用途是给你做散点图时定位用,不要拿它去做精密空间计算。
如果要用Excel做透视表,推荐直接把“城市名称+年份”组合成复合键,然后用年份做列,城市做行,生成一张标准的宽面板表。这样后续无论是导入统计软件还是做图表,都顺手很多。
2.2 Shp文件的属性字段与空间要素对应关系
Shp格式的部分,是很多人觉得头大的地方。拆开来看其实不复杂。一个完整的地级市Shp压缩包,里面至少包含四类文件:.shp(几何数据)、.shx(索引)、.dbf(属性表)、.prj(坐标系定义)。你拿GIS软件打开后,地图上每一个多边形对应一个地级市,属性表里的每一行则是一条城市记录。
属性字段和Excel主表高度一致,但没有“年份”维度,因为Shp文件本身不支持面板结构。这套数据是按年份拆分的:每年一个独立的Shp文件,文件名类似“city_house_price_2019.shp”这种格式,年份明确写在文件名里。你如果想把多年数据放在同一张地图里做动态可视化,需要用字段合并功能把多年属性表连接起来,或者直接用代码批量处理。
属性表中还有一个字段叫“change_rate”,对应同比涨跌幅,做地图渲染时这个字段最常用。有些年份的change_rate存在负值,意味着当年房价是下跌的,做分级设色时要注意色带的选择,建议用红蓝发散色带,而不是单色渐变,否则涨跌方向看不出来。
2.3 两种格式字段一致性检查方法
拿到Excel和Shp两份数据后,第一件事别急着分析,先做一致性检查。我的经验是:Shp的.dbf属性表是从Excel主表派生出去的,理论上字段应该完全对应,但转换过程中很容易因为文件编码问题出现字段名截断或中文乱码,尤其是dbf文件的字符编码没设为UTF-8的时候,城市名称直接变成问号或者乱码。
检查方法很直接:先把Excel主表中每个城市在每一年是否存在记录统计出来,再和每个年份Shp属性表的城市数量做对比。城市数量对上了,再看城市名称是否完全匹配,重点关注名称里带“市”“自治州”“地区”后缀的城市,去重和格式统一非常容易出现纰漏。我自己常用的检查代码只有十来行,用pandas读Excel,再用geopandas读全部年份的Shp,循环做差集比对,一两分钟就能把所有年份扫一遍。
3. 两种存储格式的适用场景与切换要点
3.1 Excel表格适合做什么样的分析
Excel格式的价值在于“面板分析友好”。做房价研究的人最常用到的分析场景,无非是三类:第一,描述性统计,比如计算全国或某区域15年间房价的均值、中位数、标准差;第二,面板回归,比如房价和城市经济变量的关系;第三,城市间排序和梯队划分,比如把2025年所有城市按房价从高到低排个序,看看哪些城市晋升了、哪些城市掉队了。
这三类场景的共同特征是:需要多行多列的数据结构、需要按城市或年份做分组运算、需要和外部经济统计数据做关联匹配。它们都不需要地图,Excel表格直接导入就行。特别是面板回归,Stata或Python里面处理标准长表是最顺手的。你要做的无非是把“coded”“year”设为面板ID,然后把需要控制的变量合并进来。
3.2 Shp文件在空间分析中的真正用途
Shp格式是空间可视化和空间计量分析的入口。最典型的用法是画房价分级图:选择某一年份,按房价或涨跌幅做分级渲染,一眼看出高房价城市在哪些区域聚集,都市圈的外溢效应是怎么在空间上表现的。这是Excel永远给不了的分析视角。
空间计量则是更进阶的用法。比如计算房价的空间自相关Moran’s I,或者构建空间权重矩阵做空间回归,这些都要求你手里有真实的几何边界,而不是只有城市经纬度点。地级市Shp恰好提供了这个基础。唯一需要注意的是,在做空间权重矩阵时,如果使用“邻接权重”,要处理好岛屿城市和飞地问题,否则权重矩阵会出错。
3.3 格式互转时的字段与坐标系保险措施
如果你需要把Excel和Shp来回切换,有几个保险措施必须做。从Excel转到Shp时,最核心的原则是“先转坐标系,后转格式”。Excel里的经纬度是GCJ-02,而GIS软件默认读取后的投影可能是WGS84 Web墨卡托,如果不先把经纬度明确声明为GCJ-02并做投影转换,画出来的地图边界会和实际地形发生整体偏移。虽然等比例缩放时问题不大,但一旦和底图叠加,会偏出好几公里。
从Shp转到Excel则要关注属性表的导出编码。dbf文件默认的编码格式可能是ANSI或Latin-1,中文城市名导出去后大概率乱码。我的习惯是:先在GIS软件里把属性表导出为CSV,指定UTF-8编码,再用pandas读取CSV转成Excel,路径绕了一下,但中文永远安全。
另外,无论是哪种方向的操作,做完之后一定要抽查几个城市,边界是否重合、字段值是否对齐、年份是否正确。格式转换丢数据是最常见也最隐蔽的错误,它不报错,也不会弹出警告,只会在你计算某个指标时给你一个莫名其妙的偏差。
4. 上手实操:清洗、面板化与地图可视化
4.1 用Pandas做缺失值和异常值筛查的一小时流程
数据拿到手,缺失值处理是第一步。我建议流程是这样的:先看整体缺失比例,再看缺失集中在哪些城市、哪些年份。如果某个城市在某个年份没有记录,大概率是当年样本量太小,被规则过滤掉了,而不是数据丢失。这类缺失不要盲目用插值填充,直接保留缺失值并在分析中做排除处理,反而更干净。
异常值筛查也要讲究方法,不要用简单的“3倍标准差”一刀切。房价数据的分布有很强的城市分层特征,省会城市和普通地级市的房价差距极大,放在一起算标准差,会把大量高房价城市标记为异常值。正确做法是按城市分组做异常检测,或者使用中位数绝对偏差方法,这样可以排除城市层级的影响,只标记单个城市内部年份之间的突变值。
筛查完之后,还有一个很关键的步骤是画时间趋势图来目检。随机挑二三十个城市,把15年的中位数价格折线画出来,肉眼看一遍曲线是否平滑。人工目检虽然不“科学”,但对发现系统性问题非常有效——如果某个城市某年价格突然跳崖或者暴涨3倍,多半是数据问题,回去翻原始记录,把原因搞清楚再继续。
4.2 建立标准“城市-年份”面板数据的细节
做面板分析前,数据要从“每一行一个城年记录”的长表,拆出清晰的索引层级。这里有一个细节容易犯错:如果直接用“城市名+年份”做复合索引,两个不同城市如果名称相似或带后缀不一致,比如“A市”和“A地区”,在合并时会被当成完全不同的实体,导致面板出现断裂。
最稳妥的方式是使用行政区划代码作为唯一主键,城市名称只作为辅助标签。行政区划代码是从国家统计口径继承下来的,不会因为城市改名而变化,是真正的稳定标识。做面板合并时,用代码做连接键,城市名称用来做展示,这样逻辑最清晰,也不容易撞车。
4.3 用GeoPandas完成一张房价空间分布图
Shp文件的可视化,我用GeoPandas最顺手,pyecharts能做交互但地理底图精度不够,ArcGIS太重,这里给一套直接用GeoPandas跑通的地图绘制流程。核心代码如下:
import geopandas as gpd import matplotlib.pyplot as plt # 读取某一年份的Shp文件 gdf = gpd.read_file("city_house_price_2019/city_house_price_2019.shp", encoding="utf-8") # 先检查坐标系,再去掉没有价格的记录 print(gdf.crs) gdf = gdf[gdf["median_price"].notna()] # 如果crs不是GCJ02或WGS84,先转成WGS84再出图 if gdf.crs is not None and gdf.crs.to_string() != "EPSG:4326": gdf = gdf.to_crs(epsg=4326) # 绘制分级图 fig, ax = plt.subplots(figsize=(12, 10)) gdf.plot( column="median_price", cmap="OrRd", legend=True, ax=ax, edgecolor="white", linewidth=0.3, legend_kwds={"label": "二手房成交中位数(元/平方米)"} ) ax.set_title("2019年各地级市二手房价格分布", fontsize=16) ax.axis("off") plt.savefig("house_price_2019.png", dpi=300, bbox_inches="tight")这套代码跑出来,图层干净,图例明确。需要注意几个细节:第一,用“column”参数指定要渲染的字段;第二,用“edgecolor”控制边界线的颜色,否则相邻城市色块边界会糊在一起;第三,出图时一定要关闭坐标轴,把注记去掉,否则保存出来的地图带着经纬度刻度线,丑不说,还显得不专业。
如果想把多年地图合并成动态图或者分面图,也很简单,循环读取每年的Shp文件,用subplot分面排列,每一年的图标题标注年份,保存成一张大图。动态变化一目了然,特别适合放在研究报告里展示城市房价梯队演变。
5. 实际使用中容易翻车的几个问题
5.1 行政区划调整造成的“幽灵城市”
这是做长周期地级市数据最容易翻车的地方。15年时间跨度里,部分地区的行政区划发生过调整,有些县升为县级市、有的地级市被并入其他地级市,还有的新设了功能区。如果你直接用2025年的行政区划去匹配2011年的数据,你会发现某些城市凭空消失,某些城市人数暴涨,还有的城市代码对不上号。
这套数据里的处理策略是:以2025年的行政区划为基准,把历史年份中已经被撤销或合并的城市数据,统一归类到现存的隶属城市名下。这样做的好处是时间序列连续,不会出现断档;代价是早期年份中“老城市”的历史波动被抹平了。你在做分析时,建议在论文或报告里专门备注一句“行政区划以2025年为准”,别人看到也不会因此扣分。如果你对某个被合并城市的历史房价感兴趣,原始明细里其实有保留拆分前的记录,只是主表里做了汇总,需要自己去翻历史版本。
5.2 Shp属性表连接失败的常见原因
在GIS软件里把Excel和Shp做属性连接,是最常见的操作,也是最容易翻车的操作。我总结下来,连接失败的原因几乎都出在“键值不匹配”上。Excel表里的城市名称可能是“A市”,而Shp属性表里的城市名称可能是“A”,后缀不一致,自然连接不上。
解决办法有两个:一是预处理时统一名称格式,全部加上“市”“州”“地区”等后缀,并去除首尾空格;二是直接用行政区划代码做连接,这个最稳妥。代码是数字型字段,只要两边类型一致,连接必然成功。如果看到代码列的类变成了浮点数,Excel里显示成“123456.0”,先转回整型再连接,这类隐藏技术债务往往会耗掉数据分析流程最宝贵的时间。
5.3 坐标系和单位对结果的影响
坐标系的错误是最隐蔽的错误,因为它不会让你的程序崩溃,只会让你的结果看起来“差不多但不对劲”。比如你在欧洲的某套GIS软件里打开Shp,它可能默认把数据理解为WGS84,而实际上数据是GCJ-02,这两种坐标系在中国区域内的偏差大概在几十米到几百米之间。对于地级市面要素来说,几百米的偏移肉眼几乎看不出来,但一旦叠加到高精度的道路或小区图层上,错位就很明显。
应对方式很简单:第一步,读取Shp之后立刻查看crs属性;第二步,如果需要和其他WGS84空间数据叠加,用to_crs(epsg=4326)主动转换;第三步,如果只是做地图展示不管精度,也要明确自己在哪个坐标系下工作,并在图注里写清楚。不做这三级检查,做出来的空间分析结论就是建在沙地上的。
另一个容易踩的坑是单位问题。属性表里的价格是元/平方米,有些GIS软件在读取数值字段时会根据表头信息自动推断单位,不会报错,但如果你在计算房价增长率时手误把“元/平方米”当成“万元/平方米”,所有比值都会放大一万倍,出来的图表直接不可用。建议每次计算前先打印一下数据的describe,确认数值量级在自己预期范围内,再往下走。
5.4 长周期对比时数据源断裂的警觉
15年跨度里,任何数据都会面临数据源前后不一致的问题。早期年份某平台的覆盖率不高,后期又有新平台加入,样本结构其实一直在缓慢变化。这套数据在早年间偏少、近年偏多,会导致一种系统偏差:越靠近现在,价格越可能被高估,因为样本覆盖更完整。做趋势解读时,最好结合当地成交量一起看,只有当成交量同步放大时,价格上涨才算有基本面支撑,光看价格本身很容易被样本结构误导。
建议使用数据时,重点关注“相对差异”而不是“绝对数值”。比如某城市比另一城市贵30%,这个相对关系在15年间是稳定的对比项;但某个城市从8000涨到16000,如果同期样本量从200套暴增到2000套,这个涨幅里有多少是真实价格上涨,有多少是样本结构变化,要打一个问号。专业的做法是找到同口径的样本子集,单独再做一遍指数化。
写在最后的一点个人体会
这类长周期、多城市、双格式的房价数据,表面上看是一个“拿来就能用”的数据包,但实际上真正能把它跑出可靠结论的人,都是在细节上花过很大功夫的。我个人的经验是,拿到数据之后,前三天不要做任何分析,先把字段含义摸透、把坐标系搞清楚、把行政区划变动理清,再用一天时间把数据做一次全面体检。前面慢一点,后面分析阶段就能快很多。如果你打算用这份数据发报告或者写论文,强烈建议把数据核查的过程也记录下来,整理成一个数据说明文档,这既能提升结论的可信度,也方便别人复现你的研究。