将 kmeans 的 clusters/centers 整理回原始数据框

Wrangling clusters/centers of kmeans back into original data frame

这是一些数据。

df <- data.frame(groupvar=rep(c('a','b'),100),v1=rnorm(200),v2=rnorm(200))

现在我在每个组中做 k 均值:

require(dplyr)

kobjs = df %>% group_by(groupvar) %>%
  do(kclust = kmeans(cbind(.$v1,.$v2),centers=5))

"kobjs" 看起来像这样:

  groupvar      kclust
    (fctr)       (chr)
1        a <S3:kmeans>
2        b <S3:kmeans>

我想抓取集群分配(理想情况下是中心点)并将它们附加到原始数据框。我以为你可以用扫帚来做这个:

require(broom)
merged = kobjs %>%
  group_by(groupvar) %>% do(augment(.$kclust[[1]],df))

但这会以某种方式产生 400X4 矩阵而不是 200X4。那是怎么发生的?我如何获得我想要的行为?

EDIT1:根据 aosmith 的一些见解,以我想要的方式解决了问题。可能有一种方法可以让它更优雅(left_join 是必要的吗?)但这是我想要的行为:

kobjs = df %>%
  do(kmeans(cbind(.$v1,.$v2),centers=5) %>%
       fitted(method="centers") %>% 
       data.frame(cluster=rownames(.),entry=1:length(.),row.names=NULL)) %>%
  left_join(df %>% group_by(groupvar) %>% mutate(entry=1:n()),
            by=c("entry","groupvar"))

目前您正在对整个 df 使用 augment,而不是仅对每个组使用子集。这就是为什么您得到的数据集是预期长度的两倍。

因此您需要使用 kobjs 执行如下操作。我在制作 kobjs.

之前将种子设置为 16
kobjs %>%
    group_by(groupvar) %>% 
    do(augment(.$kclust[[1]], df[df$groupvar == .$groupvar,]))

Source: local data frame [200 x 5]
Groups: groupvar [2]

   .rownames groupvar          v1         v2 .cluster
       (chr)   (fctr)       (dbl)      (dbl)   (fctr)
1          1        a  0.30291472  0.2203811        1
2          3        a -0.51381305  0.1480162        1
3          5        a -0.75246517 -0.6407782        2
4          7        a  0.06453416  1.2965984        3
5          9        a -0.62353541 -1.3240648        2
6         11        a  0.18435121 -1.0513837        5
7         13        a -0.26481666  2.8117979        4
8         15        a  0.56643441  0.1434451        1
9         17        a -0.30406035 -0.1477244        1
10        19        a  1.62538120 -0.5972593        5
..       ...      ...         ...        ...      ...

为了得到更像你想要的东西。

您还有其他选择。例如,您可以在原始 do 步骤中使用 augment

set.seed(16)
df %>% group_by(groupvar) %>%
    do(augment(kmeans(cbind(.$v1,.$v2),centers=5), .))

Source: local data frame [200 x 4]
Groups: groupvar [2]

   groupvar          v1         v2 .cluster
     (fctr)       (dbl)      (dbl)   (fctr)
1         a  0.30291472  0.2203811        1
2         a -0.51381305  0.1480162        1
3         a -0.75246517 -0.6407782        2
4         a  0.06453416  1.2965984        3
5         a -0.62353541 -1.3240648        2
6         a  0.18435121 -1.0513837        5
7         a -0.26481666  2.8117979        4
8         a  0.56643441  0.1434451        1
9         a -0.30406035 -0.1477244        1
10        a  1.62538120 -0.5972593        5
..      ...         ...        ...      ...

您还可以从 kmeans 对象中提取 cluster,然后使用以下 do 编码将它们添加到数据集中。但是,这不使用 broom

set.seed(16)
df %>% group_by(groupvar) %>%
    do(data.frame(., kclust = kmeans(cbind(.$v1,.$v2),centers=5)$cluster))

Source: local data frame [200 x 4]
Groups: groupvar [2]

   groupvar          v1         v2 kclust
     (fctr)       (dbl)      (dbl)  (int)
1         a  0.30291472  0.2203811      1
2         a -0.51381305  0.1480162      1
3         a -0.75246517 -0.6407782      2
4         a  0.06453416  1.2965984      3
5         a -0.62353541 -1.3240648      2
6         a  0.18435121 -1.0513837      5
7         a -0.26481666  2.8117979      4
8         a  0.56643441  0.1434451      1
9         a -0.30406035 -0.1477244      1
10        a  1.62538120 -0.5972593      5
..      ...         ...        ...    ...

编辑 以添加在单个 do 调用中从模型中保存两个内容的示例。

您可以在 do 中拟合和命名模型对象,然后从中提取多个汇总值,但这涉及到大括号的使用(我不确定它们是否包含在您的非理性恐惧中方括号 ;-)).

这里有两种方法,首先创建model,将拟合值拉出为fit,然后将其与原始数据集绑定在一起(这就是第一个. data.frame代表)。

df %>% group_by(groupvar) %>%
    do( { 
        model = kmeans(cbind(.$v1, .$v2), centers = 5)
        fit = fitted(model, methods = "centers")
        data.frame(., fit, cluster = rownames(fit), row.names = NULL) 
    })

我并不总是喜欢做很多命名,所以第二个选项直接在 model 上工作并跳过 fit 步骤。

df %>% group_by(groupvar) %>%
    do( { 
        model = kmeans(cbind(.$v1, .$v2), centers = 5)
        data.frame(., fitted(model, methods = "centers"), cluster = model$cluster, row.names = NULL) 
    })