项目背景:作为中国最具代表性的一线城市存量房市场,北京二手房的交易活跃度与价格分布不仅是经济基本面的晴雨表,更蕴含着深刻的城市微观空间特征。本项目基于 Kaggle 链家网开源数据集,通过 Python 进行数据清洗与 EDA,结合 SQL 进行多维度聚合提炼,最终产出宏观流动性与微观资产溢价的商业洞察报告。

🛒 数据来源Housing price in Beijing-RUIQURM
🔗 完整源码获取点击访问我的 GitHub 仓库获取完整 Python 与 SQL 源码

核心可视化成果 (Tableau Dashboard)

宏观市场交易与流动性趋势 (Focus_ 31万次交易行为)
微观房源资产特征与溢价洞察 (Focus_ 13万套独立房源)

(注:由于静态图片展示受限,如果您对动态交互看板感兴趣,请点击此处查看 Tableau Public 在线版本)

数据资产预处理与特征工程 (Python 阶段)

原始数据集存在大量不规则文本、缺失值以及类型错配等数仓痛点。为保证后续 BI 看板与统计建模的绝对精准,在第一阶段构建了严密的数据清洗与特征衍生链路。

数据导入

导入数据:

1
2
3
4
5
import pandas as pd
import numpy as np

df = pd.read_csv('new.csv', encoding='gbk', dtype=str, keep_default_na=False)
df.head().T

编写自定义扫描算法,遍历全局数据集。通过 pd.to_numeric 的强制转换掩码(Mask)机制,精准定位并打印出潜伏在数值型字段(如 DOM、价格)中的异常中文字符与不合理小数。将“被动报错”转化为“主动排查”,彻底阻断了脏数据污染下游分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
df = pd.read_csv('new.csv', encoding='gbk', dtype=str, keep_default_na=False)
print("--- 🚨 全字段异常扫描仪启动 🚨 ---\n")
for col in df.columns:
temp_num = pd.to_numeric(df[col], errors='coerce')
is_text_mask = df[col].notna() & (df[col] != '') & temp_num.isna()
has_text = is_text_mask.any()
has_decimal = (temp_num.dropna() % 1 != 0).any()
if has_text or has_decimal:
print(f"⚠️ 发现异常字段: 【{col}】")
if has_text:
bad_texts = df.loc[is_text_mask, col].unique()[:3]
print(f" => 混入了文本或乱码: {bad_texts} ...")
if has_decimal:
decimals = temp_num[temp_num % 1 != 0].dropna().unique()[:3]
print(f" => 混入了带小数的数字: {decimals} ...")
print("-" * 40)
print("\n--- 扫描结束 ---")

为避免 Pandas 默认的 float64/int64 类型导致内存溢出,我设计了动态类型分配逻辑。脚本会扫描离散特征列的极限值(Min/Max),将其智能向下转型至 Int8Int16

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
df = pd.read_csv('new.csv', encoding='gbk', dtype=str, keep_default_na=False)
SCHEMA_MAP = {
'string': ['url', 'id', 'Cid'],
'float': ['Lng', 'Lat', 'totalPrice', 'price', 'square', 'ladderRatio', 'communityAverage']
}
auto_int_cols = ['DOM', 'followers', 'buildingType', 'renovationCondition', 'buildingStructure',
'district', 'constructionTime', 'livingRoom', 'drawingRoom', 'kitchen',
'bathRoom', 'elevator', 'fiveYearsProperty', 'subway']
for dtype, cols in SCHEMA_MAP.items():
for col in cols:
if dtype == 'string':
df[col] = df[col].astype(str)
elif dtype == 'float':
df[col] = pd.to_numeric(df[col], errors='coerce')
for col in auto_int_cols:
temp_series = pd.to_numeric(df[col], errors='coerce').round()
c_min = temp_series.min()
c_max = temp_series.max()
if pd.isna(c_min) or pd.isna(c_max):
df[col] = temp_series.astype('Int8')
final_type = 'Int8'
elif c_min >= np.iinfo(np.int8).min and c_max <= np.iinfo(np.int8).max:
df[col] = temp_series.astype('Int8')
final_type = 'Int8'
elif c_min >= np.iinfo(np.int16).min and c_max <= np.iinfo(np.int16).max:
df[col] = temp_series.astype('Int16')
final_type = 'Int16'
elif c_min >= np.iinfo(np.int32).min and c_max <= np.iinfo(np.int32).max:
df[col] = temp_series.astype('Int32')
final_type = 'Int32'
else:
df[col] = temp_series.astype('Int64')
final_type = 'Int64'
print(f"字段: 【{col:20}】 | Min: {str(c_min):<6} | Max: {str(c_max):<10} => 智能分配: {final_type}")

除此之外,还有两个字段没有录入成功,那就是“tradeTime”和“floor”,tradeTime直接df[‘tradeTime’] = pd.to_datetime(df[‘tradeTime’], errors=’coerce’)让其变成datetime类型,floor可能会出现未知等脏数据,所以我们用value_counts()检查一下floor这个字段排行前30的数据总量

1
2
floor_counts = df['floor'].value_counts()
print(floor_counts.head(30))

发现分为五种”顶、高、中、低、底”,而且检查这个head()发现每组中间用空格,那么用正则表达式就可以简单筛选一下,把floor分成floor_type、total_floors两部分。

1
2
3
4
5
df['tradeTime'] = pd.to_datetime(df['tradeTime'], errors='coerce')
floor_split = df['floor'].str.extract(r'([中高低底顶])\s*(\d+)')
df['floor_type'] = floor_split[0]
df['total_floors'] = pd.to_numeric(floor_split[1], errors='coerce').astype('Int16')
df.drop('floor', axis=1, inplace=True)

最后检查每个字段的数据类型

1
df.info()

NINETEEN

数据清洗

检查缺失值与他们之间关系:

1
2
3
4
import missingno as msno
msno.matrix(df)#显示缺失值的位置(白色为缺失)
msno.bar(df)#显示每列缺失比例
msno.heatmap(df)#显示缺失值直接的关联

A

检查是否有重复值:

1
print(df.duplicated().sum())

发现没有重复值,检查异常值情况:

1
df.describe().round(2).T

B

由上面检查的情况来看,此数据表,有严重的缺失值,无重复值,有异常值(total_price总价格是1000元,北京二手房有这么便宜的房子吗?price每平米价格,最小值是1元,bathRoom 最大有2011间卫生间,constructionTime建筑年代,是0年,显然不合理,ladderRatio梯户比,电梯数与每层住户数的比例,一层最多竟然高达10009400显然不合理、DOM最大有1677,这个数值也很可疑,因为75%的分位数才37。)

首先处理缺失值:

处理这个缺失值,DOM、livingRoom、drawingRoom、bathRoom、bulidingType、constructionTime、elevator、fiveYearsProperty、subway、communityAverage、total_floors、floor_type这是缺失的字段
其中,这个livingRoom、drawingRoom、bathRoom、elevator、fiveYearsProperty、subway缺失值极小,连100都不到,其中communityAverage缺少500不到,bulidingType缺少2000左右,total_floors和floor_type缺少1000不到,constructionTime缺少20000数值,DOM缺失极大

第一层:极小缺失量(不到 100 及不到 1000)—— 物理切除法(Dropna)
包含字段: livingRoom, drawingRoom, bathRoom, elevator, fiveYearsProperty, subway, total_floors, floor_type

1
2
3
drop_cols = ['livingRoom', 'drawingRoom', 'bathRoom', 'elevator', 
'fiveYearsProperty', 'subway', 'total_floors', 'floor_type']
df.dropna(subset=drop_cols, inplace=True)

第二层:衍生类属性缺失(约 500)—— 业务推导法(算术计算)
包含字段: communityAverage (小区均价)
处理方法:使用同小区 (Cid) 其他房源的单价 (price) 的均值(Mean)进行精确填补。

1
2
df['communityAverage'] = df['communityAverage'].fillna(
df.groupby('Cid')['price'].transform('mean'))

第三层:建筑类属性缺失(2000以上)—— 空间聚类填充法
包含字段: buildingType (建筑类型), constructionTime (建成年代),DOM(挂牌天数)
处理方法:
buildingType (代号类): 使用众数(Mode)填充。
constructionTime (年份类): 使用同小区 (Cid) 的中位数(Median)填充。如果同小区全空,退化到同行政区 (district) 填充。
DOM(年分类):使用同小区 (Cid) 的中位数(Median)填充。如果同小区全空,退化到同行政区 (district) 填充。

1
2
3
4
5
6
7
8
9
df['buildingType'] = df['buildingType'].fillna(df['buildingType'].mode()[0])
cid_median = df.groupby('Cid')['constructionTime'].transform('median').round()
df['constructionTime'] = df['constructionTime'].fillna(cid_median)
dist_median = df.groupby('district')['constructionTime'].transform('median').round()
df['constructionTime'] = df['constructionTime'].fillna(dist_median)
cid_dom_median = df.groupby('Cid')['DOM'].transform('median').round()
df['DOM'] = df['DOM'].fillna(cid_dom_median)
dist_dom_median = df.groupby('district')['DOM'].transform('median').round()
df['DOM'] = df['DOM'].fillna(dist_dom_median)

注意:如果直接fillna()去嵌套这个cid_median可能会出现小数点的情况,那么需要对这个groupby分组后的数据进行round()防止再次出现‘cannot safely cast non-equivalent float64 to int8’

其次处理异常值:

total_price总价格是1000元,北京二手房有这么便宜的房子吗?price每平米价格,最小值是1元,bathRoom 最大有2011间卫生间,constructionTime建筑年代,是0年,显然不合理,ladderRatio梯户比,电梯数与每层住户数的比例,一层最多竟然高达10009400显然不合理DOM最大有1677,这个数值也很可疑,因为75%的分位数才37。

针对这个total_price和price,我们可以针对这个communityAverage和square的数值做一个简单的取值范围,小区平均价格最低为10847,面积最小值为6.9,那么这个房价总值不得低于7w元,也就是数值7
global_min_price = df[‘communityAverage’].min()
df_clean_price = df[(df[‘totalPrice’] * 10000) > (global_min_price * df[‘square’])]
然后就是这个ladderRatio,我们的思路是“用数据打败数据”,求出99%和0.1%的分位数来探明真实的业务边界,最后把不在这个区间范围的数据给删除

1
2
3
4
5
6
7
8
9
10
lower_DOM = df['DOM'].quantile(0.001) 
upper_DOM = df['DOM'].quantile(0.999)
df = df[(df['DOM'] >= lower_DOM) & (df['DOM'] <= upper_DOM)]
global_min_price = df['communityAverage'].min()
df_clean_price = df[(df['totalPrice'] * 10000) > (global_min_price * df['square'])]
lower_bound = df_clean_price['ladderRatio'].quantile(0.001)
upper_bound = df_clean_price['ladderRatio'].quantile(0.999)
df_final = df_clean_price[(df_clean_price['ladderRatio'] >= lower_bound) &
(df_clean_price['ladderRatio'] <= upper_bound)]
注意:在完成total_price、price、DOM、ladderRatio的数据清洗后,发现这个bathRoom、constructionTime的数据也回复正常了

最后检查数据是否还有问题,没问题直接导出数据

C

多维业务指标体系构建与数仓提炼 (SQL 阶段)

经 Python 阶段完成数据清洗与特征预建后,原始数据已具备规整的结构与可靠的字段质量。然而,面向多维度、多粒度的业务分析场景,仍需进一步完成数据向业务语义的转化。

北京楼市“冷热交替”的动态时间窗口分析

业务目标:计算二手房市场的环比(MoM)、同比(YoY)增长率,以及通过挂牌天数(DOM)的滚动平均线,找出市场从“卖方市场”向“买方市场”转换的拐点。

利用高级窗口函数(Window Functions),以月为单位,计算北京二手房市场的MoM(环比)和YoY(同比)增长率。(按照行政区划分)

1
2
3
4
5
6
7
8
with new_biao as (
select toStartOfMonth(tradeTime) as trademonth,district,round(avg(price),2) as avg_price
from real_estate_db.beijing_house_sales
group by trademonth,district)
select trademonth,district,avg_price,round((avg_price - lag(avg_price,1) over(partition by district order by trademonth))/nullif(lag(avg_price,1) over(partition by district order by trademonth),0),4) as price_mom,
round((avg_price - lag(avg_price,12) over(partition by district order by trademonth))/nullif(lag(avg_price,12) over(partition by district order by trademonth),0),4) as price_year
from new_biao
order by district,trademonth

D

利用高级窗口函数(Window Functions),以三个月为范围,找出市场从“卖方市场”向“买方市场”转换的拐点

1
2
3
4
5
6
7
with new_biao as (
select toStartOfMonth(tradeTime) as trademonth,district,round(avg(DOM),2) as avg_DOM
from real_estate_db.beijing_house_sales
group by trademonth,district)
select trademonth,district,avg_DOM,round(avg(avg_DOM) over(partition by district order by trademonth rows between 2 preceding and current row),2) as DOM_rolling_3m
from new_biao
order by district,trademonth

E

在构建这个指标时,我评估了不同窗口期的表现。如果窗口太短(如 1 个月),容易受到节假日噪音干扰;如果窗口太长(如 6 个月),指标会产生严重的滞后性,无法指导当下的业务决策。结合房产交易 1-2 个月的自然周期,我最终选择了 3 个月(单季度)作为滚动窗口,在降噪和敏锐度之间找到了最佳平衡

抗跌资产筛选(经济下行期的避险模型)

拉出历年价格数据,分析在市场整体下行或横盘的周期里,哪些行政区、哪类房源(学区 vs 远郊)的价格方差(Variance)最小,表现出最强的“抗风险能力”。(其实就是看那些地方房价最保值)

方差大:价格上蹿下跳,涨得猛跌得也惨(高风险资产)。
方差小:价格稳如老狗,不管外界怎么跌,它都不怎么掉价(抗跌避险资产)。
背景:2017年3月,北京出台了极其严格的“317新政”,楼市直接进入了长达几年的横盘/下行期。我们就拉取 2017 年之后的数据,看看哪些区域的哪种房子方差最小。

1
2
3
4
5
6
select district as "行政区",buildingStructure as "建筑结构",count(*) as "成交样本量",round(avg(price),2) as "平均单价",round(varPop(price),2) as price_variance,round(stddevPop(price),2) as price_stddev
from real_estate_db.beijing_house_sales
where tradeTime >= '2017-03-17'
group by district,buildingStructure
having "成交样本量" > 100
order by price_variance asc

F

最抗跌的“刚需底盘”(看第 1 行): 【行政区 4,建筑结构 2】的房子,平均单价大概是 4.3 万/平米,它的标准差只有 6,416 元。
业务翻译: 这说明在下行周期里,这种房子的价格咬得很死,绝大部分成交价就在 43000 ± 6400 这个窄带里波动。谁也别想捡大漏,房东也不可能乱开高价。这就是典型的“保值硬通货”。
泡沫最大的“高危资产”(看第 24 行): 【行政区 10,建筑结构 6】的房子,平均单价高达 10 万/平米,但它的标准差竟然飙到了 21,824 元!
业务翻译: 这说明同样是这个区的这种房子,价格上蹿下跳极其剧烈!有的能卖 12 万,有的只能卖 8 万。这种通常是带炒作属性的“顶级学区房”或者“概念豪宅”。在下行期,这种房子的价格水分最大,是买家“屠龙刀”疯狂砍价的重灾区。

现在出现了新的问题:我知道保值了,但我怎么知道哪些房子会“溢价”?

量化‘全量溢价因子’与‘隐形折价陷阱’,实现房价波动的多源归因。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
WITH new_biao AS (
SELECT
id,
Cid,
Lng,
Lat,
tradeTime,
YEAR(tradeTime) AS tradeYear,
MONTH(tradeTime) AS tradeMonth,
DOM,
followers,
totalPrice,
price,
square,
livingRoom,
drawingRoom,
kitchen,
bathRoom,
buildingType,
constructionTime,
renovationCondition,
buildingStructure,
ladderRatio,
elevator,
fiveYearsProperty,
subway,
district,
total_floors,
AVG(price) OVER (PARTITION BY district, YEAR(tradeTime)) AS avg_price_district_year
FROM real_estate_db.beijing_house_sales
WHERE price > 0 )
SELECT
id AS `房源ID`,
Lng AS `经度`,
Lat AS `纬度`,
tradeTime AS `交易具体时间`,
tradeYear AS `交易年份`,
tradeMonth AS `交易月份`,
DOM AS `挂牌天数(DOM)`,
followers AS `关注人数`,
totalPrice AS `总价(万)`,
price AS `成交单价`,
square AS `面积(平米)`,
district AS `行政区`,
round(greatCircleDistance(Lng, Lat, 116.397, 39.908) / 1000, 2) AS `距市中心距离(公里)`,
subway AS `是否有地铁`,
livingRoom AS `卧室数量`,
drawingRoom AS `客厅数量`,
kitchen AS `厨房数量`,
bathRoom AS `卫生间数量`,
buildingType AS `房屋用途`,
buildingStructure AS `建筑结构`,
renovationCondition AS `装修情况`,
elevator AS `是否有电梯`,
total_floors AS `总楼层数`,
(tradeYear - constructionTime) AS `交易时房龄`,
fiveYearsProperty AS `是否满五`,
multiIf(ladderRatio <= 0.33, '优质低密', ladderRatio <= 0.5, '常规居住', '高密塔楼') AS `梯户比类型`,
round(price - avg_price_district_year, 2) AS `每平米绝对溢价`
FROM new_biao
ORDER BY tradeYear DESC, district ASC;

以同区同年均价为锚,逐房计算每平米绝对溢价;叠加面积、时间、区位、地铁、房龄、梯户比等全量因素,构建‘溢价归因矩阵’——正值为显性溢价(近地铁、低梯户、次新房),负值为隐性折价(高梯户、远郊、老房龄、无电梯)。

搭建“价值-流动性”波士顿矩阵

通过SQL的NTILE()甚至K-Meons思想,将房源分为四类
高单价-短挂牌DOM(核心抢手货)
高单价-长挂牌DOM(高溢价滞销房)
低单价-短挂牌DOM(极品笋盘,快速被秒)
低单价-长挂牌DOM(硬伤老破小)
这里推荐使用NTINE分桶来进行分组,原因:第一,规避“时空错位”的降维打击,如果用自定义阈值(比如 price >= 50000 THEN ‘高价’),这个模型一跑,早些年的数据和远郊区县的数据会全部掉进“低价”象限,矩阵直接瘫痪。第二,要做的是将市场切分成四个象限(高价值/高流动、低价值/低流动等)。波士顿矩阵的横纵坐标交叉点,在标准商业应用中,本来就是行业中位数或者平均值。第三,挂牌天数(DOM)虽然长尾,但排名逻辑不受影响,NTILE 靠的是排序(ORDER BY)而不是数值本身。不管是挂了 60 天还是 1000 天,在 NTILE(2) 看来,它们都排在后 50%,都会被稳稳地丢进“长挂牌 DOM”的象限里。极端的长尾数值根本无法污染排序切分的结果。

1
2
3
4
5
6
7
8
9
10
11
with new_biao as (
select id,price,DOM,district,year(tradeTime) as tradeYear,NTILE(2) over(partition by district,year(tradeTime) order by price desc) as price_rank,
NTILE(2) over(partition by district,year(tradeTime) order by DOM) as dom_rank
from real_estate_db.beijing_house_sales)
select id as `房源ID`,district as `行政区`,tradeYear as `交易年份`,price as `成交单价`,DOM as `挂牌天数`,multiIf(
price_rank = 1 AND dom_rank = 1, '高单价-短挂牌DOM (核心抢手货)',
price_rank = 1 AND dom_rank = 2, '高单价-长挂牌DOM (高溢价滞销房)',
price_rank = 2 AND dom_rank = 1, '低单价-短挂牌DOM (极品笋盘/秒杀房)','低单价-长挂牌DOM (硬伤老破小)'
) AS `波士顿矩阵分类`
from new_biao
order by tradeYear desc,district asc

G

挖掘真正的“学区占坑房”特征

通过组合条件(面积〈15平米,无厨无卫,高单价),从数据海中精准提纯出这些“魔幻资产”,并分析它们的交易频次是否显著高于普通住宅。

每5平米为区间,检查各区域的价格

1
2
3
4
select count(*) as `房源数量`,floor(square/5) * 5 as `面积区间_下限`,round(avg(price),2) as `平均单价`
from real_estate_db.beijing_house_sales
group by `面积区间_下限`
order by `面积区间_下限` asc

H

在进行极小户型(<30㎡)的探索性分析时,数据呈现出明显的价格拐点(Inflection Point)。 当面积 ≥15㎡ 时,房屋均价维持在 5-6 万元/㎡ 的合理居住区间;但当面积跌破 15㎡(进入 5-10㎡ 区间)时,均价发生断崖式突变,直接飙升至 10-12 万元/㎡。 这在统计学上构成了典型的特征突变,证明 15㎡ 以下的房产已经彻底剥离了居住价值,其超高溢价完全来自于背后的‘学区期权’。因此,本报告将 15㎡ 确立为锁定‘魔幻占坑房’的科学临界值。

I

开始计算魔幻占坑房

1
2
3
4
5
6
7
8
9
10
11
12
with new_biao as (
select Cid,Lng,Lat,square,max(kitchen) as kitchen,max(bathRoom) as bathRoom,max(price) as max_price,count(id) as trade_count,avg(DOM) as avg_dom
from real_estate_db.beijing_house_sales
group by Cid,Lng,Lat,square),
new_biao2 as (
select * ,multiIf(square < 15 and kitchen=0 and bathRoom = 0 and max_price >=10000,'魔幻住宅','普通住宅') as house_type
from new_biao)
select house_type as `资产类型`,count(*) as `涉及物理房源总数`,round(avg(square),1) as `平均面积`,round(avg(max_price),0) as `历史平均最高单价`,sum(trade_count) as `历史总成交单数`,
round(avg(trade_count),2) as `平均每套房被转手次数`,round(avg(avg_dom),1) as `平均挂牌天数`
from new_biao2
group by house_type
order by `平均每套房被转手次数` desc

J

tableau制作可视化图表遇到的问题

在制作房源流动性波士顿矩阵的时候,发现district没有划分区域,还是数字形式,但是我们并不知道每个数字对应的行政区,所以我们可以通过查询每个数字的最大最小经纬度来判断

1
2
3
select district,count(*),max(Lng),max(Lat),min(Lng),min(Lat) 
from real_estate_db.beijing_house_sales
group by district

K

总结:1 东城区、2 丰台区、3 亦庄开发区、4 大兴区、5 房山区、6 昌平区、7 朝阳区、8 海淀区、9 石景山区、10 西城区、11 通州区、12 门头沟区、13 顺义区

项目全景总结:重塑存量资产价值的“三维”分析闭环

回顾整个北京二手房市场深度分析项目,真正的难点并非跑通一段 Python 脚本或 SQL 聚合,而是如何穿透三十万次非标准化交易的海量噪音,挖掘出隐藏在钢筋水泥背后的资产流动性与溢价规律。

本项目从始至终贯穿了以下三个核心维度的分析哲学:

维度一:全栈的数据工程与严谨的数仓思维

  • 自动化异常扫描与极致降维:面对数据类型不统一及引发 cannot safely cast non-equivalent float64 to int8 的报错,编写了全字段异常扫描仪,精准定位了数值字段中掺杂的文本与带小数的脏数据 。通过引入 np.iinfo 动态评估数据边界,将核心字段智能降维至 Int8 至 Int64 类型,并采用正则 ([中高低底顶])\s*(\d+) 拆分出楼层信息,大幅优化了数据结构 。
  • 分层填补与物理边界切除:针对缺失值制定了分层处理策略:对缺失不足 1000 条的极小量字段执行物理切除;对于挂牌天数(DOM)等缺失较多的特征,使用同小区(Cid)或行政区的中位数进行空间聚类填充 。通过计算小区最低均价 10847 元与最小面积 6.9 平方米,推导出 7 万元的总价理论底线,并采用 0.1% 和 99.9% 的分位数果断剔除了 DOM 高达 1677 天、梯户比达到 10009400 的极端不合理数据 。

维度二:敏锐的商业嗅觉与深度资产洞察

  • 抗跌资产与高危泡沫的量化识别:锁定 2017 年“317新政”后的楼市横盘/下行期,利用方差模型精准筛选出“保值硬通货”与“高危资产” 。数据证实,【行政区 4,建筑结构 2】的房源最为抗跌,均价约 4.3 万/平米,标准差仅为 6,416 元;而带有炒作属性的【行政区 10,建筑结构 6】房源,尽管均价高达 10 万/平米,但标准差飙升至 21,824 元,揭示了极大的价格水分 。
  • 时空错位修复与核心定价权解构:在量化电梯溢价时,敏锐察觉到直接对比导致的“无电梯价格更高”的逻辑悖论 。通过引入交易年份(tradeYear)窗口函数,成功剥离了 2018 年静态小区均价造成的“时空错位”干扰 。结合天安门(116.397, 39.908)为圆心的 greatCircleDistance 空间距离测算,以及梯户比分级(<=0.33 为优质低密,>0.5 为高密塔楼),彻底穿透了影响存量房定价的微观肌理 。

维度三:创新的策略建模与流动性周期解构

  • 防衰减的“价值-流动性”波士顿矩阵:摒弃了极易引发“降维打击”的绝对价格阈值,创新性地采用 NTILE(2) 窗口函数,按行政区和年份进行相对排序分桶 。该模型稳健地将房源切分为“高单价-短挂牌”的核心抢手货与“低单价-长挂牌”的硬伤老破小等四大象限,彻底杜绝了长尾极端天数对业务分类的污染 。
  • “魔幻占坑房”与市场拐点挖掘:通过特征组合(面积 <15 平米、无厨无卫、单价 >=1 万),从海量数据中精准提纯出超高溢价的学区房源 。数据呈现出剧烈的价格突变点:15 平米以上房屋均价维持在 5-6 万元/㎡ 的居住区间,而一旦跌破 15 平米临界值,均价则断崖式飙升至 10-12 万元/㎡ 。此外,通过反复评估降噪与敏锐度的平衡,选取 3 个月作为 DOM 滚动平均线窗口,有效排除了单一月份节假日噪音与半年期数据的滞后性,精准捕捉到了市场从卖方向买方转换的周期拐点 。