假设我有向量x
x <- c(1, 1, 1.1, 2, 1, 2.1, 2.6)
tol <- 0.4
我如何获得在公差范围(tol
)内"唯一"的元素组的索引,如下面的列表所示。我不知道事先有多少这样的小组。
[[1]]
[1] 1 2 3 5
[[2]]
[1] 4 6
[[3]]
[1] 7
谢谢
不是100%可靠,因为它在list
s上使用unique
,但您可以尝试:
unique(apply(outer(x,x,function(a,b) abs(a-b)<tol),1,which))
#[[1]]
#[1] 1 2 3 5
#
#[[2]]
#[1] 4 6
#
#[[3]]
#[1] 7
@Roland在评论中提出的观点表明你们的要求有些模棱两可。例如,如果x<-c(1, 1.3, 1.6)
,我的线路给出了三个组:1-2、2-3和1-2-3。这是因为,从1
的角度来看,它只与1.3
相似,但从1.3
的角度来看,它与1
和1.6
都相似。
使用RANN
中的nn2
查找半径内最近邻的替代方法:
library(RANN)
x <- c(1, 1, 1.1, 2, 1, 2.1, 2.6)
tol=0.4
nn <- nn2(x,x,k=length(x),searchtype="radius",radius=tol)
m <- unique(apply(nn$nn.idx,1,sort), MARGIN=2)
sapply(seq_len(ncol(m)), function(i) m[which(m[,i] > 0),i])
##[[1]]
##[1] 1 2 3 5
##
##[[2]]
##[1] 4 6
##
##[[3]]
##[1] 7
x <- c(1, 1.3, 1.6)
nn <- nn2(x,x,k=length(x),searchtype="radius",radius=tol)
m <- unique(apply(nn$nn.idx,1,sort), MARGIN=2)
sapply(seq_len(ncol(m)), function(i) m[which(m[,i] > 0),i])
##[[1]]
##[1] 1 2
##
##[[2]]
##[1] 1 2 3
##
##[[3]]
##[1] 2 3
指出:
- 调用
nn2
在等于tol
的半径范围内,查找x
的每个元素相对于x
的所有元素的所有最近邻居。结果nn$nn.idx
是一个矩阵,其行包含x
中每个元素的最近邻索引。矩阵是密集的,并根据需要填充零。 - 集群是通过对每一行进行排序来执行的,以便可以提取唯一的行。输出
m
是一个矩阵,其中每列包含集群中的索引。同样,这个矩阵是密集的,并根据需要填充0。 - 通过对每一列进行子设置以删除零条目来提取结果列表。
对于大型x
来说,这可能更有效,因为nn2
使用KD-Tree,但正如nicola指出的那样,对于重叠的元素(相对于容差),它也会遇到同样的问题。
也许这是一个杀死蚊子的锤子,但我想到了单变量密度聚类:dbscan
库使您能够做到这一点:
library(dbscan)
groups <- dbscan(as.matrix(x), eps=tol, minPts=1)$cluster
#### [1] 1 1 1 2 1 2 3
你不需要事先知道组的数目。
它在输出中给你集群编号,但如果你愿意,你可以取组的平均值并将其四舍五入到最接近的整数。一旦得到了这些,就可以生成如下所示的列表:
split(seq_along(x), groups)
#### $`1`
#### [1] 1 2 3 5
#### ...
编辑:重叠行为:该算法将同一组属性赋予在彼此容忍度范围内的所有元素(按接近度工作)。所以如果有重叠,你可能会得到比预期更少的组。
下面是对base r中的cut
函数的另一个尝试。我们首先尝试创建名为sq
的范围向量,然后遍历属于任何特定范围的x
元素。
sq <- seq(min(x)-tol,max(x)+tol*2,tol*2)
# [1] 0.6 1.4 2.2 3.0
sapply(1:(length(sq)-1), function(i) which(!is.na(cut(x, breaks =c(sq[i], sq[i+1])))))
# [[1]]
# [1] 1 2 3 5
# [[2]]
# [1] 4 6
# [[3]]
# [1] 7
它不产生任何副本。(不需要使用unique
,因为@nicola的答案就是这样)
它的工作原理如下,在sapply
中,我们首先搜索[0.6, 1.4]
范围内的元素,然后搜索[1.4, 2.2]
,最后搜索[2.2, 3.0]
。