将 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)
})
这是一些数据。
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
.
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)
})