r

时间:2017-11-03 10:44:45

标签: r multidimensional-array cluster-analysis spatial

我需要使用邻域策略在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

我正在使用Windows x64机器,使用RStudio 1.0.153和R版本3.4.2(夏季短暂)

更新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个不同的群集,即使所有体素都已连接。

2 个答案:

答案 0 :(得分:1)


您可以使用spatstat包来执行此操作。你需要新的 从github创建分支connected.pp3,如果你可以安装 加载devtoolsremotes包(我在这里使用 remotes):

library(remotes)
install_github("spatstat/spatstat")

library(spatstat)

网格和边界框

grid <- expand.grid(0:4,0:4,0:4)
bb <- box3(range(grid[,1]), range(grid[,2]), range(grid[,3]))

稀疏数据数组(以及稀疏行的id)

grid$id <- 1:nrow(grid)
set.seed(42)
a <- grid[sample(nrow(grid), 20),]
a
#>     Var1 Var2 Var3  id
#> 115    4    2    4 115
#> 117    1    3    4 117
#> 36     0    2    1  36
#> 102    1    0    4 102
#> 78     2    0    3  78
#> 63     2    2    2  63
#> 88     2    2    3  88
#> 16     0    3    0  16
#> 77     1    0    3  77
#> 82     1    1    3  82
#> 53     2    0    2  53
#> 116    0    3    4 116
#> 106    0    1    4 106
#> 29     3    0    1  29
#> 52     1    0    2  52
#> 104    3    0    4 104
#> 107    1    1    4 107
#> 13     2    2    0  13
#> 51     0    0    2  51
#> 60     4    1    2  60

转换为3D点图案并查找连接的组件(返回为 所谓的分数标记)。正如@gdkrmr指出的任何一点 距离小于2是邻居(这里我们使用1.8,但任何东西 sqrt(3)和2之间应该有效。

x <- pp3(a[,1], a[,2], a[,3], bb)
x_labelled <- connected.pp3(x, R = 1.8)
df <- data.frame(cluster_id = marks(x_labelled), point_id = a$id)

为了更好的打印,我们根据群集ID进行排序

df[order(df$cluster_id, df$point_id),]
#>    cluster_id point_id
#> 1           1      115
#> 14          2       29
#> 19          2       51
#> 15          2       52
#> 11          2       53
#> 20          2       60
#> 6           2       63
#> 9           2       77
#> 5           2       78
#> 10          2       82
#> 7           2       88
#> 4           2      102
#> 16          2      104
#> 13          2      106
#> 17          2      107
#> 12          2      116
#> 2           2      117
#> 8           3       16
#> 3           3       36
#> 18          4       13

答案 1 :(得分:0)

这是一个纯粹的R解决方案,如果sqrt(d) < 2

,它会利用相邻体素的最大距离为d <= 3
library(rgl)
library(magrittr)
sparse_format <- (sparse_array > 0) %>%
  which(., arr.ind = TRUE)
neighborhoods <- sparse_format %>%
  dist %>%
  as.matrix %>%
  {. < 2}
n <- nrow(sparse_format)

perm <- 1:n
for (i in 1:n) {
  perm[i:n] <- perm[i:n][
    order(neighborhoods[perm[i], perm][i:n], 
          decreasing = TRUE)
  ]
}
neighborhoods <- neighborhoods[perm, perm]
sparse_format <- sparse_format[perm, ]

cluster <- 1:n
for (i in 1:n) {
  cl_idx <- cluster[i]
  cluster[neighborhoods[, i]] <- cl_idx
}
plot3d(sparse_format, col = cluster)

更新:添加neighborhoods矩阵的排序以查找连接的群集。这已变得非常慢(对于您的示例图像约为30秒),但我认为仍有很多优化空间。如果您想要一个非常快速的解决方案,请查看Julia语言,尤其是Images.jl

更新:快速完成第一个循环。