0.前言

沪上即将毕业党,面临着租房的压力(为什么不是买房?呵呵呵呵你想什么呢。。。。),因此想收集下上海租房信息。比较了几个网站,发现链家数据较易爬取,格式工整,维度丰富,因此,从链家入手,用Python进行数据采集,用R语言对采集后的信息进行数据分析,最后用K-Means聚类算法对房源分类并对租金进行建模,以期真正将所学用到所用之处。(哈哈哈哈哈,但愿~~)
注意:本文只针对初入数据分析的小小白,大神可以绕道~

1.数据获取

首先打开链家网站,进入租房板块[https://sh.lianjia.com/zufang/]

总共38667套出租房源。在列表中信息也算比较丰富了,有求租者最关心的区域、房型、面积、朝向、楼层、租金以及图片等信息。是否需要抓取这部分信息呢?别急,点开列表中某一个房源,我们会发现主页内提供的信息更为齐全,因此确定我们提取的信息来源为房源主页。

(1)工作环境

我用的是Python 3.X版本(忘了具体哪个版本了。。)。建议用chrome打开网页,因为chrome的开发者工具(网页右击“检查”)能大大方便数据的爬取,用Jupyter notebook作为IDE。在Jupyter notebook工作前,需要安装几个爬虫要用的库。打开DOS,依次输入:

pip install requests
pip install BeautifulSoup4
pip install Jupyter notebook
Jupyter notebook

即可。

(2)爬取思路

通过观察网址,发现每个房源的网址信息都 是https://sh.lianjia.com/zufang/xxx.html格式,其中xxx为房源编号。
三万多房源,当然不可能在一页内全部展示,这里理所当然采用了分页,页面上显示了前100个分页信息。通过观察每页的网址,发下每页的格式同样非常简单https://sh.lianjia.com/zufang/pgx/,其中x表示当前页数,比如点击https://sh.lianjia.com/zufang/pg56/就进入了分页56.

因此确定了爬取思路,非常简单,就是两个for循环,第一层循环为分页,从第1页到第100页;第二层循环为每个分页内30个房源,在这一层循环中爬取每个房源的信息。

(3)代码

首先导入需要的库库包包~

import requests
from bs4 import BeautifulSoup
import re

然后我写了一个自编函数,用来针对每一个房源,爬取相应信息:

def getHouseDetail(url):result={}res=requests.get(url)soup=BeautifulSoup(res.text,'html.parser')for info in soup.select('.overview'):otherinfo3=info.select('.zf-room')[0].textresult['小区名称']=otherinfo3[otherinfo3.find('小区:')+3:otherinfo3.find('\n',otherinfo3.find('小区:'))]result['面积']=float(otherinfo3[otherinfo3.find('面积:')+3:otherinfo3.find('平米')])result['户型']=otherinfo3[otherinfo3.find('户型:')+3:otherinfo3.find(' ',otherinfo3.find('户型:'))]result['区域']=otherinfo3[otherinfo3.find('位置:')+3:otherinfo3.find(' ',otherinfo3.find('位置:'))]result['楼层']=otherinfo3[otherinfo3.find('楼层:')+3:otherinfo3.find(' ',otherinfo3.find('楼层:'))]result['最高楼层']=int(otherinfo3[otherinfo3.find('共')+1:otherinfo3.find('层',otherinfo3.find('共'))])result['朝向']=otherinfo3[otherinfo3.find('朝向:')+3]if(len(info.select('.tips'))>0):result['装修情况']=info.select('.tips')[0].textelse:result['装修情况']='暂无数据'result['租金']=int(info.select('.total')[0].text)if(otherinfo3[otherinfo3.find('卫  ')+3:otherinfo3.find('卫  ')+5]=='整租'):result['出租方式']='整租'else:result['出租方式']='暂无数据'result['地铁']=otherinfo3[otherinfo3.find('地铁:')+3:otherinfo3.find('\n',otherinfo3.find('地铁:'))]#result['该小区出租数']=int(otherinfo3[otherinfo3.find('- ')+2:otherinfo3.find('套出租中')])  return result

本来想用正则表达式,然而才疏学浅,中文的匹配一直匹配不来,所以用了笨方法:字符串的索引+截取((⊙o⊙)…)
然后进入刚刚说的双层大循环。这里爬取了前100页信息,总共三千条数据~~

housedetail=[]
for n in range(1,101):res=requests.get('https://sh.lianjia.com/zufang/pg'+str(n)+'/')soup=BeautifulSoup(res.text,'html.parser')for info in soup.select('.pic-panel'):link=info.select('a')[0]['href']housedetail.append(getHouseDetail(link))
print(housedetail) 

然后数据就以列表的形式呈现了,如图,好像有点乱:

然后将list转为dataframe,这里使用了pandas这个超强悍的数据分析工具完成了一个小小的功能,哈哈哈,颇有高射炮打蚊子的赶脚~由于本文接下来用R进行数据分析,因此无缘再见了,在这里给pandas深深鞠一躬,来日方长,今后的数据分析后会有期~

import pandas
df = pandas.DataFrame(housedetail)
df

这样数据是不是好看多了,不像上面乱糟糟一团了^^

最后也是最重要的一步,就是把dataframe的文件保存成csv文件,方便后面导入R进行数据处理

df.to_csv('housedata.csv')

一行命令搞定。housedata.csv就是我们本次爬取的数据文件。

下面撸起袖子奋力干,进入数据分析的实操环节,该环节分为:
- 数据清洗和探索
- 模型建立


2.数据清洗和探索

(1)统计性描述

首先读取数据,并对数据进行一个描述性统计。

# 读取数据集,注意“地铁”和“小区名称”不能为factor
house <- read.csv("E:/zufangdata/housedata.csv",colClasses = c('地铁'='character','小区名称'='character'))
# 将X列丢弃
house <- house[,-1]
# 看下各个变量的情况
str(house)

比如我们可以大致看出,户型多达35种之多,装修情况分为4种等。
接下来对数据框进行描述性统计。注意:链家将缺失值标注为“暂无数据”。

(2)缺失值处理

图中可以看出,有些缺失值可能隐匿在“other”中,有些可以直观看到,比如“出租方式”缺失值占了大部分,“装修”缺失值占一半多,“朝向”缺失值占1/4左右。同时由于“地铁”和“小区名称”没有作为因子统计,因此缺失值情况不知道。因此考虑统一将“暂无数据”转化为NA,然后统计各列缺失情况。这里我们自定义一个统计每列缺失值数目的函数na.sum:

#定义一个统计字段缺失值数目的函数
na.sum <- function(x){x[x=='暂无数据'] <- NAresult = sum(is.na(x))return(result)
}

然后统计各列缺失值:

这样对缺失情况一目了然。这里可以猜想,“地铁”“朝向”和“装修情况”可能是因为房子在这几个字段上变现不佳,因此并未录入信息。

对于缺失值,比较粗糙的处理一般有以下几种方法:
(1)对于存在大量缺失值的变量直接剔除
(剔除变量的做法不会带来额外误差,简单粗暴,但是总觉得会心里隐隐不安~)
比如这里的“出租方式”缺失值太多,因此将该列删除。(同时经消息查证也可发现,链家租房都是整租,因此该列无参考价值)

house <- house[-1]

(2)对于存在非特别多缺失值的变量可以人工补齐
比如“朝向”和“装修情况”中可以将NA作为一个新因子。“地铁”字符型向量,后续考虑只保留地铁线路信息,比如对于“距地铁7号线长寿路638米”只保留7号线这一信息,因此“地铁”同样可以转化为因子,NA也可以作为一个新因子。对于数值型变量不存在缺失值情况。(如果存在可采用众数、中位数等方式补齐)这种做法貌似心安了一点,但是也会引入大量误差呵呵呵。。。

最后补充一个貌似精致点的做法:利用其它的变量去估计缺失变量的值,这样通常会更靠谱一点,当然也不能完全这样说,毕竟只要是估计值就不可避免的带来误差,但心理上总会觉得这样更好哈哈……

扯远了。下面对“地铁”字段信息信息重构,需要用到R中字符串处理包

install.packages('stringr')

信息重构:

library(stringr)
#将“地铁”中的线路信息提取出
house$地铁 <- str_extract(house$地铁,'\\d+')
#将提取后出现的NA重新填充为‘暂无数据’
house$地铁[is.na(house$地铁)] <- '暂无数据'

这样“地铁”字段也成为了因子。

(3)数据初探

这一部分我们先观察单变量信息,再观察多变量的关系信息

- 户型

首先安装用于画图的包ggplot2,然后导入

install.packages('ggplot2')
library(ggplot2)

画出户型分布的直方图

#户型分布
#户型分布
type_freq <- data.frame(table(house$户型))
type_plot <- ggplot(data = type_freq, mapping = aes(x = reorder(Var1, -Freq),y = Freq)) + geom_bar(stat = 'identity', fill = '#33FF99') + theme(axis.text.x  = element_text(angle = 45, vjust = 0.5)) + xlab('户型') + ylab('套数')
type_plot

只有前几种户型比较多,其余的都非常少,属于长尾分布类型(严重偏态)。因此,考虑选取前9种户型,将其他户型统统归为“其他”。

size_type <- c('2室1厅1卫','2室2厅1卫','1室1厅1卫','3室2厅2卫','3室2厅1卫','1室0厅1卫','2室2厅2卫','3室1厅1卫','1室2厅1卫')
house$size.new <- ifelse(house$户型 %in% size_type, house$户型,'其他')
type_freq <- data.frame(table(house$size.new))
# 绘图
typenew_plot <- ggplot(data = type_freq, mapping = aes(x = reorder(Var1, -Freq),y = Freq)) + geom_bar(stat = 'identity', fill = '#33FF99') + theme(axis.text.x  = element_text(angle = 45, vjust = 0.5)) + xlab('户型') + ylab('套数')
typenew_plot

重新作图如下:

从图中可以看出,房源最多的为2室房,这种房型最实用,符合实际的楼盘户型数量。像3室1厅1卫和1室2厅1卫这些不太实用的户型,开发商开发较少,因此市面上数量也较少,户型分布图符合逻辑

- 楼层、装修、地铁、朝向

从以上几个直方图中,我们可以获知以及推断以下信息:

(1)房源中,中层楼、高层楼数量相当,底层楼较少
(2)在已有的装修数据中,精装修最多。这是因为该项作为选填项,必然精装修的屋主更倾向于完善装修信息,但这并不代表精装房屋修最多。
(3)大部分房屋处于交通不便利区域,即附近没有地铁,因此地铁栏数据缺失。此外,作为上海的中心线路,1号线周围房源最多。
(4)房屋朝向大部分为朝南和南北,阳光充足。
(5)浦东房源最多,可能是因为浦东新区近年来飞速发展,楼盘数量猛涨。

- 面积

由于后文需要对租金进行建模,因此我们要先看下面积的正态性。首先定义一个norm.test函数,代码如下:

norm.test <- function(x, breaks = 20, alpha = 0.05,plot = TRUE){if(plot == TRUE){#设置图形界面(多图合为一张图)opar <- par(no.readonly = TRUE)layout(matrix(c(1,1,2,3),2,2,byrow = TRUE),width = c(2,2),heights = c(2,2))#绘制直方图hist(x, freq = FALSE, breaks = seq(min(x),max(x), length = breaks), main = 'x的直方图',ylab = '核密度值')#添加核密度图lines(density(x), col = 'red', lty = 1, lwd 2)#添加正态分布图x <- x[order(x)]lines(x, dnorm(x, mean(x), sd(x)),col = 'blue', lty = 2, lwd = 2.5)#添加图例legend('topright',legend = c('核密度曲线','正态分布曲线'),col = c('red','blue'), lty = c(1,2),lwd = c(2,2.5), bty = 'n')#绘制Q-Q图qqnorm(x, xlab = '实际分布', ylab = '正态分布',main = 'x的Q-Q图', col = 'blue')qqline(x)#绘制P-P图P <- pnorm(x, mean(x), sd(x))cdf <- 0for(i in 1:length(x)){cdf[i] <- sum(x <= x[i])/length(x)}plot(cdf, P, xlab = '实际分布', ylab = '正态分布',main = 'x的P-P图', xlim = c(0,1),ylim = c(0,1), col = 'blue')abline(a = 0, b = 1)par(opar)}#定量的shapiro检验if (length(x) <= 5000) {shapiro <- shapiro.test(x)if(shapiro$p.value > alpha)print(paste('定量结果为:', 'x服从正态分布,','P值 =',round(shapiro$p.value,5), '> 0.05'))elseprint(paste('定量结果为:', 'x不服从正态分布,','P值 =',round(shapiro$p.value,5), '<= 0.05'))shapiro}else {ks <- ks.test(x,'pnorm')if(ks$p.value > alpha)print(paste('定量结果为:', 'x服从正态分布,','P值 =',round(ks$p.value,5), '> 0.05'))elseprint(paste('定量结果为:', 'x不服从正态分布,','P值 =',round(ks$p.value,5), '<= 0.05'))ks}
}

下面看面积的正态分布性


P-P图和Q-Q图都是用来检验数据是否符合正态分布的图形,当数据符合正态分布时,P-P图中各点近似呈一条直线,而Q-Q图中的点近似地在一条直线附近。通过P-P图和Q-Q图可以看出,面积似乎不太符合正态分布,这个时候我们可以再进行定量分析,W检验(Shapiro-Wilk检验)用于小数据样本,KS检验(Kolmogorov-Smirnov检验)用于大数据样本(这里将5000个样本作为分水岭),得出面积分布不是正态分布的结论

- 租金

同样,用norm.test对租金进行正态分布性检验。


结论是租金依然为非正态分布

- 租金和面积关系

画出二者关系的散点图

#租金面积关系散点图
plot(house$面积,house$租金,main="面积vs租金",xlab='面积(单位:平米)',ylab='租金(单位:元)',col='#33FF99',pch=19)
abline(lm(house$租金~house$面积),col='#FF6666',lwd=2,lty=1)
lines(lowess(house$面积,house$租金),col='#9933FF',lwd=2,lty=2)
legend('topright',legend = c('最佳拟合线性直线','最佳拟合平滑曲线'),col = c('#FF6666','#9933FF'), lty = c(1,2),lwd = c(2,2.5), bty = 'n')

由图中可以看出,随着面积的增长,租金也在上升,大部分房源的面积在100平米以内,租金在一万块以内(然鹅面积100平以上,租金一万块以上的也不算少,吃惊(⊙o⊙)…),右上角还有个别离群值出现,面积300+平米,租金4万+以上。随便扯点我的想法,这些都是复式别墅了吧,能租得起这种房子的应该也买得起上海的房子,但是如果换成买房子,每个月还贷四万,还20年,换来的也不过是市中心不超过150平左右的房子,居住质量并没有租房来得高,所以宁可租房也不买房吧~

- 户型和面积的关系

由上图可以看出,小户型(即1室)平均面积在三四十平左右(1室2厅1卫虽然没有被归入其他,但是从分布图中我们可以发现也算是频数比较靠后的房型了);常见房源中,最大户型的平均面积为140平米左右,超过140平米的户型就不太常见了(属于“其他”类了)。


3.模型建立

(1)房源分类

首先用聚类分析把这些房源分分类。这里我们采用最方便的K-means聚类算法,简单地说就是通过计算离差平方和来进行分类。聚类原则:组内差距要小,组间差距要大。我们用面积和租金这两个变量对房源进行分类。
首先问题是该把房源分为几类?我们定义一个计算K组组内离差平方和,然后作图的函数,用来观察合适的K:

k.plot <- function(data, nc, seed=1234){#假设分为一组时的总的离差平方和              k <- (nrow(data)-1)*sum(apply(data,2,var)) for (i in 2:nc){#必须指定随机种子数set.seed(seed) k[i] <- kmeans(data, centers=i, iter.max = 100)$tot.withinss}plot(1:nc, k, type="b", xlab="Number of Clusters",ylab="Within groups sum of squares",col = '#9933FF',lwd = 2, main = 'Choose best Clusters')
}

然后调用该函数,观察不同K值下的离差平方和:

#归一化
standrad <- data.frame(scale(house[,c('面积','租金')]))
#不同K值下的离差平方和
myplot <-k.plot(standrad, nc = 15)

从图中可以看到,当K=5时,明显曲线下降速度变缓,这说明我们将房源分成5类会比较合适。因此确定这里K=5.
下面进行K-means聚类

# 将样本数据聚为5类
set.seed(1234)
clust <- kmeans(x = standrad, centers = 5, iter.max = 100)
table(clust$cluster)

结果:

下面按照聚类结果,我们来看看各类中的区域分布以及靠近地铁线路情况:



下面看下各类中的平均面积(平米),平均租金(元)和均价(元/平米)

# 按聚类结果,比较各类中房子的平均面积、平均租金和平均单价
y <- data.frame(house$租金,house$面积,house$租金/house$面积)
colnames(y) <- c('租金.元','面积.平米','均价.元/平米')
aggregate(y, list(clust$cluster), mean)

结果:

最后我们来可视化探究一下,看一下聚类后面积和租金的散点图分布:

# 绘制面积租金价的散点图,并按聚类进行划分
p <- ggplot(data = y[,1:2], mapping = aes(x = 面积.平米,y = 租金.元, color = factor(clust$cluster)))
p <- p + geom_point(pch = 20, size = 3)
p + scale_colour_manual(values = c("red","blue", "green", "orange",'yellow'))

结合数据初探一节的信息,我们可以看出:

无论哪类房型,浦东都是最多的,这与浦东地广人稀,近年来蓬勃发展,年轻人多,租房需求旺盛密切相关。
(1)第1组为大户型,平均面积154平米,月租每平米平均价格为114元,月租较高,这是因为主要位于浦东、徐汇、黄浦、长宁等市中心区域,交通便利(仅48个房源无地铁信息,市区线路更发达),但是房源不太多(仅238套)。
(2)第2组介于中型至大型户型,平均面积120平,位于浦东、闵行较多,月租均价不高(88元/平米),主要位于浦东、闵行,属于郊区,因此月租不,但是房源多。
(3)第3组为中户型,平均面积87平米,月租均价56元/平米,主要位于浦东、宝山、闵行、松江,总体上比第2组地理位置更郊区,因此月租低,房源数量多。
(4)第4组为小户型,房源数量上最多,平均面积50平米,月租均价92元/平米,主要位于浦东、闵行、徐汇、杨浦。由于徐汇和杨浦为市区,因此拉高了这一组整体月租水平。
(5)第5组为超大户型(就是传说中有钱人住的房子。。),平均面积为243平米,每平米均价也是最高的,144元/平米,这可能是因为这些房子都是超高档小区了,物业管理费应该不菲,配套设施齐全,都算在租金内了(租得起这种房子的人应该也不在乎每个月再多出大几千吧~)。这类房源大多分布在浦东和黄浦(在黄浦可一点都不意外呢、、咳咳)。同时我们聚类分析的散点图中可以看出,这一组点特别散,靠近右上方的离群点距离前几组好远好远,最高的租金靠近6万,最大的面积500多平米。
我们把这两个“最”的数据单独拉出来看看:

attach(house)
#按租金降序
house.expensive <- house[order(-租金),]
#按面积降序
house.largest <- house[order(-面积),]

最贵的房子:

最大的房子:

(这大概就是贫穷限制了我的想象吧~~~)

上海租房房源数据分析(基于R的案例分析)(一)相关推荐

  1. R语言案例分析:多元数据的基本统计分析

    R语言案例分析:多元数据的基本统计分析 来自<多元统计分析与R语言建模> 第四版 数据集下载 我们利用该数据集中的Case1来完成下面的R语言操作: options(digits = 4) ...

  2. R语言案例分析:财政收入的多元相关与回归分析

    R语言案例分析:财政收入的多元相关与回归分析 数据集下载 (mvcase3.xls)中的表Case3. y:财政收入  x1:国内生产总值  x2:能源消费总量  x3:从业人员总数  x4:全社会固 ...

  3. 数据挖掘——基于R文本情感分析(2)

    姑且算是搞定了这个. 最后使用的是在twitter中搜索.因为callback url的关系,之前一直无法直接搜索twitter内容,今天直接把callback url删除了就成功了. 推荐两个很好的 ...

  4. 时间序列分析——基于R语言案例数据课后数据

    去这个www.crup.com.cn网址搜,里面全都有.免费的.

  5. python销售数据分析方法_Python数据分析之药品销售案例分析(上)

    一.一维数组 1.1 一维数据可以由numpy中的Array函数或者Pandas包中的Series函数创建,series函数是建立在array函数基础上,功能更加强大一些. 1.2Array 函数 利 ...

  6. python商业数据分析报告范文_python案例分析之电商销售数据分析

    import pandas as pd #读取文件 data= pd.read_csv('./dataset.csv') ########################### 查看数据概览 #### ...

  7. 寻找影响葡萄酒质量的化学成分(基于R,相关性分析)

    哪个化学成分影响葡萄酒的质量? 单变量情节部分 从数据可得,该数据集有4898 条数据和13个变量. 查看 quality 的分布 从图中可得,质量分数的分布范围是3至9,大部分的质量分数居中,最多为 ...

  8. 基于R语言的代理模型(高斯过程、贝叶斯优化、敏感性分析、异方差性等)高级技术应用

    基于R语言的代理模型(高斯过程.贝叶斯优化.敏感性分析.异方差性等)高级技术应用 直播时间:10月30日-10月31日.11月6日-7日(4天+1周辅导练习) (上午9:30-12:00  下午14: ...

  9. KNN模型算法研究与案例分析

    KNN模型算法研究与案例分析( 白宁超 2018年8月29日15:39:13 ) 导读:机器学习算法中KNN属于比较简单的典型算法,既可以做聚类又可以做分类使用.本文通过一个模拟的实际案例进行讲解.整 ...

最新文章

  1. shell脚本按行读取文件的几种方式
  2. python变量定义大全_详解python变量与数据类型
  3. oracle用户名密码过期引起的网站后台无法登录
  4. 物联网智慧城市为降低城市能源消耗做出贡献
  5. sql2005关闭c2审核_C2审核–使用C2审核模式SQL Server审核和通用标准合规性
  6. 5G 时代的车联网混战!
  7. 一分钟详解机器人手眼标定MATLAB及C++实现
  8. Halcon教程十二:回形针识别进阶
  9. @Resource详解-代码示例
  10. LAMP部署phpadmin
  11. 包政讲营销录音(1)
  12. opencv 基于ORB特征点图像拼接
  13. 神奇的 ViewDragHelper,让你轻松定制拥有拖拽能力的 ViewGroup
  14. 制作多维度分组交叉销售统计表
  15. Word表格跨页自动显示表头 自动添加标题
  16. 谷歌网站收录移动设备易用性有问题?
  17. Java项目如何接入钉钉群机器人
  18. 2012年最值得关注的10家云计算公司
  19. 一些改变世界的编程高手
  20. 《动手学深度学习》第三十三天---AdaGrad算法,RMSProp算法,AdaDelta算法,Adam算法

热门文章

  1. 汇编语言-计算总平成绩
  2. python 强类型 弱类型_Python到底是强类型语言还是弱类型语言
  3. 每日一记 - 3.6
  4. GOOGLE Chrome谷歌翻译失效(用不了/打不开)的解决方法
  5. 【转】超实用网站,一次性分享出来
  6. 短视频平台推广怎么做
  7. 2022下半年火爆互联网商业模式揭晓,乐分享如何超强吸粉?详解
  8. 企业如何与客户建立良好的客户关系
  9. KDE声响效劳器──aRts
  10. 微信免资金代金券(V3版)java代码