打破k均值集群

时间:2018-09-23 15:04:21

标签: statistics k-means data-analysis

我有应用k均值的数据集,并且有两个聚类,但是从特定点(x,y)到两个聚类的距离相同,然后该点将进入哪个聚类。请帮我。 预先感谢。

1 个答案:

答案 0 :(得分:3)

tldr;

在平局的情况下,k-均值聚类将随机将歧义点分配给聚类。 (这是基于R对k均值聚类kmeans的实现。)


基于R中的iris数据的具体示例

  1. 让我们从加载必要的R库开始

    library(broom)
    library(tidyverse)
    
  2. 在此示例中,我们将使用Petal.Length数据集中的Petal.Widthiris测量值,为简单起见,将“ virginica”测量值排除在外,以便“ setosa”和“多功能”测量值构成了我们的两组。

    df <- iris %>%
        filter(Species != "virginica") %>%
        select(starts_with("Petal"), Species)
    
  3. 我们现在使用k = 2的k均值聚类,并为每个(Petal.LengthPetal.Width)度量分配聚类标签;由于哪个组是“ 1”而哪个组是“ 2”的分配是随机的,因此我们使用固定的种子来提高可重复性。

    set.seed(2018)
    kcl <- kmeans(df %>% select(-Species), 2)
    df <- augment(kcl, df)
    
  4. 我们显示了Petal.LengthPetal.Width的散点图;已知的Species标签用不同的颜色显示,推断的聚类关联用不同的符号显示。

    ggplot(df, aes(Petal.Length, Petal.Width, colour = Species)) +
        geom_point(aes(shape = .cluster), size = 3)
    

    enter image description here

  5. 让我们手动计算成对成对距离的集群内和;因为我们以后也会需要它,所以我们将创建一个函数calculate_d

    calculate_d <- function(df) {
        df %>%
            select(.cluster, Petal.Length, Petal.Width) %>%
            group_by(.cluster) %>%
            nest() %>%
            mutate(dist = map_dbl(data, ~sum(as.matrix(dist(.x)^2)) / (2 * nrow(.x)))) %>%
            pull(dist)
    }
    
    calculate_d(df)
    #[1]  2.0220 12.7362
    

    请注意,距离与集群内平方和(WCSS)的距离如何相同

    kcl$withinss
    #[1]  2.0220 12.7362
    
  6. 现在,让我们添加一个新的度量,该度量到两个聚类中心的距离是相同的欧几里得距离:为此,如果您将它们以直线连接,则选择正好位于两个聚类中心之间的中间的点。然后,我们需要的是一些基本的三角函数来构造该点:

    z <- kcl$centers[2, ] - kcl$center[1, ]
    theta <- atan(z[2] / z[1])
    
    dy <- sin(theta) * dist(kcl$centers) / 2
    dx <- cos(theta) * dist(kcl$centers) / 2
    
    x <- as.numeric(kcl$centers[1, 1] + dx)
    y <- as.numeric(kcl$centers[1, 2] + dy)
    

    我们将新点和2个聚类中心存储在新的data.frame中。前两行给出聚类“ 1”和“ 2”的位置,第三行包含我们的新点。

    df2 <- bind_rows(as.data.frame(kcl$centers), c(Petal.Length = x, Petal.Width = y))
    

    让我们在(Petal.LengthPetal.Width)测量的顶部显示新点以及聚类中心。

    df2 <- bind_rows(as.data.frame(kcl$centers), c(Petal.Length = x, Petal.Width = y))
    ggplot(df, aes(Petal.Length, Petal.Width)) +
        geom_point(aes(colour = Species, shape = .cluster), size = 3) +
        geom_point(data = df2, aes(Petal.Length, Petal.Width), size = 4)
    

    enter image description here

    我们确认新点和每个聚类中心之间的平方欧几里得距离确实相同;为此,我们计算新点“ 3”到聚类中心“ 1”和“ 2”的成对距离:

    as.matrix(dist(df2))[, 3]
    #     1      2      3
    #1.4996 1.4996 0.0000
    
  7. 现在,让我们将新点添加到(Petal.LengthPetal.Width)测量中,并计算成对平方距离的簇内和,首先将新点分配给簇“ 1” ”,然后将新点分配给群集“ 2”。

    # Add new point and assign to cluster "1"
    df.1 <- df %>%
        bind_rows(cbind.data.frame(
            Petal.Length = x,
            Petal.Width = y,
            Species = factor("setosa", levels = levels(df$Species)),
            .cluster = factor(1, levels = 1:2)))
    calculate_d(df.1)
    #[1]  4.226707 12.736200
    
    # Add new point and assign to cluster "2"
    df.2 <- df %>%
        bind_rows(cbind.data.frame(
            Petal.Length = x,
            Petal.Width = y,
            Species = factor("versicolor", levels = levels(df$Species)),
            .cluster = factor(2, levels = 1:2)))
    calculate_d(df.2)
    #[1]  2.02200 14.94091
    

    请注意,即使新点到任一聚类中心的距离都完全相同,聚类内部平方对的距离也不同。但是请注意,集群内平方对距离的 sum 相同!

    sum(calculate_d(df.1))
    #[1] 16.96291
    
    sum(calculate_d(df.2))
    #[1] 16.96291
    
    identical(sum(calculate_d(df.2)), sum(calculate_d(df.1)))
    # [1] TRUE
    
  8. 为表明kmeans将新点随机分配给任一聚类,我们反复对数据进行聚类。为此,我们定义了一个便利函数,该函数在k均值聚类之后返回新点的相应Species

    kmeans_cluster_data <- function(df) {
        kcl <- kmeans(df %>% select(-Species), 2)
        df <- augment(kcl, df)
        map_cluster_to_Species <- df[1:(nrow(df) - 1), ] %>%
            count(Species, .cluster) %>%
            split(., .$.cluster)
        map_cluster_to_Species[[
            df[nrow(df), ] %>%
                pull(.cluster) %>%
                as.character()]]$Species %>% as.character()
    }
    

    我们现在将同一数据重复群集100次。

    bind_cols(
        Iteration = 1:100,
        Species = map_chr(1:100, ~kmeans_cluster_data(df.1 %>% select(-.cluster)))) %>%
    ggplot(aes(Iteration, Species, group = 1)) +
        geom_line() +
        labs(title = "Assignment of new point to group")
    

    enter image description here

    请注意如何将新点随机分配给Species组。