为了预测湾区的房价, 我选择了来自湾区住房销售数据库和 Zillow 的房价数据集此数据集基于 2013 年 1 月至 2015 年 12 月期间出售的房屋它具有许多学习特点, 数据集可从此处下载
数据预处理
- import pandas as pd
- sf = pd.read_csv('final_data.csv')
- sf.head()
有几个我们不需要的特征, 比如 info,z_address,zipcode(我们有 neighborhood 作为位置变量),zipid 和 zestimate(这是 Zillow 估计的价格, 我们不希望我们的模型受到这个影响), 所以会丢弃它们
- sf.drop(sf.columns[[0, 2, 3, 15, 17, 18]], axis=1, inplace=True)
- sf.info()
zindexvalue 的数据类型应该是数字, 所以让我们更改它
- sf['zindexvalue'] = sf['zindexvalue'].str.replace(',', '')
- sf['zindexvalue'] = pd.to_numeric(sf['zindexvalue'])
- sf.lastsolddate.min(), sf.lastsolddate.max()
- (01/02/2013, 12/31/2015)
房屋销售期限为 2013 年 1 月至 2015 年 12 月
我现在使用 describe()方法来显示数字变量的汇总统计信息
sf.describe()
计数, 平均值, 最小值和最大值是不言自明的 std 显示标准偏差, 25%,50%和 75%的行显示相应的百分位数
为了了解我们处理的数据类型, 我们绘制了每个数值变量的直方图
- %matplotlib inline
- import matplotlib.pyplot as plt
- sf.hist(bins=50, figsize=(20,15))
- plt.savefig("attribute_histogram_plots")
- plt.show()
图 1. 每个数值变量的直方图
有些直方图有一点偏右, 但这并不是异常的
让我们创建一个包含经度和纬度的散点图来显示数据:
- sf.plot(kind="scatter", x="longitude", y="latitude", alpha=0.2)
- plt.savefig('map1.png')
图 2. 数据的散点图
现在让我们根据价格从高到低绘图:
- sf.plot(kind="scatter", x="longitude", y="latitude", alpha=0.4, figsize=(10,7),
- c="lastsoldprice", cmap=plt.get_cmap("jet"), colorbar=True,
- sharex=False)
图 3. 湾区房价
这张图片告诉我们, 最昂贵的房子出售在北部地区
我们要预测的变量是最后售出的价格那么我们来看看每个独立变量与这个因变量的相关程度
- corr_matrix = sf.corr()
- corr_matrix["lastsoldprice"].sort_values(ascending=False)
当平方尺和浴室数量增加时, 最后售出的价格往往会增加您可以看到建成年份和最后售出价格之间存在小的负相关关系最后, 接近零的系数表明没有线性相关
我们现在要通过使用 Pandas 的 scatter_matrix 函数来可视化变量之间的相关性我们只关注一些有前途的变量, 这些变量似乎与最后售出的价格最相关
- from pandas.plotting import scatter_matrix
- attributes = ["lastsoldprice", "finishedsqft", "bathrooms", "zindexvalue"]
- scatter_matrix(sf[attributes], figsize=(12, 8))
- plt.savefig('matrix.png')
图 4. 散布矩阵
影响最后售出价格的最有可能的变量是平方英尺, 所以让我们放大它们的相关散点图
- sf.plot(kind="scatter", x="finishedsqft", y="lastsoldprice", alpha=0.5)
- plt.savefig('scatter.png')
图 5. 平方英尺与最近出售的价格
相关性确实非常强; 你可以清楚地看到上升的趋势, 并且这些点不是太分散
由于每栋房屋面积不同, 每个社区的房价都不同, 我们真正需要的是每平方英尺的价格所以, 我们添加一个新的变量 price_per_sqft 然后, 我们检查这个新的自变量与最后售出价格的相关程度
- sf['price_per_sqft'] = sf['lastsoldprice']/sf['finishedsqft']
- corr_matrix = sf.corr()
- corr_matrix["lastsoldprice"].sort_values(ascending=False)
不巧的是, 新的 price_per_sqft 变量只显示与最后售出价格非常小的正相关但是我们仍然需要这个变量来分组社区
数据中有 71 个街区, 我们将对他们进行分组
- len(sf['neighborhood'].value_counts())
- 71
以下步骤将街区分为三组: 1. 低价格; 2. 高价低频; 3. 高价高频
- freq = sf.groupby('neighborhood').count()['address']
- mean = sf.groupby('neighborhood').mean()['price_per_sqft']
- cluster = pd.concat([freq, mean], axis=1)
- cluster['neighborhood'] = cluster.index
- cluster.columns = ['freq', 'price_per_sqft','neighborhood']
- cluster.describe()
这些是低价格的街区:
- cluster1 = cluster[cluster.price_per_sqft < 756]
- cluster1.index
- Index([Bayview, Central Richmond, Central Sunset, Crocker Amazon,
- Daly City, Diamond Heights, Excelsior, Forest Hill,
- Forest Hill Extension, Golden Gate Heights, Ingleside,
- Ingleside Heights, Ingleside Terrace, Inner Parkside,
- Inner Richmond, Inner Sunset, Lakeshore, Little Hollywood,
- Merced Heights, Mission Terrace, Mount Davidson Manor,
- Oceanview, Outer Mission, Outer Parkside, Outer Richmond,
- Outer Sunset, Parkside, Portola, Silver Terrace, Sunnyside,
- Visitacion Valley, West Portal, Western Addition,
- Westwood Highlands, Westwood Park],
- dtype=object, name=neighborhood
这些是高价格和低频率的街区:
- cluster_temp = cluster[cluster.price_per_sqft >= 756]
- cluster2 = cluster_temp[cluster_temp.freq <123]
- cluster2.index
- Index([Buena Vista Park, Central WaterfrontDogpatch, Corona Heights, Haight-Ashbury, Lakeside, Lone Mountain, Midtown Terrace,
- North Beach, North Waterfront, ParnassusAshbury, Presidio Heights, Sea Cliff, St. Francis Wood, Telegraph Hill, Twin Peaks], dtype=object, name=neighborhood)
这些是高价格和高频率的街区:
- cluster3 = cluster_temp[cluster_temp.freq >=123]
- cluster3.index
- Index([Bernal Heights, Cow Hollow, Downtown, Eureka ValleyDolores HeightsCastro, Glen Park, Hayes Valley, Lake, Lower Pacific Heights, Marina, Miraloma Park, Mission, Nob Hill, Noe Valley, North Panhandle, Pacific Heights, Potrero Hill, Russian Hill, South Beach, South of Market, Van NessCivic Center, Yerba Buena],
- dtype=object, name=neighborhood)
我们根据集群添加一个组列:
- def get_group(x):
- if x in cluster1.index:
- return 'low_price'
- elif x in cluster2.index:
- return 'high_price_low_freq'
- else:
- return 'high_price_high_freq'
- sf['group'] = sf.neighborhood.apply(get_group)
在执行上述预处理之后, 我们不再需要以下列: address, lastsolddate, latitude, longitude, neighborhood, price_per_sqft, 所以将它们删除
- sf.drop(sf.columns[[0, 4, 6, 7, 8, 13]], axis=1, inplace=True)
- sf = sf[['bathrooms', 'bedrooms', 'finishedsqft', 'totalrooms', 'usecode', 'yearbuilt','zindexvalue', 'group', 'lastsoldprice']]
- sf.head()
数据看起来完美!
但在构建模型之前, 我们需要为这两个分类变量创建哑变量: usecode 和 group
- X = sf[['bathrooms', 'bedrooms', 'finishedsqft', 'totalrooms', 'usecode', 'yearbuilt',
- 'zindexvalue', 'group']]
- Y = sf['lastsoldprice']
- n = pd.get_dummies(sf.group)
- X = pd.concat([X, n], axis=1)
- m = pd.get_dummies(sf.usecode)
- X = pd.concat([X, m], axis=1)
- drops = ['group', 'usecode']
- X.drop(drops, inplace=True, axis=1)
- X.head()
如下图所示:
训练并建立线性回归模型
- from sklearn.cross_validation import train_test_split
- X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.3, random_state=0)
- from sklearn.linear_model import LinearRegression
- regressor = LinearRegression()
- regressor.fit(X_train, y_train)
- LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)
搞定! 我们现在有一个可用的线性回归模型
计算 R 平方:
- y_pred = regressor.predict(X_test)
- print('Linear Regression R squared: %.4f' % regressor.score(X_test, y_test))
- Linear Regression R squared: 0.5619
因此, 我们的模型为 56.19%这并不令人兴奋
计算均方根误差(RMSE):
- import numpy as np
- from sklearn.metrics import mean_squared_error
- lin_mse = mean_squared_error(y_pred, y_test)
- lin_rmse = np.sqrt(lin_mse)
- print('Linear Regression RMSE: %.4f' % lin_rmse)
- Linear Regression RMSE: 616071.5748
我们的模型能够在真实价格的 616071 美元之内预测测试集中每个房子的价值
计算平均绝对误差(MAE):
- from sklearn.metrics import mean_absolute_error
- lin_mae = mean_absolute_error(y_pred, y_test)
- print('Linear Regression MAE: %.4f' % lin_mae)
- Linear Regression MAE: 363742.1631
随机森林
让我们尝试一个更复杂的模型来看看是否可以改进结果 - RandomForestRegressor:
- from sklearn.ensemble import RandomForestRegressor
- forest_reg = RandomForestRegressor(random_state=42)
- forest_reg.fit(X_train, y_train)
- RandomForestRegressor(bootstrap=True, criterion=mse, max_depth=None, max_features=auto, max_leaf_nodes=None,
- min_impurity_split=1e-07, min_samples_leaf=1,
- min_samples_split=2, min_weight_fraction_leaf=0.0,
- n_estimators=10, n_jobs=1, oob_score=False, random_state=42,
- verbose=0, warm_start=False)
- print('Random Forest R squared": %.4f' % forest_reg.score(X_test, y_test))
- Random Forest R squared: 0.6491
- y_pred = forest_reg.predict(X_test)
- forest_mse = mean_squared_error(y_pred, y_test)
- forest_rmse = np.sqrt(forest_mse)
- print('Random Forest RMSE: %.4f' % forest_rmse)
- Random Forest RMSE: 551406.0926
好多了! 让我们再试试
- Gradient boosting
- from sklearn import ensemble
- from sklearn.ensemble import GradientBoostingRegressor
- model = ensemble.GradientBoostingRegressor()
- model.fit(X_train, y_train)
- GradientBoostingRegressor(alpha=0.9, criterion='friedman_mse', init=None,
- learning_rate=0.1, loss='ls', max_depth=3, max_features=None,
- max_leaf_nodes=None, min_impurity_decrease=0.0,
- min_impurity_split=None, min_samples_leaf=1,
- min_samples_split=2, min_weight_fraction_leaf=0.0,
- n_estimators=100, presort='auto', random_state=None,
- subsample=1.0, verbose=0, warm_start=False
- print('Gradient Boosting R squared": %.4f' % model.score(X_test, y_test))
- Gradient Boosting R squared: 0.6616
- y_pred = model.predict(X_test)
- model_mse = mean_squared_error(y_pred, y_test)
- model_rmse = np.sqrt(model_mse)
- print('Gradient Boosting RMSE: %.4f' % model_rmse)
- Gradient Boosting RMSE: 544579.8296
这些是迄今为止我们获得的最佳结果, 因此, 我认为这是我们的最终模型
特征的重要性
我们在模型中使用了 19 个特征 (变量) 让我们找出哪些功能很重要, 反之亦然
- feature_labels = np.array(['bathrooms', 'bedrooms', 'finishedsqft', 'totalrooms', 'yearbuilt', 'zindexvalue',
- 'high_price_high_freq', 'high_price_low_freq', 'low_price', 'Apartment', 'Condominium', 'Cooperative',
- 'Duplex', 'Miscellaneous', 'Mobile', 'MultiFamily2To4', 'MultiFamily5Plus', 'SingleFamily',
- 'Townhouse'])
- importance = model.feature_importances_
- feature_indexes_by_importance = importance.argsort()
- for index in feature_indexes_by_importance:
- print('{}-{:.2f}%'.format(feature_labels[index], (importance[index] *100.0)))
最重要的特征是 finishedsqft,zindexvalue,bathrooms,totalrooms,yearsbuilt 等等而最不重要的特征是 Apartment, 这意味着无论是否是公寓, 与售价无关总体而言, 这 19 个特征中的大多数都被使用
到你了!
希望这篇文章能够让你了解机器学习回归项目是什么样的正如您所看到的, 大部分工作都在数据准备步骤中, 而机器学习大部分时间花在这些过程上
现在是时候行动, 开始探索和清理您的数据尝试两种或三种算法, 并让我知道它是怎么回事
这篇文章的源代码可以在这里找到我很乐意收到任何反馈或问题
来源: http://www.jianshu.com/p/8eb079672b24