【问题标题】:Wrangling clusters/centers of kmeans back into original data frame将 kmeans 的集群/中心重新整理回原始数据帧
【发布时间】:2015-10-07 07:42:39
【问题描述】:

这是一些数据。

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

【问题讨论】:

    标签: r dplyr broom


    【解决方案1】:

    目前,您在整个 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) 
        })
    

    【讨论】:

    • 谢谢。第一个例子是我正在寻找的行为。我喜欢其他两种方法(我猜我对方括号有一种非理性的恐惧),但问题是我不知道如何在同一个 kmeans 调用中同时获取集群分配和中心。例如:do(data.frame(., kclust = kmeans(cbind(.$v1,.$v2),centers=5)$cluster, kcenter = fit(kmeans(cbind(.$v1,.$v2),centers =5)) 是两个 kmeans 调用而不是一个。有什么方法可以通过同一个调用同时获取两个?
    • 没关系,我想我明白了 - 请参阅原始帖子中的编辑。
    • @NicholasRoot 我添加了一个编辑,以显示一些替代方法,用于在单个 do 调用中拟合模型并从中提取内容(无需多次拟合)。
    猜你喜欢
    • 2018-10-28
    • 2018-01-28
    • 1970-01-01
    • 1970-01-01
    • 2015-02-17
    • 2019-11-12
    • 2017-06-01
    • 2019-08-06
    • 2017-01-01
    相关资源
    最近更新 更多