中秋前后内地楼市「金九」成色的讨论又热了起来。每逢这种时候「平均成交价涨了百分之几」总是第一个被引用的数字。我手上正好有一组对照物香港差饷物业估价署每个月同时发布两套数——一份「平均价格」表一份「价格指数」。同一批成交、同一个发布者我把这两套数拉出来对了一遍先说结论对大单位住宅平均价的同比是 40.9%官方指数只认 9.3%差出 31.5 个百分点。目录一、187 个文件一套「编号(年份段)」的命名体系二、日期字段里没有日解析器会替你「编」一个三、平均价与指数同一批成交两个涨幅四、P 标记你看到的「拐点」是临时数字四个要点收尾一、187 个文件一套「编号(年份段)」的命名体系这个数据集在资料一线通上有187 个资源全部零鉴权。命名是一套编号体系1.1M.csv是表 1.1 的月度版、1.4Q.csv是表 1.4 的季度版、1.4A.csv是年度版1982–1998 的历史数据和 1999 之后的数据拆成不同文件。我抽了 7 份抓下来每份抓两轮间隔约 20 秒7 份字节数与指纹全部一致所以本文的数字是当天稳定的。有一个细节http://的链接会 301 跳到https://爬虫记得到重定向之后再落盘。每份表的物理结构一样第一行是标题词与词之间塞了两个空格第二行列头每个数值列后面跟一个列名 - Remarks的备注列。备注列不是摆设它装着本文第三、四节的两个关键信号。二、日期字段里没有日解析器会替你「编」一个先看期标签。同一个发布者三份表用了三种期格式表期格式实例月度1.4MMM-YYYY01-1993季度1.4QMM-MM/YYYY10-12/1979年度1.4AYYYY1980 注意尾部空格月度格式最危险。01-1993这种字符串最常见的处理是丢给dateutil自由解析我实测的结果fromdateutilimportparserasdtparserimportcsv,io textopen(原始返回/rvd/1.4M_r0.csv,encodingutf-8-sig).read()rowslist(csv.DictReader(io.StringIO(text.splitlines()[1])))firstrows[0][Month]# 01-1993gotdtparser.parse(first)print(first,-,got.date())print(day ,got.day,| 今天几号,__import__(datetime).date.today().day)# 01-1993 - 1993-01-28# day 28 | 今天几号 2801-1993被解析成1993 年 1 月 28 日——数据里根本没有「日」解析器把你跑脚本那天的日期填了进去。全表 404 行day全部是 28而且你 29 号跑它就是 29。月份和年份是对的排序和按月聚合不会出错但只要你的代码拿day做判断月初/月末、与别的日粒度数据 join就是一个随运行日期变化的隐形地雷。正确姿势很朴素拆出月和年显式构造datetime.date(y, m, 1)不要让解析器自由发挥。三、平均价与指数同一批成交两个涨幅核心对照来了。用 2026 年 8 月对 2025 年 8 月同一份文件体系里的两个口径importcsv,iodefload(name):textopen(f原始返回/rvd/{name}_r0.csv,encodingutf-8-sig).read()returnlist(csv.reader(io.StringIO(text)))idxload(1.4M)# 价格指数prcload(1.2M)# 平均价格15 区defrow_by(data,period):returnnext(rforrindataifrandr[0]period)deff(x):returnfloat(x)ifx.strip()elseNonei25row_by(idx,08-2025);i26row_by(idx,08-2026)p25row_by(prc,08-2025);p26row_by(prc,08-2026)# 指数All Classes 在第 15 列每列带一个 Remarks 列idx_yoy(f(i26[15])/f(i25[15])-1)*100# 平均价15 个数值列的简单平均defavg(r):vals[float(r[i])foriinrange(1,len(r),2)ifr[i].strip().replace(.,).isdigit()]returnsum(vals)/len(vals)avg_yoy(avg(p26)/avg(p25)-1)*100print(round(idx_yoy,2),round(avg_yoy,2))# 10.98 18.66**全类口径指数同比 11.0%平均价同比 18.7%差 7.7 个百分点。**分面积段看更夸张口径官方指数同比平均价同比差Class A40 m² 以下10.1%11.2%1.0ppClass C100–160 m²12.6%12.7%0.1ppClass D160–272 m²9.6%13.2%3.6ppClass E272 m² 以上9.3%40.9%31.5pp原因不难理解平均价是当月成交的算术平均哪个月贵房子的成交占比高平均价就往上蹿指数剔除了成交结构的变化量的是「同类房子」的价格轨迹。小单位成交密、结构稳两个口径只差 1 个百分点大单位一个月没几单平均价基本由单笔交易决定——E 类九龙 2026 年 5 月到 8 月的平均价是 176,109 → 255,412 → 322,918 → 257,132港元/平方米官方统计口径单位四个月振幅 83.4%这样的数拿去算同比就是 40.9%。官方其实早就提示过你。1.2M 的备注列里有781 个Z标记其中 E 类三个地区就占 639 个最极端的 E 类九龙332 行里 304 行带 Z91.6%。Z标的就是那些成交样本太少、数字不具代表性的格子——平均价表自带「请勿直接引用」的标记只是读 CSV 的人很少看备注列。顺带验一个三表交叉官方回报率表5.1M说 2026 年 7 月 A 类回报率 3.4%用平均租金 ×12 ÷ 平均价现算是 4.12%差 0.72 个百分点——回报率也不是拿两张平均表相除就能得到的。四、P 标记你看到的「拐点」是临时数字指数表还有第二个信号。1.4M 的备注列里P临时数字出现在06、07、08 三期每期 8 列全覆盖其余 400 期一个 P 都没有。而最近 12 期的环比是这样的去年 9 月 1.59%此后 8 期在 0.3% 到 1.9% 之间波动6 月骤降到 0.12%7 月转负 −0.84%12 期里唯一的负值8 月 0.06% 走平。7 月到 8 月A、B 两类和 ABC 组合指数纹丝不动。这组数字是这样验出来的rowsload(1.4M)[1:]# 跳过标题行data[rforrinrowsifrandr[0]!]# P 标记在哪些期Remarks 列在数值列后一位p_rows[r[0]forrindataifany(Pinr[i]foriinrange(2,len(r),2)ifr[i])]print(p_rows)# [06-2026, 07-2026, 08-2026]# 最近 12 期环比All Classes 在第 15 列tail[(r[0],f(r[15]))forrindataiff(r[15])][-13:]mom[(b[0],round((b[1]/a[1]-1)*100,2))fora,binzip(tail,tail[1:])]print(min(mom,keylambdam:m[1]))# (07-2026, -0.84)涨势熄火的信号恰好全部落在临时数字区。这不是说趋势是假的——06 到 08 三期互相之间确实「不走」了——而是说这三期的每个数字下个月都可能被修订07 那个 −0.84% 尤其不能急着写进任何结论。做时间序列的人一般知道「最新一期会修」容易忽略的是修订前它就是你数据集的最后一行任何「最新趋势」的自动推断都建在 P 上。最后一个背景问题指数的 100 在哪表里没有任何地方写基期。我拿 1999 年 12 个月的 All Classes 算了均值99.975 ≈ 100——基期是 1999 年全年均值靠数据自己证出来的。这也提醒一件事指数的绝对值比如今天的 320.5没有独立含义一切结论只应该以变化率的形式出现。四个要点收尾「平均价」和「指数」是两个量。引用涨跌幅时先问口径平均价含成交结构效应E 类实测差出 31.5 个百分点备注列是数据的一部分。P临时、Z样本不足不具代表性都在备注列里读完值请读标记没有日的日期不要让解析器猜。MM-YYYY会被 dateutil 填上「今天的日子」显式构造才是可复现的报告最新一期前先看它是不是 P。趋势判断建在会被修订的临时数字上就等着下个月打脸。本文全部数字可复现7 份 CSV 双轮抓取的原始返回与审计脚本12 项断言一起放在仓库取数时间 2026-09-28。数据来源于中国香港差饷物业估价署公开统计本文只讨论数据口径与工程处理不构成任何置业或投资建议。参考链接https://www.rvd.gov.hk/datagovhk/1.4M.csvhttps://www.rvd.gov.hk/datagovhk/1.2M.csvhttps://data.gov.hk/en-data/api/3/action/package_show?idhk-rvd-tsinfo_rvd-property-market-statistics