2013-12-16 38 views
30

我在數據幀df1上運行k-means聚類,並且正在尋找一種簡單的方法來計算每個觀測的最近聚類中心新數據框架df2(​​具有相同的變量名稱)。將df1視爲訓練集,將df2視爲測試集;我想在訓練集上進行聚類,並將每個測試點分配給正確的聚類。k-均值聚類後爲新數據分配簇的簡單方法

我知道如何與apply功能和一些簡單的用戶定義函數做到這一點(的話題以前的職位通常有提出類似的東西):

df1 <- data.frame(x=runif(100), y=runif(100)) 
df2 <- data.frame(x=runif(100), y=runif(100)) 
km <- kmeans(df1, centers=3) 
closest.cluster <- function(x) { 
    cluster.dist <- apply(km$centers, 1, function(y) sqrt(sum((x-y)^2))) 
    return(which.min(cluster.dist)[1]) 
} 
clusters2 <- apply(df2, 1, closest.cluster) 

不過,我準備這個集羣例如學生不熟悉apply函數的課程,所以我更喜歡是否可以使用內置函數將羣集分配給df2。有沒有方便的內置功能來找到最近的集羣?

+1

[這是一個很好的資源嘗試不同的聚類方法有R代碼和解釋](http://manuals.bioinformatics.ucr.edu/home/R_BioCondManual# TOC-Clustering-Exercises)還有[biganalytics包](http://cran.r-project.org/web/packages/biganalytics/biganalytics.pdf),它不依賴於記憶,並且有一個K-means算法 – marbel

+1

向學生介紹apply()(他們最終會反覆使用它)似乎更加明智,並且使用相對簡單的方法,而不是將它們引入到一堆不同的包中,這些包在特殊場合下必須記住(如果他們再次使用它們)。 – naught101

回答

31

你可以使用flexclust包,它具有用於k均值的實施predict方法:

library("flexclust") 
data("Nclus") 

set.seed(1) 
dat <- as.data.frame(Nclus) 
ind <- sample(nrow(dat), 50) 

dat[["train"]] <- TRUE 
dat[["train"]][ind] <- FALSE 

cl1 = kcca(dat[dat[["train"]]==TRUE, 1:2], k=4, kccaFamily("kmeans")) 
cl1  
# 
# call: 
# kcca(x = dat[dat[["train"]] == TRUE, 1:2], k = 4) 
# 
# cluster sizes: 
# 
# 1 2 3 4 
#130 181 98 91 

pred_train <- predict(cl1) 
pred_test <- predict(cl1, newdata=dat[dat[["train"]]==FALSE, 1:2]) 

image(cl1) 
points(dat[dat[["train"]]==TRUE, 1:2], col=pred_train, pch=19, cex=0.3) 
points(dat[dat[["train"]]==FALSE, 1:2], col=pred_test, pch=22, bg="orange") 

flexclust plot

也有轉換方法從簇函數的結果轉換等stats::kmeanscluster::pam到類kcca的對象,反之亦然:

as.kcca(cl, data=x) 
# kcca object of family ‘kmeans’ 
# 
# call: 
# as.kcca(object = cl, data = x) 
# 
# cluster sizes: 
# 
# 1 2 
# 50 50 
7

我注意到關於這個問題的方法和flexclust方法的一些事情是,它們相當慢(基準測試集合中有100萬個觀察結果和2個特徵)。

件原始模型是相當快的:

set.seed(144) 
df1 <- data.frame(x=runif(1e6), y=runif(1e6)) 
df2 <- data.frame(x=runif(1e6), y=runif(1e6)) 
system.time(km <- kmeans(df1, centers=3)) 
# user system elapsed 
# 1.204 0.077 1.295 

我張貼在這個問題的解決方案是在計算測試設置羣集分配緩慢,因爲它單獨調用closest.cluster爲每個測試設定點:

system.time(pred.test <- apply(df2, 1, closest.cluster)) 
# user system elapsed 
# 42.064 0.251 42.586 

同時,flexclust包似乎增添了不少開銷,無論我們把與as.kcca擬合模型或安裝一個新的自己與kcca(雖然PR ediction末的速度要快得多)

# APPROACH #1: Convert from the kmeans() output 
system.time(km.flexclust <- as.kcca(km, data=df1)) 
# user system elapsed 
# 87.562 1.216 89.495 
system.time(pred.flexclust <- predict(km.flexclust, newdata=df2)) 
# user system elapsed 
# 0.182 0.065 0.250 

# Approach #2: Fit the k-means clustering model in the flexclust package 
system.time(km.flexclust2 <- kcca(df1, k=3, kccaFamily("kmeans"))) 
# user system elapsed 
# 125.193 7.182 133.519 
system.time(pred.flexclust2 <- predict(km.flexclust2, newdata=df2)) 
# user system elapsed 
# 0.198 0.084 0.302 

似乎有另一種明智的做法在這裏:使用快速k最近像kd樹的鄰居解決發現的內每個測試設置觀察近鄰集羣質心集。這可以緊湊地寫和比較快速的:

library(FNN) 
system.time(pred.knn <- get.knnx(km$center, df2, 1)$nn.index[,1]) 
# user system elapsed 
# 0.315 0.013 0.345 
all(pred.test == pred.knn) 
# [1] TRUE 
+1

這個答案非常有價值。在k-means模型中使用predict()所涉及的開銷很瘋狂。花了1.5個小時爲我處理一小部分光柵。通過使用您的集羣中心方法,我能夠在不到15秒的時間內運行該過程。非常感謝你。 – SeldomSeenSlim