针对分组坐标运行DBSCAN

问题描述

我试图针对某些分组坐标运行DBSCAN,以获取子集群。我已经对一些空间数据进行了聚类,现在我想根据它们中点的密度进一步划分这些区域。我认为DBSCAN可能是最好的方法

我的问题是我无法弄清楚如何分别对每个集群运行DBSCAN,然后将集群分配输出为新列。这里是一些示例数据:

library(dplyr)
library(dbscan)

# Create sample data
df <- data.frame(
  "ID"=1:200,"X"=c(1.0083,1.3166,1.3072,1.1311,1.2984,1.2842,1.1856,1.3451,1.1932,1.0926,1.2464,1.3197,1.2331,1.2996,1.3482,1.1944,1.2800,1.3051,1.4471,0.9068,1.3150,1.1846,1.0232,1.0005,1.0640,1.3177,1.1015,0.9598,1.0354,1.2203,0.8388,0.8655,1.3387,1.0133,1.0106,1.1753,1.3200,1.0139,1.1511,1.3508,1.2747,1.3681,1.1074,1.2735,1.2245,0.9695,1.3250,1.0537,1.2020,1.3093,0.9268,1.3244,1.2626,1.3123,1.2819,1.1063,0.8759,1.0063,1.0173,1.0187,1.2396,1.0241,1.2619,1.2682,1.0008,1.0827,1.3639,1.3099,1.0004,0.8886,1.2359,1.1370,1.2783,1.0803,1.1918,1.1156,1.3313,1.1205,1.0776,1.3895,1.3559,0.8518,1.1315,1.3521,1.2281,1.2589,0.9974,1.1487,1.4204,0.9998,1.0154,1.0098,0.8851,1.0252,0.9331,1.2197,1.0084,1.2303,1.2808,1.3125,0.5500,0.6694,0.3301,0.3787,0.6492,0.6568,0.6773,0.3769,0.6237,0.7265,0.5509,0.3579,0.7201,0.2631,0.3881,0.7596,0.3343,0.7049,0.3430,0.2951,0.5483,0.7699,0.3806,0.6555,0.2524,0.4030,0.6329,0.5006,0.2701,0.0822,0.5442,0.5233,0.7105,0.5660,0.3962,0.3187,0.3143,0.5673,0.3731,0.7310,0.6376,0.4864,0.8865,0.3352,0.7540,0.0690,0.7983,0.6990,0.4090,0.5658,0.5636,0.5420,0.7223,0.6146,0.5648,0.2711,0.3422,0.7214,0.2196,0.2848,0.6496,0.7907,0.7418,0.7825,0.4550,0.4361,0.7417,0.2661,0.8978,0.7875,0.2343,0.3853,0.6874,0.7761,0.2905,0.6092,0.5329,0.6189,0.0684,0.5726,0.5740,0.7060,0.4609,0.3568,0.7037,0.2874,0.6200,0.7149,0.5100,0.7059,0.2520,0.3105,0.6870,0.7888,0.3674,0.6514,0.7271,0.6679,0.3752,0.7067),"Y"=c(-1.2547,-1.1499,-1.1803,-1.0626,-1.2877,-1.1151,-1.0958,-1.1339,-1.0808,-1.5461,-1.0775,-1.1431,-1.0499,-1.1521,-1.1675,-1.0963,-1.1407,-1.1916,-1.1229,-1.2297,-1.1308,-1.0341,-1.3071,-1.2370,-1.5043,-1.1154,-1.5452,-1.0349,-1.5412,-1.0348,-1.3620,-1.3776,-1.1830,-1.2552,-1.2354,-1.0838,-1.1214,-1.2396,-1.4937,-1.0793,-1.1857,-1.0679,-1.5425,-1.1633,-1.1620,-1.0750,-1.3493,-1.4155,-1.1354,-1.0615,-1.1494,-1.1582,-1.1800,-1.5230,-1.3019,-1.2484,-1.5490,-1.2435,-1.0487,-1.2330,-1.1234,-1.0924,-1.0702,-1.0446,-1.1077,-1.1144,-1.2170,-1.2715,-1.1537,-1.5077,-1.1305,-1.3396,-1.2107,-1.5458,-1.1482,-1.1224,-1.3690,-1.2058,-1.1685,-1.3400,-1.5033,-1.2152,-1.3805,-1.1439,-1.5183,-1.4288,-1.1252,-1.2511,-1.5429,-1.3333,-1.1851,-1.1367,-1.3952,-1.1240,-1.2113,-1.1632,-1.1965,-0.9917,-0.7416,-0.7729,-1.1279,-0.9323,-0.9372,-0.7013,-1.1746,-0.9191,-0.9356,-0.7873,-1.1957,-0.9838,-0.5825,-1.0738,-0.9302,-0.7713,-0.9407,-0.7774,-0.8160,-0.9861,-1.0440,-0.9896,-0.6478,-0.8865,-1.0601,-1.0640,-0.9898,-0.5989,-0.7375,-0.7689,-0.9799,-0.9147,-1.1048,-0.9735,-0.8591,-0.7913,-1.0085,-0.7231,-0.9688,-0.9272,-0.9395,-0.9494,-0.7859,-1.0817,-0.7262,-0.9915,-0.9329,-1.0953,-1.0425,-1.0806,-1.0132,-0.8514,-1.0785,-1.1109,-0.8542,-1.0849,-0.9665,-0.5940,-0.6145,-0.7830,-0.9601,-0.8996,-0.7717,-0.7447,-1.0406,-1.0067,-0.5710,-0.9839,-1.0594,-0.7069,-1.1202,-0.9705,-1.0100,-0.6377,-1.0632,-0.9450,-0.9163,-0.7865,-1.0090,-1.1005,-1.0049,-0.8042,-1.0781,-0.6829,-0.5962,-1.0759,-0.7918,-0.9732,-0.7353,-0.5615,-1.2002,-0.9295,-0.9944,-1.1570,-0.9524,-0.9257,-0.9360,-1.1328,-0.7661),"cluster"=c(1,1,2,2))

# How do you run DBSCAN against the points within each cluster?

我首先想到我会尝试在dplyr中使用group_by函数,但DBSCAN需要输入数据矩阵,而group_by不适用于矩阵。

matrix <- as.matrix(df[,-1])
set.seed(1234)
db = matrix %>%
  group_by(cluster) %>%
  dbscan(matrix,0.4,4)

#Error in UseMethod("group_by_") : 
#  no applicable method for 'group_by_' applied to an object of class "c('matrix','double','numeric')"

我也尝试过使用by(),但是对于每个群集分组都会得到重复的结果,这是不正确的:

by(data = df,INDICES = df$cluster,FUN = function(x) {
  out <- dbscan(as.matrix(df[,c(2:3)]),eps=.0215,minPts=4)
})

#df$cluster: 1
#DBSCAN clustering for 200 objects.
#Parameters: eps = 0.0215,minPts = 4
#The clustering contains 10 cluster(s) and 138 noise points.
#
#  0   1   2   3   4   5   6   7   8   9  10 
#138  11  12   4   5   8   2   4   8   4   4 
#
#Available fields: cluster,eps,minPts
#-------------------------------------------------------------------------- 
#df$cluster: 2
#DBSCAN clustering for 200 objects.
#Parameters: eps = 0.0215,minPts

有人能指出我正确的方向吗?

解决方法

为清楚起见,dbscan::dbscandata.frame对象上工作正常。您无需转换为矩阵。它返回一个对象,该对象包含一个向量,该向量的维数与输入中的记录数相同。问题是,dplyr将变量作为单独的向量而不是data.framematrix对象公开给其他函数。您可以随意执行以下操作:

df %>%
  group_by(cluster) %>%
  mutate(
    dbscan_cluster = dbscan::dbscan(
      data.frame(X,Y),eps = 0.0215,minPts = 4
    )[["cluster"]]
  )

dplyr不是必需的,by也可以工作,您只需要提供一个通用函数,而不是直接引用源对象的函数。您的数据必须已经按集群排序。

df$dbscan_cluster <- unlist(
  by(
    df,INDICES = df$cluster,function(x) dbscan::dbscan(x[,c(2,3)],minPts = 4)[["cluster"]]
  )
)

但是,如果您没有好的方法来选择epsilon,您仍然可以获得垃圾结果。您可以考虑改用dbscan::optics