使用 dplyr 和 broom 在训练和测试集上计算 kmeans

Posted

技术标签:

【中文标题】使用 dplyr 和 broom 在训练和测试集上计算 kmeans【英文标题】:Using dplyr and broom to compute kmeans on a training and test set 【发布时间】:2017-02-27 05:15:11 【问题描述】:

我正在使用 dplyr 和 broom 为我的数据计算 kmeans。我的数据包含 X 和 Y 坐标的测试和训练集,并按某个参数值(在本例中为 lambda)分组:

mds.test = data.frame()
for(l in seq(0.1, 0.9, by=0.2)) 
  new.dist <- run.distance.model(x, y, lambda=l)
  mds <- preform.mds(new.dist, ndim=2)
  mds.test <- rbind(mds.test, cbind(mds$space, design[,c(1,3,4,5)], lambda=rep(l, nrow(mds$space)), data="test"))


> head(mds.test)
                        Comp1       Comp2 Transcripts Genes Timepoint Run lambda data
7A_0_AAGCCTAGCGAC -0.06690476 -0.25519106       68125  9324     Day 0  7A    0.1 test
7A_0_AAATGACTGGCC -0.15292848  0.04310200       28443  6746     Day 0  7A    0.1 test
7A_0_CATCTCGTTCTA -0.12529445  0.13022908       27360  6318     Day 0  7A    0.1 test
7A_0_ACCGGCACATTC -0.33015913  0.14647857       23038  5709     Day 0  7A    0.1 test
7A_0_TATGTCGGAATG -0.25826098  0.05424976       22414  5878     Day 0  7A    0.1 test
7A_0_GAAAAAGGTGAT -0.24349387  0.08071162       21907  6766     Day 0  7A    0.1 test

我有head 上面的测试数据集,但我也有一个名为mds.train 的数据集,其中包含我的训练数据坐标。我的最终目标是对按 lambda 分组的两个集合运行 k-means,然后计算训练中心测试数据的 within.ss、between.ss 和 total.ss。感谢扫帚上的a great resource,我只需执行以下操作即可为测试集的每个 lambda 运行 kmeans:

test.kclusts  = mds.test %>% 
  group_by(lambda) %>% 
  do(kclust=kmeans(cbind(.$Comp1, .$Comp2), centers=length(unique(design$Timepoint))))

然后我可以为每个 lambda 中的每个集群计算这些数据的中心:

test.clusters = test.kclusts %>% 
  group_by(lambda) %>%  
  do(tidy(.$kclust[[1]])) 

这就是我卡住的地方。我如何计算与reference page(例如kclusts %&gt;% group_by(k) %&gt;% do(augment(.$kclust[[1]], points.matrix)))上类似显示的特征分配,其中我的points.matrixmds.test,它是一个data.frame,其中length(unique(mds.test$lambda)) 的行数是应有的行数?有没有办法以某种方式使用训练集中的中心来根据测试分配计算 glance() 统计数据?

任何帮助将不胜感激!谢谢!

编辑:更新进度。我已经弄清楚如何汇总测试/培训作业,但在尝试从两组计算 kmeans 统计数据时仍然遇到问题(测试中心的培训作业和培训中心的测试作业)。更新代码如下:

test.kclusts  = mds.test %>% group_by(lambda) %>% do(kclust=kmeans(cbind(.$Comp1, .$Comp2), centers=length(unique(design$Timepoint))))
test.clusters = test.kclusts %>% group_by(lambda) %>%  do(tidy(.$kclust[[1]])) 
test.clusterings = test.kclusts %>% group_by(lambda) %>% do(glance(.$kclust[[1]]))
test.assignments = left_join(test.kclusts, mds.test) %>% group_by(lambda) %>% do(augment(.$kclust[[1]], cbind(.$Comp1, .$Comp2)))

train.kclusts  = mds.train %>% group_by(lambda) %>% do(kclust=kmeans(cbind(.$Comp1, .$Comp2), centers=length(unique(design$Timepoint))))
train.clusters = train.kclusts %>% group_by(lambda) %>%  do(tidy(.$kclust[[1]])) 
train.clusterings = train.kclusts %>% group_by(lambda) %>% do(glance(.$kclust[[1]]))
train.assignments = left_join(train.kclusts, mds.train) %>% group_by(lambda) %>% do(augment(.$kclust[[1]], cbind(.$Comp1, .$Comp2)))

test.assignments$data = "test"
train.assignments$data = "train"
merge.assignments = rbind(test.assignments, train.assignments)
merge.assignments %>% filter(., data=='test') %>% group_by(lambda) ... ? 

我在下面附上了一个图表,说明了我在这一点上的进展。重申一下,我想为训练数据中心的测试分配/坐标(中心看起来的图)计算 kmeans 统计量(在平方和内、总平方和之间以及在平方和之间):

【问题讨论】:

看起来有一些关于制作 tidymodels 配方或模型规范的讨论,例如recipes::step_kmeans() (请参阅此处的对话和此处的链接:github.com/tidymodels/embed/issues/77#issuecomment-801300749。但尚未完成。还有 widyr::widely_kmeans() 但这也不容易应用于测试集. 【参考方案1】:

一种方法是……

    通过broom提取指定集群质心的表(基于训练集构建)。 计算测试集中每个点与使用训练集构建的每个聚类质心的距离。可以通过fuzzyjoin 包做到这一点。 测试点与欧几里得距离最短的聚类质心代表其分配的聚类。 您可以从那里计算任何感兴趣的指标。

使用从 tidymodels 的集群 example 中提取的更简单数据集,请参见下文。

library(tidyverse)
library(rsample)
library(broom)
library(fuzzyjoin)

# data and train / test set-up
set.seed(27)
centers <- tibble(
  cluster = factor(1:3), 
  num_points = c(100, 150, 50),  # number points in each cluster
  x1 = c(5, 0, -3),              # x1 coordinate of cluster center
  x2 = c(-1, 1, -2)              # x2 coordinate of cluster center
)

labelled_points <- 
  centers %>%
  mutate(
    x1 = map2(num_points, x1, rnorm),
    x2 = map2(num_points, x2, rnorm)
  ) %>% 
  select(-num_points) %>% 
  unnest(cols = c(x1, x2))

points <- 
  labelled_points %>% 
  select(-cluster)

set.seed(1234)

split <- rsample::initial_split(points)
train <- rsample::training(split)
test <- rsample::testing(split)

# Fit kmeans on train then assign clusters to test
kclust <- kmeans(train, centers = 3)

clust_centers <- kclust %>% 
  tidy() %>% 
  select(-c(size, withinss))

test_clusts <- fuzzyjoin::distance_join(mutate(test, index = row_number()), 
                         clust_centers,
                         max_dist = Inf,
                         method = "euclidean",
                         distance_col = "dist") %>% 
  group_by(index) %>% 
  filter(dist == min(dist)) %>% 
  ungroup()
#> Joining by: c("x1", "x2")

# resulting table
test_clusts
#> # A tibble: 75 x 7
#>     x1.x    x2.x index  x1.y  x2.y cluster  dist
#>    <dbl>   <dbl> <int> <dbl> <dbl> <fct>   <dbl>
#>  1  4.24 -0.946      1  5.07 -1.10 3       0.847
#>  2  3.54  0.287      2  5.07 -1.10 3       2.06 
#>  3  3.71 -1.67       3  5.07 -1.10 3       1.47 
#>  4  5.03 -0.788      4  5.07 -1.10 3       0.317
#>  5  6.57 -2.49       5  5.07 -1.10 3       2.04 
#>  6  4.97  0.233      6  5.07 -1.10 3       1.34 
#>  7  4.43 -1.89       7  5.07 -1.10 3       1.01 
#>  8  5.34 -0.0705     8  5.07 -1.10 3       1.07 
#>  9  4.60  0.196      9  5.07 -1.10 3       1.38 
#> 10  5.68 -1.55      10  5.07 -1.10 3       0.758
#> # ... with 65 more rows

# calc within clusts SS on test
test_clusts %>% 
  group_by(cluster) %>% 
  summarise(size = n(),
            withinss = sum(dist^2),
            withinss_avg = withinss / size)
#> # A tibble: 3 x 4
#>   cluster  size withinss withinss_avg
#>   <fct>   <int>    <dbl>        <dbl>
#> 1 1          11     32.7         2.97
#> 2 2          35     78.9         2.26
#> 3 3          29     62.0         2.14

# compare to on train
tidy(kclust) %>% 
  mutate(withinss_avg = withinss / size)
#> # A tibble: 3 x 6
#>        x1    x2  size withinss cluster withinss_avg
#>     <dbl> <dbl> <int>    <dbl> <fct>          <dbl>
#> 1 -3.22   -1.91    40     76.8 1               1.92
#> 2  0.0993  1.06   113    220.  2               1.95
#> 3  5.07   -1.10    72    182.  3               2.53

# plot of test and train points
test_clusts %>% 
  select(x1 = x1.x, x2 = x2.x, cluster) %>% 
  mutate(type = "test") %>% 
  bind_rows(
    augment(kclust, train) %>% 
      mutate(type = "train") %>% 
      rename(cluster = .cluster)
    ) %>% 
  ggplot(aes(x = x1, 
             y = x2, 
             color = as.factor(cluster)))+
  geom_point()+
  facet_wrap(~fct_rev(as.factor(type)))+
  coord_fixed()+
  labs(title = "Cluster Assignment on Training and Holdout Datasets",
       color = "Cluster")+
  theme_bw()

由reprex package (v2.0.0) 于 2021 年 8 月 19 日创建

(有关在 tidymodels 中简化此操作的对话链接,请参阅 OP 评论。)

【讨论】:

以上是关于使用 dplyr 和 broom 在训练和测试集上计算 kmeans的主要内容,如果未能解决你的问题,请参考以下文章

我可以在整个数据集上使用 StandardScaler(),还是应该分别在训练集和测试集上计算?

使用扫帚(增强)和dplyr与黄土适合时出错

TensorFlow:神经网络在训练集和测试集上的准确率始终为 100%

R语言plotly可视化:使用plotly可视化简单线性回归模型的回归线使用不同颜色区分训练集和测试集可视化分析模型在测试集上的泛化性能(linear regression plots)

R语言使用caret包的predict函数对模型在测试集上的表现进行推理和预测predict函数对测试数据集进行数据预处理(和训练集的初始方式保持一致):缺失值填充数值变量最小最大缩放独热编码

如何评估不同模型在一个数据集上的性能?