分析北京链家房价数据并对北京房价进行预测

本次课程设计主题为数据分析与机器学习,选择分析北京链家房价数据,通过数据分析,了解整个数据情况,然后使用机器学习算法Lasso选择特征,GBDT回归算法建立模型,对北京房价进行预测。

1694338663996

图1 数据部分展示

整个数据建模的过程为:1、读取数据,了解数据;2、数据预处理;3、使用Lasso回归选择属性;4、建立GBDT回归模型;5、使用新数据进行预测。

  1. 读取数据

北京房价存储在new.csv文件中,通过pandas库进行数据的读取,过程如图2所示,接着查看数据前五行,并对数据信息进行查看,过程如图3和图4所示,然后查看数据类型dtype等于0的列及其信息,过程如图5所示,删除Cid,id,url等不需要的数据列,查看数据缺失值百分比,过程如图6所示,然后进行数据类型的转换,接着删除缺失值,过程如图2所示。

图2 读取数据

图3 数据的描述统计

图4 查看数据信息

图5 输出dtype等于0的列及其信息

图6 数据缺失值的百分比

绘制DOM列的箱线图和分布图,结果如图7 和图8所示,并用中位数填充DOM列的空值,显示DOM列的百分位数,结果如图9所示。

IMG_256

图7 DOM列箱线图

IMG_256

图8 DOM列分布图

图9 DOM列百分位数

  1. 数据预处理

接着合并数据,并展示数据,结果如图10和图11所示,输出数据的维度和属性个数,结果如图12所示,对livingRoom、DOM等列转换数据类型,转换为指定的数据类型,然后进行特征编码,对房屋类型、装修、是否有电梯等数据进行转换,然后重新设置索引,过程如图13所示,显示转换的 数据类型,结果如图14所示,显示数据的唯一值,结果如图15所示。然后对totalPrice列进行分析,按区域划分,计算每个区域totalPrice的最大值、最小值、均值等,过程如图16所示,然后删除Lat、Lng列,并将清洗后的数据导出到csv文件中,展示清洗的数据,过程如图18所示,然后显示编码后的数据形状,过程如19所示。

图10 合并之后的数据

图11 显示数据信息

图12 数据的唯一值

图13转换类型

图14 转换后的数据

图15 数据的唯一值

图16 每个区域总价的统计信息

图17 显示数据信息

图18 清洗之后的数据并展示

  1. 使用Lasso回归选择属性

在进行建立模型之前,还需要选择和totalPrice最相关的属性,这里通过Lasso回归选择和总价最相关的属性,选择Lasso回归系数绝对值大于0的属性。在选择之前,需要先对特征进行编码,因为文本类型的数据无法参与建模,因此先使用独热编码对特征进行编码,编码后的数据形状如图19所示,然后拆分数据集为训练集和测试集,结果如图20所示,再使用Lasso回归建立模型,输出回归模型得分,如图21和图22所示所示,选择Lasso回归系数的绝对值大于0的属性,并输出选择的变量名,结果如图23所示,然后统计选择的变量个数,结果如图24所示。

图19 编码之后的数据形状

图20 拆分数据之后特征和标签的形状

图21 Lasso回归选择得分

 

图22 Lasso回归得分

图23 Lasso回归选择的变量名

图24 选择的变量个数与维度

 

  1. 建立GBDT模型

在选择完属性之后,然后就可以建立GBDT模型,对北京房价进行拟合了,GBDT模型已经集成在了sklearn库当中,因此直接调用相关函数就可以建立相关模型,在建模之前,也要先对特征进行编码,编码之后拆分数据集为训练集和测试集,结果如图25所示,建立模型之后,直接输出模型的score,过程如图26所示,然后保存模型,使用pickle模块,将模型等信息保存到sav文件中。

图25 编码之后数据的形状和拆分后的形状

图26 GBDT模型得分

  1. 对新的数据进行预测

在保存模型之后,可以对模型等信息进行读取,然后对新的数据进行预测,过程如图27所示。

图27 GBDT模型预测值

#!/usr/bin/env python
# coding: utf-8

import sys
print(sys.executable)

## 导入库
import numpy as np
import pandas as pd
import re
import datetime as dt
import pickle
import warnings
import matplotlib.pyplot as plt
import seaborn as sns

## 导入sklearn模型
from sklearn.linear_model import Ridge, Lasso
from sklearn import metrics
from sklearn.model_selection import train_test_split,cross_val_score
from sklearn.preprocessing import LabelEncoder, scale, OneHotEncoder
from sklearn.ensemble import GradientBoostingRegressor

## 设置
warnings.filterwarnings('ignore') 
plt.style.use(['seaborn-pastel'])

# 读取数据
beijing = pd.read_csv("new.csv", parse_dates=["tradeTime"], encoding='gb2312')
print(beijing.columns.values)
beijing.head()

# 数据描述信息
beijing.describe()

# 删除不需要的数据列
beijing.drop("Cid", axis = 1, inplace=True)
beijing.drop("id", axis = 1, inplace=True)
beijing.drop("url", axis = 1, inplace=True)
beijing.drop("price", axis = 1, inplace=True)

# 显示数据信息
beijing.info()

# ## 数据清洗

# 整理变量

for row in range(beijing.shape[1]):
    if(beijing.iloc[:,row].dtype=="O"):
        print("{}: {}\n".format(beijing.columns[row],beijing.iloc[:,row].unique()))

#提取第11列中的数字
beijing.iloc[:,11] = np.array(beijing.iloc[:,11].str.extract("([0-9]+)")).reshape(-1,1)

#列表
sel = [7,8,10,11,13]
#遍历列表,把7,8,10,11,13列的数据转换成数值型数据
for s in sel:
    beijing.iloc[:,s] = pd.to_numeric(beijing.iloc[:,s], errors='coerce')

# 删除缺失值
beijing.isna().mean()*100

# 对DOM列进行数据可视化

plt.clf()
sns.boxplot(y = beijing["DOM"])
plt.xlabel("DOM")
plt.ylabel("distribution")
plt.show()

plt.clf()
sns.kdeplot(data = beijing.loc[:,"DOM"])
plt.show()

beijing["DOM"].quantile([0.25,0.5,0.75,1])

# DOM列的缺失值使用DOM列的中位数来填充

beijing["DOM"] = beijing["DOM"].fillna(beijing["DOM"].median())


# 删除有缺失值的行
beijing.dropna(axis = 0, how = "any", inplace = True)

# 转换列的类型
beijing = beijing.astype({"DOM":"int64","livingRoom":"int64","drawingRoom":"int64","bathRoom":"int64","floor":"int64",
                          "buildingType":"O","constructionTime":"int64","renovationCondition":"O","buildingStructure":"O",
                          "elevator":"O", "fiveYearsProperty":"O","subway":"O","district":"O"}, errors = 'ignore')
# 显示数据信息
beijing.info()

# 删除totalPrice列
feature_df = beijing.drop("totalPrice", axis = 1)
# 取出totalPrice列
response_df = beijing["totalPrice"]
#与feature_df合并totalPrice列
beijing = feature_df.merge(response_df, left_index = True, right_index = True)
#显示数据前五行
beijing.head()

# 显示数据信息
beijing.info()
#输出数据信息
print("Shape of dataframe:    {}".format(beijing.shape))
print(beijing.apply(lambda x: x.unique()))

# 转换数据类型
beijing = beijing.apply(lambda x: x.astype("int64") if(x.dtype =="O") else x)

# 数据编码
# 房屋类型
buildingType = {
    1:"Tower",
    2:"Bunglow",
    3:"Plate/Tower",
    4:"Plate"
}
#装修类型
renovationCondition = {
    1:"Other",
    2:"Rough",
    3:"Simplicity",
    4:"Hardcover"
}
#装修结构
buildingStructure = {
    1:"Unavailable",
    2:"Mixed",
    3:"Brick/Wood",
    4:"Brick/Concrete",
    5:"Steel",
    6:"Steel/Concrete"
}
#电梯
elevator = {
    1:"Present",
    0:"Absent"
}
#地铁
subway = {
    1:"Nearby",
    0:"Far"
}
#产权
fiveYearProperty = {
    1:"Ownership<5y",
    0:"Ownership>5y"
}
#行政区域
district = {
    1 : "DongCheng",
    2 : "FengTai",
    3 : "DaXing",
    4 : "FaXing",
    5 : "FangShang",
    6 : "ChangPing",
    7 : "ChaoYang",
    8 : "HaiDian",
    9 : "ShiJingShan",
    10 : "XiCheng",
    11 : "TongZhou",
    12 : "ShunYi",
    13 : "MenTouGou"
}
#更改标签
correct_label = {
    11:buildingType,
    13:renovationCondition,
    14:buildingStructure,
    16:elevator,
    17:fiveYearProperty,
    18:subway,
    19:district
}

#遍历更改的标签,并输出信息
for key,val in correct_label.items():
    print(key,val)

# 把装修类型、行政区域等列的数据更改标签
for key,val in correct_label.items():
    beijing.iloc[:,key] = beijing.iloc[:,key].replace(val)

#输出数据信息
print(beijing.shape)
print(beijing.apply(lambda x: x.unique()))

# 更改数据标签,把livingRoom变成0,drawingRoom变成0..
beijing = beijing.astype({"livingRoom":"O","drawingRoom":"O","bathRoom":"O","kitchen":"O"}, errors = 'ignore')

# 重新设置索引
beijing.reset_index(inplace=True, drop = True)

# 理解数据
names = beijing.columns#列名
#区分数据类型
cat_idx = []#新建空列表
num_idx = []#新建空列表
for i in range(20):#i的范围从0到19
    column = beijing.iloc[:,i]#使用iloc定位第i列
    if(column.dtype=="O"):#如果列的类型为0
        cat_idx.append(i)#把i存入cat_idx列表
        print("{}\nUnique values:\n{}\n".format(names[i],column.unique()))#输出信息
    elif(column.dtype!='<M8[ns]'):#如果列的类型为<M8[ns]
        num_idx.append(i)#把i存入num_idx列表

beijing.iloc[:,num_idx[2:]].describe()#提取num_idx从第三列开始的数据,显示统计信息

beijing.iloc[:,cat_idx].describe()#提取cat_idx的数据,显示统计信息

# 按照行政区域划分,求房屋总价的均值、最小值、最大值、中位数
temp = beijing["totalPrice"].groupby(beijing["district"]).agg([np.mean,np.min,np.max,np.median])
temp

# 删除Lat列
beijing.drop("Lat", axis = 1, inplace=True)
#删除Lng列
beijing.drop("Lng", axis = 1, inplace=True)

#显示数据信息
beijing.info()

#把dtype数据导出为字典
dataClasses = beijing.dtypes.to_dict()

#导出清洗之后的数据
beijing.to_csv('cleaned_beijing.csv', encoding='utf-8')

# 数据预处理
#读取清洗之后的数据
beijing = pd.read_csv("./cleaned_beijing.csv", parse_dates=["tradeTime"], index_col = 0, encoding='utf-8')
beijing.head()
#转换数据
beijing = beijing.astype(dataClasses, errors = 'ignore')

#数据dtype等于object的列
cat_mask = beijing.dtypes == object
#提取数据dtype等于object的列
cat_cols = beijing.columns[cat_mask].tolist()
#独热编码
l_enc = OneHotEncoder(handle_unknown='ignore', sparse = False)
l_enc.fit(beijing[cat_cols])
def encode_df(df, enc, cat_cols):#对数据列进行编码
    encoded_array = enc.transform(df[cat_cols])#对cat_cols列进行独热编码
    #转换为DataFrame列
    encoded_df = pd.DataFrame(encoded_array, columns = enc.get_feature_names(input_features = cat_cols))
    #删除totalPrice列
    feature_df = df.drop("totalPrice", axis = 1)
    response_df = df["totalPrice"]#取出totalPrice列
    #合并feature_df和独热编码之后的数据
    feature_df = feature_df.merge(encoded_df, left_index = True, right_index = True).drop(columns = cat_cols, axis=1)
    #合并feature_df和totalPrice列
    df = feature_df.merge(response_df, left_index = True, right_index = True)
    df['tradeTime'] = df['tradeTime'].astype('datetime64[ns]')#转换数据
    df['tradeTime'] = df['tradeTime'].map(dt.datetime.toordinal)#转换数据
    return(df)#返回数据

#调用encode_df函数
l_beijing = encode_df(beijing, l_enc, cat_cols)

# 数据形状
l_beijing.shape

# 数据建模

def split_df(df):#拆分数据
    X = df.drop("totalPrice", axis = 1).values#删除totalPrice列,当成x
    y = df["totalPrice"].values.reshape(-1,1)#取出totalPrice,当成Y
    return(X,y)#返回数据

X,y = split_df(l_beijing)#调用split_df函数
print(X.shape,y.shape)#输出数据形状

# 拆分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=193)

# 使用Lasso回归进行特征选择
#建立Lasso模型
lasso = Lasso(alpha = 0.005, normalize = True)
#拟合数据
lasso.fit(X_train, y_train)
#对数据预测
lasso_pred = lasso.predict(X_test)
#输出得分
score = lasso.score(X_test, y_test)
print("Lasso特征选择模型得分:" + str(score))

# 拟合数据
lasso_coef = lasso.fit(X, y).coef_

# 取出0到67的数据属性
names_coef = [x for x in l_beijing.columns[0:68]]
#绘制图像
plt.clf()
plt.figure(figsize=(20,10))
plt.plot(range(len(names_coef)), lasso_coef)
plt.xticks(range(len(names_coef)), names_coef, rotation = 90)
for i in range(0,68):
    plt.axvline(x = i, color = "gray", linestyle='--', alpha = 0.4)
plt.axhline(y = 0, color = "skyblue")
plt.ylabel("Coefficients")
plt.show()

# 选择系数绝对值大于0的变量
s_vars = np.where(abs(lasso_coef)>0)[0].tolist()

# 输出选择的变量
for i in s_vars:
    print("{} ".format(names_coef[i],lasso_coef[i]))

# 选择的变量名
s_var_names = ['tradeTime', 'square','communityAverage', 'livingRoom', 'drawingRoom',
               'kitchen','bathRoom', 'buildingType', 'renovationCondition', 'buildingStructure', 
               'elevator','fiveYearsProperty', 'subway','district']
len(s_var_names)

#定位选择的变量名
beijing_subset = beijing.loc[:,s_var_names+['totalPrice']]
print(beijing_subset.shape)

#选择dtype等于object的数据
cat_mask = beijing_subset.dtypes == object
#选择列名
cat_cols = beijing_subset.columns[cat_mask].tolist()
#独热编码
r_enc = OneHotEncoder(handle_unknown='ignore', sparse = False)
r_enc.fit(beijing_subset[cat_cols])
r_beijing = encode_df(beijing_subset, r_enc, cat_cols)
print("Encoded data:")
print(r_beijing.shape)

# 拆分数据
X, y = split_df(r_beijing)
print(X.shape,y.shape)

#拆分数据集为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=193)

# 使用正则化Ridge回归建模
model_parameters = {
    'n_estimators': 500,
    'max_depth': 6,
    'min_samples_split': 5,
    'learning_rate': 0.01,
    'loss': 'ls'
}
#GBDT回归
gbReg = GradientBoostingRegressor(**model_parameters)

#拟合模型
gbReg.fit(X_train, y_train)


score= gbReg.score(X_test, y_test)
print("gbdt回归模型得分:" + str(score))

#存储模型
filename = 'sklearn_model.sav'
encoder_name = 'one_hot_encoder.sav'
pickle.dump(gbReg, open(filename, 'wb'))
pickle.dump(r_enc, open(encoder_name, 'wb'))

# 加载模型
mdl = pickle.load(open(filename, 'rb'))
enc = pickle.load(open(encoder_name, 'rb'))
mdl.score(X_test, y_test)

beijing.loc[1,s_var_names+['totalPrice']]

df_dict = {
    'tradeTime':'2023-07-28',
    'square':132.38,
    'communityAverage':71539,
    'livingRoom':'2',
    'drawingRoom':'2',
    'kitchen':'1',
    'bathRoom':'2',
    'buildingType':'Tower',
    'renovationCondition':'Hardcover',
    'buildingStructure':'Steel/Concrete',
    'elevator': 'Present',
    'fiveYearsProperty':'Ownership<5y',
    'subway': 'Far',
    'district':'ChaoYang',
    'totalPrice': 530
}    
#创建数据
df = pd.DataFrame(df_dict, index = [0])
#编码
df_enc = encode_df(df, enc, cat_cols)
#拆分数据
X, y = split_df(df_enc)
print(X.shape,y.shape)
print("Predicted value : {}\nActual value    : {}".format(mdl.predict(X)[0], y[0][0]))

# EDA

df = beijing.loc[:,s_var_names+['totalPrice']]

print(np.quantile(df["totalPrice"], [0.25,0.5,0.75]))

 

posted @ 2023-09-12 18:42  rizel  阅读(437)  评论(0)    收藏  举报