Code on Github
相关文章:
Kaggle Competition 进阶 – House Prices Prediction 房价预测 Part 1
Kaggle Competition 进阶 – House Prices Prediction 房价预测 Part 2
顺着上一篇最后的反思,通过借鉴网上一些成熟的例子,这次的模型修改主要做了以下几件事:
- 完成了重要特征的估计
- 通过 mapping 减少了 categorical 特征中的类别,进而减少回归中自变量的数量避免 overfit
- 通过比较特征值回归系数的大小决定该特征值的重要程度
- 添加了 Pipeline 的使用
- 使用了更复杂的 stacking 方法
LotFrontage 的估计
LotFrontage
作为一个相对重要的用来衡量房产距离主要街道的距离的特征,在上一次因为没有找到合适的估计方法而被舍弃了。
我这次的处理方法是根据房产类型和房产占地面积大小进行估计。理由是因为不同类型的房产因为用途不同,交通方便程度也不尽相同。比如农业房产会更偏僻,而商业房产则大部分是临街的。第二是房产占地面积,一般占地面积越大的房产距离主要街道也越远。根据这两个判断,我分别做了下面两张图。
从上面两张图可以证明不同类型的房产和不同大小的房产LotFrontage
值都是相对集中的。所以对于缺失的LotFrontage
值,我选择使用根据房产类型和占地大小分组后的中位数来替代。
full['LotFrontage'] = full.groupby(['MSZoning','LotAreaCut'])['LotFrontage'].transform(lambda x:x.fillna(x.median()))
使用 Mapping 减少自变量数量
首先减少回归模型中自变量的数量可以避免 overfitting 的发生。因为根据我的经验,在这个项目中 overfitting 还是比较常见的。
具体的减少特征值的方法主要是将相同类型特征集合处理。举例来说,对于变量Neighborhood
,它主要表示房产在城市中所处的位置,因为不同位置的商业价值不同,所以在不同位置的房产价格基础也不同。在之前的分析中,我对每一个 Neighborhood 赋予了一个 dummy, 这样变量Neighborhood
在模型中就会带来25个自变量。但是在这25个不同的 Neighborhood 中,一定有几个 Neighborhood 的商业价值是相似的,通过分析可以将相似的 Neighborhood 分为一类,就可以大为减少模型中自变量的数量。
我的处理方法是根据不同 Neighborhood 房产价格的中位数将他们分为了7类:
NridgHt: 1st (315000), NoRidge: 1st (301500.0)
StoneBr: 2nd (278000)
Timber: 3rd (228475.0), Somerst: 3rd (225500.0), Veenker: 3rd (218000.0), Crawfor: 3rd (200624.0), ClearCr: 3rd (200250.0), CollgCr: 3rd (197200.0), Blmngtn: 3rd (191000.0)
NWAmes: 4th (182900.0), Gilbert: 4th (181000.0), SawyerW: 4th (179900.0)
Mitchel: 5th (153500.0), NPkVill: 5th (146000.0), NAmes: 5th (140000.0), SWISU: 5th (139500.0), Blueste: 5th (137500.0), Sawyer: 5th (135000.0), BrkSide: 5th (124300.0), Edwards: 5th (119500.0), OldTown: 5th (119000.0)
BrDale: 6th (106000.0), IDOTRR: 6th (103000.0)
MeadowV: 7th (88000.0)
类似的,我也对MSSubClass
以及 Quality/Condition 类的特征做了相同的处理,
def map_values():
full["oNeighborhood"] = full.Neighborhood.map({
'NridgHt':'1st', 'NoRidge':'1st',
'StoneBr':'2nd',
'Timber': '3rd', 'Somerst': '3rd', 'Veenker': '3rd', 'Crawfor': '3rd', 'ClearCr': '3rd', 'CollgCr': '3rd', 'Blmngtn': '3rd',
'NWAmes': '4th', 'Gilbert': '4th', 'SawyerW': '4th',
'Mitchel': '5th', 'NPkVill': '5th', 'NAmes': '5th', 'SWISU': '5th', 'Blueste': '5th', 'Sawyer': '5th', 'BrkSide': '5th', 'Edwards': '5th', 'OldTown': '5th',
'BrDale': '6th', 'IDOTRR': '6th',
'MeadowV': '7th'})
full["oMSSubClass"] = full.MSSubClass.map({
60:'1st', 120:'1st',
80:'2nd', 75:'2nd', 20:'2nd', 70:'2nd',
160:'3rd', 40:'3rd', 85:'3rd', 90:'3rd', 50:'3rd', 190:'3rd',
45:'4th', 30:'4th',180:'4th',
150:'2nd'})
full['oOverallQual'] = full.OverallQual.map({
10:5, 9:5, 8:4, 7:4 ,6:3, 5:3, 4:2, 3:2, 2:1, 1:1})
full['oOverallCond'] = full.OverallCond.map({
10:5, 9:5, 8:4, 7:4 ,6:3, 5:3, 4:2, 3:2, 2