项目:kaggle房价预测_努力努力再成长的博客-程序员宅基地

技术标签: kaggle  

一、项目背景

项目描述:比赛项目由 Kaggle 举办,要求选手依据爱荷华州房子的质量、面积、街区、壁炉个数等79个变量预测房子的价格。
项目网址House Prices: Advanced Regression Techniques

二、代码展示

tips:原代码在jupyter notebook上由python编写完成
# Kaggle房价预测项目

# 首先,导入需要用到的包
import pandas as pd
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns
color = sns.color_palette()
sns.set_style('darkgrid')
import warnings
def ignore_warn(*args, **kwargs):
    pass
warnings.warn = ignore_warn

from scipy import stats
from scipy.stats import norm, skew

# 读取数据集
train = pd.read_csv('E:/房价预测项目/train.csv')
test = pd.read_csv('E:/房价预测项目/test.csv')

1.对数据集进行可视化探索

train.head(5)

在这里插入图片描述

test.head(5)

在这里插入图片描述

# 对训练集train做相关性探索

corrmat = train.corr()
f, ax = plt.subplots(figsize=(12, 9))
sns.heatmap(corrmat, vmax=.8, square=True)

在这里插入图片描述

#选出10个与房价相关性最强的变量查看相关性系数

k = 10 #选择变量的个数
cols = corrmat.nlargest(k, 'SalePrice')['SalePrice'].index
cm = np.corrcoef(train[cols].values.T) # 
sns.set(font_scale=1.25)
hm = sns.heatmap(cm, cbar=True, annot=True, square=True, fmt='.2f', annot_kws={
    'size': 10}, yticklabels=cols.values, xticklabels=cols.values)
plt.show()

在这里插入图片描述
通过上面的热力图,可以得到如下的结论:
1.‘OverallQual’, ‘GrLivArea’ 和 'TotalBsmtSF’与房价的相关性很强,可以后面再深入探索
2.‘GarageCars’(车库能放多少量车) 和 ‘GarageArea’ (车库面积)和房价同样有比较强的相关性,但这两个变量本身的相关性也很强,因为车库面积和车库能放多少车本身就是有强相关性的,可能存在多重共线性问题,可以去掉一个
3.‘TotalBsmtSF’ 和 ‘1stFloor’ 似乎都是代表地下室面积,这里不确定这两个变量含义的区别,不过它们也有很强的相关性,考虑去掉一个

#TotalBsmtSF含义为地下室面积,发现地下室面积与房价似乎有更强的潜在线性关系,同时在右侧似乎也有一个异常值存在

fig, ax = plt.subplots()
ax.scatter(x=train.TotalBsmtSF,y=train.SalePrice)
plt.xlabel('TotalBsmtSF')
plt.ylabel('SalePrice')
plt.show()

# 可以看到有一个异常点在右下方

在这里插入图片描述

# GrLivArea为居住面积,发现有强相关关系,发现右边有2个异常点
fig, ax = plt.subplots()
ax.scatter(x=train.GrLivArea,y=train.SalePrice)
plt.xlabel('GrLivArea')
plt.ylabel('SalePrice')
plt.show()

在这里插入图片描述

# GarageArea为车库面积,发现有正相关关系
fig, ax = plt.subplots()
ax.scatter(x=train.GarageArea,y=train.SalePrice)
plt.xlabel('GarageArea')
plt.ylabel('SalePrice')
plt.show()

在这里插入图片描述

# 房屋质量与房价从经验上应该是强相关,可视化进行验证
var = 'OverallQual'
data = pd.concat([train['SalePrice'], train[var]], axis=1)
f, ax = plt.subplots(figsize=(8, 6))
fig = sns.boxplot(x=var, y="SalePrice", data=data)
fig.axis(ymin=0, ymax=800000);

在这里插入图片描述

# 可以再看一下壁炉数量和房价的关系,发现壁炉越多,房价越高
var = 'Fireplaces'
data = pd.concat([train['SalePrice'], train[var]], axis=1)
f, ax = plt.subplots(figsize=(16, 8))
fig = sns.boxplot(x=var, y="SalePrice", data=data)
fig.axis(ymin=0, ymax=800000)
plt.xticks(rotation=90)

在这里插入图片描述

2.数据预处理

# 1.首先对train进行处理,删除可视化中出现的异常值
train = train.drop(train[(train['TotalBsmtSF']>5000) & (train['SalePrice']<200000)].index)
train = train.drop(train[(train['GrLivArea']>4000) & (train['SalePrice']<200000)].index)
# 作图发现房价的分布是右偏的,根据前提假设需要做相应的转换使其符合正态分布
sns.distplot(train['SalePrice'] , fit=norm);

(mu, sigma) = norm.fit(train['SalePrice'])
print( '\n mu = {:.2f} and sigma = {:.2f}\n'.format(mu, sigma))

plt.legend(['Normal dist. ($\mu=$ {:.2f} and $\sigma=$ {:.2f} )'.format(mu, sigma)],
            loc='best')
plt.ylabel('Frequency')
plt.title('SalePrice distribution')

fig = plt.figure()
res = stats.probplot(train['SalePrice'], plot=plt)
plt.show()

mu = 180932.92 and sigma = 79467.79
在这里插入图片描述
在这里插入图片描述

# 这里采用对数变换的方法使其符合正态分布
train["SalePrice"] = np.log(train["SalePrice"])

sns.distplot(train['SalePrice'] , fit=norm);

(mu, sigma) = norm.fit(train['SalePrice'])
print( '\n mu = {:.2f} and sigma = {:.2f}\n'.format(mu, sigma))

plt.legend(['Normal dist. ($\mu=$ {:.2f} and $\sigma=$ {:.2f} )'.format(mu, sigma)],
            loc='best')
plt.ylabel('Frequency')
plt.title('SalePrice distribution')

fig = plt.figure()
res = stats.probplot(train['SalePrice'], plot=plt)
plt.show()

mu = 12.02 and sigma = 0.40
在这里插入图片描述
在这里插入图片描述

# 2.将train和test联合起来一起进行数据处理
train_id = train['Id']
test_id = test['Id']
ntrain = train.shape[0]
ntest = test.shape[0]
y_train = train.SalePrice.values
all_data = pd.concat((train, test)).reset_index(drop=True)
all_data.drop(['SalePrice'], axis=1, inplace=True)
print("all_data size is : {}".format(all_data.shape))  #     all_data size is : (2917, 80)
# 由于ID对预测没有作用,删除ID字段
all_data.drop(['Id'], axis=1, inplace=True)

# 查看缺失值比率
all_data_na = (all_data.isnull().sum() / len(all_data)) * 100
all_data_na = all_data_na.drop(all_data_na[all_data_na==0].index).sort_values(ascending=False)[:30]
all_data_na
missing_data = pd.DataFrame({
    'missing_data' : all_data_na})
missing_data.head(20)

在这里插入图片描述

#将缺失度用图表的方式展示
f, ax = plt.subplots(figsize=(15, 12))
plt.xticks(rotation='90')
sns.barplot(x=all_data_na.index, y=all_data_na)
plt.xlabel('Features', fontsize=15)
plt.ylabel('Percent of missing values', fontsize=15)
plt.title('Percent missing data by feature', fontsize=15)

在这里插入图片描述

# 对于缺失率在80%以上的特征删除
all_data = all_data.drop('PoolQC', axis=1)
all_data = all_data.drop('MiscFeature', axis=1)
all_data = all_data.drop('Alley', axis=1)
all_data = all_data.drop('Fence', axis=1)
all_data.shape      #  (2917, 75)
# 对于其他缺失值进行处理, 壁炉为空可能是没有,用none填充
all_data['FireplaceQu'] = all_data['FireplaceQu'].fillna('none')

# LotFrontage代表房屋前街道的长度, 房屋前街道的长度应该和一个街区的房屋相同,可以取同一个街区房屋的街道长度的平均值
all_data['LotFrontage'] = all_data.groupby('Neighborhood')['LotFrontage'].transform(lambda x: x.fillna(x.median()))

# 对于Garage类的4个特征,缺失率一致,一起处理,可能是没有车库,用none填充
for c in ('GarageType', 'GarageFinish', 'GarageQual', 'GarageCond'):
    all_data[c] = all_data[c].fillna('none')

# 对于garage,同样猜测缺失值缺失的原因可能是因为房屋没有车库,连续型变量用0填充
for c in ( 'GarageYrBlt', 'GarageArea', 'GarageCars'):
    all_data[c] = all_data[c].fillna(0)
    
#对于地下室相关的连续变量,缺失同样认为房屋可能是没有地下室,用0填充
for c in ('BsmtFinSF1', 'BsmtFinSF2', 'BsmtUnfSF','TotalBsmtSF', 'BsmtFullBath', 'BsmtHalfBath'):
    all_data[c] = all_data[c].fillna(0)

#地下室相关离散变量,同理用None填充
for c in ('BsmtQual', 'BsmtCond', 'BsmtExposure', 'BsmtFinType1', 'BsmtFinType2'):
    all_data[c] = all_data[c].fillna('None')
    
# Mas为砖石结构相关变量,缺失值我们同样认为是没有砖石结构,用0和none填补缺失值
all_data["MasVnrType"] = all_data["MasVnrType"].fillna("None")
all_data["MasVnrArea"] = all_data["MasVnrArea"].fillna(0)

#MSZoning代表房屋所处的用地类型,先看下不同取值
all_data.groupby('MSZoning')['MasVnrType'].count().reset_index()
# 由于业务上房屋类型是必须的,一般都有,考虑用众数填充
all_data['MSZoning'] = all_data['MSZoning'].fillna(all_data['MSZoning'].mode()[0])

# 由于数据Functional缺失即为Typ,所以进行填充Typ
all_data["Functional"] = all_data["Functional"].fillna("Typ")

# 对于Utilities,观察到除了一个“NoSeWa”和2个NA之外,所有记录都是“AllPub”,对于房价预测用处很小,删除这个特征
all_data.drop(['Utilities'], axis=1, inplace=True)

查看剩余的缺失值:

all_data_na = (all_data.isnull().sum() / len(all_data)) * 100
all_data_na = all_data_na.drop(all_data_na[all_data_na==0].index).sort_values(ascending=False)[:30]
all_data_na
missing_data = pd.DataFrame({
    'missing_data' : all_data_na})
missing_data

在这里插入图片描述

# 填充剩余的缺失值
for i in missing_data.index:
    print(all_data[i].head())  #未展示

for i in ( 'SaleType', 'KitchenQual', 'Electrical', 'Exterior2nd','Exterior1st'):
    all_data[i] = all_data[i].fillna(all_data[i].mode()[0])
# 查看缺失值的比率,发现已经处理完毕,all_data里已经没有缺失值
all_data_na = (all_data.isnull().sum() / len(all_data)) * 100
all_data_na = all_data_na.drop(all_data_na[all_data_na==0].index).sort_values(ascending=False)[:30]
all_data_na
missing_data = pd.DataFrame({
    'missing_data' : all_data_na})
missing_data

在这里插入图片描述

3.特征工程

特征工程可以说是最重要的步骤了,机器学习大佬吴恩达说过:“数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已”

# 对于一些数值型特征,数值并不表示大小,将其值转换为字符型
all_data['MSSubClass'] = all_data['MSSubClass'].apply(str)

all_data['OverallCond'] = all_data['OverallCond'].astype(str)

all_data['YrSold'] = all_data['YrSold'].astype(str)
all_data['MoSold'] = all_data['MoSold'].astype(str)

创造一些特征:

# 将地下室面积、1楼面积、2楼面积相加得到总面积特征
all_data['TotalSF'] = all_data['TotalBsmtSF'] + all_data['1stFlrSF'] + all_data['2ndFlrSF']
# 由前面的可视化将房子建造时间做一个划分,以1990进行划分,1990前为0,1990后为1
all_data['YearBuilt_cut'] = all_data['YearBuilt'].apply(lambda x: 1 if x>1990 else 0)

all_data['Total_sqr_footage'] = (all_data['BsmtFinSF1'] + all_data['BsmtFinSF2'] +
                                 all_data['1stFlrSF'] + all_data['2ndFlrSF'])

all_data['Total_Bathrooms'] = (all_data['FullBath'] + (0.5 * all_data['HalfBath']) +
                               all_data['BsmtFullBath'] + (0.5 * all_data['BsmtHalfBath']))

all_data.shape    # (2917, 79)
# 将all_data分开为训练集与测试集两部分,查看新特征与房价的相关性
new_train = all_data[:ntrain]
new_test = all_data[ntrain:]
new_train['SalePrice'] = y_train
# 画图查看新的总面积特征和房价的线性相关性,发现有强相关性,是一个比较好的新特征
fig, ax = plt.subplots()
ax.scatter(x=new_train['TotalSF'], y=new_train['SalePrice'])
plt.xlabel('TotalSF', fontsize=12)
plt.ylabel('SalePrice', fontsize=12)

# 发现有右下角有两个异常点,考虑删除
new_train = new_train.drop(new_train[(new_train['TotalSF']>7000) & (new_train['SalePrice']<200000)].index)
plt.show()

在这里插入图片描述

# 观察建筑年限不同导致的房价差异
yearb = 'YearBuilt_cut'
data = pd.concat([new_train['SalePrice'], new_train[yearb]], axis=1)
fig, ax = plt.subplots()
f = sns.boxplot(x=yearb, y='SalePrice', data=data)
# 发现房价在建筑年限上有较大的分布差异

在这里插入图片描述

# 查看Total_sqr_footage与房价的相关性
fig, ax = plt.subplots()
ax.scatter(x=new_train['Total_sqr_footage'], y=new_train['SalePrice'])
plt.xlabel('Total_sqr_footage', fontsize=12)
plt.ylabel('SalePrice', fontsize=12)
plt.show()

# 发现有强相关性

在这里插入图片描述

# 查看Total_Bathrooms与房价的相关性
bath_num = 'Total_Bathrooms'
data = pd.concat([new_train['SalePrice'], new_train[bath_num]], axis=1)
fig, ax = plt.subplots()
f = sns.boxplot(x=bath_num, y='SalePrice', data=data)

在这里插入图片描述

# 观察到Total_Bathrooms等于5或6时都只有一行,且对应房价较为异常,删除这两个值
new_train.loc[:, 'Total_Bathrooms'].value_counts()
new_train = new_train.drop(new_train[new_train['Total_Bathrooms'] >= 5.0].index)
# 将new_train与new_test重新组合成all_data进行数据的统一处理
ntrain = new_train.shape[0]
ntest = new_test.shape[0]
y_train = new_train.SalePrice.values
all_data = pd.concat((new_train, new_test)).reset_index(drop=True)
all_data.drop(['SalePrice'], axis=1, inplace=True)
print("all_data size is : {}".format(all_data.shape))  # all_data size is : (2915, 78)

对特征进行编码:

# 对有序性离散变量使用label encoder 进行编码
from sklearn.preprocessing import LabelEncoder

cols = ('FireplaceQu', 'BsmtQual', 'BsmtCond', 'GarageQual', 'GarageCond', 
        'ExterQual', 'ExterCond','HeatingQC', 'KitchenQual', 'BsmtFinType1', 
        'BsmtFinType2', 'Functional', 'BsmtExposure', 'GarageFinish', 'LandSlope',
        'LotShape', 'PavedDrive', 'Street', 'CentralAir', 'MSSubClass', 'OverallCond', 
        'YrSold', 'MoSold')
for c in cols:
    lbe = LabelEncoder()
    lbe.fit(list(all_data[c].values))
    all_data[c] = lbe.transform(list(all_data[c].values))
print(all_data.shape)   # (2915, 78)
all_data.head(5)

在这里插入图片描述

numeric_feats = all_data.dtypes[all_data.dtypes != "object"].index

# 查看所有数字特征的偏度
skewed_feats = all_data[numeric_feats].apply(lambda x: skew(x.dropna())).sort_values(ascending=False)
skewness = pd.DataFrame({
    'Skew' :skewed_feats})
skewness.head(10)

在这里插入图片描述

# 查看有多少特征的偏度不符合要求,并进行转换
skewness = skewness[abs(skewness) > 0.75]
print("有{}个特征需要转换 ".format(skewness.shape[0]))
  # 有59个特征需要转换
from scipy.special import boxcox1p
skewed_features = skewness.index
lam = 0.15
for feat in skewed_features:
    #all_data[feat] += 1
    all_data[feat] = boxcox1p(all_data[feat], lam)
# 将无序型离散变量转化为哑变量(one-hot编码)
all_data = pd.get_dummies(all_data)

为了避免多重共线性问题,删除皮尔森系数大于0.9的特征:

shreshold = 0.9
corr_all_data = all_data.corr().abs()
# 取矩阵的上三角部分,判断系数大于0.9的并删除
data_up = corr_all_data.where(np.triu(np.ones(corr_all_data.shape), k=1).astype(np.bool))

drop_col = [ column for column in data_up.columns if any(data_up[column] > 0.9)]
all_data = all_data.drop(columns=drop_col)
all_data.shape    # (2915, 207)
# 将训练集与测试集分开,用于建模与测试
train = all_data[:ntrain]
test = all_data[ntrain:]
train.head()

在这里插入图片描述

四、建模与预测

采用岭回归-Ridge Regression模型进行建模与预测
(还可以尝试使用其他模型进行对比,如随机森林)

# 导入模型相关的库
from sklearn.linear_model import Ridge, RidgeCV, ElasticNet, LassoCV, LassoLarsCV
from sklearn.model_selection import cross_val_score
def rmse_cv(model):
    rmse= np.sqrt(-cross_val_score(model, train, y_train, scoring="neg_mean_squared_error", cv = 5))
    return(rmse)
#导入ridge模型
model_ridge = Ridge()

# 对超参数取值进行猜测和验证
alphas = [0.05, 0.1, 0.3, 1, 3, 5, 10, 15, 30, 50, 75]
cv_ridge = [rmse_cv(Ridge(alpha = alpha)).mean() for alpha in alphas]
# 画图查看不同超参数的模型的分数
cv_ridge = pd.Series(cv_ridge, index = alphas)
cv_ridge.plot(title = "Validation - Just Do It")
plt.xlabel("alpha")
plt.ylabel("rmse")
cv_ridge

在这里插入图片描述
在这里插入图片描述

# alpha参数用我们之前验证过的10,然后用训练集对模型进行训练
clf = Ridge(alpha=10)
clf.fit(train,y_train)
# 输出 Ridge(alpha=10, copy_X=True, fit_intercept=True, max_iter=None, normalize=False,random_state=None, solver='auto', tol=0.001)
# 对测试集进行预测,并导出结果
predict = clf.predict(test)
test_pre = pd.DataFrame()
test_pre['ID'] = test_id
test_pre['SalePrice'] = np.exp(predict)
test_pre.to_csv('submission.csv', index=False)
test_pre.head()

在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_43195011/article/details/108465142

智能推荐

编译android源码官方教程(1)硬件、系统要求_banyinlve3147的博客-程序员宅基地

https://source.android.com/source/requirements.htmlRequirementsIN THIS DOCUMENTHardware requirementsSoftware requirementsOperating systemJava Development Kit (JDK)Key pa...

关于构造函数和析构函数的执行顺序_霾都一猿的博客-程序员宅基地_父子构造函数和析构函数的执行顺序分别是怎样的

一条法则就是:先构造的后析构,后构造的先析构,它相当于一个栈,先进后出。Exampleclass animal{public: animal() { cout<<"animal construct"<<endl; } ~animal() { cout<<"animal destruct"<<endl; }};class fish :public animal{

html 修改iframe,javascript – 如何将HTML内容设置为iframe_陈熙昊的博客-程序员宅基地

我有一个HTML字符串我想使用JavaScript将其设置为iframe。我试图设置这样的HTML:contentWindow.document.body.innerHTML要么contentDocument.body.innerHTML要么document.body.innerHTML但IE提供“访问被拒绝”。或“对象不支持此属性或方法”。或“操作的最终元素无效”。错误。这是我的完整代码:$(d...

从零学习Jenkins部署SpringBoot项目_hresh的博客-程序员宅基地

前言之前项目中虽然使用过 Jenkins,但是都只是使用,没有自己搭建过,完整部署一个项目。既然工作中没有独立部署过,那么就只能自己动手实操一番,技多不压身嘛。一开始是在本机上下载安装 Jenkins,然后将本地项目上传到 Gitlab 上,期望在 Jenkins 上构建项目,然后进行项目的部署和发布。但是在动手过程中发现本机 Jenkins 新建任务时走不通,要么就搭建虚拟机,或者搞台服务器,刚好手头有一台自己玩的服务器,刚好也刚贴合实际生产,所以才有了下文。准备工作JDK准备java环境,jd

Java子类抛出的异常类型不能比父类抛出的异常类型更宽泛_头秃的程序员小王的博客-程序员宅基地_子类抛出的异常不能

Java子类抛出的异常类型不能比父类抛出的异常类型更宽泛,只对非检查异常有效。 如下的例子会报错,Exception为受查异常。import java.util.Arrays;import java.util.LinkedList;import java.util.List;class ParentException extends Exception { publ...

office2016之word中阿尔法α相关希腊字母的输入_世事慕竹的博客-程序员宅基地

快捷方法:输入03B1==》AIt+X对应的是:α                  输入03B2==》AIt+X对应的是:β                 输入03B+{1,2,3,4...} ==》AIt+X 依次对应的是:α,β...1.插入==》符号==》其他字符2.符号==》普通文本==》希腊语和科普特语==》下拉下滑条一下找到相应字符 ...

随便推点

Spring+Mybatis多数据源的实现_Donald_Draper的博客-程序员宅基地

ticle/details/5074371[/url][size=medium]第一种方法:创建两个会话工厂[/size]配置如下[code=&quot;java&quot;] &gt;

不着急、不害怕、不要脸_weixin_34411563的博客-程序员宅基地

作者:冯唐第一:做好本职工作。于事我已经尽人力,接下来我只能听天命。 第二:理解领导期望。很多时候,人不是被领导逼死的,人是被自己逼死的。不要每次都给自己近乎苛刻的要求。鸡蛋煎不圆,世界继续转。 第三:漠视无关噪音。一些无关的人说些有的没的,不要往耳朵里去,更不要往心里去。遇到这些无关的人,认真问两个问题:关你屁事?关我屁事? 第四:行程排满。进入办公室后,马上进入工作状态,时间按十五分钟间隔...

ModuleNotFoundError: No module named ‘gensim‘_benbencan的博客-程序员宅基地

ModuleNotFoundError: No module named 'gensim'https://stackoverflow.com/questions/56243568/modulenotfounderror-no-module-named-gensim所有由于python版本冲突带来的问题都可以参考 这篇文档 ,关键在页面最底下的部分

2.12 python 发送request请求方法封装(依赖requests)_雨停江南Lujier的博客-程序员宅基地_def send_request(driver, url, params, method='post

import loggingimport requestsfrom requests import Sessionclass MyRequest: def sendRequest(self, url, method, params=None, data=None, headers=None, json=None, cookies=None,timeout=10): ''' :param url: 接口请求地址 .

大数据课程重大福利,终身学习权限参与就有机会获取__miccretti的博客-程序员宅基地

加微信zhanglindashuju 就可以观看免费观看大数据视频1,海量日志收集利器以及flume的基本介绍 2,如何透彻了解spark 3,mapreguce的编程模型和执行机构 4,kalka的介绍与应用 5,大数据分析引擎:lmpala 6,大数据批处理之hive详解 7,资深程序员必备技能之大数据任务调度 8,大数据从入门到进阶hi数据仓库的...

1、Netty服务器端简单的DEMO源码分析_zengweidaren的博客-程序员宅基地

package com.zengweidaren.demo1.netty.source.echo;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.*;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioSe..