kaggle简单实战——房价预测(xgboost实现)

       最近正在学习xgboost,因此在kaggle上用xgboost做了个简单的小项目——波士顿房价预测(https://www.kaggle.com/c/house-prices-advanced-regression-techniques),顺便记录一下过程。因为只是简单练手,所以做的比较粗糙,没有调参、降维、参数重要性分析等等,所以还有很大的进步空间,目前结果在kaggle上的得分为0.13166,排名1244,仅供大家参考。

 

       顺便给大家推荐下kaggle,类似以前大一时刷oj,挺好的平台,自动评分和排名,往往对比之后才能知道自己水平如何,练手挺好。

一、数据查看

数据集是直接在kaggle上下载的,下载下来之后首先简单看下数据信息(为了方便我用了下jupyter notebook)。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
%matplotlib inline
train_data=pd.read_csv("c:/JupyterProject/input/train.csv",index_col=0)
train_data.info()
train_data.shape

可以看到数据量不大,但是在存在数据缺失。

 

 因此首先先把缺失值给处理了。

二、缺失值处理

j=train_data.drop('SalePrice',axis=1).columns
num1=0
num2=0
for i in j:
    if train_data.dtypes[i]!='object':
        num1+=1
    else:
        num2+=1
print('数值型特征数量:',num1,'非数值型特征数量',num2)

统计了一下,数值型属性一共有36个,非数值型属性43个。

#统计出有缺失值的属性和缺失的量
miss=train_data.isnull().sum()
miss.sort_values(inplace=True,ascending=False)
miss=miss[miss>0]
print(miss)

 可以看到在一些属性的数据缺失量是特别大的,如果按照一些教程来说就直接删除了事。但是仔细看看其实是不应该删除的,以PoolQC为例

 NA代表的是没有pool,而不是数据为空,所以我们用None替换掉NA,这样就不会再统计为空值了。

而一些数值型的属性,我喜欢用众数来进行填充(有些属性里边数值就0、1这两中,用众数也避免了极值的影响,用均值填充的话有时候不太合适,但均值、中位数也可以,具体情况具体处理)。

年份的话特殊处理了一下,以5年为一个段,以yearRange1、2、3、4来表示,把年份给离散化了。

#PoolQC表示是否有游泳池,空值代表没有,用None来填充
train_data['PoolQC'].fillna('None',inplace=True)
#MiscFeature杂项功能:其他类别中未涵盖的杂项功能
train_data['MiscFeature'].fillna('None',inplace=True)
train_data['Alley'].fillna('None',inplace=True)
train_data['Fence'].fillna('None',inplace=True)
train_data['FireplaceQu'].fillna('None',inplace=True)
#LotFrontage地块临街面:与地产相连的线性街道英尺,众数填充
train_data['LotFrontage'].fillna(train_data['LotFrontage'].mode()[0],inplace=True)
#GarageYrBlt表示建造时间,范围1872-2010,将其离散化
year_map = pd.concat(pd.Series('YearRange' + str(i+1),
           index=range(1871+i*5,1876+i*5)) for i in range(0, 28))
train_data['GarageYrBlt'] = train_data['GarageYrBlt'].map(year_map)
train_data['GarageYrBlt'].fillna('None',inplace=True)
train_data['GarageCond'].fillna('None',inplace=True)
train_data['GarageFinish'].fillna('None',inplace=True)
train_data['GarageQual'].fillna('None',inplace=True)
train_data['GarageType'].fillna('None',inplace=True)
train_data['BsmtFinType2'].fillna('None',inplace=True)
train_data['BsmtExposure'].fillna('None',inplace=True)
train_data['BsmtCond'].fillna('None',inplace=True)
train_data['BsmtQual'].fillna('None',inplace=True)
train_data['BsmtFinType1'].fillna('None',inplace=True)
train_data['MasVnrType'].fillna('None',inplace=True)
#MasVnrArea:砌体饰面面积(平方英尺)
train_data['MasVnrArea'].fillna(0,inplace=True)
train_data['Electrical'].fillna(train_data['Electrical'].mode()[0],inplace=True)

缺失值填充完后在统计一下是或否有缺失值,避免遗漏。

三、异常值处理

这里以房屋售价为标准,对于大于3倍标准差的点视为异常值,直接drop掉

train_data=train_data[np.abs(train_data['SalePrice']-train_data['SalePrice'].mean())<=(3*train_data['SalePrice'].std())]

 这里异常值处理的比较简单粗暴,仔细点的话应该画出图来再看看,把孤立点和其他明显有异常的点也给drop掉。

四、数据分布处理

正常的数据应该是接近于正态分布的,因此我们可以看下数据的分布是否正常。以房屋售价为例,可以看出数据分布是有问题的,计算出的偏度也大于0.75了,因此使用ln(x+1)进行转化,使得转化后的数据大致服从正态分布

 

 处理后的数据已经好多了,大致能够服从正态分布了(np.log1p的逆函数为np.expm1,得到预测值之后,需要用np.expm1处理预测出的房屋售价)

同理,对于其他数值型的属性,我们也计算下他们的偏度,如果大于0.75了,同样用np.log1p处理。

num_list=[]
obj_list=[]
for i in j:
    if train_data.dtypes[i]!='object':
        num_list.append(i)
    else:
        obj_list.append(i)
print('数值型特征:',num_list)
print('非数值型特征',obj_list)

deal_num_list=[]
for i in num_list:
    if train_data[i].skew()>0.75:
        print(i,"偏度: %f" % train_data[i].skew())
        deal_num_list.append(i)
print(deal_num_list)

for i in deal_num_list:
    train_data[i] = np.log1p(train_data[i])

 这里处理得也是简单粗暴了一些,对于一些属性值只有0和1两种类型的是不应该处理的,应该提出。这里偷了点懒,只要偏度大于0.75统统处理了,没有排出特殊情况,实际使用的时候应该注意。

并且这时候处理了哪些属性,对应的测试集也应该相应处理。

四、处理测试集

测试集的处理与训练集类似,测试集也同样存在空值,因此先把空值填充下。

test_data['PoolQC'].fillna('None',inplace=True)
#MiscFeature杂项功能:其他类别中未涵盖的杂项功能
test_data['MiscFeature'].fillna('None',inplace=True)
test_data['Alley'].fillna('None',inplace=True)
test_data['Fence'].fillna('None',inplace=True)
test_data['FireplaceQu'].fillna('None',inplace=True)
#LotFrontage地块临街面:与地产相连的线性街道英尺,众数填充
test_data['LotFrontage'].fillna(test_data['LotFrontage'].mode()[0],inplace=True)
#GarageYrBlt表示建造时间,范围1872-2010,将其离散化
year_map = pd.concat(pd.Series('YearRange' + str(i+1),
           index=range(1871+i*5,1876+i*5)) for i in range(0, 28))
test_data['GarageYrBlt'] = test_data['GarageYrBlt'].map(year_map)
test_data['GarageYrBlt'].fillna('None',inplace=True)
test_data['GarageCond'].fillna('None',inplace=True)
test_data['GarageFinish'].fillna('None',inplace=True)
test_data['GarageQual'].fillna('None',inplace=True)
test_data['GarageType'].fillna('None',inplace=True)
test_data['BsmtFinType2'].fillna('None',inplace=True)
test_data['BsmtExposure'].fillna('None',inplace=True)
test_data['BsmtCond'].fillna('None',inplace=True)
test_data['BsmtQual'].fillna('None',inplace=True)
test_data['BsmtFinType1'].fillna('None',inplace=True)
test_data['MasVnrType'].fillna('None',inplace=True)
#MasVnrArea:砌体饰面面积(平方英尺)
test_data['MasVnrArea'].fillna(0,inplace=True)
test_data['Electrical'].fillna(test_data['Electrical'].mode()[0],inplace=True)
test_data['BsmtFullBath'].fillna(test_data['BsmtFullBath'].mode()[0],inplace=True)
test_data['BsmtHalfBath'].fillna(test_data['BsmtHalfBath'].mode()[0],inplace=True)
test_data['Utilities'].fillna(test_data['Utilities'].mode()[0],inplace=True)
test_data['Functional'].fillna(test_data['Functional'].mode()[0],inplace=True)
test_data['BsmtFinSF1'].fillna(test_data['BsmtFinSF1'].mode()[0],inplace=True)
test_data['KitchenQual'].fillna(test_data['KitchenQual'].mode()[0],inplace=True)
test_data['MSZoning'].fillna(test_data['MSZoning'].mode()[0],inplace=True)
test_data['Exterior1st'].fillna(test_data['Exterior1st'].mode()[0],inplace=True)
test_data['Exterior2nd'].fillna(test_data['Exterior2nd'].mode()[0],inplace=True)
test_data['SaleType'].fillna(test_data['SaleType'].mode()[0],inplace=True)
test_data['GarageCars'].fillna(test_data['GarageCars'].mode()[0],inplace=True)
test_data['TotalBsmtSF'].fillna(test_data['TotalBsmtSF'].mode()[0],inplace=True)
test_data['BsmtUnfSF'].fillna(test_data['BsmtUnfSF'].mode()[0],inplace=True)
test_data['BsmtFinSF2'].fillna(test_data['BsmtFinSF2'].mode()[0],inplace=True)
test_data['GarageArea'].fillna(test_data['GarageArea'].mode()[0],inplace=True)

看上去有点多,其实是偷懒了,把训练集的代码复制过来改改,再处理了写测试集独有的空值。

for i in deal_num_list:
    test_data[i] = np.log1p(test_data[i])

在训练集用np.log1p处理过的属性,在测试集里同样对于处理。

五、one-hot编码

one-hot编码的时候出了点问题:测试集和训练集编码之后的属性数量不一致了,调用xgboost时直接报错。

new_t=train_data.drop('SalePrice',axis=1)
print(new_t.shape)
print(test_data.shape)
new=pd.concat([new_t,test_data])
new = pd.get_dummies(new)

所以这里采用了先将训练集和测试集合成一个datafarm,进行one-hot编码后,再拆封成测试集和训练集。

六、调用xgboost

在处理下训练集

# 特征标准化
x = RobustScaler().fit_transform(train_data.drop(['SalePrice'], axis=1).values)
# 提取标签
y = train_data['SalePrice'].values

然后测试集

test_data= RobustScaler().fit_transform(test_data.values)

xgboost的参数很关键,这里随便网上找了一下相关参数改了改,方便的话可以弄台电脑反复计算一下最佳参数

Xgboost = xgb.XGBRegressor(colsample_bytree=0.38, gamma=0.045,
                             learning_rate=0.05, max_depth=3,
                             min_child_weight=1.98, n_estimators=2200,
                             reg_alpha=0.46, reg_lambda=0.85,
                             subsample=0.53, silent=1,
                             random_state = 1, nthread = -1)
Xgboost.fit(x, y)
# 测试集结果预测
ans = Xgboost.predict(test_data)
result=pd.DataFrame(ans)
result.to_csv("e:/test/result2.csv")

然后预测出的结果需要再用np.expm1处理下

data=pd.read_csv('e:/test/result2.csv',index_col=0)
data['SalePrice'] = np.expm1(data["SalePrice"])
data.to_csv("e:/test/finall_result.csv")

然后完工。

反思

过程比较简单粗暴,实际用的时候别这么直接。可以算下各个参数的重要性,剔除掉一些不重要的属性。可以用PCA处理下,另外还需要不断调参优化……

有问题的地方也欢迎大家指出。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值