数据集及源码 [ https://github.com/JCATHoney/python-data-analysis
](https://github.com/JCATHoney/python-data-analysis)
一、问题描述
在对房价的影响因素进行模型研究之前,首先对各变量进行描述性分析,以初步判断房价的影响因素,进而建立房价预测模型
总体步骤如下: (一) 因变量分析:单位面积房价分析
(二) 自变量分析: 2.1 自变量自身分布分析 2.2 自变量对因变量影响分析
(三)建立房价预测模型 3.1 线性回归模型 3.2 对因变量取对数的线性模型 3.3 考虑交互项的对数线性
(四)预测: 假设有一家三口,父母为了能让孩子在东城区上学,想买一套邻近地铁的两居室,面积是70平方米,中层楼层,那么房价大约是多少呢?
二、数据集
研究二手房价的影响因素,建立房价预测模型,数据存放在“sndHsPr.csv”中。
变量说明如下:
变量 | 含义 |
---|---|
dist | 所在区 |
roomnum | 室的数量 |
halls | 厅的数量 |
AREA | 房屋面积 |
floor | 楼层 |
subway | 是否临近地铁 |
school | 是否学区房 |
price | 平米单价 |
三、描述性统计
3.1数据预处理
-
主要关注金额,日期等字段的处理
-
把握整体样本量,后续合理采样
-
将城区变量转换为中文
import pandas as pd import numpy as np import math import matplotlib.pyplot as plt import matplotlib import seaborn as sns import statsmodels.api as sm from numpy import corrcoef,array #from IPython.display import HTML, display from statsmodels.formula.api import ols import os os.chdir(r"C:\Users\Away\Desktop\笔记\数据分析") # In[1]: import pandas as pd mdata = pd.read_csv(r'sndHsPr.csv') mdata.head() #%% #1、描述性统计数据预处理阶段 describe=mdata.describe()#价格基本情况 # count 16210.000000 # mean 61151.810919 # std 22293.358147 # min 18348.000000 # 25% 42812.250000 # 50% 57473.000000 # 75% 76099.750000 # max 149871.000000 print(mdata.shape[0])#数据量:16210 这里为什么要统计数据量呢?因为做检验时数据量不能过大,一般不超过5000,否则p值会失效 #%% ## 1、描述性统计 #数据简单处理,重点关注价格日期等字段 #查看各个字段基本情况 data0 = mdata describe=data0.describe(include="all").T #print(data0.dtypes) #价格转换为万元 data0.price = data0.price/10000 #type_dict = {'借':'out','贷':'income'} #card_t3['type1'] = card_t3.t_type.map(type_dict) #将区域名转换为中文 dist_dict = { 'chaoyang' : "朝阳", 'dongcheng' : "东城", 'fengtai' : "丰台", 'haidian' : "海淀", 'shijingshan' : "石景山", 'xicheng': "西城" } data0['dist']=data0.dist.map(dist_dict)
_中文和负号的显示: _
matplotlib.rcParams['axes.unicode_minus']=False#解决保存图像时负号'-'显示为方块的问题 plt.rcParams['font.sans-serif'] = ['SimHei']#指定默认字体 ,解决不能显示中文字体的问题#
3.2 变量描述性分析
-
因变量 price
#因变量图形(主要看看形态) data0.price.plot(kind='hist',bins=20,color='lightblue')#右偏严重 plt.xlabel("单位面积房价(万元/平方米)") plt.ylabel("频数") print(data0.price.agg(['mean','median','std'])) #查看price的均值、中位数和标准差等更多信息 print(data0.price.quantile([0.25,0.5,0.75])) pd.concat([(data0[data0.price==min(data0.price)]),(data0[data0.price==max(data0.price)])])#查看房价最高和最低的两条观测
2.自变量
-
首先对自变量进行分类,同一类的统一分析(for)
-
除了AREA(房屋面积),都是分类变量。
分类变量,主要用条形图、盒须图
##1.2、自变量描述(除了房屋面积都是分类变量) for i in range(7): if i!=3: print(data0.columns.values[i],":") print(data0[data0.columns.values[i]].agg(['value_counts']).T) print('==================================') else: continue print('AREA:') print(data0.AREA.agg(['min','mean','max','median','std']).T) #%% #1.2.1 dist 区域 多分类变量 #频次统计 data0.dist.value_counts().plot(kind='bar') #%% #不同城区的房价 data0.price.groupby(data0.dist).mean().sort_values(ascending=True).plot(kind='barh')#能看出来和城区还是有关系的 #%% data1 = data0[['dist','price']] sns.boxplot(x='dist',y='price',data=data1) #dat1.boxplot(by='dist',patch_artist=True) plt.ylabel("单位面积房价(万元/平方米)") plt.xlabel("城区") plt.title("城区对房价的分组箱线图")#城区还是有明显影响的 #%% #1.2.2 roomnum 卧室数-roomnum 多分类 data2=data0[['roomnum','price']] data2.price.groupby(data2.roomnum).mean().plot(kind='bar') #data2.boxplot(by='roomnum',patch_artist=True)# patch_artist 是否填充箱体的颜色; plt.figure() sns.boxplot(x='roomnum',y='price',data=data2)#没啥关系
部分结果:
连续变量,相关分析: 注意点:
-
直方图呈现右偏,进行取对数操作
-
第一次对y取对数后|r|反而降了,所以将x也取对数
-
最后的达到的效果要使在x,y两个方向都是呈现正态分布
# 1.2.7 AREA 连续变量 dataA=data0[['AREA','price']] plt.scatter(dataA.AREA,dataA.price,marker='.')#发散型,右偏-对Y取对数 # area,price都是连续变量,进行相关分析 xg1=dataA[['price','AREA']].corr(method='pearson')#返回一个dataframe# r=-0.074 中度负相关。 #对Y取对数 dataA['price_ln'] = np.log(dataA['price']) plt.figure(figsize=(8,8)) plt.scatter(dataA.AREA,dataA.price_ln,marker='.') plt.ylabel("单位面积房价(取对数后)") plt.xlabel("面积(平方米)")#小户型贵 #求AREA_ln和price_ln的相关系数 xg3=dataA[['price_ln','AREA']].corr(method='pearson')#|r|=0.058 #房屋面积和单位面积房价(取对数后)的散点图 dataA['price_ln'] = np.log(dataA['price']) #对price取对数 dataA['AREA_ln'] = np.log(dataA['AREA']) #对price取对数 plt.figure(figsize=(8,8)) plt.scatter(dataA.AREA_ln,dataA.price_ln,marker='.') plt.ylabel("单位面积房价(取对数后)") plt.xlabel("面积(平方米)") #求AREA_ln和price_ln的相关系数矩阵 data1=array(dataA['price_ln']) data2=array(dataA['AREA_ln']) datB=array([data1,data2]) corrcoef(datB)# 0.09,高度相关,且x,y两个方向都是正态分布
初始散点图 对x,y取对数后的散点图:
-
目前结论: 明显影响:区,地铁,学区 不明显:客厅,楼层 基本不影响:卧室(roomnum)
-
描述性统计结束,提出我们的假设(学区房贵,靠近地铁贵。。。。),后面就开始假设检验分析,验证结论
四、建模
-
采样:根据城区分层抽样,每区400,定阈值
data_new = get_sample(data0,sampling='stratified',k=400,stratified_col=['dist']) #2400个样本,阈值确定原则如下: """大致原则如下(自然科学取值偏小、社会科学取值偏大): n<100 alfa取值[0.05,0.2]之间 100<n<500 alfa取值[0.01,0.1]之间 500<n<3000 alfa取值[0.001,0.05]之间 """
-
方差分析逐个检查分类变量的解释力度 注意点:对于分类变量回归分析加上C(分类变量),否则自动识别为连续变量。但是也可以自己创建哑变量(自己选择基准)
#逐个检查分类变量的解释力度,方差分析 import statsmodels.api as sm from statsmodels.formula.api import ols print("dist的P值为:%.4f" %sm.stats.anova_lm(ols('price ~ C(dist)',data=data_new).fit())._values[0][4]) print("roomnum的P值为:%.4f" %sm.stats.anova_lm(ols('price ~ C(roomnum)',data=data_new).fit())._values[0][4])#明显高于0.001->不显著->独立 print("halls的P值为:%.4f" %sm.stats.anova_lm(ols('price ~ C(halls)',data=data_new).fit())._values[0][4])#高于0.001->边际显著->暂时考虑 print("floor的P值为:%.4f" %sm.stats.anova_lm(ols('price ~ C(floor)',data=data_new).fit())._values[0][4])#高于0.001->边际显著->暂时考虑 print("subway的P值为:%.4f" %sm.stats.anova_lm(ols('price ~ C(subway)',data=data_new).fit())._values[0][4]) print("school的P值为:%.4f" %sm.stats.anova_lm(ols('price ~ C(school)',data=data_new).fit())._values[0][4]) ''' dist的P值为:0.0000 roomnum的P值为:0.1014 #去掉roomnum,其他保留 halls的P值为:0.0002 floor的P值为:0.0013 subway的P值为:0.0000 school的P值为:0.0000 '''
-
结果处理: 去掉roomnum 对与不太显著的变量“halls”做因子化处理变成二分变量
#%% # 厅数和楼层的影响不太显著。对于厅数可以做因子化处理,变成二分变量('有厅','无厅') data_new['hall_new'] = data_new.halls data_new.hall_new[data_new.hall_new>0]='有厅' data_new.hall_new[data_new.hall_new==0]='无厅'
3、线性回归模型
# 3 线性回归模型 lm1 = ols("price ~ C(dist)+school+subway+C(floor)+C(hall_new)++AREA", data=data_new).fit()#这里也可以自己设计基准创建哑变量 lm1_summary = lm1.summary() lm1_summary #回归结果展示1 R方=0.599 开始忘了加hall_new 发现加了对模型没啥改进作用
-
残差分析
#初始线性回归模型残差分析 data_new['pred1']=lm1.predict(data_new)#模型预测结果 data_new['resid1']=lm1.resid#取残差 data_new.plot('pred1','resid1',kind='scatter') #模型诊断图,存在异方差现象,对因变量取对数 #预测值增加,残差呈现喇叭口发散状况。考虑取对数
改进前残差分析,异方差现象 3.1取对数的线性模型
# 3.1 改进取对数后再次建模 data_new['price_ln'] = np.log(data_new['price']) data_new['AREA_ln'] = np.log(data_new['AREA']) lm2 = ols("price_ln ~ C(dist)+school+subway+C(floor)+AREA_ln", data=data_new).fit() lm2_summary = lm2.summary() lm2_summary #回归结果展示2 R方=0.614 有一丢丢提升 #%% #残差分析 data_new['pred2']=lm2.predict(data_new)#模型预测结果 data_new['resid2']=lm2.resid#取残差 data_new.plot('pred2','resid2',kind='scatter')
异方差现象改善 3.2 加上交互项的非线性模型
-
思考:为什么要叫交互项? 各个因变量对自变量的影响也不是独立的,比如采样分析后发现石景上区的学区房比非学区房还便宜,与其他地方都相反。地区这个变量就和学区这个变量之间有交互作用*
schools=['石景山','丰台','朝阳','东城','海淀','西城'] for i in schools: print(i+'非学区房\t',round(data_new[(data_new['dist']==i)&(data_new['school']==0)]['price'].mean(),2),'万元/平方米\t',i+'学区房\t',round(data_new[(data_new['dist']==i)&(data_new['school']==1)]['price'].mean(),2),'万元/平方米')
# 3.2 加上交互项对数模型 lm3 = ols("price_ln ~ C(dist)*school+subway+C(floor)+AREA_ln", data=data_new).fit() lm3_summary = lm3.summary() lm3_summary #回归结果展示 R方=0.618
预测
#选一个好的模型来做预测 x_new1=data_new.head(1) x_new1 #%% x_new1['dist']='东城' x_new1['roomnum']=2 x_new1['AREA_ln']=np.log(80) x_new1['subway']=1 x_new1['school']=1 x_new1['floor']='middle' x_new1['hall_new']="有厅" #%% #预测值 print("单位面积房价:",round(math.exp(lm5.predict(x_new1)),2),"万元/平方米") print("总价:",round(math.exp(lm5.predict(x_new1))*70,2),"万元")
结果: 单位面积房价: 7.8 万元/平方米 总价: 545.99 万元
好贵啊。。。(〒︿〒)