转摘:https://segmentfault.com/a/1190000015613967正则表达式
本篇将继续上一篇数据分析以后进行数据挖掘建模预测,这两部分构成了一个简单的完整项目。结合两篇文章经过数据分析和挖掘的方法能够达到二手房屋价格预测的效果。segmentfault
下面从特征工程开始讲述。数组
2、特征工程app
特征工程包括的内容不少,有特征清洗,预处理,监控等,而预处理根据单一特征或多特征又分不少种方法,如归一化,降维,特征选择,特征筛选等等。这么多的方法,为的是什么呢?其目的是让这些特征更友好的做为模型的输入,处理数据的好坏会严重的影响模型性能,而好的特征工程有的时候甚至比建模调参更重要。dom
下面是继上一次分析以后对数据进行的特征工程,博主将一个一个帮你们解读。ide
1 """ 2 特征工程 3 """ 4 # 移除结构类型异常值和房屋大小异常值 5 df = df[(df['Layout']!='叠拼别墅')&(df['Size']<1000)] 6 7 # 去掉错误数据“南北”,由于爬虫过程当中一些信息位置为空,致使“Direction”的特征出如今这里,须要清除或替换 8 df['Renovation'] = df.loc[(df['Renovation'] != '南北'), 'Renovation'] 9 10 # 因为存在个别类型错误,如简装和精装,特征值错位,故须要移除 11 df['Elevator'] = df.loc[(df['Elevator'] == '有电梯')|(df['Elevator'] == '无电梯'), 'Elevator'] 12 13 # 填补Elevator缺失值 14 df.loc[(df['Floor']>6)&(df['Elevator'].isnull()), 'Elevator'] = '有电梯' 15 df.loc[(df['Floor']<=6)&(df['Elevator'].isnull()), 'Elevator'] = '无电梯' 16 17 # 只考虑“室”和“厅”,将其它少数“房间”和“卫”移除 18 df = df.loc[df['Layout'].str.extract('^\d(.*?)\d.*?') == '室'] 19 20 # 提取“室”和“厅”建立新特征 21 df['Layout_room_num'] = df['Layout'].str.extract('(^\d).*', expand=False).astype('int64') 22 df['Layout_hall_num'] = df['Layout'].str.extract('^\d.*?(\d).*', expand=False).astype('int64') 23 24 # 按中位数对“Year”特征进行分箱 25 df['Year'] = pd.qcut(df['Year'],8).astype('object') 26 27 # 对“Direction”特征 28 d_list_one = ['东','西','南','北'] 29 d_list_two = ['东西','东南','东北','西南','西北','南北'] 30 d_list_three = ['东西南','东西北','东南北','西南北'] 31 d_list_four = ['东西南北'] 32 df['Direction'] = df['Direction'].apply(direct_func) 33 df = df.loc[(df['Direction']!='no')&(df['Direction']!='nan')] 34 35 # 根据已有特征建立新特征 36 df['Layout_total_num'] = df['Layout_room_num'] + df['Layout_hall_num'] 37 df['Size_room_ratio'] = df['Size']/df['Layout_total_num'] 38 39 # 删除无用特征 40 df = df.drop(['Layout','PerPrice','Garden'],axis=1) 41 42 # 对于object特征进行onehot编码 43 df,df_cat = one_hot_encoder(df)
因为一些清洗处理在上一篇文章已经提到,因此从17行代码开始。函数
Layout 先看看没经处理的Layout特征值是什么样的:性能
1 df['Layout'].value_counts()
你们也都看到了,特征值并非像想象中的那么理想。有两种格式的数据,一种是"xx室xx厅"
,另外一种是"xx房间xx卫"
,可是绝大多数都是xx室xx厅的数据。而对于像"11房间3卫"
或者"5房间0卫"
这些的Layout明显不是民住的二手房(不在咱们的考虑范围以内),所以最后决定将全部"xx房间xx卫"格式的数据都移除掉,只保留"xx室xx厅"
的数据。学习
Layout特征的处理以下:测试
第2行的意思是只保留"xx室xx厅"数据,可是保留这种格式的数据也是不能做为模型的输入的,咱们不如干脆将"室"和"厅"都提取出来,单独做为两个新特征(如第5和6行),这样效果可能更好。
具体的用法就是使用 str.extract()
方法,里面写的是正则表达式。
1 # 只考虑“室”和“厅”,将其它少数“房间”和“卫”移除 2 df = df.loc[df['Layout'].str.extract('^\d(.*?)\d.*?') == '室'] 3 4 # 提取“室”和“厅”建立新特征 5 df['Layout_room_num'] = df['Layout'].str.extract('(^\d).*', expand=False).astype('int64') 6 df['Layout_hall_num'] = df['Layout'].str.extract('^\d.*?(\d).*', expand=False).astype('int64')
Year分析:为建房的年限时间。年限种类不少,分布在1950和2018之间,若是每一个不一样的 Year 值都做为特征值,咱们并不能找出 Year 对 Price 有什么影响,由于年限划分的太细了。所以,咱们只有将连续数值型特征 Year 离散化,作分箱处理。
如何分箱还要看实际业务需求,博主为了方便并无手动分箱,而使用了pandas的 qcut 采用中位数进行分割,分割数为8等份。
1 # 按中位数对“Year”特征进行分箱 2 df['Year'] = pd.qcut(df['Year'],8).astype('object')
将 Year 进行分箱的结果:
Direction分析:这个特征没处理以前更乱,原觉得是爬虫的问题,可是亲自到链家看过,朝向确实是这样的
如上所见,像"西南西北北"
或者"东东南南"
这样的朝向是不符合常识的(反正我是理解不了)。所以,咱们须要将这些凌乱的数据进行处理,具体实现方式是博主本身写了一个函数 direct_func
,主要思想就是将各类重复但顺序不同的特征值合并,好比"西南北"
和"南西北"
,并将不合理的一些值移除,如"西南西北北"
等。
而后经过 apply()
方法将 Direction 数据格式转换,代码以下:
1 # 对“Direction”特征 2 d_list_one = ['东','西','南','北'] 3 d_list_two = ['东西','东南','东北','西南','西北','南北'] 4 d_list_three = ['东西南','东西北','东南北','西南北'] 5 d_list_four = ['东西南北'] 6 df['Direction'] = df['Direction'].apply(direct_func) 7 df = df.loc[(df['Direction']!='no')&(df['Direction']!='nan')]
处理完结果以下,全部的内容相同而顺序不一样的朝向都合并了,异常朝向也被移除了。
建立新特征:
有时候仅靠已有的一些特征是不够的,须要根据对业务的理解,定义一些的新特征,而后尝试这些新特征对模型的影响,在实战中会常用这种方法。
这里尝试将"室"与"厅"的数量相加做为一个总数量特征,而后将房屋大小Size与总数量的比值做为一个新特征,可理解为 "每一个房间的平均面积大小"
。固然,新特征不是固定的,可根据本身的理解来灵活的定义。
1 # 根据已有特征建立新特征 2 df['Layout_total_num'] = df['Layout_room_num'] + df['Layout_hall_num'] 3 df['Size_room_ratio'] = df['Size']/df['Layout_total_num'] 4 5 # 删除无用特征 6 df = df.drop(['Layout','PerPrice','Garden'],axis=1)
最后删除旧的特征 Layout,PerPrice,Garden
One-hot coding:
这部分是 One-hot 独热编码
,由于像 Region,Year(离散分箱后),Direction,Renovation,Elevator等特征都是定类的非数值型类型,而做为模型的输入咱们须要将这些非数值量化。
在没有必定顺序(定序类型)的状况下,使用独热编码处理定类数据是很是经常使用的作法,在pandas中很是简单,就是使用 get_dummies()
方法,而对于像Size这样的定比数据则不使用独热,博主这里用了一个本身封装的函数实现了定类数据的自动量化处理。
对于定类,定序,定距,定比这四个很是重要的数据类型相信加入知识星球的伙伴都很是熟悉了,想要了解的同窗能够扫描最后二维码查看。
1 # 对于object特征进行onehot编码 2 df,df_cat = one_hot_encoder(df)
以上的特征工程就完成了。
特征相关性
下面使用 seaborn
的 heatmap
方法对特征相关性进行可视化。
1 # data_corr 2 colormap = plt.cm.RdBu 3 plt.figure(figsize=(20,20)) 4 # plt.title('Pearson Correlation of Features', y=1.05, size=15) 5 sns.heatmap(df.corr(),linewidths=0.1,vmax=1.0, square=True, cmap=colormap, linecolor='white', annot=True)
颜色偏红或者偏蓝都说明相关系数较大,即两个特征对于目标变量的影响程度类似,即存在严重的重复信息,会形成过拟合现象。所以,经过特征相关性分析,咱们能够找出哪些特征有严重的重叠信息,而后择优选择。
3、数据建模预测
为了方便理解,博主在建模上作了一些精简,模型策略方法以下:
Cart决策树
的回归模型对二手房房价进行分析预测交叉验证
方法充分利用数据集进行训练,避免数据划分不均匀的影响。GridSearchCV
方法优化模型参数R2评分
方法对模型预测评分上面的建模方法比较简单,旨在让你们了解建模分析的过程。随着逐渐的深刻了解,博主会介绍更多实战内容。
数据划分
1 # 转换训练测试集格式为数组 2 features = np.array(features) 3 prices = np.array(prices) 4 5 # 导入sklearn进行训练测试集划分 6 from sklearn.model_selection import train_test_split 7 features_train, features_test, prices_train, prices_test = train_test_split(features, prices, test_size=0.2, random_state=0)
将以上数据划分为训练集和测试集,训练集用于创建模型,测试集用于测试模型预测准确率。使用sklearn的 model_selection
实现以上划分功能。
创建模型
1 from sklearn.model_selection import KFold 2 from sklearn.tree import DecisionTreeRegressor 3 from sklearn.metrics import make_scorer 4 from sklearn.model_selection import GridSearchCV 5 6 # 利用GridSearchCV计算最优解 7 def fit_model(X, y): 8 """ 基于输入数据 [X,y],利于网格搜索找到最优的决策树模型""" 9 10 cross_validator = KFold(10, shuffle=True) 11 regressor = DecisionTreeRegressor() 12 13 params = {'max_depth':[1,2,3,4,5,6,7,8,9,10]} 14 scoring_fnc = make_scorer(performance_metric) 15 grid = GridSearchCV(estimator = regressor, param_grid = params, scoring = scoring_fnc, cv = cross_validator) 16 17 # 基于输入数据 [X,y],进行网格搜索 18 grid = grid.fit(X, y) 19 # print pd.DataFrame(grid.cv_results_) 20 return grid.best_estimator_ 21 22 # 计算R2分数 23 def performance_metric(y_true, y_predict): 24 """计算并返回预测值相比于预测值的分数""" 25 from sklearn.metrics import r2_score 26 score = r2_score(y_true, y_predict) 27 28 return score
使用了 KFold
方法减缓过拟合,GridSearchCV
方法进行最优参数自动搜查,最后使用R2
评分来给模型打分。
调参优化模型
1 import visuals as vs 2 3 # 分析模型 4 vs.ModelLearning(features_train, prices_train) 5 vs.ModelComplexity(features_train, prices_train) 6 7 optimal_reg1 = fit_model(features_train, prices_train) 8 9 # 输出最优模型的 'max_depth' 参数 10 print("最理想模型的参数 'max_depth' 是 {} 。".format(optimal_reg1.get_params()['max_depth'])) 11 12 predicted_value = optimal_reg1.predict(features_test) 13 r2 = performance_metric(prices_test, predicted_value) 14 15 print("最优模型在测试数据上 R^2 分数 {:,.2f}。".format(r2))
因为决策树容易过拟合的问题,咱们这里采起观察学习曲线的方法查看决策树深度,并判断模型是否出现了过拟合现象。如下是观察到的学习曲线图形:
经过观察,最理想模型的参数"max_depth"
是10,此种状况下达到了误差与方差的最优平衡,最后模型在测试数据上的R2分数,也即二手房房价预测的准确率为:0.81
。