首先我们要对数据进行分析,可分为以下几个主要步骤:
这里我们重点要讲的是数据可视化分析,即对一些重要对特征逐个画图观察。
打开表格:
我们看到上述数据有11个特征变量,1个目标变量Price。11个特征分别为:DirectionDistrictElevatorFloorGardenIdLayoutRegionRenovationSizeYear
我们分别对Elevator,Floor,Layout,Region,Renovation,Size,Year这7个特征进行可视化分析。
代码:
#Elevator特征分析miss_value=len(df.loc[(df['Elevator'].isnull()),'Elevator'])print('Elevator缺失值个数为:'+str(miss_value))#移除表格中可能存在的错误的值df['Elevator']=df.loc[(df['Elevator']=='有电梯')|(df['Elevator']=='无电梯'),'Elevator']#以楼层大于6的有电梯,小于等于6层没有电梯为标准,填补缺失值df.loc[(df['Floor']>6)&(df['Elevator'].isnull()),'Elevator']=='有电梯'df.loc[(df['Floor']<=6)&(df['Elevator'].isnull()),'Elevator']=='无电梯'f,[ax1,ax2]=plt.subplots(1,2,figsize=(20,10))sns.countplot(df['Elevator'],ax=ax1)ax1.set_title('有无电梯数量对比')ax1.set_xlabel('是否有电梯')ax1.set_ylabel('数量')sns.barplot(x='Elevator',y='Price',data=df,ax=ax2)ax2.set_title('有无电梯价格对比')ax2.set_xlabel('是否有电梯')ax2.set_ylabel('价格')plt.show()执行结果:
分析目的:分析有无电梯两种二手房对数量和价格。
使用方法:采用seaborn完成可视化。
观察结果:我们发现Elevator特征是有大量缺失值。一般有大量缺失值时,需要根据实际情况考虑。常用的方法有平均值/中位数填补法,直接移除,或根据其他特征建模预测等。
这里我们用填补法。由于有无电梯不是数值,不存在平均值和中位数,这里根据楼层(Floor)断有无电梯,一般的楼层大于6的都有电梯,而小于等于6层的一般都没有电梯。
在填补缺失值后继续观察,有电梯的二手房数量更多,且房价较高。
#Floor特征分析f,ax1=plt.subplots(figsize=(20,5))sns.countplot(df['Floor'],ax=ax1)ax1.set_title('各楼层二手房数量',fontsize=15)ax1.set_xlabel('楼层')ax1.set_ylabel('数量')plt.show()执行结果:分析目的:分析不同的楼层二手房数量。
观察结果:其中6层的二手房数量最多,但是单独的楼层特征没有什么意义,因为每个小区住房的总楼层数都不一样,我们需要知道楼层的相对高度。
此外,楼层与文化也有很重要的联系,比如在中国文化有七上八下,七层可能受欢迎等。一般来说中间楼层比较受欢迎,价格也高,底层和顶层受欢迎度较低,价格也相对较低。
楼层是一个非常复杂的特征,对房价影响也比较大。
#Layout特征分析f,ax1=plt.subplots(figsize=(20,20))sns.countplot(y='Layout',data=df,ax=ax1)ax1.set_title('房屋户型',fontsize=15)ax1.set_xlabel('数量')ax1.set_ylabel('户型')plt.show()执行结果:
分析目的:分析不同户型的数量。
观察结果:这个特征分类下有很多不规则的命名,以上特征是不能作为机器学习模型的数据输入的,需要使用特征工程进行相应的处理。
df_house_count=df.groupby('Region')['Price'].count().sort_values(ascending=False).to_frame().reset_index()df_house_mean=df.groupby('Region')['PerPrice'].mean().sort_values(ascending=False).to_frame().reset_index()f,[ax1,ax2,ax3]=plt.subplots(3,1,figsize=(20,15))sns.barplot(x='Region',y='PerPrice',palette='Blues_d',data=df_house_mean,ax=ax1)ax1.set_title('北京各区二手房每平米单价对比',fontsize=15)ax1.set_xlabel('区域')ax1.set_ylabel('每平米单价')sns.barplot(x='Region',y='Price',palette="Greens_d",data=df_house_count,ax=ax2)ax2.set_title('北京各大区二手房数量对比',fontsize=15)ax2.set_xlabel('区域')ax2.set_ylabel('数量')sns.boxplot(x='Region',y='Price',data=df,ax=ax3)ax3.set_title('北京各大区二手房房屋总价',fontsize=15)ax3.set_xlabel('区域')ax3.set_ylabel('房屋总价')plt.show()执行结果:
分析目的:分析不同区域的房价和数量,并进行对比。
使用方法:用pandas的网络透视功能groupby分组排序。区域特征可视化采用seaborn完成。颜色使用调色板palette参数,颜色越浅数量越少,反之越多。
二手房房数量对比:从数量统计上来看,海淀区和朝阳区二手房数量最多,约接近3000套,因为二者属于大区。其次是丰台区,近几年正在改造建设,需求量大。
二手房房屋总价对比:通过箱型图看到,各大区域房屋总价中位数都都在1000万以下,且房屋总价离散值较高,西城最高达到了6000万,说明房屋价格特征并不是理想的正态分布。
#Renovation特征分析df['Renovation'].value_counts()f,[ax1,ax2,ax3]=plt.subplots(1,3,figsize=(20,5))sns.countplot(df['Renovation'],ax=ax1)sns.barplot(x='Renovation',y='Price',data=df,ax=ax2)sns.boxplot(x='Renovation',y='Price',data=df,ax=ax3)plt.show()执行结果:
分析目的:分析不同装修程度的二手房数量和房价。
观察结果:对于数量来说,精装修的二手房最多,简装其次;对于价格来说,毛坯房价格最高,其次是精装修的。
分析目的:分析不同大小的二手房和价格的关系。
使用方法:通过distplot和kdeplot绘制柱状图观察Size特征的分布情况,属于长尾类型的分布,这说明有很多面积很大且超出正常范围的二手房。
通过regplot绘制了Size和Price之间的散点图,发现Size特征基本与Price呈现线性关系,符合基本常识,面积越大,价格越高。
观察结果:有两组明显的异常点:面积不到10平米但价格超出10000万和面积超过了1000平米价格很低两种情况。
经过查看发现这两组异常值分别是别墅和商用房,因此出现异常,故将其移除再次观察Size分布和Price关系。
这里也说明我们在观察数据的时候,要紧密结合实际业务需求来分析,才能得出更准确的结果。
#Year特征分析grid=sns.FacetGrid(df,row='Elevator',col='Renovation',palette='seismic',size=4)grid.map(plt.scatter,'Year','Price')#grid.add_legend()执行结果:
分析目的:分析不同年代对房价变化的影响。
使用方法:在Renovation和Elevator的分类条件下,使用FacetGrid分析Year特征
特征工程的目的是让这些特征更友好的作为模型的输入,处理数据的好坏会严重的影响模型性能。
这里我们对已有的Layout特征,Year特征和Direction特征进行处理,创建新特征,删除无用特征,最后进行One-hot独热编码。
处理Layout特征
df['Layout'].value_counts()#移除X房间X卫的格式非民住df=df.loc[df['Layout'].str.extract('^\d(.*)\d.*')=='室']df.head()#用str.extract()方法,将"室"和"厅"都提取出来,单独作为两个新特征df['Layout_room_num']=df['Layout'].str.extract('(^\d).*',expand=False).astype('int64')df['Layout_hall_num']=df['Layout'].str.extract('^\d.*(\d).*',expand=False).astype('int64')处理Year特征
#将连续数值型特征Year离散化,做分箱处理#如何分箱还要看实际业务需求,这里为了方便,使用了pandas的qcut采用中位数进行分割,分割数为8等份df['Year']=pd.qcut(df['Year'],8).astype('object')df['Year'].value_counts()处理Direction特征
df['Direction'].value_counts()#写函数direct_func来整理上面较乱的Directiondefdirect_func(x):ifnotisinstance(x,str):raiseTypeErrorx=x.strip()x_len=len(x)x_list=pd.unique([yforyinx])ifx_len!=len(x_list):return'no'if(x_len==2)&(xnotind_list_two):m0=x[0]m1=x[1]returnm1+m0elif(x_len==3)&(xnotind_list_three):fornind_list_three:if(x_list[0]inn)&(x_list[1]inn)&(x_list[2]inn):returnnelif(x_len==4)&(xnotind_list_four):returnd_list_four[0]else:returnx#通过apply()方法将Direction数据格式转换d_list_one=['东','西','南','北']d_list_two=['东西','东南','东北','西南','西北','南北']d_list_three=['东西南','东西北','东南北','西南北']d_list_four=['东西南北']df['Direction']=df['Direction'].apply(direct_func)df=df.loc[(df['Direction']!='no')&(df['Direction']!='nan')]df['Direction'].value_counts()创建新特征
#根据对业务的理解,定义新特征,然后观察这些新特征对模型有什么影响#根据已有特征创建新特征df['Layout_total_num']=df['Layout_room_num']+df['Layout_hall_num']df['Size_room_ratio']=df['Size']/df['Layout_total_num']删除无用特征
df=df.drop(['Layout','PerPrice','Garden','District'],axis=1)df.head()One-hot独热编码是将定类的非数值型类型量化的一种方法,在pandas中使用get_dummies()方法实现。这里使用一个自定义的封装的函数实现了定类数据的自动量化处理。
colormap=plt.cm.RdBuplt.figure(figsize=(20,20))sns.heatmap(df.corr(),linewidth=0.1,vmax=1.0,square=True,cmap=colormap,linecolor='white',annot=True)
这里还需要注意特征太多有可能会导致heatmap图画失败。
本次建模主要方法为:使用Cart决策树的回归模型对二手房房价进行分析预测;使用交叉验证方法充分利用数据集进行训练,避免数据划分不均匀的影响;使用GridSearchCV方法优化模型参数;使用R2评分方法对模型预测评分。
数据划分
#特征变量和目标变量features=df.drop('Price',axis=1)prices=df['Price']#把分类特征都转成数值型后有{}行{}列print('北京二手房房价有数据{0}条,字段{1}个'.format(*df.shape))#将数据集划分为训练集与测试集features=np.array(features)prices=np.array(prices)#导入sklearn进行训练测试集划分fromsklearn.model_selectionimporttrain_test_splitfeatures_train,features_test,prices_train,prices_test=train_test_split(features,prices,test_size=0.2,random_state=0)建立模型
#建立模型fromsklearn.model_selectionimportKFoldfromsklearn.treeimportDecisionTreeRegressorfromsklearn.metricsimportmake_scorerfromsklearn.model_selectionimportGridSearchCV#通过交叉认证缓解数据集过拟合的现象#建立决策树回归模型#通过GridSearchCV找到最优深度参数(基于输入数据[X,y]利于网格搜索找到最优的决策树模型)deffit_model(X,y):cross_validator=KFold(10,shuffle=True)regressor=DecisionTreeRegressor()params={'max_depth':[1,2,3,4,5,6,7,8,9,10]}scoring_fnc=make_scorer(performance_metric)grid=GridSearchCV(estimator=regressor,param_grid=params,scoring=scoring_fnc,cv=cross_validator)#网格搜索grid=grid.fit(X,y)returngrid.best_estimator_评估验证
#计算R2分数fromsklearn.metricsimportr2_scoredefperformance_metric(y_true,y_predict):score=r2_score(y_true,y_predict)returnscore#调参优化模型#通过可视化模型学习曲线,观察是否出现过拟合问题#visuals为自定义函数importvisualsasvs#分析模型vs.ModelLearning(features_train,prices_train)vs.ModelComplexity(features_train,prices_train)optimal=fit_model(features_train,prices_train)#输出最优模型的参数'max_depth'print('最优模型的参数max_depth是:{}'.format(optimal.get_params()['max_depth']))predicted_value=optimal.predict(features_test)r2=performance_metric(prices_test,predicted_value)#每次交叉验证得到的数据集不同,因此每次运行的结果也不一定相同print('最优模型在测试数据上R^2分数{:.2f}'.format(r2))
可以看到,最理想模型的参数max_depth是10,此时达到了偏差与方差的最优平衡。模型在测试数据上的R2分数为:0.77,即二手房房价预测的准确率。
以上,完成了一个项目的简单分析。可以改进的方向有以下3个: