【问题标题】:How to predict cluster labeling using DBSCAN object and Gower distance matrix for new data in R如何使用 DBSCAN 对象和 Gower 距离矩阵预测 R 中新数据的集群标记
【发布时间】:2017-09-23 06:27:17
【问题描述】:

我在根据训练数据上的 dbscan 聚类模型预测测试数据的聚类标签时遇到问题。 我在创建模型时使用了高尔距离矩阵:

> gowerdist_train <- daisy(analdata_train,
                   metric = "gower",
                   stand = FALSE,
                   type = list(asymm = c(5,6)))

使用这个 gowerdist 矩阵,创建的 dbscan 聚类模型是:

> sb <- dbscan(gowerdist_train, eps = .23, minPts = 50)

然后我尝试使用 predict 使用上面的 dbscan 对象标记测试数据集:

> predict(sb, newdata = analdata_test, data = analdata_train)

但我收到以下错误:

frNN(rbind(data, newdata), eps = object$eps, sort = TRUE, ...) : x 必须是一个数字矩阵

我可以猜测这个错误可能来自哪里,这可能是由于缺少尚未为测试数据创建的 gower 距离矩阵。 我的问题是,我应该为所有数据(datanal_train + datanal_test)分别创建一个高尔距离矩阵并将其输入预测吗?算法如何知道测试数据与训练数据的距离是多少,以便进行标记?

在这种情况下,newdata 参数会是包含所有(训练 + 测试)数据的新高尔距离矩阵吗? predict 中的数据参数是训练距离矩阵 gowerdist_train?

我不太确定的是预测算法如何区分新创建的 gowerdist_all 矩阵中的测试和训练数据集?

这两个矩阵(所有数据的新 gowerdist 和 gowerdist_train)显然不会具有相同的维度。此外,我只为测试数据创建一个高尔距离矩阵是没有意义的,因为距离必须与测试数据相关,而不是与测试数据本身相关。


编辑:

我尝试对所有数据(训练 + 测试)使用高尔距离矩阵作为我的新数据,并在输入预测时收到错误:

> gowerdist_all <- daisy(rbind(analdata_train, analdata_test),
                         metric = "gower",
                         stand = FALSE,
                         type = list(asymm = c(5,6)))
> test_sb_label <- predict(sb, newdata = gowerdist_all, data = gowerdist_train)

ERROR: Error in 1:nrow(data) : argument of length 0 另外: 警告消息:在 rbind(data, newdata) 中: 结果不是向量长度的倍数(arg 1)

所以,我建议的解决方案不起作用。

【问题讨论】:

  • DBSCAN 上的 predict 定义并不明确。通常,它表明您在应该进行分类时使用了聚类。
  • 好的,所以我联系了负责维护 dbscan R 包的人,并被告知在 dbscan 包中执行预测存在问题(它不适用于距离矩阵)并且我应该在相应的论坛上提出这个问题,以便它在包中得到修复。与此同时,我决定尝试构建一种算法来完成这项工作。
  • 我说的是对这样做的理论支持,而不是实现(因为我不使用R,所以我不太关心包)。单个点可能导致集群合并,因此“预测”函数需要能够返回“它会导致集群 1+2 合并”或“它会在此处导致一个新集群”等。使用 DBSCAN 进行“预测”的想法是有问题的,通常表明该方法早期的想法很糟糕。

标签: r matrix distance predict dbscan


【解决方案1】:

我决定创建一个代码,在 dbscan 中使用 KNN 算法来使用 gower 距离矩阵预测集群标记。该代码不是很漂亮,并且绝对不是编程效率,但它可以工作。很高兴有任何可以改进它的建议。

pseydocode 是: 1)为所有数据计算新的高尔距离矩阵,包括测试和训练 2)在kNN函数(dbscan包)中使用上述距离矩阵来确定每个测试数据点的k个最近邻。 3)确定每个测试点的所有最近点的聚类标签。其中一些将没有集群标签,因为它们本身就是测试点 4)创建一个计数矩阵来计算每个测试点的k个最近点的聚类频率 5) 使用非常简单的似然计算根据其邻居簇(最大频率)为测试点选择簇。这部分还考虑了相邻的测试点。也就是说,只有当您将相邻测试点的数量添加到其他集群时,最大频率最大时才会选择测试点的集群。否则,它不会为该测试点决定集群并等待下一次迭代,因为希望它的更多相邻测试点已经根据他们的邻居确定了他们的集群标签。 6) 重复上述步骤(步骤 2-5),直到确定所有集群

** 注意:此算法并非一直收敛。 (一旦你做了数学,很明显为什么会这样)所以,在代码中,当非聚集测试点的数量在一段时间后没有改变时,我会打破算法。然后我用新的 knn 再次重复 2-6(更改最近邻居的数量,然后再次运行代码)。这将确保在下一轮的决定中涉及更多的分数。我已经尝试过更大和更小的knn's并且两者都有效。很高兴知道哪个更好。到目前为止,我不必运行代码两次以上来决定测试数据点的集群。

代码如下:

#calculate gower distance for all data (test + train)
gowerdist_test <- daisy(all_data[rangeofdataforgowerdist],
                        metric = "gower",
                        stand = FALSE,
                        type = list(asymm = listofasymmvars),
                        weights = Weights)
summary(gowerdist_test) 

然后使用下面的代码为测试数据标记集群。

#library(dbscan)
# find the k nearest neibours for each point and order them with distance
iteration_MAX <- 50
iteration_current <- 0
maxUnclusterRepeatNum <- 10
repeatedUnclustNum <- 0
unclusteredNum <- sum(is.na(all_data$Cluster))
previousUnclustereNum <- sum(is.na(all_data$Cluster))
nn_k = 30 #number of neighbourhoods

while (anyNA(all_data$Cluster) & iteration_current < iteration_MAX) 
{
  if (repeatedUnclustNum >= maxUnclusterRepeatNum) {
    print(paste("Max number of repetition (", maxUnclusterRepeatNum ,") for same unclustered data has reached. Clustering terminated unsuccessfully."))
    invisible(gc())
    break;
  }

      nn_test <- kNN(gowerdist_test, k = nn_k, sort = TRUE)

    # for the TEST points in all data, find the closets TRAIN points and decide statistically which cluster they could belong to, based on the clusters of the nearest TRAIN points
    test_matrix <- nn_test$id[1: nrow(analdata_test),] #create matrix of test data knn id's
    numClusts <- nlevels(as.factor(sb_train$cluster))
    NameClusts <- as.character(levels(as.factor(sb_train$cluster)))
    count_clusters <- matrix(0, nrow = nrow(analdata_test), ncol = numClusts + 1)  #create a count matrix that would count number of clusters + NA
    colnames(count_clusters) <- c("NA", NameClusts) #name each column of the count matrix to cluster numbers

    # get the cluster number of each k nearest neibhour of each test point
    for (i in 1:nrow(analdata_test)) 
      for (j in 1:nn_k)
      {  
        test_matrix[i,j] <- all_data[nn_test$id[i,j], "Cluster"]
      }
    # populate the count matrix for the total clusters of the neighbours for each test point
    for (i in 1:nrow(analdata_test))
      for (j in 1:nn_k)
      {  
       if (!is.na(test_matrix[i,j])) 
           count_clusters[i, c(as.character(test_matrix[i,j]))] <- count_clusters[i, c(as.character(test_matrix[i,j]))] + 1
       else 
          count_clusters[i, c("NA")] <- count_clusters[i, c("NA")] + 1
      }
    # add NA's (TEST points) to the other clusters for comparison
    count_clusters_withNA <- count_clusters
    for (i in 2:ncol(count_clusters))
      {  
      count_clusters_withNA[,i] <- t(rowSums(count_clusters[,c(1,i)]))
    }

    # This block of code decides the maximum count of cluster for each row considering the number other test points (NA clusters) in the neighbourhood
    max_col_countclusters <- apply(count_clusters,1,which.max) #get the column that corresponds to the maximum value of each row
    for (i in 1:length(max_col_countclusters)) #insert the maximum value of each row in its associated column in count_clusters_withNA
      count_clusters_withNA[i, max_col_countclusters[i]] <- count_clusters[i, max_col_countclusters[i]]
    max_col_countclusters_withNA <- apply(count_clusters_withNA,1,which.max) #get the column that corresponds to the maximum value of each row with NA added 
    compareCountClust <- max_col_countclusters_withNA == max_col_countclusters  #compare the two count matrices
    all_data$Cluster[1:nrow(analdata_test)] <- ifelse(compareCountClust, NameClusts[max_col_countclusters - 1], all_data$Cluster) #you subtract one because of additional NA column


    iteration_current <- iteration_current + 1

    unclusteredNum <- sum(is.na(all_data$Cluster))
    if (previousUnclustereNum == unclusteredNum)
      repeatedUnclustNum <- repeatedUnclustNum + 1
    else {
      repeatedUnclustNum <- 0
      previousUnclustereNum <- unclusteredNum
    }

    print(paste("Iteration: ", iteration_current, " - Number of remaining unclustered:", sum(is.na(all_data$Cluster))))
    if (unclusteredNum == 0)
      print("Cluster labeling successfully Completed.")

    invisible(gc())
}

我猜你可以将它用于任何其他类型的聚类算法,你如何决定训练数据的聚类标签并不重要,只要它们在运行代码之前在你的 all_data 中。 希望这有帮助。 不是最有效或最严格的代码。所以,很高兴看到如何改进它的建议。

*注意:我使用 t-SNE 将 train 的聚类与测试数据进行比较,看起来非常干净。所以,它似乎正在工作。

【讨论】:

    猜你喜欢
    • 2020-10-23
    • 2016-10-08
    • 2018-05-14
    • 2022-12-01
    • 2013-06-20
    • 1970-01-01
    • 1970-01-01
    • 2022-06-28
    • 2021-11-23
    相关资源
    最近更新 更多