1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > zillow房价预测

zillow房价预测

时间:2024-02-26 19:42:32

相关推荐

zillow房价预测

数据介绍

该项目是Data Castle上的美国King County房价预测训练赛,用到的数据取自于kaggle datasets,由@harlfoxem提供并分享,但是只选取了其中的子集,并对数据做了一些预处理使数据更加符合回归分析比赛的要求。

数据主要包括5月至5月美国King County的房屋销售价格以及房屋的基本信息。 数据分为训练数据和测试数据,分别保存在kc_train.csv和kc_test.csv两个文件中。 其中训练数据主要包括10000条记录,14个字段,主要字段说明如下:

第一列“销售日期”("SaleDate"):5月到5月房屋出售时的日期第二列“销售价格”("SalePrice"):房屋交易价格,单位为美元,是目标预测值第三列“卧室数”("BedroomNum"):房屋中的卧室数目第四列“浴室数”("BathroomNum"):房屋中的浴室数目第五列“房屋面积”("LivingArea"):房屋里的生活面积第六列“停车面积”("ParkingArea"):停车坪的面积第七列“楼层数”("Floor"):房屋的楼层数第八列“房屋评分”("Rating"):King County房屋评分系统对房屋的总体评分第九列“建筑面积”("BuildingArea"):除了地下室之外的房屋建筑面积第十列“地下室面积”("BasementArea"):地下室的面积第十一列“建筑年份”("BuildingYear"):房屋建成的年份第十二列“修复年份”("RepairYear"):房屋上次修复的年份第十三列"纬度"("Latitude"):房屋所在纬度第十四列“经度”("Longitude"):房屋所在经度

测试数据主要包括3000条记录,13个字段,跟训练数据的不同是测试数据并不包括房屋销售价格,需要通过由训练数据所建立的模型以及所给的测试数据,得出测试数据相应的房屋销售价格预测值。

# 导入数据分析需要的包import numpy as npimport pandas as pdimport matplotlib.pyplot as pltimport seaborn as sns%matplotlib inlineplt.style.use('ggplot')

分别导入训练数据和测试数据,并按照数据介绍中的内容给每一列加上列名

trainDf = pd.read_csv('kc_train.csv', header = None,

names = ['SaleDate', 'SalePrice', 'BedroomNum', 'BathroomNum', 'LivingArea',

'ParkingArea', 'Floor', 'Rating', 'BuildingArea', 'BasementArea',

'BuildingYear', 'RepairYear', 'Latitude', 'Longitude'])

testDf = pd.read_csv('kc_test.csv', header = None,

names = ['SaleDate', 'BedroomNum', 'BathroomNum', 'LivingArea',

'ParkingArea', 'Floor', 'Rating', 'BuildingArea', 'BasementArea',

'BuildingYear', 'RepairYear', 'Latitude', 'Longitude'])

将训练集和测试集合并,便于对数据进行统一处理

fullDf = trainDf.append(testDf, ignore_index = True )

查看数据信息

trainDf.info()

以下为输出结果

<class 'pandas.core.frame.DataFrame'>

RangeIndex: 10000 entries, 0 to 9999

Data columns (total 14 columns):

SaleDate 10000 non-null int64

SalePrice 10000 non-null int64

BedroomNum 10000 non-null int64

BathroomNum 10000 non-null float64

LivingArea 10000 non-null int64

ParkingArea 10000 non-null int64

Floor 10000 non-null float64

Rating 10000 non-null int64

BuildingArea 10000 non-null int64

BasementArea 10000 non-null int64

BuildingYear 10000 non-null int64

RepairYear 10000 non-null int64

Latitude 10000 non-null float64

Longitude 10000 non-null float64

dtypes: float64(4), int64(10)

memory usage: 1.1 MB

训练集一共有10000条数据,且数据集中没有缺失值。可以看到数据类型全是数值型。

下面对某些数据进行处理:将销售日期转换为日期格式,并提取出年份和月份;将建筑年份分区,转化为分类变量。

# 将销售日期转换为日期格式,并提取出年份和月份fullDf['SaleDate_year'] = pd.to_datetime(fullDf['SaleDate'], format='%Y%m%d').dt.yearfullDf['SaleDate_month'] = pd.to_datetime(fullDf['SaleDate'], format='%Y%m%d').dt.month

建筑年份从1900年到,每隔分一组

temp_list = [i for i in range(1900, )]

bins = temp_list[::15]

fullDf['BuildingYearBins'] = pd.cut(fullDf['BuildingYear'], bins=bins)

使用pandas的get_dummies方法进行one-hot编码,得到虚拟变量,添加前缀‘Year’

BuildingYearBinsDf = pd.get_dummies(fullDf['BuildingYearBins'], prefix='Year')

添加虚拟变量到数据集中

fullDf = pd.concat([fullDf, BuildingYearBinsDf], axis=1)

将建筑年份和销售日期两列删除

fullDf.drop(['BuildingYear', 'SaleDate'], axis=1, inplace=True)

查看房价的分布情况

%matplotlib notebook

trainDf['SalePrice'].hist(bins=30)

房价分布情况

# 查看房价数据的描述统计量trainDf.SalePrice.describe()# 以下为输出结果count 1.000000e+04mean5.428749e+05std3.729258e+05min7.500000e+0425%3.225000e+0550%4.507000e+0575%6.450000e+05max6.885000e+06Name: SalePrice, dtype: float64

可以看到房价呈现明显的右偏态分布,大部分房价位于0-100万美元的区间,但房价最高接近700万美元。从该列的描述统计量中也能发现,房价平均值大于房价中位数,表现出右偏态的特征。

由于要分析影响房价的因素,所以作出每个变量与房价之间的关系图

plotDf = fullDf.iloc[0:10000, :] # 提取出总数据集的前10000行,即训练集的内容,用该DataFrame作图sns.barplot(x='SaleDate_year', y='SalePrice', data=plotDf)

销售年份和房价的关系图

sns.barplot(x='SaleDate_month', y='SalePrice', data=plotDf)

销售月份和房价的关系图

和的房屋销售价格比较接近。每个月的房价都会有小幅波动,4、5、6、10、11月的房价处在较高水平。因此将销售月份这一变量转化为虚拟变量加入总数据集中,删除原来的销售月份列。由于销售年份与房价相关性不大,将该列也删去。

SaleDateMonthDf = pd.get_dummies(fullDf['SaleDate_month'], prefix='Month')fullDf = pd.concat([fullDf, SaleDateMonthDf], axis=1)fullDf.drop(['SaleDate_year', 'SaleDate_month'], axis=1, inplace=True)

sns.boxplot(x='BedroomNum', y='SalePrice', data=plotDf)

卧室数和房价的关系图

sns.boxplot(x='BathroomNum', y='SalePrice', data=plotDf)

浴室数和房价的关系图

plt.scatter(plotDf['LivingArea'], plotDf['SalePrice'])

房屋面积和房价的关系图

卧室数、浴室数为离散型变量,用箱形图;房屋面积为连续型变量,用散点图。可以看出这三者与房价均成正相关,原因也很好理解,这三个变量越大意味着房子越大,越大的房子一般都会越贵。

sns.scatterplot(x='ParkingArea', y='SalePrice', data=plotDf)

停车面积和房价的关系图

sns.boxplot(x='Floor', y='SalePrice', data=plotDf)

楼层和房价的关系图

停车面积和楼层数与房屋价格的相关性不大。从图中可以看出有很多房屋停车面积很小但是价格却很贵。而房屋楼层高可能意味着每一层的面积小,比如同样都是300平方米的房子,三层楼的占地只要100平方米,而一层楼的占地就要300平方米,很多情况下是后者的地价更贵。将这两个变量从数据集中删除。

fullDf.drop(['ParkingArea', 'Floor'], axis=1, inplace=True)

sns.boxplot(x='Rating', y='SalePrice', data=plotDf)

房屋评分和房价的关系图

sns.scatterplot(x='BuildingArea', y='SalePrice', data=plotDf)

建筑面积和房价的关系图

从图中可以看出房屋评分对房价起着相当重要的作用,基本上评分越高的房子房价也越贵。建筑面积这个变量与房屋面积类似,建筑面积越大一般都会导致房价越高。

sns.scatterplot(x='BasementArea', y='SalePrice', data=plotDf)

地下室面积和房价的关系图

可以看到地下室面积这一列数据中有很多值为0,新建一个分类变量‘HasBasement’:如果BasementArea为0则该变量为0,否则该变量为1

fullDf['HasBasement'] = 0fullDf.at[fullDf['BasementArea'] > 0, 'HasBasement'] = 1

sns.barplot(x='BuildingYearBins', y='SalePrice', data=plotDf)

建筑年份和房价的关系图

sns.boxplot(x='RepairYear', y='SalePrice', data=plotDf)

修复年份和房价的关系图

可以看到年代很久远的房子和最近的新房子售价较高,说明建筑年份与房价有一定的相关性。年代久远的房子可能是因为其历史价值而被追捧,新房子的价格高也说明了相比旧房子人们更喜欢新建的房子。而修复年份与房价的相关性不大,很多房子没有修复,但是它们的价格差距很大,修复过的房子其价格也没有明显的规律,因此将修复年份列从数据集中删除。建筑年份已转化为虚拟变量,将建筑年份列也删除。

fullDf.drop(['RepairYear', 'BuildingYearBins'], axis=1, inplace=True)

sns.scatterplot(x='Longitude', y='Latitude', hue='SalePrice', data=plotDf)

房屋的经纬度分布

将房屋的经纬度作为坐标点画出散点图,并用颜色表示房价的高低。

利用matplotlib的Basemap模块可以在地图上根据经纬度画出点

from mpl_toolkits.basemap import Basemap

Bm = Basemap(llcrnrlon=-122.6, llcrnrlat=47.1, urcrnrlon=-121.5, urcrnrlat=47.8, resolution='h')

Basemap前四个参数是画出的经纬度的范围,最后一个参数是分辨率

Bm.drawmapboundary(fill_color='aqua') #将地图填充为蓝色

Bm.drawcoastlines() # 画出海岸线

lons = plotDf.Longitude

lats = plotDf.Latitude

x, y = Bm(lons, lats) # x、y为房屋的经纬度

Bm.scatter(x, y, marker='o', color='m') #用散点图画出每个房屋的位置

地图上房屋的分布

图中黑线是陆地和海洋的分隔线,黑线中间的部位是海洋河流,黑线外边的区域是陆地。可以看到在陆地上样本点的分布很密集。

分别选出房价最高和最低的十个点,在地图上画出它们的位置。

max_lons = plotDf.sort_values(by='SalePrice', ascending=False).Longitude[:10]max_lats = plotDf.sort_values(by='SalePrice', ascending=False).Latitude[:10]min_lons = plotDf.sort_values(by='SalePrice', ascending=False).Longitude[-10:]min_lats = plotDf.sort_values(by='SalePrice', ascending=False).Latitude[-10:]

Bm1 = Basemap(llcrnrlon=-122.6, llcrnrlat=47.1, urcrnrlon=-121.5, urcrnrlat=47.8, resolution='h')

Bm1.drawmapboundary(fill_color='aqua')

Bm1.drawcoastlines()

x1, y1 = Bm1(max_lons, max_lats)

Bm1.scatter(x1, y1, marker='o', color='m')

房价前十的房屋在地图上的分布

Bm2 = Basemap(llcrnrlon=-122.6, llcrnrlat=47.1, urcrnrlon=-121.5, urcrnrlat=47.8, resolution='h')Bm2.drawmapboundary(fill_color='aqua')Bm2.drawcoastlines()x2, y2 = Bm2(min_lons, min_lats)Bm2.scatter(x2, y2, marker='o', color='m')

房价后十的房屋在地图上的分布

可以看到房价最高的十个点基本都分布在湖边,而房价最低的十个点基本都分布在内陆,这也验证了seaborn画出的散点图,房价高的点基本上都位于水畔,风景好的地方房价自然也高

特征选择

根据数据可视化的结果,与房价比较相关的几个特征为卧室数、浴室数、房屋面积、房屋评价、销售月份、建筑年份。

下面使用几种不同的方法来选取我们训练模型时需要的特征

1. 相关系数

求出每两个特征之间的相关系数,并用热力图画出。由于建筑年份和销售月份是虚拟变量,不便于单独拿出来和其他特征进行相关性比较,也会使热力图显得很乱,这里没有显示这两个特征与其他特征的相关系数。

sns.heatmap(fullDf.iloc[0:10000, [0,1,2,3,4,5,6,7,8,29]].corr(), annot = True, vmin = 0, vmax = 1)

特征之间的相关系数热力图

可以看到与房价的相关系数在0.1以上的几个特征为:卧室数、浴室数、房屋面积、建筑面积、房屋评价、地下室面积、是否有地下室。

2. 迭代特征选择

假设最后选择的特征集合为X',所有特征的集合为X,辅助特征集合为X”。

初始化X’=空集,X”=X;对于X”中的所有特征,每次取出一个进行训练,选出一轮中得分最高的那个特征加入X',同时在X"中删除该特征;重复第二步,直到新加入任何特征模型性能都无提升

# 引入线性回归模型和交叉检验from sklearn import linear_modelfrom sklearn.model_selection import cross_val_score

lm = linear_model.LinearRegression()

df = fullDf[0:10000]

features = ['BasementArea', 'BathroomNum', 'BedroomNum', 'BuildingArea',

'Latitude', 'LivingArea', 'Longitude', 'Rating',

'Year_(1900, 1915]', 'Year_(1915, 1930]', 'Year_(1930, 1945]',

'Year_(1945, 1960]', 'Year_(1960, 1975]', 'Year_(1975, 1990]',

'Year_(1990, ]', 'Year_(, ]', 'Month_1', 'Month_2',

'Month_3', 'Month_4', 'Month_5', 'Month_6', 'Month_7', 'Month_8',

'Month_9', 'Month_10', 'Month_11', 'Month_12', 'HasBasement']

y = df['SalePrice']

selected_features = []

rest_features = features[:]

best_score = -1e+12

'''

交叉检验的评分标准选择'neg_mean_squared_error',即均方误差,在这里为负数,绝对值越小表示误差越小,

因此初始的best_score设置为一个绝对值很大的负值

'''

while len(rest_features)>0:

temp_best_i = ''

temp_best_score = -1e+12

for feature_i in rest_features:

temp_features = selected_features + [feature_i]

X = df[temp_features]

scores = cross_val_score(lm, X, y, cv=5, scoring='neg_mean_squared_error')

score = np.mean(scores)

if score > temp_best_score:

temp_best_score = score

temp_best_i = feature_i

print("select",temp_best_i,"acc:",temp_best_score)

if temp_best_score > best_score:

best_score = temp_best_score

selected_features += [temp_best_i]

rest_features.remove(temp_best_i)

else:

break

print("best feature set: ",selected_features,"score: ",best_score)

以下为输出结果

select LivingArea acc: -71349166475.62779

select Latitude acc: -61308736701.538574

select Rating acc: -57228772728.23087

select Longitude acc: -55217175788.554344

select Year_(1900, 1915] acc: -54266373901.291626

select Year_(1915, 1930] acc: -53265361117.21611

select Year_(1930, 1945] acc: -52314470279.37959

select Year_(1945, 1960] acc: -51128635898.9767

select BedroomNum acc: -50497821841.750175

select Year_(1960, 1975] acc: -49859660488.61521

select BathroomNum acc: -49271438161.992

select Month_4 acc: -49167258638.85094

select Month_3 acc: -49102030213.97478

select Year_(1990, ] acc: -49067174888.75468

select Month_12 acc: -49049580993.40038

select Year_(, ] acc: -49034235274.66737

select Year_(1975, 1990] acc: -48798669332.794945

select HasBasement acc: -48793266204.60174

select Month_7 acc: -48789312424.92695

select Month_9 acc: -48788246493.10238

select Month_10 acc: -48793653843.01216

best feature set: ['LivingArea', 'Latitude', 'Rating', 'Longitude', 'Year_(1900, 1915]', 'Year_(1915, 1930]', 'Year_(1930, 1945]', 'Year_(1945, 1960]', 'BedroomNum', 'Year_(1960, 1975]', 'BathroomNum', 'Month_4', 'Month_3', 'Year_(1990, ]', 'Month_12', 'Year_(, ]', 'Year_(1975, 1990]', 'HasBasement', 'Month_7', 'Month_9'] score: -48788246493.10238

迭代法选择出的特征为房屋面积、经纬度、房屋评价、建筑年份、卧室数、浴室数、销售月份、是否有地下室。

3. 随机森林特征选择法——Gini Importance

使用Gini指数表示节点的纯度,Gini指数越大纯度越低。然后计算每个节点的Gini指数 - 子节点的Gini指数之和,记为Gini decrease。最后将所有树上相同特征节点的Gini decrease加权的和记为Gini importance,该数值会在0-1之间,该数值越大即代表该节点(特征)重要性越大。

from sklearn.model_selection import train_test_splitfrom sklearn.ensemble import RandomForestRegressor

X为数据集的特征,y为数据集的标签

X = fullDf[:][0:10000]

X.drop('SalePrice', axis=1, inplace=True)

y = trainDf['SalePrice']

将数据集分为训练集和测试集

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

rfReg = RandomForestRegressor(n_estimators=50)

rfReg.fit(X_train, y_train)

combine_lists = lambda item: [item[0], item[1]]

feature_importances = list(map(combine_lists, zip(X_train.columns, rfReg.feature_importances_)))

feature_importances = pd.DataFrame(feature_importances, columns=['feature', 'importance']).sort_values(by='importance', ascending=False)

feature_importances #输出每个特征的Gini Importance,并按从大到小的顺序排序

每个特征的Gini Importance大小

根据以上三种特征选择的结果,最后选取地下室面积、浴室数、卧室数、建筑面积、经纬度、房屋面积、房屋评价、建筑年份、销售月份这几个特征,删除是否有地下室的特征。

fullDf.drop('HasBasement', axis=1, inplace=True)

算法选择

选择线性回归算法和随机森林算法,评价指标选择决定系数(R Squared)

from sklearn.metrics import r2_score

X = fullDf[:][0:10000]

X.drop('SalePrice', axis=1, inplace=True)

y = trainDf['SalePrice']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

rfReg = RandomForestRegressor(n_estimators=50)

lReg = linear_model.LinearRegression()

rfReg.fit(X_train, y_train)

lReg.fit(X_train, y_train)

print('RandomForest_R2: {}nLinearRegression_R2: {}'.format(r2_score(y_test, rfReg.predict(X_test)),

r2_score(y_test, lReg.predict(X_test))))

以下为输出结果

RandomForest_R2: 0.8329728730385538

LinearRegression_R2: 0.6319265123160838

从两种模型的决定系数可以看出,随机森林算法比线性回归算法的效果要好。因此选择随机森林算法对测试集进行预测。

房价预测

使用随机森林算法对测试集进行预测。由于选择的某些特征量纲不一样,它们的单位不在一个数量级上,可能会导致有的特征被忽略,为了消除量纲的影响,对特征进行归一化。另外,由于房屋价格的分布呈右偏态,对其取对数,使其分布接近正态分布。

from sklearn.preprocessing import MinMaxScaler

tempDf = fullDf[:]

tempDf.drop('SalePrice', axis=1, inplace=True) # tempDf为测试集和训练集中不包含房价的所有特征

minmax_scaler = MinMaxScaler()

minmax_scaler.fit(tempDf)

X = minmax_scaler.transform(tempDf)

X = pd.DataFrame(X, columns=tempDf.columns) # X为测试集和训练集归一化后的所有特征

new_X = X[:][0:10000] # new_X为训练集的所有特征

y = np.log(trainDf['SalePrice']) # y为取对数后的房价

predict_X = X[:][10000:] # predict_X为测试集的所有特征

rfReg = RandomForestRegressor(n_estimators=50)

rfReg.fit(new_X, y)

predict_y = rfReg.predict(predict_X)

predict_y = np.exp(predict_y) # 将房价由对数形式还原

predictDf = pd.DataFrame(predict_y, columns=['price'])

predictDf.to_csv('predict.csv', index = False) # 将结果转化成规定的形式输出到csv文件中

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。