# 1 前言
?? 這兩年開始畢業(yè)設計和畢業(yè)答辯的要求和難度不斷提升,傳統的畢設題目缺少創(chuàng)新和亮點,往往達不到畢業(yè)答辯的要求,這兩年不斷有學弟學妹告訴學長自己做的項目系統達不到老師的要求。
為了大家能夠順利以及最少的精力通過畢設,學長分享優(yōu)質畢業(yè)設計項目,今天要分享的是
?? 基于大數據招聘崗位數據分析與可視化系統
??學長這里給一個題目綜合評分(每項滿分5分)
- 難度系數:3分
- 工作量:3分
- 創(chuàng)新點:5分
1 課題背景
首先通過爬蟲采集鏈家網上所有二手房的房源數據,并對采集到的數據進行清洗;然后,對清洗后的數據進行可視化分析,探索隱藏在大量數據背后的規(guī)律;最后,采用一個聚類算法對所有二手房數據進行聚類分析,并根據聚類分析的結果,將這些房源大致分類,以對所有數據的概括總結。通過上述分析,我們可以了解到目前市面上二手房各項基本特征及房源分布情況,幫助我們進行購房決策。
2 實現效果
整體數據文件詞云
各區(qū)域二手房房源數量折線圖
二手房房屋用途水平柱狀圖
二手房基本信息可視化分析
各區(qū)域二手房平均單價柱狀圖
各區(qū)域二手房單價和總價箱線圖
二手房單價最高Top20
二手房單價和總價熱力圖
二手房單價熱力圖
二手房總價小于200萬的分布圖
二手房建筑面積分析
二手房建筑面積分布區(qū)間柱狀圖
二手房房屋屬性可視化分析
二手房房屋戶型占比情況
從二手房房屋戶型餅狀圖中可以看出,2室1廳與2室2廳作為標準配置,一共占比接近一半。其中3室2廳和3室1廳的房源也占比不少,其他房屋戶型的房源占比就比較少了。
二手房房屋裝修情況
二手房房屋朝向分布情況
二手房建筑類型占比情況
3 數據采集
該部分通過網絡爬蟲程序抓取鏈家網上所有二手房的數據,收集原始數據,作為整個數據分析的基石。
鏈家網網站結構分析
鏈家網二手房主頁界面如下圖,主頁上面紅色方框位置顯示目前二手房在售房源的各區(qū)域位置名稱,中間紅色方框位置顯示了房源的總數量,下面紅色方框顯示了二手房房源信息縮略圖,該紅色方框區(qū)域包含了二手房房源頁面的URL地址標簽。圖2下面紅色方框顯示了二手房主頁上房源的頁數。
鏈家網二手房主頁截圖上半部分:
二手房房源信息頁面如下圖。我們需要采集的目標數據就在該頁面,包括基本信息、房屋屬性和交易屬性三大類。各類信息包括的數據項如下:
1)基本信息:小區(qū)名稱、所在區(qū)域、總價、單價。
2)房屋屬性:房屋戶型、所在樓層、建筑面積、戶型結構、套內面積、建筑類型、房屋朝向、建筑結構、裝修情況、梯戶比例、配備電梯、產權年限。
3)交易屬性:掛牌時間、交易權屬、上次交易、房屋用途、房屋年限、產權所屬、抵押信息、房本備件。
網絡爬蟲程序關鍵問題說明
1)問題1:鏈家網二手房主頁最多只顯示100頁的房源數據,所以在收集二手房房源信息頁面URL地址時會收集不全,導致最后只能采集到部分數據。
解決措施:將所有二手房數據分區(qū)域地進行爬取,100頁最多能夠顯示3000套房,該區(qū)域房源少于3000套時可以直接爬取,如果該區(qū)域房源超過3000套可以再分成更小的區(qū)域。
2)問題2:爬蟲程序如果運行過快,會在采集到兩、三千條數據時觸發(fā)鏈家網的反爬蟲機制,所有的請求會被重定向到鏈家的人機鑒定頁面,從而會導致后面的爬取失敗。
解決措施:①為程序中每次http請求構造header并且每次變換http請求header信息頭中USER_AGENTS數據項的值,讓請求信息看起來像是從不同瀏覽器發(fā)出的訪問請求。②爬蟲程序每處理完一次http請求和響應后,隨機睡眠1-3秒,每請求2500次后,程序睡眠20分鐘,控制程序的請求速度。
4 數據清洗
對于爬蟲程序采集得到的數據并不能直接分析,需要先去掉一些“臟”數據,修正一些錯誤數據,統一所有數據字段的格式,將這些零散的數據規(guī)整成統一的結構化數據。
原始數據主要需要清洗的部分
主要需要清洗的數據部分如下:
1)將雜亂的記錄的數據項對齊
2)清洗一些數據項格式
3)缺失值處理
3.2.3 數據清洗結果
數據清洗前原始數據如下圖,
清洗后的數據如下圖,可以看出清洗后數據已經規(guī)整了許多。
5 數據聚類分析
該階段采用聚類算法中的k-means算法對所有二手房數據進行聚類分析,根據聚類的結果和經驗,將這些房源大致分類,已達到對數據概括總結的目的。在聚類過程中,我們選擇了面積、總價和單價這三個數值型變量作為樣本點的聚類屬性。
k-means算法原理
基本原理
k-Means算法是一種使用最普遍的聚類算法,它是一種無監(jiān)督學習算法,目的是將相似的對象歸到同一個簇中。簇內的對象越相似,聚類的效果就越好。該算法不適合處理離散型屬性,但對于連續(xù)型屬性具有較好的聚類效果。
聚類效果判定標準
使各個樣本點與所在簇的質心的誤差平方和達到最小,這是評價k-means算法最后聚類效果的評價標準。
算法實現步驟
1)選定k值
2)創(chuàng)建k個點作為k個簇的起始質心。
3)分別計算剩下的元素到k個簇的質心的距離,將這些元素分別劃歸到距離最小的簇。
4)根據聚類結果,重新計算k個簇各自的新的質心,即取簇中全部元素各自維度下的算術平均值。
5)將全部元素按照新的質心重新聚類。
6)重復第5步,直到聚類結果不再變化。
7)最后,輸出聚類結果。
算法缺點
雖然K-Means算法原理簡單,但是有自身的缺陷:
1)聚類的簇數k值需在聚類前給出,但在很多時候中k值的選定是十分難以估計的,很多情況我們聚類前并不清楚給出的數據集應當分成多少類才最恰當。
2)k-means需要人為地確定初始質心,不一樣的初始質心可能會得出差別很大的聚類結果,無法保證k-means算法收斂于全局最優(yōu)解。
3)對離群點敏感。
4)結果不穩(wěn)定(受輸入順序影響)。
5)時間復雜度高O(nkt),其中n是對象總數,k是簇數,t是迭代次數。
算法實現關鍵問題說明
K值的選定說明
根據聚類原則:組內差距要小,組間差距要大。我們先算出不同k值下各個SSE(Sum of
squared
errors)值,然后繪制出折線圖來比較,從中選定最優(yōu)解。從圖中,我們可以看出k值到達5以后,SSE變化趨于平緩,所以我們選定5作為k值。
初始的K個質心選定說明
初始的k個質心選定是采用的隨機法。從各列數值最大值和最小值中間按正太分布隨機選取k個質心。
關于離群點
離群點就是遠離整體的,非常異常、非常特殊的數據點。因為k-means算法對離群點十分敏感,所以在聚類之前應該將這些“極大”、“極小”之類的離群數據都去掉,否則會對于聚類的結果有影響。離群點的判定標準是根據前面數據可視化分析過程的散點圖和箱線圖進行判定。根據散點圖和箱線圖,需要去除離散值的范圍如下:
1)單價:基本都在100000以內,沒有特別的異常值。
2)總價:基本都集中在3000以內,這里我們需要去除3000外的異常值。
3)建筑面積:基本都集中在500以內,這里我們需要去除500外的異常值。
數據的標準化
因為總價的單位為萬元,單價的單位為元/平米,建筑面積的單位為平米,所以數據點計算出歐幾里德距離的單位是沒有意義的。同時,總價都是3000以內的數,建筑面積都是500以內的數,但單價基本都是20000以上的數,在計算距離時單價起到的作用就比總價大,總價和單價的作用都遠大于建筑面積,這樣聚類出來的結果是有問題的。這樣的情況下,我們需要將數據標準化,即將數據按比例縮放,使之都落入一個特定區(qū)間內。去除數據的單位限制,將其轉化為無量綱的純數值,便于不同單位或量級的指標能夠進行計算和比較。
我們將單價、總價和面積都映射到500,因為面積本身就都在500以內,不要特別處理。單價在計算距離時,需要先乘以映射比例0.005,總價需要乘以映射比例0.16。進行數據標準化前和進行數據標準化后的聚類效果對比如下:圖32、圖33是沒有數據標準化前的聚類效果散點圖;圖34、圖35是數據標準化后的聚類效果散點圖。
數據標準化前的單價與建筑面積聚類效果散點圖:
聚類結果分析
聚類結果如下
1)聚類結果統計信息如下:
2)聚類后的單價與建筑面積散點圖和總價與建筑面積散點圖。
3)聚類結果分組0、1、2、3、4的區(qū)域分布圖如下實例。文章來源:http://www.zghlxwxcb.cn/news/detail-764723.html
聚類結果分組0的區(qū)域分布圖如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-764723.html
6 部分核心代碼
# -*- coding: utf-8 -*-
"""
Created on Tue Feb 23 10:09:15 2016
K-means cluster
@author: liudiwei
"""
import numpy as np
class KMeansClassifier():
"this is a k-means classifier"
def __init__(self, k=3, initCent='random', max_iter=5000):
"""構造函數,初始化相關屬性"""
self._k = k
self._initCent = initCent#初始中心
self._max_iter = max_iter#最大迭代
#一個m*2的二維矩陣,矩陣第一列存儲樣本點所屬的族的索引值,
#第二列存儲該點與所屬族的質心的平方誤差
self._clusterAssment = None#樣本點聚類結結構矩陣
self._labels = None
self._sse = None#SSE(Sum of squared errors)平方誤差和
def _calEDist(self, arrA, arrB):
"""
功能:歐拉距離距離計算
輸入:兩個一維數組
"""
arrA_temp = arrA.copy()
arrB_temp = arrB.copy()
arrA_temp[0] = arrA_temp[0]*0.16
arrA_temp[1] = arrA_temp[1]*0.005
arrB_temp[0] = arrB_temp[0]*0.16
arrB_temp[1] = arrB_temp[1]*0.005
return np.math.sqrt(sum(np.power(arrA_temp - arrB_temp, 2)))
def _calMDist(self, arrA, arrB):
"""
功能:曼哈頓距離距離計算
輸入:兩個一維數組
"""
return sum(np.abs(arrA-arrB))
def _randCent(self, data_X, k):
"""
功能:隨機選取k個質心
輸出:centroids #返回一個m*n的質心矩陣
"""
n = data_X.shape[1] - 3 #獲取特征值的維數(要刪除一個用于標記的id列和經緯度值)
centroids = np.empty((k,n)) #使用numpy生成一個k*n的矩陣,用于存儲質心
for j in range(n):
minJ = min(data_X[:,j+1])
rangeJ = max(data_X[:,j+1] - minJ)
#使用flatten拉平嵌套列表(nested list)
centroids[:, j] = (minJ + rangeJ * np.random.rand(k, 1)).flatten()
return centroids
def fit(self, data_X):
"""
輸入:一個m*n維的矩陣
"""
if not isinstance(data_X, np.ndarray) or \
isinstance(data_X, np.matrixlib.defmatrix.matrix):
try:
data_X = np.asarray(data_X)
except:
raise TypeError("numpy.ndarray resuired for data_X")
m = data_X.shape[0] #獲取樣本的個數
#一個m*2的二維矩陣,矩陣第一列存儲樣本點所屬的族的編號,
#第二列存儲該點與所屬族的質心的平方誤差
self._clusterAssment = np.zeros((m,2))
#創(chuàng)建k個點,作為起始質心
if self._initCent == 'random':
self._centroids = self._randCent(data_X, self._k)
clusterChanged = True
#循環(huán)最大迭代次數
for _ in range(self._max_iter): #使用"_"主要是因為后面沒有用到這個值
clusterChanged = False
for i in range(m): #將每個樣本點分配到離它最近的質心所屬的族
minDist = np.inf #首先將minDist置為一個無窮大的數
minIndex = -1 #將最近質心的下標置為-1
for j in range(self._k): #次迭代用于尋找元素最近的質心
arrA = self._centroids[j,:]
arrB = data_X[i,1:4]
distJI = self._calEDist(arrA, arrB) #計算距離
if distJI < minDist:
minDist = distJI
minIndex = j
if self._clusterAssment[i, 0] != minIndex or self._clusterAssment[i, 1] > minDist**2:
clusterChanged = True
self._clusterAssment[i,:] = minIndex, minDist**2
if not clusterChanged:#若所有樣本點所屬的族都不改變,則已收斂,結束迭代
break
for i in range(self._k):#更新質心,將每個族中的點的均值作為質心
index_all = self._clusterAssment[:,0] #取出樣本所屬簇的編號
value = np.nonzero(index_all==i) #取出所有屬于第i個簇的索引值
ptsInClust = data_X[value[0]] #取出屬于第i個簇的所有樣本點
self._centroids[i,:] = np.mean(ptsInClust[:,1:4], axis=0) #計算均值,賦予新的質心
self._labels = self._clusterAssment[:,0]
self._sse = sum(self._clusterAssment[:,1])
def predict(self, X):#根據聚類結果,預測新輸入數據所屬的族
#類型檢查
if not isinstance(X,np.ndarray):
try:
X = np.asarray(X)
except:
raise TypeError("numpy.ndarray required for X")
m = X.shape[0]#m代表樣本數量
preds = np.empty((m,))
for i in range(m):#將每個樣本點分配到離它最近的質心所屬的族
minDist = np.inf
for j in range(self._k):
distJI = self._calEDist(self._centroids[j,:], X[i,:])
if distJI < minDist:
minDist = distJI
preds[i] = j
return preds
class biKMeansClassifier():
"this is a binary k-means classifier"
def __init__(self, k=3):
self._k = k
self._centroids = None
self._clusterAssment = None
self._labels = None
self._sse = None
def _calEDist(self, arrA, arrB):
"""
功能:歐拉距離距離計算
輸入:兩個一維數組
"""
return np.math.sqrt(sum(np.power(arrA-arrB, 2)))
def fit(self, X):
m = X.shape[0]
self._clusterAssment = np.zeros((m,2))
centroid0 = np.mean(X, axis=0).tolist()
centList =[centroid0]
for j in range(m):#計算每個樣本點與質心之間初始的平方誤差
self._clusterAssment[j,1] = self._calEDist(np.asarray(centroid0), \
X[j,:])**2
while (len(centList) < self._k):
lowestSSE = np.inf
#嘗試劃分每一族,選取使得誤差最小的那個族進行劃分
for i in range(len(centList)):
index_all = self._clusterAssment[:,0] #取出樣本所屬簇的索引值
value = np.nonzero(index_all==i) #取出所有屬于第i個簇的索引值
ptsInCurrCluster = X[value[0],:] #取出屬于第i個簇的所有樣本點
clf = KMeansClassifier(k=2)
clf.fit(ptsInCurrCluster)
#劃分該族后,所得到的質心、分配結果及誤差矩陣
centroidMat, splitClustAss = clf._centroids, clf._clusterAssment
sseSplit = sum(splitClustAss[:,1])
index_all = self._clusterAssment[:,0]
value = np.nonzero(index_all==i)
sseNotSplit = sum(self._clusterAssment[value[0],1])
if (sseSplit + sseNotSplit) < lowestSSE:
bestCentToSplit = i
bestNewCents = centroidMat
bestClustAss = splitClustAss.copy()
lowestSSE = sseSplit + sseNotSplit
#該族被劃分成兩個子族后,其中一個子族的索引變?yōu)樵宓乃饕?/span>
#另一個子族的索引變?yōu)閘en(centList),然后存入centList
bestClustAss[np.nonzero(bestClustAss[:,0]==1)[0],0]=len(centList)
bestClustAss[np.nonzero(bestClustAss[:,0]==0)[0],0]=bestCentToSplit
centList[bestCentToSplit] = bestNewCents[0,:].tolist()
centList.append(bestNewCents[1,:].tolist())
self._clusterAssment[np.nonzero(self._clusterAssment[:,0] == \
bestCentToSplit)[0],:]= bestClustAss
self._labels = self._clusterAssment[:,0]
self._sse = sum(self._clusterAssment[:,1])
self._centroids = np.asarray(centList)
def predict(self, X):#根據聚類結果,預測新輸入數據所屬的族
#類型檢查
if not isinstance(X,np.ndarray):
try:
X = np.asarray(X)
except:
raise TypeError("numpy.ndarray required for X")
m = X.shape[0]#m代表樣本數量
preds = np.empty((m,))
for i in range(m):#將每個樣本點分配到離它最近的質心所屬的族
minDist = np.inf
for j in range(self._k):
distJI = self._calEDist(self._centroids[j,:],X[i,:])
if distJI < minDist:
minDist = distJI
preds[i] = j
return preds
7 最后
到了這里,關于計算機畢設 大數據二手房數據爬取與分析可視化 -python 數據分析 可視化的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!