Existe-t-il une fonction dans R qui prend les centres des clusters trouvés et affecte des clusters à un nouvel ensemble de données

14

J'ai deux parties d'un ensemble de données multidimensionnelles, appelons-les trainet test. Et je veux construire un modèle basé sur l'ensemble de données du train, puis le valider sur l'ensemble de données de test. Le nombre de clusters est connu.

J'ai essayé d'appliquer le clustering k-means dans R et j'ai obtenu un objet qui contient les centres des clusters:

kClust <- kmeans(train, centers=N, nstart=M)

Y a-t-il une fonction dans R qui prend les centres des clusters trouvés et affecte des clusters à mon ensemble de données de test?

Quelles sont les autres méthodes / algorithmes que je peux essayer?

user2598356
la source
Bienvenue sur le site, @ user2598356. Pouvez-vous cadrer cela d'une manière plus générale (non spécifique à R)? Si vous ne demandez qu'une fonction R, cette question serait hors sujet pour CV (voir notre page d'aide ). De plus, il serait également hors sujet sur Stack Overflow , car il n'a pas d' exemple reproductible . Si vous pouvez le modifier pour en faire un sujet ici ou sur SO, veuillez le faire. Sinon, ce Q peut être fermé.
gung - Réintégrer Monica
Cette question semble être hors sujet car il s'agit de trouver une fonction R.
gung - Réintégrer Monica
1
Mais qu'en est-il de la dernière question: "Quelles sont les autres méthodes / algorithmes que je peux essayer?". En fait la réponse que j'ai obtenue concerne la mise en œuvre des méthodes qui est un sujet de CV, ou je me trompe?
user2598356
1
@gung Vous avez peut-être raison, auquel cas j'invite user259 ... à signaler cette question pour la migration. Cependant, la dernière partie de la question sur les autres méthodes et algorithmes suggère que notre communauté peut être en bonne position pour offrir une aide et des conseils utiles.
whuber
Merci! La fonction fonctionne bien, mais elle prend trop de temps si vous avez plus de 50 000 lignes. Une idée pour l'alléger?

Réponses:

11

Vous pouvez calculer les affectations de cluster pour un nouvel ensemble de données avec la fonction suivante:

clusters <- function(x, centers) {
  # compute squared euclidean distance from each sample to each cluster center
  tmp <- sapply(seq_len(nrow(x)),
                function(i) apply(centers, 1,
                                  function(v) sum((x[i, ]-v)^2)))
  max.col(-t(tmp))  # find index of min distance
}

# create a simple data set with two clusters
set.seed(1)
x <- rbind(matrix(rnorm(100, sd = 0.3), ncol = 2),
           matrix(rnorm(100, mean = 1, sd = 0.3), ncol = 2))
colnames(x) <- c("x", "y")
x_new <- rbind(matrix(rnorm(10, sd = 0.3), ncol = 2),
               matrix(rnorm(10, mean = 1, sd = 0.3), ncol = 2))
colnames(x_new) <- c("x", "y")

cl <- kmeans(x, centers=2)

all.equal(cl[["cluster"]], clusters(x, cl[["centers"]]))
# [1] TRUE
clusters(x_new, cl[["centers"]])
# [1] 2 2 2 2 2 1 1 1 1 1

plot(x, col=cl$cluster, pch=3)
points(x_new, col= clusters(x_new, cl[["centers"]]), pch=19)
points(cl[["centers"]], pch=4, cex=2, col="blue")

affectation de cluster

ou vous pouvez utiliser le package flexclust , qui a une predictméthode implémentée pour k-means:

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")

tracé flexclust

Il existe également des méthodes de conversion pour convertir les résultats des fonctions de cluster comme stats::kmeansou cluster::pamen objets de classe kccaet vice versa:

as.kcca(cl, data=x)
# kcca object of family ‘kmeans’ 
#
# call:
# as.kcca(object = cl, data = x)
#
# cluster sizes:
#
#  1  2 
#  50 50 
rcs
la source
Merci beaucoup! Une seule question: comment la méthode kcca gère-t-elle le nombre de démarrages (optimise-t-elle l'analyse par rapport aux points de départ)?
user2598356
Que voulez-vous dire par nombre de démarrages? La stepFlexclustfonction exécute des algorithmes de clustering à plusieurs reprises pour différents nombres de clusters et renvoie la solution de distance minimale à l'intérieur du cluster pour chacun.
rcs
1

step1: une fonction calculant la distance entre un vecteur et chaque ligne d'une matrice

calc_vec2mat_dist = function(x, ref_mat) {
    # compute row-wise vec2vec distance 
    apply(ref_mat, 1, function(r) sum((r - x)^2))
}

étape 2: une fonction qui applique l'ordinateur vec2mat à chaque ligne de la matrice d'entrée

calc_mat2mat_dist = function(input_mat, ref_mat) {

    dist_mat = apply(input_mat, 1, function(r) calc_vec2mat_dist(r, ref_mat))

    # transpose to have each row for each input datapoint
    # each column for each centroids
    cbind(t(dist_mat), max.col(-t(dist_mat)))
}

étape 3. appliquer la fonction mat2mat

calc_mat2mat_dist(my_input_mat, kmeans_model$centers)

étape 4. Utilisez éventuellement plyr :: ddply et doMC pour paralléliser mat2mat pour un ensemble de données volumineux

library(doMC)
library(plyr)

pred_cluster_para = function(input_df, center_mat, cl_feat, id_cols, use_ncore = 8) {
    # assign cluster lables for each individual (row) in the input_df 
    # input: input_df   - dataframe with all features used in clustering, plus some id/indicator columns
    # input: center_mat - matrix of centroid, K rows by M features
    # input: cl_feat    - list of features (col names)
    # input: id_cols    - list of index cols (e.g. id) to include in output 
    # output: output_df - dataframe with same number of rows as input, 
    #         K columns of distances to each clusters
    #         1 column of cluster_labels
    #         x column of indices in idx_cols

    n_cluster = nrow(center_mat)
    n_feat = ncol(center_mat)
    n_input = nrow(input_df)

    if(!(typeof(center_mat) %in% c('double','interger') & is.matrix(center_mat))){
        stop('The argument "center_mat" must be numeric matrix')
    } else if(length(cl_feat) != n_feat) {
        stop(sprintf('cl_feat size: %d , center_mat n_col: %d, they have to match!',length(cl_feat), n_feat))
    } else {
        # register MultiCore backend through doMC and foreach package
        doMC::registerDoMC(cores = use_ncore)

        # create job_key for mapping/spliting the input data
        input_df[,'job_idx'] = sample(1:use_ncore, n_input, replace = TRUE)

        # create row_key for tracing the original row order which will be shuffled by mapreduce
        input_df[,'row_idx'] = seq(n_input)

        # use ddply (df input, df output) to split-process-combine
        output_df = ddply(
            input_df[, c('job_idx','row_idx',cl_feat,id_cols)], # input big data 
            'job_idx',                       # map/split by job_idx
            function(chunk) {                # work on each chunk
                dist = data.frame(calc_mat2mat_dist(chunk[,cl_feat], center_mat))
                names(dist) = c(paste0('dist2c_', seq(n_cluster)), 'pred_cluster')
                dist[,id_cols] = chunk[,id_cols]
                dist[,'row_idx'] = chunk[,'row_idx']
                dist                        # product of mapper
                        }, .parallel = TRUE) # end of ddply
        # sort back to original row order

        output_df = output_df[order(output_df$row_idx),]
        output_df[c('job_idx')] = NULL
        return(output_df)
    }

}
XX
la source