¿Existe una función en R que tome los centros de los grupos que se encontraron y asigne los grupos a un nuevo conjunto de datos?

14

Tengo dos partes de un conjunto de datos multidimensionales, llamémoslas trainy test. Y quiero construir un modelo basado en el conjunto de datos del tren y luego validarlo en el conjunto de datos de prueba. Se conoce el número de grupos.

Intenté aplicar el agrupamiento k-means en R y obtuve un objeto que contiene los centros de los grupos:

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

¿Existe una función en R que tome los centros de los grupos que se encontraron y asigne grupos a mi conjunto de datos de prueba?

¿Cuáles son los otros métodos / algoritmos que puedo probar?

usuario2598356
fuente
Bienvenido al sitio, @ user2598356. ¿Puedes enmarcar esto de una manera más general (no específica de R)? Si solo solicita una función R, esta pregunta estaría fuera de tema para CV (consulte nuestra página de ayuda ). Además, también estaría fuera de tema en Stack Overflow , ya que no tiene un ejemplo reproducible . Si puede editar esto para incluirlo en el tema aquí o en SO, hágalo. De lo contrario, esta Q puede estar cerrada.
gung - Restablece a Monica
Esta pregunta parece estar fuera de tema porque se trata de encontrar una función R.
gung - Restablece a Monica
1
Pero, ¿qué pasa con la última pregunta: "¿Cuáles son los otros métodos / algoritmos que puedo probar?". En realidad, la respuesta que obtuve se refiere a la implementación de los métodos, que es un tema de CV, ¿o me equivoco?
user2598356
1
@gung Puede que tenga razón, en cuyo caso invito al usuario259 ... a marcar esta pregunta para la migración. Sin embargo, la última parte de la pregunta sobre otros métodos y algoritmos sugiere que nuestra comunidad puede estar en una buena posición para ofrecer ayuda y consejos útiles.
whuber
¡Gracias! La función funciona bien, pero lleva demasiado tiempo si tiene más de 50k filas. ¿Alguna idea para hacerlo más ligero?

Respuestas:

11

Puede calcular las asignaciones de clúster para un nuevo conjunto de datos con la siguiente función:

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

asignación de clúster

o puede usar el paquete flexclust , que tiene un predictmétodo implementado para 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")

diagrama de flexclust

Hay también métodos de conversión para convertir los resultados de las funciones de racimo como stats::kmeanso cluster::pama objetos de la clase kccay viceversa:

as.kcca(cl, data=x)
# kcca object of family ‘kmeans’ 
#
# call:
# as.kcca(object = cl, data = x)
#
# cluster sizes:
#
#  1  2 
#  50 50 
rcs
fuente
¡Muchas gracias! Solo una pregunta: ¿cómo maneja los métodos kcca el número de inicios (¿optimiza el análisis con respecto a los puntos de partida)?
user2598356
¿Qué quieres decir con número de arranques? La stepFlexclustfunción ejecuta algoritmos de agrupación repetidamente para diferentes números de agrupaciones y devuelve el mínimo dentro de la solución de distancia de agrupación para cada una.
rcs
1

Paso 1: una función que calcula la distancia entre un vector y cada fila de una matriz

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

Paso 2: una función que aplica la computadora vec2mat a cada fila de input_matrix

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

paso 3. aplicar la función mat2mat

calc_mat2mat_dist(my_input_mat, kmeans_model$centers)

etapa 4. Opcionalmente, use plyr :: ddply y doMC para paralelizar mat2mat para grandes conjuntos de datos

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
fuente