【问题标题】:Finding the k-largest clusters in dbscan result在 dbscan 结果中查找 k 最大的集群
【发布时间】:2020-08-27 03:31:12
【问题描述】:

我有一个数据框 df,由 2 列组成:xy 坐标。 每行代表一个点。 我将它输入到 dbscan 函数中以获取 df 中点的簇。

library("fpc")
db = fpc::dbscan(df, eps = 0.08, MinPts = 4)
plot(db, df, main = "DBSCAN", frame = FALSE)

通过print(db)可以看到dbscan返回的结果。

> print(db)
dbscan Pts=13131 MinPts=4 eps=0.08
         0    1    2  3  4   5  6 7  8  9 10 11 12 13 14 15 16 17 18 19 20 21
border 401   38   55  5  2   3  0 0  0  8  0  6  1  3  1  3  3  2  1  2  4  3
seed     0 2634 8186 35 24 561 99 7 22 26  5 75 17  9  9 54  1  2 74 21  3 15
total  401 2672 8241 40 26 564 99 7 22 34  5 81 18 12 10 57  4  4 75 23  7 18
       22 23 24 25 26 27 28  29 30 31 32 33  34 35 36 37 38 39 40 41 42 43 44
border  4  1  2  6  2  1  3   7  2  1  2  3  11  1  3  1  3  2  5  5  1  4  3
seed   14  9  4 48  2  4 38 111  5 11  5 14 111  6  1  5  1  8  3 15 10 15  6
total  18 10  6 54  4  5 41 118  7 12  7 17 122  7  4  6  4 10  8 20 11 19  9
       45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
border  2  4  2  1  3  2  1  1  3  1  0  2  2  3  0  3  3  3  3  0  0  2  3  1
seed   15  2  9 11  4  8 12  4  6  8  7  7  3  3  4  3  3  4  2  9  4  2  1  4
total  17  6 11 12  7 10 13  5  9  9  7  9  5  6  4  6  6  7  5  9  4  4  4  5
       69 70 71
border  3  3  3
seed    1  1  1
total   4  4  4

从上面的总结中,我可以看到簇 2 包含 8186 个种子点(核心点),簇 1 包含 2634 个种子点,簇 5 包含 561 个点。

我将最大的集群定义为包含最多种子点的集群。所以,在这种情况下,最大的簇是簇 2。第 1、2、3 大的簇分别是 2、1 和 5。

它们是否可以直接返回最大集群或一般 k 最大集群中的行(点)?

我可以间接地做到这一点。

  1. 我可以通过 db$cluster.
  2. 因此,我可以使用 db$cluster 作为新的数据框 df2 除了原始 x 列和 y 之外的新附加列 列。
  3. 然后,我可以根据中的簇数聚合 df2 第三列,找出每个聚类中的点数。
  4. 之后,我可以找到 k 最大的组,分别是 2、1 和 5 再次。
  5. 最后,我可以选择 df2 中第三列值为 2 的行来返回最大聚类中的点。

但上述方法重新计算了许多已知结果,如 print(db) 的摘要中所述。

【问题讨论】:

  • 创建您自己的函数,其中包含您概述的 5 个“间接”步骤。此函数的输入参数可以是 dbscan 函数、数据和 k 的结果。您的函数的输出是您想要的数据框,其中包含对应于第 k 个最大集群的行。瞧,你现在有了获得你想要的东西的直接方法。 :)
  • 我认为我应该利用 print(db) 中列出的摘要的结果,而不是从草图中重新计算所有内容。但关键是,我不知道如何从摘要中提取信息。 >_

标签: r dbscan


【解决方案1】:

dbscan 函数似乎没有保留数据。

library(fpc)
set.seed(665544)
n <- 600
df <- data.frame(x=runif(10, 0, 10)+rnorm(n, sd=0.2), y=runif(10, 0, 10)+rnorm(n,sd=0.2))
(dbs <- dbscan(df, 0.2))
#dbscan Pts=600 MinPts=5 eps=0.2
#        0  1  2  3  4  5  6  7  8  9 10 11
#border 28  4  4  8  5  3  3  4  3  4  6  4
#seed    0 50 53 51 52 51 54 54 54 53 51  1
#total  28 54 57 59 57 54 57 58 57 57 57  5

attributes(dbs)
#$names
#[1] "cluster" "eps"     "MinPts"  "isseed" 

#$class
#[1] "dbscan"

您的间接步骤不是那么间接(只需要两行),并且这些命令不会重新计算集群。所以只需运行这些命令,或者将它们放在一个函数中,然后在一个命令中调用该函数。

cluster_k <- function(dbs, data, k){
  kth <- names(rev(sort(table(dbs$cluster)))[k])
  data[dbs$cluster == kth,]
}

cluster_k(dbs=dbs, data=df, k=1)
##            x        y
## 3   6.580695 8.715245
## 13  6.704379 8.528486
## 23  6.809558 8.160721
## 33  6.375842 8.756433
## 43  6.603195 8.640206
## 53  6.728533 8.425067

## a data frame with 59 rows

【讨论】:

  • 太棒了,谢谢!由于我是 R 新手,我仍然很难相信你可以用 2 行代码将这 5 个步骤实现为一个优雅的函数。最让我惊讶的语法是“data[dbs$cluster == kth,]”。我认为这意味着(1)将 dbs$cluster 添加为 data 中的新列 data$cluster,(2)选择具有 data$cluster== kth 的行,(3)从数据中删除 data$cluster。
  • dbscan 函数返回与数据长度相同但顺序与数据相同的簇向量。这是至关重要的。它确实定义了 R 以及使程序如此吸引人的原因。所以我们可以使用那个向量,或者在这种情况下是一个逻辑向量,来索引数据的行。 :)
  • 事实上,只有一行是可能的,但通常最好让代码更具可读性。也更容易调试或修改。
  • 我也可以看看单行版本吗?我很喜欢这种简洁的写作风格。
  • 您只需将第二行中的kth 替换为第一行中kth 的表达式。 data[dbs$cluster==names(rev(sort(table(dbs$cluster)))[k],]。不过有点丑。
猜你喜欢
  • 2016-02-28
  • 2019-09-19
  • 2020-01-16
  • 2013-09-04
  • 2014-05-22
  • 2021-07-07
  • 2019-08-31
  • 1970-01-01
  • 2023-03-23
相关资源
最近更新 更多