【问题标题】:Count number of occurrences of vector in list计算列表中向量的出现次数
【发布时间】:2016-09-07 14:17:09
【问题描述】:

我有一个可变长度的向量列表,例如:

q <- list(c(1,3,5), c(2,4), c(1,3,5), c(2,5), c(7), c(2,5))

我需要计算列表中每个向量的出现次数,例如(任何其他合适的数据结构都可以接受):

list(list(c(1,3,5), 2), list(c(2,4), 1), list(c(2,5), 2), list(c(7), 1))

有没有一种有效的方法来做到这一点?实际列表有数万个项目,因此二次行为是不可行的。

【问题讨论】:

  • 您可以使用哈希函数,然后为每个哈希生成所需的列表,这大大降低了平均复杂度。
  • matchunique 也在“列表”上工作 -- match(q, unique(q)) 然后将出现的列表制成表格

标签: r


【解决方案1】:

matchunique 也接受并处理“列表”(?match 警告“列表”速度慢)。所以,与:

match(q, unique(q))
#[1] 1 2 1 3 4 3

每个元素都映射到一个整数。那么:

tabulate(match(q, unique(q)))
#[1] 2 1 2 1

并找到一个结构来呈现结果:

as.data.frame(cbind(vec = unique(q), n = tabulate(match(q, unique(q)))))
#      vec n
#1 1, 3, 5 2
#2    2, 4 1
#3    2, 5 2
#4       7 1

除了match(x, unique(x)) 方法,我们可以使用deparseing 将每个元素映射到单个值:

table(sapply(q, deparse))
#
#         7 c(1, 3, 5)    c(2, 4)    c(2, 5) 
#         1          2          1          2

此外,由于这是具有唯一整数的情况,并且假设在一个小范围内,我们可以在将每个元素转换为二进制表示之后将每个元素映射到单个整数:

n = max(unlist(q))
pow2 = 2 ^ (0:(n - 1))
sapply(q, function(x) tabulate(x, nbins = n))  # 'binary' form
sapply(q, function(x) sum(tabulate(x, nbins = n) * pow2))
#[1] 21 10 21 18 64 18

然后像以前一样tabulate

只是为了比较上述替代方案:

f1 = function(x) 
{
    ux = unique(x)
    i = match(x, ux)
    cbind(vec = ux, n = tabulate(i))
}   

f2 = function(x)
{
    xc = sapply(x, deparse)
    i = match(xc, unique(xc))
    cbind(vec = x[!duplicated(i)], n = tabulate(i))
}  

f3 = function(x)
{
    n = max(unlist(x))
    pow2 = 2 ^ (0:(n - 1))
    v = sapply(x, function(X) sum(tabulate(X, nbins = n) * pow2))
    i = match(v, unique(v))
    cbind(vec = x[!duplicated(v)], n = tabulate(i))
}

q2 = rep_len(q, 1e3)

all.equal(f1(q2), f2(q2))
#[1] TRUE
all.equal(f2(q2), f3(q2))
#[1] TRUE

microbenchmark::microbenchmark(f1(q2), f2(q2), f3(q2))
#Unit: milliseconds
#   expr       min        lq      mean    median        uq       max neval cld
# f1(q2)  7.980041  8.161524 10.525946  8.291678  8.848133 178.96333   100  b 
# f2(q2) 24.407143 24.964991 27.311056 25.514834 27.538643  45.25388   100   c
# f3(q2)  3.951567  4.127482  4.688778  4.261985  4.518463  10.25980   100 a 

另一个有趣的选择是基于排序。 R > 3.3.0 有一个grouping 函数,建立在data.table 之外,它与排序一起提供了一些属性以供进一步操作:

使所有元素的长度相等并“转置”(在这种情况下可能是最慢的操作,尽管我不确定如何提供grouping):

n = max(lengths(q))
qq = .mapply(c, lapply(q, "[", seq_len(n)), NULL)

使用排序将映射到整数的相似元素分组:

gr = do.call(grouping, qq)
e = attr(gr, "ends") 
i = rep(seq_along(e), c(e[1], diff(e)))[order(gr)]
i
#[1] 1 2 1 3 4 3

然后,像以前一样制表。 继续比较:

f4 = function(x)
{
    n = max(lengths(x))
    x2 = .mapply(c, lapply(x, "[", seq_len(n)), NULL)
    gr = do.call(grouping, x2)
    e = attr(gr, "ends") 
    i = rep(seq_along(e), c(e[1], diff(e)))[order(gr)]
    cbind(vec = x[!duplicated(i)], n = tabulate(i))
}

all.equal(f3(q2), f4(q2))
#[1] TRUE

microbenchmark::microbenchmark(f1(q2), f2(q2), f3(q2), f4(q2))
#Unit: milliseconds
#   expr       min        lq      mean    median        uq        max neval cld
# f1(q2)  7.956377  8.048250  8.792181  8.131771  8.270101  21.944331   100  b 
# f2(q2) 24.228966 24.618728 28.043548 25.031807 26.188219 195.456203   100   c
# f3(q2)  3.963746  4.103295  4.801138  4.179508  4.360991  35.105431   100 a  
# f4(q2)  2.874151  2.985512  3.219568  3.066248  3.186657   7.763236   100 a

在此比较中,q 的元素长度较小,无法容纳f3,但f3(因为大幂运算)和f4(因为mapply)在性能上会受到影响,如果使用较大元素的“列表”。

【讨论】:

    【解决方案2】:

    一种方法是粘贴每个向量,取消列出和制表,即

    table(unlist(lapply(q, paste, collapse = ',')))
    
    #1,3,5   2,4   2,5     7 
    #    2     1     2     1 
    

    【讨论】:

    • 不错的选择 - 您也可以使用 sapply 代替 lapply + unlist,即 table(sapply(q, paste, collapse = ","))
    • @docendodiscimus 是真的。当我看到列表时,我总是毫无意义地去lapply...
    猜你喜欢
    • 2022-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多