【发布时间】:2017-11-09 00:05:32
【问题描述】:
我需要使用邻域策略在 3d 数组中执行空间聚类。更清楚地说:我有一个表示为稀疏 3d 数组的 3d 图像。一些实例是 1,而大多数是 0。我想将彼此相邻的等于 1 的实例聚集在一起(即,如果我们将每个实例想象成一个立方体,我想将共享一个面、边或角且等于 1)。
我需要在 R 中执行此操作,因为此步骤是机器学习较长管道的一部分,并且我正在尝试在单个环境中实现整个管道以最大程度地减少头痛。 我发现了一个与当前here 略有相关的已回答问题。然而,在这种情况下,集群的数量是事先知道的,而在我的例子中,集群的数量可以是从 1 到等于 1 的实例数(前提是没有实例与另一个实例相邻)。
我可以为此目的编写一个函数,但这会很耗时并且可能效率不高,因为除了寻找非零实例之外,我想不出任何其他策略,检查每个相邻实例,如果其中任何一个是非零,而不是检查它的邻居等等。
由于集群步骤包含在嵌套交叉验证循环中,您可以自己看到我需要更高效的东西(或者可能只是用 C 编写的相同东西,以便更快)。
你们中有人知道任何可以帮助我的功能或包吗?
更新
为了回答评论,我的“稀疏”数组是稀疏的,因为大多数元素为零,而不是它以稀疏格式保存。 这是一个玩具示例(这确实是围绕我的原始数组的非零元素进行的裁剪,具有暗淡的 (91,109,91))。
sparse_array = structure(c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0), .Dim = c(13L, 3L, 6L))
更新 2
我正在使用 RStudio 1.0.153 和 R 版本 3.4.2(短夏)的 Windows x64 机器上工作
更新 3
我已经尝试了@gdkrmr 给出的答案,虽然它对于给出的示例运行良好,但它无法推广到更大和更复杂的图像。具体来说,它过度分离了我图像中的集群,这意味着确实相互接触的体素有时会分裂成不同的集群。 你可以自己想象它下载这个image并运行以下代码
读取 3D 图像
library(oro.nifti)
roi <- readNIfTI("image_to_cluster.nii")
roi_img <- cal_img(roi)
将数据读取为数组
array_img <- roi@.Data
以稀疏格式转换
sparse_format <- (array_img > 0) %>%
which(., arr.ind = TRUE)
找到相邻的体素
neighborhoods <- sparse_format %>%
dist %>%
as.matrix %>%
{. < 2}
分配集群标签
cluster <- 1:nrow(sparse_format)
for (i in 1:nrow(sparse_format)) {
cl_idx <- cluster[i]
cluster[neighborhoods[, i]] <- cl_idx
}
sparse_format <- sparse_format %>%
as_data_frame(.) %>%
mutate(cluster_id = cluster)
将集群写入新的 3d 图像
new_img <- roi
new_img@.Data <- array(0,c(74,92,78))
for (cl in cluster) {
new_img@.Data[sparse_format %>% filter(., cluster_id == cl) %>% select(dim1,dim2,dim3) %>% as.matrix] <- cl
}
writeNIfTI(new_img, "test", verbose=TRUE)
现在,如果您打开文件test.nii.gz(您可以使用例如mricron),您将看到在大致坐标37 23 15 处有一个大集群,它已被分成3 个不同的集群,即使所有体素已连接。
【问题讨论】:
-
1) 如果性能对您很重要,您可以使用
Rcpp包。 2) 在索引上使用dist函数应该在3d 数组中工作,每两个距离< 2的点应该是邻居,但这对于d > 3不起作用。 3)我还猜想,如果对 3d 数组的索引进行排序,您的算法可以快几个数量级。 -
感谢您的评论。你对我来说有点太快了: 1 - 我查看了 Rcpp 包,但我不确定如何使用它来达到我的目标。 2 - 感谢您的提示 3 - 第 3 点是什么意思?
-
1) 您将使用 Rcpp 在 C/C++ 中实现您自己的算法 Rcpp 只是让 C++ 和 R 的接口变得更容易。 3)如果您实现自己的算法并且知道索引已排序,则无需比较所有点与所有点,即在一维情况下:如果索引或您的索引是:
c(1, 3, 4, 5)你知道,仅检查前两个元素后,前两个不是邻居。 -
一个问题:你的数组是以稀疏格式保存的,还是一个密集的数组,在某种意义上它主要由零组成?这就是为什么您应该始终提供可重现的示例。
-
我已更新问题以回答@gdkrmr 问题并添加了可重现的示例
标签: r multidimensional-array cluster-analysis spatial