专栏首页Python数据科学数据分析实战—北京二手房房价分析(建模篇)

数据分析实战—北京二手房房价分析(建模篇)

阅读本文需要 10 分钟

上一篇和大家分享了一个入门数据分析的一个小项目 北京二手房房价分析,链接如下:

  • 数据分析实战—北京二手房房价分析

文章在sf发布之后看到有不少感兴趣的朋友给我点了赞,感谢大家的支持了。

本篇将继续上一篇数据分析之后进行数据挖掘建模预测,这两部分构成了一个简单的完整项目。结合两篇文章通过数据分析和挖掘的方法可以达到二手房屋价格预测的效果。

下面从特征工程开始讲述。

特征工程

特征工程包括的内容很多,有特征清洗,预处理,监控等,而预处理根据单一特征或多特征又分很多种方法,如归一化,降维,特征选择,特征筛选等等。这么多的方法,为的是什么呢?其目的是让这些特征更友好的作为模型的输入,处理数据的好坏会严重的影响模型性能,而好的特征工程有的时候甚至比建模调参更重要。

下面是继上一次分析之后对数据进行的特征工程,博主将一个一个帮大家解读。

 1"""
 2特征工程
 3"""
 4# 移除结构类型异常值和房屋大小异常值
 5df = df[(df['Layout']!='叠拼别墅')&(df['Size']<1000)]
 6
 7# 去掉错误数据“南北”,因为爬虫过程中一些信息位置为空,导致“Direction”的特征出现在这里,需要清除或替换
 8df['Renovation'] = df.loc[(df['Renovation'] != '南北'), 'Renovation']
 9
10# 由于存在个别类型错误,如简装和精装,特征值错位,故需要移除
11df['Elevator'] = df.loc[(df['Elevator'] == '有电梯')|(df['Elevator'] == '无电梯'), 'Elevator']
12
13# 填补Elevator缺失值
14df.loc[(df['Floor']>6)&(df['Elevator'].isnull()), 'Elevator'] = '有电梯'
15df.loc[(df['Floor']<=6)&(df['Elevator'].isnull()), 'Elevator'] = '无电梯'
16
17# 只考虑“室”和“厅”,将其它少数“房间”和“卫”移除
18df = df.loc[df['Layout'].str.extract('^\d(.*?)\d.*?') == '室']
19
20# 提取“室”和“厅”创建新特征
21df['Layout_room_num'] = df['Layout'].str.extract('(^\d).*', expand=False).astype('int64')
22df['Layout_hall_num'] = df['Layout'].str.extract('^\d.*?(\d).*', expand=False).astype('int64')
23
24# 按中位数对“Year”特征进行分箱
25df['Year'] = pd.qcut(df['Year'],8).astype('object')
26
27# 对“Direction”特征
28d_list_one = ['东','西','南','北']
29d_list_two = ['东西','东南','东北','西南','西北','南北']
30d_list_three = ['东西南','东西北','东南北','西南北']
31d_list_four = ['东西南北']    
32df['Direction'] = df['Direction'].apply(direct_func)
33df = df.loc[(df['Direction']!='no')&(df['Direction']!='nan')]
34
35# 根据已有特征创建新特征
36df['Layout_total_num'] = df['Layout_room_num'] + df['Layout_hall_num']
37df['Size_room_ratio'] = df['Size']/df['Layout_total_num']
38
39# 删除无用特征
40df = df.drop(['Layout','PerPrice','Garden'],axis=1)
41
42# 对于object特征进行onehot编码
43df,df_cat = one_hot_encoder(df)

由于一些清洗处理在上一篇文章已经提到,博主从17行代码开始。

Layout

先来看看没经处理的Layout特征值是什么样的。

1df['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# 只考虑“室”和“厅”,将其它少数“房间”和“卫”移除
2df = df.loc[df['Layout'].str.extract('^\d(.*?)\d.*?') == '室']
3
4# 提取“室”和“厅”创建新特征
5df['Layout_room_num'] = df['Layout'].str.extract('(^\d).*', expand=False).astype('int64')
6df['Layout_hall_num'] = df['Layout'].str.extract('^\d.*?(\d).*', expand=False).astype('int64')

Year

我们还有一个 Year 特征,为建房的年限时间。年限种类很多,分布在1950和2018之间,如果每个不同的 Year 值都作为特征值,我们并不能找出 Year 对 Price 有什么影响,因为年限划分的太细了。因此,我们只有将连续数值型特征 Year 离散化,做分箱处理

如何分箱还要看实际业务需求,博主为了方便并没有手动分箱,而使用了pandas的 qcut 采用中位数进行分割,分割数为8等份。

1# 按中位数对“Year”特征进行分箱
2df['Year'] = pd.qcut(df['Year'],8).astype('object')

这是将 Year 进行分箱的结果:

Direction

这个特征没处理之前更乱,原以为是爬虫的问题,但是亲自到链家看过,朝向确实是这样的。

如上所见,像"西南西北北"或者"东东南南"这样的朝向是不符合常识的(反正我是理解不了)。因此,我们需要将这些凌乱的数据进行处理,具体实现方式是博主自己写了一个函数 direct_func,主要思想就是将各种重复但顺序不一样的特征值合并,比如"西南北"和"南西北",并将不合理的一些值移除,如"西南西北北"等。

然后通过 apply() 方法将 Direction 数据格式转换,代码如下:

1# 对“Direction”特征
2d_list_one = ['东','西','南','北']
3d_list_two = ['东西','东南','东北','西南','西北','南北']
4d_list_three = ['东西南','东西北','东南北','西南北']
5d_list_four = ['东西南北']    
6df['Direction'] = df['Direction'].apply(direct_func)
7df = df.loc[(df['Direction']!='no')&(df['Direction']!='nan')]

处理完结果如下,所有的内容相同而顺序不同的朝向都合并了,异常朝向也被移除了。

创建新特征

有时候仅靠已有的一些特征是不够的,需要根据对业务的理解,定义一些的新特征,然后尝试这些新特征对模型的影响,在实战中会经常使用这种方法。

这里尝试将"室"与"厅"的数量相加作为一个总数量特征,然后将房屋大小Size与总数量的比值作为一个新特征,可理解为 "每个房间的平均面积大小"。当然,新特征不是固定的,可根据自己的理解来灵活的定义。

1# 根据已有特征创建新特征
2df['Layout_total_num'] = df['Layout_room_num'] + df['Layout_hall_num']
3df['Size_room_ratio'] = df['Size']/df['Layout_total_num']
4
5# 删除无用特征
6df = 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编码
2df,df_cat = one_hot_encoder(df)

以上的特征工程就完成了。

特征相关性

下面使用 seaborn 的 heatmap 方法对特征相关性进行可视化。

1# data_corr 
2colormap = plt.cm.RdBu
3plt.figure(figsize=(20,20))
4# plt.title('Pearson Correlation of Features', y=1.05, size=15)
5sns.heatmap(df.corr(),linewidths=0.1,vmax=1.0, square=True, cmap=colormap, linecolor='white', annot=True)

颜色偏红或者偏蓝都说明相关系数较大,即两个特征对于目标变量的影响程度相似,即存在严重的重复信息,会造成过拟合现象。因此,通过特征相关性分析,我们可以找出哪些特征有严重的重叠信息,然后择优选择。

数据建模预测

为了方便理解,博主在建模上做了一些精简,模型策略方法如下:

  • 使用Cart决策树的回归模型对二手房房价进行分析预测
  • 使用交叉验证方法充分利用数据集进行训练,避免数据划分不均匀的影响。
  • 使用GridSearchCV方法优化模型参数
  • 使用R2评分方法对模型预测评分

上面的建模方法比较简单,旨在让大家了解建模分析的过程。随着逐渐的深入了解,博主会介绍更多实战内容。

数据划分

1# 转换训练测试集格式为数组
2features = np.array(features)
3prices = np.array(prices)
4
5# 导入sklearn进行训练测试集划分
6from sklearn.model_selection import train_test_split
7features_train, features_test, prices_train, prices_test = train_test_split(features, prices, test_size=0.2, random_state=0)

将以上数据划分为训练集测试集,训练集用于建立模型,测试集用于测试模型预测准确率。使用sklearn的 model_selection 实现以上划分功能。

建立模型

 1from sklearn.model_selection import KFold
 2from sklearn.tree import DecisionTreeRegressor
 3from sklearn.metrics import make_scorer
 4from sklearn.model_selection import GridSearchCV
 5
 6# 利用GridSearchCV计算最优解
 7def 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分数
23def 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评分来给模型打分。

调参优化模型

 1import visuals as vs
 2
 3# 分析模型
 4vs.ModelLearning(features_train, prices_train)
 5vs.ModelComplexity(features_train, prices_train)
 6
 7optimal_reg1 = fit_model(features_train, prices_train)
 8
 9# 输出最优模型的 'max_depth' 参数
10print("最理想模型的参数 'max_depth' 是 {} 。".format(optimal_reg1.get_params()['max_depth']))
11
12predicted_value = optimal_reg1.predict(features_test)
13r2 = performance_metric(prices_test, predicted_value)
14
15print("最优模型在测试数据上 R^2 分数 {:,.2f}。".format(r2))

由于决策树容易过拟合的问题,我们这里采取观察学习曲线的方法查看决策树深度,并判断模型是否出现了过拟合现象。以下是观察到的学习曲线图形:

通过观察,最理想模型的参数"max_depth"是10,此种情况下达到了偏差与方差的最优平衡,最后模型在测试数据上的R2分数,也即二手房房价预测的准确率为:0.81。

总结

以上一个完整的从数据分析到挖掘的项目就结束了,对于项目而言比较简单,目的是让大家了解整个分析的过程。可提升改进的地方非常多,可以有更好更健壮的方案代替,一些改进思考如下:

  • 获取更多有价值的特征信息,比如学区,附近地铁,购物中心等
  • 完善特征工程,如进行有效的特征选择
  • 使用更优秀的模型算法建模或者使用模型融合

本文分享自微信公众号 - Python数据科学(Python_Spiderman),作者:wLsq

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-07-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 妈妈再也不用担心我忘记pandas操作了

    pandas的操作上千种,但对于数据分析的使用掌握常用的操作就可以应付了,更多的操作可以参考pandas官网。

    用户2769421
  • 13个Pandas实用技巧,有点香 !

    归纳整理了一些工作中常用到的pandas使用技巧,方便更高效地实现数据分析。文章很短,不用收藏就能Get~

    用户2769421
  • 8个数据清洗Python代码,复制可用,最长11行

    原作 Kin Lim Lee 乾明 编译整理 量子位 出品 | 公众号 QbitAI

    用户2769421
  • Pandas 4 个小 trick,都很实用!

    对于动辄就几十或几百个 G 的数据,在读取这么大数据时,有没有办法随机选取一小部分数据,然后读入内存,快速了解数据和开展 EDA ?

    double
  • 资源 | 23种Pandas核心操作,你需要过一遍吗?

    Pandas 是基于 NumPy 构建的库,在数据处理方面可以把它理解为 NumPy 加强版,同时 Pandas 也是一项开源项目。它基于 Cython,因此读...

    CDA数据分析师
  • 【MathorCup】2020年 A题 无车承运人平台线路定价问题,特征间的相关性分析

    问题 1:通过定量分析的方法,研究影响无车承运人平台进行货运线路定价的主要因素有哪些,并说明理由。 问题 2:根据附件 1 数据,通过建立数学模型,对已经成交...

    不太灵光的程序员
  • Pandas进阶修炼120题,给你深度和广度的船新体验

    本文为你介绍Pandas基础、Pandas数据处理、金融数据处理等方面的一些习题。

    数据派THU
  • Day05| 第四期-电商数据分析

    疫情期间,想必我们会增加网上购物,人们的生活越来越数字化。当我们消费时,无论是线上和线下都会产生大量的交易数据,对于商家来说数字化的运营方式非常必要,从大量的交...

    DataScience
  • Pandas 数据分析: 3 种方法实现一个实用小功能

    与时间相关,自然第一感觉便是转化为datetime格式,这里需要注意:需要首先将两列转化为 str 类型。

    double
  • 在郑州,你该买哪里的房子?

    某次和领导吃饭,无意中提到了房子的话题,说了几句自己的心得经验(虽然没有再次实操的资本),却给领导留下了深深的印象(领导,你不是又要在郑州置业了吧)。

    数据社

扫码关注云+社区

领取腾讯云代金券