测试向量是否包含在另一个向量中,包括重复项。

4

我已经苦恼了一段时间:给定两个包含可能重复元素的向量,如何测试其中一个是否完全包含另一个?%in%不能考虑重复。我无法想到一种优雅的解决方案,不依赖于apply函数族中的任何内容。

x      <- c(1, 2, 2, 2)
values <- c(1, 1, 1, 2, 2, 3, 4, 5, 6)

# returns TRUE, but x[x == 2] is greater than values[values == 2]
all(x %in% values)

# inelegant solution
"%contains%" <- 
    function(values, x){
                   n <- intersect(x, values)
                   all( sapply(n, function(i) sum(values == i) >= sum(x == i)) )
                       }

# which yields the following:
> values %contains% x
 [1] FALSE
> values <- c(values, 2)
> values %contains% x
 [2] TRUE

基准测试更新

除了Marat提供的答案,我可能已经找到了另一种解决方案。

# values and x must all be non-negative - can change the -1 below accordingly
"%contains%" <- 
    function(values, x){
            t <- Reduce(function(.x, .values) .values[-which.max(.values == .x)]
                        , x = x
                        , init = c(-1, values))
            t[1] == -1
     }

对到目前为止所有答案进行基准测试,包括thelatemail对marat的修改,使用大和小x。
library(microbenchmark)

set.seed(31415)
values <- sample(c(0:100), size = 100000, replace = TRUE)
set.seed(11235)
x_lrg <- sample(c(0:100), size = 1000, replace = TRUE)
x_sml <- c(1, 2, 2, 2)


lapply(list(x_sml, x_lrg), function(x){
    microbenchmark(    hoho_sapply(values, x)
                     , marat_table(values, x)
                     , marat_tlm(values, x)
                     , hoho_reduce(values, x)
                     ,  unit = "relative")
    })

 # Small x
 # [[1]]
 # Unit: relative
 #                  expr       min        lq     mean   median       uq      max neval
 # hoho_sapply(values, x)  1.000000  1.000000 1.000000 1.000000 1.000000 1.000000   100
 # marat_table(values, x) 12.718392 10.966770 7.487895 9.260099 8.648351 1.819833   100
 #   marat_tlm(values, x)  1.354452  1.181094 1.026373 1.088879 1.266939 1.029560   100
 # hoho_reduce(values, x)  2.951577  2.748087 2.069830 2.487790 2.216625 1.097648   100
 #
 # Large x
 # [[2]]
 # Unit: relative
 #                   expr       min        lq      mean    median        uq        max neval
 # hoho_sapply(values, x)  1.158303  1.172352  1.101410  1.177746  1.096661  0.6940260   100
 # marat_table(values, x)  1.000000  1.000000  1.000000  1.000000  1.000000  1.0000000   100
 #   marat_tlm(values, x)  1.099669  1.059256  1.102543  1.071960  1.072881  0.9857229   100
 # hoho_reduce(values, x) 85.666549 81.391495 69.089366 74.173366 66.943621 27.9766047   100

这个问题应该涵盖许多可能性 - https://dev59.com/JFwY5IYBdhLWcg3wFkej#33028695 - thelatemail
@thelatemail,谢谢;我已经在我的更新中使用了答案中引用的基准测试包。 - Soul Donut
谢谢您的更新。您能否对@TheLateMail在评论中建议的修改我的解决方案进行基准测试? - Marat Talipov
请提供x为大向量的基准测试结果。 - Marat Talipov
1
@MaratTalipov 完成了!希望这是你想要的。 - Soul Donut
1个回答

8

请尝试使用 table 标签,例如:

"%contain%" <- function(values,x) {
    tx <- table(x)
    tv <- table(values)
    z <- tv[names(tx)] - tx
    all(z >= 0 & !is.na(z))
}

一些例子:

> c(1, 1, 1, 2, 2, 3, 4, 5, 6) %contain% c(1,2,2,2)
[1] FALSE
> c(1, 1, 1, 2, 2, 3, 4, 5, 6, 2) %contain% c(1,2,2,2)
[1] TRUE
> c(1, 1, 1, 2, 2, 3, 4, 5, 6) %contain% c(1,2,2)
[1] TRUE
> c(1, 1, 1, 2, 2, 3, 4, 5, 6) %contain% c(1,2,2,7)
[1] FALSE

2
你可以把第二行改成 table(values[values %in% x]),这样就不需要对所有唯一值进行表格制作,从而减少了可能会非常庞大的工作量。 - thelatemail
@thelatemail 是 table 还是 %in% 更快呢?我会选择 table,但这取决于 x 和 variables 的大小以及重复次数。 - jeremycg
@jeremycg - 可能确实如此,我认为分配一个可能非常庞大的对象可能是一个难点。 - thelatemail

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接