这个问题的动机是如何快速查看 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))
Run Code Online (Sandbox Code Playgroud)
我们如何执行成对的二元运算,例如%in%
和集合运算intersect
,例如union
,,setdiff
?
假设我们要配对"%in%"
,怎样才能进一步执行any()
/ all()
/which()
每对中?
注意:我不想使用combn()
.
我们可以使用outer(x, y, FUN)
. x
并且y
不需要像数字向量/矩阵这样的“数字”输入;也允许像“列表”/“矩阵列表”这样的向量输入。
例如,要应用成对"%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
Run Code Online (Sandbox Code Playgroud)
由于"%in%"
本身没有向量化,我们使用Vectorized("%in%")
. 我们还需要SIMPLIFY = FALSE
,以便FUN
为每对返回一个长度为 1 的列表(x[[i]], y[[j]])
。这很重要,因为它的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]]
Run Code Online (Sandbox Code Playgroud)
必须满足的是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)]
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
像intersect
、union
和 之类的集合操作setequal
也是对称操作,我们可以类似地使用它们。
示例 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
# ...
Run Code Online (Sandbox Code Playgroud)
Set 操作 likesetdiff
也是不对称的,可以类似地处理。
备择方案
除了使用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)"
# ...
Run Code Online (Sandbox Code Playgroud)
然后我们可以在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
# ...
Run Code Online (Sandbox Code Playgroud)