如何执行像`%in%`这样的成对运算,并为向量列表设置运算

时间:2016-08-21 16:23:43

标签: r

这个问题由How can I quickly see if any elements of multiple vectors are equal 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%和设置操作说intersectunionsetdiff

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

注意:我不想使用combn()

1 个答案:

答案 0 :(得分:4)

我们可以使用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)是“列表”。请阅读Why is this matrix not numeric?了解更多信息。

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

示例1:应用any()

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

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

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

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

设置intersectunionsetequal等操作也是对称操作,我们也可以使用它们。

示例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
#  ...