|
|
|
|
移动端

用Python研究了三千套房子,告诉你究竟是什么抬高了房价?

关于房价,一直都是全民热议的话题,毕竟不少人终其一生都在为之奋斗。房地产的泡沫究竟有多大不得而知?今天我们抛开泡沫,回归房屋最本质的内容,来分析一下房价的影响因素究竟是什么?

作者:佚名来源:Python中文社区|2018-01-23 08:46


用Python研究了三千套房子,告诉你究竟是什么抬高了房价?

关于房价,一直都是全民热议的话题,毕竟不少人终其一生都在为之奋斗。

房地产的泡沫究竟有多大不得而知?今天我们抛开泡沫,回归房屋最本质的内容,来分析一下房价的影响因素究竟是什么?

1、导入数据

  1. import numpy as np 
  2.  
  3. import pandas as pd 
  4.  
  5. import matplotlib.pyplot as plt 
  6.  
  7. import seaborn as sn 
  8.  
  9. import missingno as msno 
  10.  
  11. %matplotlib inline 
  12.  
  13. train = pd.read_csv('train.csv',index_col=0)  
  14.  
  15. #导入训练集 
  16.  
  17. test = pd.read_csv('test.csv',index_col=0)  
  18.  
  19. #导入测试集 
  20.  
  21. train.head(3) 
  22.  
  23. print('train训练集缺失数据分布图'
  24.  
  25. msno.matrix(train) 
  26.  
  27. print('test测试集缺失数据分布图'
  28.  
  29. msno.matrix(test) 

从上面的数据缺失可视化图中可以看出,部分特征的数据缺失十分严重,下面我们来对特征的缺失数量进行统计。

2、目标Y值分析

  1. ##分割Y和X数据 
  2.  
  3. y=train['SalePrice'
  4.  
  5. #看一下y的值分布 
  6.  
  7. prices = pd.DataFrame({'price':y,'log(price+1)':np.log1p(y)}) 
  8.  
  9. prices.hist() 

观察目标变量y的分布和取对数后的分布看,取完对数后更倾向于符合正太分布,故我们对y进行对数转化。

  1. y = np.log1p(y) #+1的目的是防止对数转化后的值无意义 

3、合并数据 缺失处理

  1. #合并训练特征和测试集 
  2.  
  3. all_df = pd.concat((X,test),axis=0) 
  4.  
  5. print('all_df缺失数据图'
  6.  
  7. msno.matrix(all_df) 
  8.  
  9. #定义缺失统计函数 
  10.  
  11. def show_missing(feature): 
  12.  
  13.     missing = feature.columns[feature.isnull().any()].tolist()     
  14.  
  15. return  missing 
  16.  
  17. print('缺失特征的数据缺失量统计:'
  18.  
  19. all_df[show_missing(all_df)].isnull().sum() 
  20.  
  21. #先处理numeric数值型数据 
  22.  
  23. #挨个儿看一下分布 
  24.  
  25. fig,axs = plt.subplots(3,2,figsize=(16,9)) 
  26.  
  27. all_df['BsmtFinSF1'].hist(ax = axs[0,0])#众数填充 
  28.  
  29. all_df['BsmtFinSF2'].hist(ax = axs[0,1])#众数 
  30.  
  31. all_df['BsmtUnfSF'].hist(ax =  axs[1,0])#中位数 
  32.  
  33. all_df['TotalBsmtSF'].hist(ax = axs[1,1])#均值填充 
  34.  
  35. all_df['BsmtFullBath'].hist(ax = axs[2,0])#众数 
  36.  
  37. all_df['BsmtHalfBath'].hist(ax = axs[2,1])#众数 
  38.  
  39. #lotfrontage用均值填充 
  40.  
  41. mean_lotfrontage = all_df.LotFrontage.mean() 
  42.  
  43. all_df.LotFrontage.hist() 
  44.  
  45. print('用均值填充:'
  46.  
  47. cat_input(all_df,'LotFrontage',mean_lotfrontage) 
  48.  
  49. cat_input(all_df,'BsmtFinSF1',0.0) 
  50.  
  51. cat_input(all_df,'BsmtFinSF2',0.0) 
  52.  
  53. cat_input(all_df,'BsmtFullBath',0.0) 
  54.  
  55. cat_input(all_df,'BsmtHalfBath',0.0) 
  56.  
  57. cat_input(all_df,'BsmtUnfSF',467.00) 
  58.  
  59. cat_input(all_df,'TotalBsmtSF',1051.78) 
  60.  
  61. #在处理字符型,同样,挨个看下分布 
  62.  
  63. fig,axs = plt.subplots(4,2,figsize=(16,9)) 
  64.  
  65. all_df['MSZoning'].hist(ax = axs[0,0])#众数填充 
  66.  
  67. all_df['Utilities'].hist(ax = axs[0,1])#众数 
  68.  
  69. all_df['Exterior1st'].hist(ax =  axs[1,0])#众数 
  70.  
  71. all_df['Exterior2nd'].hist(ax = axs[1,1])#众数填充 
  72.  
  73. all_df['KitchenQual'].hist(ax = axs[2,0])#众数 
  74.  
  75. all_df['Functional'].hist(ax = axs[2,1])#众数 
  76.  
  77. all_df['SaleType'].hist(ax = axs[3,0])#众数 
  78.  
  79. cat_input(all_df,'MSZoning','RL'
  80.  
  81. cat_input(all_df,'Utilities','AllPub'
  82.  
  83. cat_input(all_df,'Exterior1st','VinylSd'
  84.  
  85. cat_input(all_df,'Exterior2nd','VinylSd'
  86.  
  87. cat_input(all_df,'KitchenQual','TA'
  88.  
  89. cat_input(all_df,'Functional','Typ'
  90.  
  91. cat_input(all_df,'SaleType','WD'
  92.  
  93. #再看一下缺失分布 
  94.  
  95. msno.matrix(all_df) 

binggo,数据干净啦!下面开始处理特征,经过上述略微复杂的处理,数据集中所有的缺失数据都已处理完毕,可以开始接下来的工作啦!

缺失处理总结:在本篇文章所使用的数据集中存在比较多的缺失,缺失数据包括数值型和字符型,处理原则主要有两个:

一、根据绘制数据分布直方图,观察数据分布的状态,采取合适的方式填充缺失数据;

二、非常重要的特征描述,认真阅读,按照特征描述填充可以解决大部分问题。

4、特征处理

让我们在重新仔细审视一下数据有没有问题?仔细观察发现MSSubClass特征实际上是分类特征,但是数据显示是int类型,这个需要改成str。

  1. #观察特征属性发现,MSSubClass是分类特征,但是数据给的是数值型,需要对其做转换 
  2.  
  3. all_df['MSSubClass']=all_df['MSSubClass'].astype(str) 
  4.  
  5. #将分类变量转变成数值变量 
  6.  
  7. all_df = pd.get_dummies(all_df) 
  8.  
  9. print('分类变量转换完成后有{}行{}列'.format(*all_df.shape))  

分类变量转换完成后有2919行316列

  1. #标准化处理 
  2.  
  3. numeric_cols = all_df.columns[all_df.dtypes !='uint8'
  4.  
  5. #x-mean(x)/std(x) 
  6.  
  7. numeric_mean = all_df.loc[:,numeric_cols].mean() 
  8.  
  9. numeric_std = all_df.loc[:,numeric_cols].std() 
  10.  
  11. all_df.loc[:,numeric_cols] = (all_df.loc[:,numeric_cols]-numeric_mean)/numeric_std  

再把数据拆分到训练集和测试集

  1. train_df = all_df.ix[0:1460]#训练集 
  2.  
  3. test_df = all_df.ix[1461:]#测试集  

5、构建基准模型

  1. from sklearn import cross_validation 
  2.  
  3. from sklearn import linear_model 
  4.  
  5. from sklearn.learning_curve import learning_curve 
  6.  
  7. from sklearn.metrics import explained_variance_score 
  8.  
  9. from sklearn.grid_search import GridSearchCV 
  10.  
  11. from sklearn.model_selection import cross_val_score 
  12.  
  13. from sklearn.ensemble import RandomForestRegressor 
  14.  
  15. y = y.values #转换成array数组 
  16.  
  17. X = train_df.values #转换成array数组 
  18.  
  19. cv = cross_validation.ShuffleSplit(len(X),n_iter=3,test_size=0.2) 
  20.  
  21. print('岭回归交叉验证结果:'
  22.  
  23. for train_index,test_index in cv: 
  24.  
  25.     ridge = linear_model.Ridge(alpha=1).fit(X,y)     
  26.  
  27.    print('train_score:{0:.3f},test_score:{1:.3f}\n'.format(ridge.score(X[train_index],y[train_index]), ridge.score(X[test_index],y[test_index]))) 
  28.  
  29. print('随机森林交叉验证结果:'
  30.  
  31. for train_index,test_index in cv: 
  32.  
  33.     rf = RandomForestRegressor().fit(X,y)     
  34.  
  35.     print('train_score:{0:.3f},test_score:{1:.3f}\n'.format(rf.score(X[train_index],y[train_index]), rf.score(X[test_index],y[test_index]))) 

哇!好意外啊,这两个模型的结果表现都不错,但是随机森林的结果似乎更好,下面来看看学习曲线情况。

我们采用的是默认的参数,没有调优处理,得到的两个基准模型都存在过拟合现象。下面,我们开始着手参数的调整,希望能够改善模型的过拟合现象。

6、参数调优

岭回归正则项缩放系数alpha调整

  1. alphas =[0.01,0.1,1,10,20,50,100,300] 
  2.  
  3. test_scores = [] 
  4.  
  5. for alp in alphas: 
  6.  
  7.     clf = linear_model.Ridge(alp) 
  8.  
  9.     test_score = -cross_val_score(clf,X,y,cv=10,scoring='neg_mean_squared_error'
  10.  
  11.     test_scores.append(np.mean(test_score)) 
  12.  
  13. import matplotlib.pyplot as plt 
  14.  
  15. %matplotlib inline 
  16.  
  17. plt.plot(alphas,test_scores) 
  18.  
  19. plt.title('alpha vs test_score'

alpha在10-20附近均方误差最小

随机森林参数调优

随机森林算法,本篇中主要调整三个参数:maxfeatures,maxdepth,n_estimators

  1. #随机森林的深度参数 
  2.  
  3. max_depth=[2,4,6,8,10] 
  4.  
  5. test_scores_depth = [] 
  6.  
  7. for depth in max_depth: 
  8.  
  9.     clf = RandomForestRegressor(max_depth=depth) 
  10.  
  11.     test_score_depth = -cross_val_score(clf,X,y,cv=10,scoring='neg_mean_squared_error'
  12.  
  13.     test_scores_depth.append(np.mean(test_score_depth)) 
  14.  
  15. #随机森林的特征个数参数 
  16.  
  17. max_features =[.1, .3, .5, .7, .9, .99] 
  18.  
  19. test_scores_feature = [] 
  20.  
  21. for feature in max_features: 
  22.  
  23.     clf = RandomForestRegressor(max_features=feature) 
  24.  
  25.     test_score_feature = -cross_val_score(clf,X,y,cv=10,scoring='neg_mean_squared_error'
  26.  
  27.     test_scores_feature.append(np.mean(test_score_feature)) 
  28.  
  29. #随机森林的估计器个位数参数 
  30.  
  31. n_estimators =[10,50,100,200,500] 
  32.  
  33. test_scores_n = [] 
  34.  
  35. for n in n_estimators: 
  36.  
  37.     clf = RandomForestRegressor(n_estimators=n) 
  38.  
  39.     test_score_n = -cross_val_score(clf,X,y,cv=10,scoring='neg_mean_squared_error'
  40.  
  41.     test_scores_n.append(np.mean(test_score_n)) 

随机森林的各项参数来看,深度位于8,选择特征个数比例为0.5,估计器个数为500时,效果最好。下面分别利用上述得到的最优参数分别重新训练,看一下学习曲线,过拟合现象是否得到缓解?

再回想一下,我们最初的基线模型学习曲线的形状,是不是得到了一定程度的缓解?OK,下面我们采用模型融合技术,对数据进行预测。

  1. #预测 
  2.  
  3. ridge = linear_model.Ridge(alpha=10).fit(X,y) 
  4.  
  5. rf = RandomForestRegressor(n_estimators=500,max_depth=8,max_features=.5).fit(X,y) 
  6.  
  7. y_ridge = np.expm1(ridge.predict(test_df.values)) 
  8.  
  9. y_rf = np.expm1(rf.predict(test_df.values)) 
  10.  
  11. y_final = (y_ridge + y_rf)/2 

本篇房价预测的模型搭建已经完成。同样,再梳理一边思路:

一、本篇用到的房价数据集存在比较多的数据缺失,且分类变量十分多。在预处理阶段需要将训练集和测试集合并,进行缺失填充和one-hot独热变量处理,保证数据处理过程的一致性,在数据缺失填充过程中,需要综合考虑特征的实际描述和数据的分布,选择合适的填充方式填充;

二、为防止数据变量不统一带来的模型准确率下降,将数值型特征进行标准化处理,数据处理完成后,按照数据合并的方式,再还原到训练集和测试集;

三、先构建岭回归和随机森林基准模型,进行三折交叉验证,绘制学习曲线,存在明显的过拟合现象;

四、接下来分别对两个基准模型进行参数调优,获得使得均方误差最小的参数,返回到训练集进行训练;

五、采用并行模型融合的方式,计算两个模型预测结果的均值作为测试集的预测结果。

【编辑推荐】

  1. Python语言,如何在人工智能和大数据时代中占绝对的优势
  2. 编程语言流行度总结,Java 很稳,Python 是潜力股
  3. Python 3特色用法:新特性汇总
  4. Python成为高收入国家增长最快的语言
  5. 带你深入理解Python字符编码
【责任编辑:庞桂玉 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

Wicked Cool Java中文版

本书主要介绍由Sun微系统公司创建的Java编程语言。 除了核心内容外,Java还有许多免费的财富,即开放源代码的库。本书就是为了介绍这些库...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊