摘要:本次实战项目的主要目的是分析北京二手房房价,项目源自博文入门数据分析最好的实战项目一和入门数据分析最好的实战项目二。
本次实战项目的主要目的是分析北京二手房房价,项目源自博文:入门Python数据分析最好的实战项目(一)和入门Python数据分析最好的实战项目(二)。本篇文章仅记录博主在学习过程中的思路。
首先我们要对数据进行分析,可分为以下几个主要步骤:
导入数据
检查缺失值情况并对表格进行简单处理
数据可视化分析
这里我们重点要讲的是数据可视化分析,即对一些重要对特征逐个画图观察。
打开表格:
我们看到上述数据有 11 个特征变量,1 个目标变量 Price。11 个特征分别为:
Direction
District
Elevator
Floor
Garden
Id
Layout
Region
Renovation
Size
Year
我们分别对 Elevator, Floor, Layout, Region, Renovation, Size, Year 这 7 个特征进行可视化分析。
Elevator 特征分析代码:
# 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 特征分析代码:
# 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()
执行结果:
分析目的:
分析不同的楼层二手房数量。
使用方法:
采用seaborn完成可视化。
观察结果:
其中 6 层的二手房数量最多,但是多带带的楼层特征没有什么意义,因为每个小区住房的总楼层数都不一样,我们需要知道楼层的相对高度。
此外,楼层与文化也有很重要的联系,比如在中国文化有七上八下,七层可能受欢迎等。一般来说中间楼层比较受欢迎,价格也高,底层和顶层受欢迎度较低,价格也相对较低。
楼层是一个非常复杂的特征,对房价影响也比较大。
Layout 特征分析代码:
# 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()
执行结果:
分析目的:
分析不同户型的数量。
使用方法:
采用seaborn完成可视化。
观察结果:
这个特征分类下有很多不规则的命名,以上特征是不能作为机器学习模型的数据输入的,需要使用特征工程进行相应的处理。
代码:
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参数,颜色越浅数量越少,反之越多。
观察结果:
二手房每平方米单价对比:西城区的房价最贵均价大约 11 万/平,因为西城在二环以里,且是热门学区房的聚集地。其次是东城大约 10 万/平,然后是海淀大约 8.5 万/平,其它均低于 8 万/平。
二手房房数量对比:从数量统计上来看,海淀区和朝阳区二手房数量最多,约接近 3000 套,因为二者属于大区。其次是丰台区,近几年正在改造建设,需求量大。
二手房房屋总价对比:通过箱型图看到,各大区域房屋总价中位数都都在 1000 万以下,且房屋总价离散值较高,西城最高达到了 6000 万,说明房屋价格特征并不是理想的正态分布。
Renovation 特征分析代码:
# 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()
执行结果:
分析目的:
分析不同装修程度的二手房数量和房价。
使用方法:
采用seaborn完成可视化。
观察结果:
对于数量来说,精装修的二手房最多,简装其次;对于价格来说,毛坯房价格最高,其次是精装修的。
代码:
# Size特征分析 f, [ax1, ax2] = plt.subplots(1, 2, figsize=(15,5)) # 建房时间分布情况 sns.distplot(df["Size"], bins=20, ax=ax1, color="r") sns.kdeplot(df["Size"], ax=ax1, shade=True) # 建房时间和出售价格的关系 sns.regplot(x="Size", y="Price", data=df, ax=ax2) plt.show() # 查看异常值 df.loc[df["Size"] < 10] df.loc[df["Size"] > 1000] # 移除上述两种异常值 df = df[(df["Layout"]!="叠拼别墅") & (df["Size"]<1000)] # 重新进行可视化发现就没有明显的异常点 sns.regplot(x="Size", y="Price", data=df) plt.show()
执行结果:
分析目的:
分析不同大小的二手房和价格的关系。
使用方法:
通过distplot和 kdeplot 绘制柱状图观察 Size 特征的分布情况,属于长尾类型的分布,这说明有很多面积很大且超出正常范围的二手房。
通过 regplot 绘制了 Size 和 Price 之间的散点图,发现 Size 特征基本与Price呈现线性关系,符合基本常识,面积越大,价格越高。
观察结果:
有两组明显的异常点:面积不到 10 平米但价格超出 10000 万和面积超过了 1000 平米价格很低两种情况。
经过查看发现这两组异常值分别是别墅和商用房,因此出现异常,故将其移除再次观察Size分布和Price关系。
这里也说明我们在观察数据的时候,要紧密结合实际业务需求来分析,才能得出更准确的结果。
Year 特征分析代码:
# 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 特征
观察结果:
观察数据可视化图表可以看出,整个二手房房价趋势是随着时间增长而增长的,2000 年以后建造的二手房房价相较于 2000 年以前有很明显的价格上涨。此外,1980年之前几乎不存在有电梯二手房数据,说明1980年之前还没有大面积安装电梯,且在 1980 年之前无电梯二手房中,简装二手房占绝大多数,精装反而很少。
特征工程的目的是让这些特征更友好的作为模型的输入,处理数据的好坏会严重的影响模型性能。
这里我们对已有的 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 来整理上面较乱的 Direction def direct_func(x): if not isinstance(x,str): raise TypeError x = x.strip() x_len = len(x) x_list = pd.unique([y for y in x]) if x_len != len(x_list): return "no" if (x_len == 2) & (x not in d_list_two): m0 = x[0] m1 = x[1] return m1+m0 elif (x_len == 3) & (x not in d_list_three): for n in d_list_three: if (x_list[0] in n) & (x_list[1] in n) & (x_list[2] in n): return n elif (x_len == 4) & (x not in d_list_four): return d_list_four[0] else: return x # 通过 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() 方法实现。这里使用一个自定义的封装的函数实现了定类数据的自动量化处理。
def one_hot_encoder(df, nan_as_category = True): original_columns = list(df.columns) categorical_columns = [col for col in df.columns if df[col].dtype == "object"] df = pd.get_dummies(df, columns= categorical_columns, dummy_na= nan_as_category) new_columns = [c for c in df.columns if c not in original_columns] return df, new_columns # 对于object特征进行onehot编码 df, df_cat = one_hot_encoder(df)
特征相关性
对数据经过以上处理后,可以用 seaborn 的 heatmap 方法对特征相关性进行可视化。
colormap = plt.cm.RdBu plt.figure(figsize=(20, 20)) sns.heatmap(df.corr(), linewidth=0.1, vmax=1.0, square=True, cmap=colormap, linecolor="white", annot=True)
heatmap 可以根据颜色观察特征的相关性。颜色偏红或者偏蓝都说明相关系数较大,即两个特征对于目标变量的影响程度相似,也就是说存在严重的重复信息,会造成过拟合现象。
我们能通过特征相关性分析,找出哪些特征有严重的重叠信息,然后择优选择。
这里还需要注意特征太多有可能会导致 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 进行训练测试集划分 from sklearn.model_selection import train_test_split features_train, features_test, prices_train, prices_test = train_test_split(features, prices, test_size=0.2, random_state=0)
建立模型
# 建立模型 from sklearn.model_selection import KFold from sklearn.tree import DecisionTreeRegressor from sklearn.metrics import make_scorer from sklearn.model_selection import GridSearchCV # 通过交叉认证缓解数据集过拟合的现象 # 建立决策树回归模型 # 通过GridSearchCV找到最优深度参数(基于输入数据[X,y] 利于网格搜索找到最优的决策树模型) def fit_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) return grid.best_estimator_
评估验证
# 计算 R2 分数 from sklearn.metrics import r2_score def performance_metric(y_true, y_predict): score = r2_score(y_true, y_predict) return score # 调参优化模型 # 通过可视化模型学习曲线,观察是否出现过拟合问题 # visuals 为自定义函数 import visuals as vs # 分析模型 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 个:
爬取数据的准确性和完整性
特征的进一步提取
不同模型的融合与实验,以达到最优效果
不足之处,欢迎指正
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/42030.html
摘要:入门数据分析最好的实战项目二数据初探首先导入要使用的科学计算包可视化以及机器学习包。检查缺失值情况发现了数据集一共有条数据,其中特征有明显的缺失值。 作者:xiaoyu 微信公众号:Python数据科学 知乎:python数据分析师 目的:本篇给大家介绍一个数据分析的初级项目,目的是通过项目了解如何使用Python进行简单的数据分析。数据源:博主通过爬虫采集的链家全网北京二手房数据(...
摘要:入门数据分析最好的实战项目二数据初探首先导入要使用的科学计算包可视化以及机器学习包。检查缺失值情况发现了数据集一共有条数据,其中特征有明显的缺失值。 作者:xiaoyu 微信公众号:Python数据科学 知乎:python数据分析师 目的:本篇给大家介绍一个数据分析的初级项目,目的是通过项目了解如何使用Python进行简单的数据分析。数据源:博主通过爬虫采集的链家全网北京二手房数据(...
摘要:作者微信公众号数据科学知乎数据分析师上一篇和大家分享了一个入门数据分析的一个小项目北京二手房房价分析,链接如下入门数据分析最好的实战项目一文章在发布之后看到有不少感兴趣的朋友给我点了赞,感谢大家的支持了。 作者:xiaoyu 微信公众号:Python数据科学 知乎:python数据分析师 上一篇和大家分享了一个入门数据分析的一个小项目 北京二手房房价分析,链接如下: 入门Python...
阅读 805·2021-11-16 11:56
阅读 1631·2021-11-16 11:45
阅读 3089·2021-10-08 10:13
阅读 4020·2021-09-22 15:27
阅读 710·2019-08-30 11:03
阅读 624·2019-08-30 10:56
阅读 918·2019-08-29 15:18
阅读 1686·2019-08-29 14:05