聚類-R語言

R實戰代碼

1. 層次聚類-R語言

install.packages("flexclust",destdir = "D:\\Softwares\\R\\R-3.6.1\\packages")
install.packages("NbClust",destdir = "D:\\Softwares\\R\\R-3.6.1\\packages")
library(NbClust)
par(mfrow=c(1,1))
# 加載指定包中的指定數據集,以原名字存儲
data(nutrient,package = "flexclust")   
# 將行名變爲小寫,= rownames() ;       列名 names(nutrient)  # 列名=colnames()
row.names(nutrient) <- tolower(row.names(nutrient))  

s1:預處理:標準化+距離矩陣

# 對變量標準化,scale(x, center = TRUE, scale = TRUE) 默認按列進行標準化w爲均值0,方差爲1;x:二維對象,
nutrient_scaled <- scale(nutrient)

d <- dist(nutrient_scaled)         # 計算二維表中所有行之間的距離,默認歐氏距離,
#返回一維向量,需要轉換爲矩陣方便查看,但是下面的層次聚類中使用的還得是轉換之前的

s2: 層次聚類並可視化
對距離矩陣進行層次聚類,hclust(d,method):d:距離矩陣,method是方法,平均聯動比較好

fit_average <- hclust(d,method = "average")      # 得到一個tree對象
plot(fit_average,hang = -1,            # hang:標籤的懸掛方式,對齊懸掛在0的下面
     cex=0.8,main = "Average Linkage Clustering")  # cex:相對於默認字號的字號

s3:選擇聚類個數(NbCluster)

NbClust:對標準化後的樣本數據陣確定最佳聚類數,提供了30個用於確定聚類數量的指標,向用戶提出最佳聚類方案。
NbClust(data , distance = “euclidean”, min.nc = 2, max.nc = 15, method = NULL, index = “all”, alphaBeale = 0.1)
允許的最少聚類個數,最大聚類的個數
method:使用的聚類分析方法“ ward.D”,“ ward.D2”,“ single”,“ complete”,“ average”,“ mcquitty”,“ median”,“ centroid”,“ kmeans”。

devAskNewPage(ask = T)  # 在新頁面之前提示,控制(對於當前設備)在開始新的輸出頁面之前是否提示用戶。
nc <- NbClust(nutrient_scaled,distance = "euclidean", min.nc = 2,max.nc = 15,method = "average")

table(nc$Best.nc[1,]) # 列出裏面存儲的第一行(26個指標的推薦類數)的頻數表
# 畫出頻數的柱狀圖,直觀顯示推薦的類數,可以看出投票最多的聚類個數是2,3,5,15,可以挨個試試效果
barplot(table(nc$Best.nc[1,]),xlab = "Number of Clusters",ylab = "Number of Criteria",main = "Number of Clusters chosen by 26 Criteria")

s4: 獲取最終聚類方案

cutree() : Cut a Tree into Groups of Data , tree可以是hclust中的樹結果,通過制定劃分個數k

clusters <- cutree(fit_average,k=5)     # 以5類顯示結果,返回值是帶標籤的向量,標籤是每一個樣本,值爲該樣本對應的類別
table(clusters)                         # 顯示類別和類內對應個數的頻數表

# 對原始數據或標準化數據按照每行對應的類別進行分類聚合,描述不同類的中位數
aggregate(nutrient,by=list(cluster=clusters),median)             # 此處爲原始度量形式
aggregate(nutrient_scaled,by=list(cluster=clusters),median)      # 此處爲標準度量形式

# 結果重新繪圖
plot(fit_average,hang = -1, cex=0.8,main = "Average Linkage Clustering")  # 原不加修飾的樹狀圖
rect.hclust(fit_average,k=5)     # 在上幅圖上,對原樹狀圖疊加一個5類的分類框

rect.hclust(tree,k,border):在層次聚類的樹狀圖的分支周圍繪製矩形,以突出顯示相應的簇。
tree:hclust得到的tree對象;k是類數
border是不同矩形的顏色向量

2. k-means聚類-R語言

rm(list = ls())
install.packages("rattle",destdir = "D:\\Softwares\\R\\R-3.6.1\\packages")
library(rattle)

data(wine,package = "rattle") # 載入葡萄酒數據集,第一列是分爲的3類,聚類時丟棄這一列
data <- scale(wine[,-1])  # 標準化

確定聚類的個數

# 作出類內平方和對聚類數量的曲線
wssplot <- function(data,nc=15,seed=1234)    # nc:考慮的最大聚類數目,默認值15,seed:隨機數種子,因爲該算法是隨機初始化
{
  wss <- (nrow(data)-1)*sum(apply(data,2,var))  # 當類數是1時,總平方和
  for (i in 2:nc) {       # 對不同的類數
    set.seed(seed)
    wss[i] <- sum(kmeans(data,centers = i)$withinss)  # 得到該類數下的kmeans聚類後的組內平方和
  }
  plot(1:nc,wss,type = "b",xlab = "Number of Clusters",ylab = "Within groups Sum of Squares")
}

wssplot(data)

法二:

library(NbClust)
devAskNewPage(ask = T)   # 同時生成4張圖時,是兩個兩個顯示的,一般就顯示最後的兩張,設置這個詢問後,每次都讓你按鍵進入下兩個的顯示
nc <- NbClust(data,min.nc = 2,max.nc = 15,method = "kmeans")
par(mfrow=c(1,1))
table(nc$Best.nc[1,])
barplot(table(nc$Best.nc[1,]),
        xlab = "推薦的聚類個數",ylab="支持的指標個數",
        main="26個指標推薦的聚類個數")

k-means Cluster

set.seed(1234)
fit <- kmeans(data,centers = 3,nstart = 25)  # 隨機初始化25次,選擇效果最好的那一次初始化
fit$size        # 查看聚類個數
fit$centers     # 查看類中心(標準化後的)
aggregate(wine[,-1],by=list(fit$cluster),mean)  # 查看原始數據的類心(類均值),原始數據按聚類結果的cluster分組

評價聚類效果

t <- table(wine$Type,fit$cluster)  # 類似於混淆矩陣的交叉列表,顯示分錯類的個數
library(flexclust)
randIndex(t)   # 蘭德指數=0.897
# 等價於如下
comPart(wine$Type,fit$cluster,type = "ARI")  # ARI:調整後的RI,其他類型:"J":Jaccard Index,"FM":Fowlkes-Mallows
# 等價於如下,默認調整後的RI
randIndex(wine$Type,fit$cluster)  # 參數correct=T

3. 基於中心點的劃分聚類 (k-means的穩健版本)

由於k-means基於均值,所以對異常值敏感,如果圍繞中心點(最有代表性的觀測值)劃分則更穩健,還適用於混合數據類型,不僅限於連續型

library(cluster)
set.seed(1234)
fit_pam <- pam(wine[,-1],k=3,stand=T)  # stand:自動標準化,-均值/標準差
fit_pam$medoids    # 查看中心點,實際的觀測值

評價聚類效果

t_pam <- table(wine$Type,fit_pam$clustering)
randIndex(t_pam)        # 0.699

NbCluster中的CCC指標可以驗證數據是否可以聚類

nc <- NbClust(data,min.nc = 2,max.nc = 15,method = "kmeans")
plot(nc$All.index[,4])

案例:旅遊用戶評分聚類分析

一. 選題介紹

近年來,隨着旅遊市場的不斷髮展,選擇旅遊的人數大幅增長。但是在前往未知的旅行地時,遊客往往並不明確自己喜歡去什麼景點,只能在網上看別人的攻略和評論。而旅遊網站上有大量的評價信息和用戶信息,網站可以根據這些已有的信息對用戶進行細分,判斷不同的用戶喜歡什麼類型的景點或建築,從而有針對性的向不同的用戶推薦不同的符合他們口味的地點,起到個性化推薦的作用。
常用的個性化推薦算法包括協同過濾和矩陣分解,但是均比較簡單,適合小型系統。而聚類作爲無監督學習的代表,適合在大型系統中將用戶區分開。因此本案例意圖對旅行中給出評分的用戶進行聚類,以期對不同的用戶推薦不同的旅行內容。

二. 數據獲取與描述

本案例使用的數據來自於UCI機器學習數據庫,是谷歌用戶對歐洲不同類別的景點的評分數據。用戶評分範圍在0-5之間,並且每個類別的景點已計算了用戶的平均評分。由於原始數據太大,共有5454條樣本,所以對其進行無放回隨機抽樣,得到500條樣本。
經過數據預處理後的樣本前六行如圖1所示,共有18個變量,分別是用戶ID,教堂、度假村、沙灘、公園、劇院、博物館、購物商場、動物園、飯店、酒吧、當地服務、旅店住宿、藝術畫廊、舞蹈俱樂部、觀光景點、紀念碑和花園。
在這裏插入圖片描述
圖 1 評分數據前六行

三.模型建立-K均值聚類

本案例的目的是想將評分相似的用戶聚成一類,一類的用戶可能有相似的旅行場所的興趣,以便在後期可以針對用戶進行個性化推薦,即對不同類的用戶推薦不同的內容。聚類屬於無監督學習,是基於距離來判斷樣本間的相似程度。常用聚類方法有層次聚類和劃分聚類,包括K均值聚類和圍繞中心點的劃分聚類等。由於層次聚類和K中心點聚類均適用於小數聚集的情況,而K均值聚類由於簡潔與效率應用最爲廣泛,所以本案例採用了K均值聚類方法。
K均值聚類方法的基本思想是:
(1). 首先在所有數據中隨機選擇K個數據點,作爲初始的聚類中心
(2). 然後計算其餘各點到各聚類中心的距離,將這些點劃分到距離它最近的聚類中心
(3). 重新計算新的類中心,並將數據重新劃分到最近的類中
(4). 經過不斷迭代,直至損失函數不再減少。
R軟件中的K均值聚類中的第k類採用的目標函數是:
在這裏插入圖片描述
由於算法中要進行均值的計算,所以要求變量的取值必須是連續的,而且算法容易受到異常值的影響。本案例的數據經過預處理後基本符合要求,使用K均值聚類方法如下:

1. 確定聚類的個數K

由於算法需要在開始時隨機選擇K個點作爲初始聚類中心,所以要提前確定聚類的個數。圖2展示了數據被分爲不同的聚類個數時總的類內的平方和,可以看出當聚類數爲3時,圖中有一個小的拐點,之後曲線的下降變緩,所以適當的類數可以是3。
在這裏插入圖片描述
圖 2 聚類個數與組內組內平方和的對比圖
R還提供了一種更準確的判斷方法,使用NbClust包提供的更多指標來選擇類數。如圖3所示。可以發現圖中顯示了共有23個指標給出了自己建議的類數,其中有7個指標支持聚成3類,因此根據“投票”原則,可以選擇推薦個數最多的聚類方案。本案例綜合兩幅圖的結果,將聚類個數設爲3。
在這裏插入圖片描述
圖 3 NbClust包確定聚類個數
由於初始中心點是隨機選擇的,並且對結果是否收斂影響較大,所以聚類時設定了嘗試25次不同的初始配置,最終採用最好的一次作爲初始配置。

四.結果分析

聚類後的總平方和爲8483,每一類的組內平方和分別爲1080、2870、2379。每一類的個數分別爲105、196、199。根據前兩個主成分畫出所有數據點的聚類圖,如圖4所示,可以發現前兩個成分解釋了36.56%的可能性。數據被很清晰的分爲了三類。
在這裏插入圖片描述
圖 4 聚類結果圖
將原始數據的聚類中心輸出,如表1所示。
在這裏插入圖片描述
可以發現,第一類的用戶對觀光景點、紀念碑、花園、沙灘這些旅遊地點評分較高,均值均超過了2.5分,而對旅店、飯店、博物館、舞蹈俱樂部評分最低。說明這類人羣可能愛好優美的自然風光和室外景點,而相對不喜歡去旅途中室內的場所。對這類人羣可以推薦他們室外的風景好的觀光景點。
對第二類用戶可以發現他們對劇院的平均評分最高,達到了4.14分,對博物館、購物商場、公園、飯店的評分也很高,超過了3分,而對教堂、畫廊、舞蹈俱樂部、紀念碑這些地點評分最低,低於1.6分。說明這類人羣可能愛好休閒娛樂較強的可以放鬆減壓的場所,而相對不喜歡去一些氣氛肅穆的場所。對於這類人羣就可以推薦各種休閒減壓的旅行場所。
在第三類用戶的評分中,可以看出他們對飯店的評分最高爲4.17分,其次還有購物商場、酒吧和當地服務。而其他場所均比較低,舞蹈俱樂部和紀念碑評分最低,在1分以下。說明這類用戶最喜歡吃吃吃和買買買,也注重當地服務的體驗,很可能是不差錢的外地遊客,喜歡當地的新鮮的風土人情。對於他們就可以推薦各種當地特色美食和特產。
綜合三類用戶來看,公園、劇院和商場這三種場所評分均較高,均超過2分,說明人們都比較喜歡去這三類地方。而舞蹈俱樂部的評分均最低,均在2分以下,說明一般的普通遊客在旅遊時並不是很喜歡這種場所。因此就可以避免向普通用戶推薦這類場所,或者再進一步細化,只向愛好跳舞的遊客推薦。

五.總結

本案例使用了K均值聚類根據對旅遊場所的評分將用戶聚爲了三類,在以後可以對不同類的用戶進行個性化推薦。但是聚類是一種比較弱的個性化推薦,因爲這種方法的本質是識別一類的用戶,對該類的所有用戶推薦相同的內容。在實際中可以考慮將用戶聚類作爲第一步,縮小不同用戶的範圍,然後再針對一類的用戶使用其他推薦算法進行更細緻精確的推薦。

六. 代碼

library(NbClust)
library(ggplot2)
library(cluster)
library(ggfortify)
library(dplyr)
rm(list = ls())

data <- read.csv("E:\\Bing\\研究生\\多元\\旅行評分\\谷歌旅行評分\\google_review_ratings.csv",header=T,stringsAsFactors=F)
data$X <- NULL
data <- na.omit(data)
data$Category.11 <- as.numeric(data$Category.11)
names(data) <- c("id","church","resort","beach","park","theatre",
                 "museum","mall","zoo","restaurant","pub_bar","localService",
                 "burger_pizza_shop","hotel","juiceBar","artGallery","danceClub",
                 "swimmingPool","gym","bakery","beauty_spas","cafe","viewPoint","monument","garden")
set.seed(1234)
da <- data[sample(1:nrow(data),500),]       # 隨機抽樣,抽一半樣本,490
da <- da[order(as.numeric(row.names(da))),]  # 索引排序
summary(da)

## 篩選變量
da <- da[,-c(13,15,18:22)]
head(da)
da <- da[,-1]
df <- scale(da)

## 確定聚類的個數
# 作出類內平方和對聚類數量的曲線
# # nc:考慮的最大聚類數目,seed:隨機數種子
wssplot <- function(data,nc=15,seed=1234) 
{
  wss <- (nrow(data)-1)*sum(apply(data,2,var))  # 當類數是1時,總平方和
  for (i in 2:nc) {       # 對不同的類數
    set.seed(seed)
    wss[i] <- kmeans(data,centers = i)$tot.withinss  # 得到該類數下的kmeans聚類後的組內平方和
  }
  plot(1:nc,wss,type = "b",xlab = "Number of Clusters",ylab = "Within groups Sum of Squares")
}
wssplot(df)

## 法二:
set.seed(123)
devAskNewPage(ask = T)   
nc <- NbClust(df,min.nc = 2,max.nc = 15,method = "kmeans")
par(mfrow=c(1,1))
table(nc$Best.nc[1,])
barplot(table(nc$Best.nc[1,]),
        xlab = "推薦的聚類個數",ylab="支持的指標個數",
        main="23個指標推薦的聚類個數",col="lightblue")

## kmeans聚類
set.seed(1234)
fit <- kmeans(df,3,nstart = 25)
fit$centers               # 類中心,標準化後的
options(digits = 3)
group <- aggregate(da,by=list(fit$cluster),mean) %>% t()
autoplot(fit,df,frame=T)    # 聚類結果可視化
fit$cluster               # 顯示類別
fit$totss          # 總平方和
fit$withinss
fit$size
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章