如何对一组向量执行“%in%”之类的成对操作和设置操作

  • 本文关键字:操作 %in% 设置 执行 向量 一组
  • 更新时间 :
  • 英文 :


这个问题的动机是:我如何才能快速看到多个向量的任何元素在R中是否相等?,但不完全相同/重复。

作为一个小例子,假设我们有一个包含4个向量的列表:
set.seed(0)
lst <- list(vec1 = sample(1:10, 2, TRUE), vec2 = sample(1:10, 3, TRUE),
            vec3 = sample(1:10, 4, TRUE), vec4 = sample(1:10, 5, TRUE))

我们如何执行成对二进制运算如%in%和集合运算如intersect, union, setdiff ?

假设我们想要配对"%in%",我们如何在每对中进一步执行any()/all()/which() ?

注意:我不想使用combn()

可以使用outer(x, y, FUN)xy不需要像数值向量/矩阵那样是"数值"输入;也允许像"list"/"matrix list"这样的向量输入。

例如,要应用成对"%in%"操作,我们使用

z <- outer(lst, lst, FUN = Vectorize("%in%", SIMPLIFY = FALSE, USE.NAMES = FALSE))
#     vec1      vec2      vec3      vec4     
#vec1 Logical,2 Logical,2 Logical,2 Logical,2
#vec2 Logical,3 Logical,3 Logical,3 Logical,3
#vec3 Logical,4 Logical,4 Logical,4 Logical,4
#vec4 Logical,5 Logical,5 Logical,5 Logical,5

由于"%in%"本身没有矢量化,我们使用Vectorized("%in%")。我们还需要SIMPLIFY = FALSE,以便FUN(x[[i]], y[[j]])的每一对返回一个长度为1的列表。这很重要,因为outer的工作方式如下:

y[[4]] | FUN(x[[1]], y[[4]])  FUN(x[[2]], y[[4]])  FUN(x[[1]], y[[4]])  FUN(x[[2]], y[[4]])
y[[3]] | FUN(x[[1]], y[[3]])  FUN(x[[2]], y[[3]])  FUN(x[[1]], y[[3]])  FUN(x[[2]], y[[4]])
y[[2]] | FUN(x[[1]], y[[2]])  FUN(x[[2]], y[[2]])  FUN(x[[1]], y[[2]])  FUN(x[[2]], y[[4]])
y[[1]] | FUN(x[[1]], y[[1]])  FUN(x[[2]], y[[1]])  FUN(x[[1]], y[[1]])  FUN(x[[2]], y[[4]])
         -------------------  -------------------  -------------------  -------------------
         x[[1]]               x[[2]]               x[[3]]               x[[4]]

必须满足length(FUN(x, y)) == length(x) * length(y)。而如果SIMPLIFY = FALSE,这并不一定成立。

上面的结果z是一个"矩阵列表",其中class(z)是"矩阵",而typeof(z)是"列表"。为什么这个矩阵不是数字?。


如果我们想进一步对z的每个元素应用一些汇总函数,我们可以使用lapply。这里我举两个例子。

示例1:应用any()

由于any(a %in% b)any(b %in% a)相同,即操作是对称的,所以我们只需要处理z的下三角形:

lz <- z[lower.tri(z)]

lapply返回一个未命名的列表,但为了可读性,我们想要一个命名列表。我们可以使用矩阵索引(i, j)作为name:

ind <- which(lower.tri(z), arr.ind = TRUE)
NAME <- paste(ind[,1], ind[,2], sep = ":")
any_lz <- setNames(lapply(lz, any), NAME)
#List of 6
# $ 2:1: logi FALSE
# $ 3:1: logi TRUE
# $ 4:1: logi TRUE
# $ 3:2: logi TRUE
# $ 4:2: logi FALSE
# $ 4:3: logi TRUE

intersect, unionsetequal这样的集合操作也是对称操作,我们可以类似地处理它们。

示例2:应用which()

which(a %in% b)不是对称运算,所以我们必须处理全矩阵。

NAME <- paste(1:nrow(z), rep(1:nrow(z), each = ncol(z)), sep = ":")
which_z <- setNames(lapply(z, which), NAME)
# List of 16
#  $ 1:1: int [1:2] 1 2
#  $ 2:1: int(0) 
#  $ 3:1: int [1:2] 1 2
#  $ 4:1: int 3
#  $ 1:2: int(0) 
#  $ 2:2: int [1:3] 1 2 3
#  ...

setdiff这样的集合操作也是不对称的,可以类似地处理。


替代

除了使用outer(),我们还可以使用R表达式来获得上面的z。同样,我以二进制操作"%in%"为例:

op <- "'%in%'"    ## operator
lst_name <- names(lst)
op_call <- paste0(op, "(", lst_name, ", ", rep(lst_name, each = length(lst)), ")")
# [1] "'%in%'(vec1, vec1)" "'%in%'(vec2, vec1)" "'%in%'(vec3, vec1)"
# [4] "'%in%'(vec4, vec1)" "'%in%'(vec1, vec2)" "'%in%'(vec2, vec2)"
# ...

然后我们可以在lst中解析和求值这些表达式。我们可以对结果列表的名称使用组合索引:

NAME <- paste(1:length(lst), rep(1:length(lst), each = length(lst)), sep = ":")
z <- setNames(lapply(parse(text = op_call), eval, lst), NAME)
# List of 16
#  $ 1:1: logi [1:2] TRUE TRUE
#  $ 2:1: logi [1:3] FALSE FALSE FALSE
#  $ 3:1: logi [1:4] TRUE TRUE FALSE FALSE
#  $ 4:1: logi [1:5] FALSE FALSE TRUE FALSE FALSE
#  $ 1:2: logi [1:2] FALSE FALSE
#  ...

最新更新