如何在向量列表中查找向量的特定索引,其中索引在向量中给出? (没有 for 循环)

How to look up specific indices of vectors in a list of vectors, where the indices are given in a vector? (without a for loop)

我想找到一种高效的操作来在列表中进行以下查找:

L = list(10:15,11:20)
a = c(3,7)
b = numeric()
for(i in 1:length(a)) b[i] = L[[i]][a[i]]

我认为 for 循环效率低下,我想这可以更快地完成,例如,使用 sapply。我的主要目标是在 L 很长时有效地执行此操作。

您可以使用 Mapmapply。由于 mapply 可以自动简化为向量,我们可以在这里使用它一次性得到 b

b <- mapply(function(list_members, indices) list_members[indices],
       list_members = L, indices = a, SIMPLIFY = TRUE)

b
#> [1] 12 17

另一个apply方法是sapply()

sapply(1:length(a), function(x) L[[x]][a[x]])
[1] 12 17

我们可以使用

library(dplyr)
stack(setNames(L, a)) %>%
   group_by(ind) %>% 
   summarise(out = values[[as.numeric(as.character(first(ind)))]]) %>%
   pull(out)
[1] 12 17

或者在 base R 中使用 vapply 会更快

vapply(seq_along(L), \(i) L[[i]][a[i]], numeric(1))
[1] 12 17

或使用imap作为紧凑选项

library(purrr)
imap_dbl(setNames(L, a), ~ .x[as.numeric(.y)])
 3  7 
12 17 

更新:

您对 for 循环的厌恶是没有根据的。 b 正确初始化后,基本 R for 循环仅比 Rcpp 解决方案慢,而且仅此而已。请参阅下面更新的基准。 loop1 解决方案已正确初始化。


使用 unlistcumsumlengths:

的基础 R 向量化解决方案
b <- unlist(L)[a + c(0, cumsum(lengths(L)[1:(length(L) - 1L)]))]

基准测试(投入 Rcpp 解决方案)*

library(purrr)

L <- lapply(sample(4:10, 1e5, TRUE), seq)
a <- sapply(lengths(L), function(x) sample(x, 1))

Rcpp::cppFunction("IntegerVector ListIndex(const List& L, const IntegerVector& a) {
const int n = a.size();
IntegerVector b (n);
for (int i = 0; i < n; i++) b(i) = as<IntegerVector>(L[i])(a(i) - 1);
return b;
}")
    
microbenchmark::microbenchmark(sapply = sapply(1:length(a), function(x) L[[x]][a[x]]),
                               vapply = vapply(seq_along(L), function(i) L[[i]][a[i]], numeric(1)),
                               purr = imap_dbl(setNames(L, a), ~ .x[as.numeric(.y)]),
                               unlist = unlist(L)[a + c(0, cumsum(lengths(L)[1:(length(L) - 1L)]))],
                               rcpp = ListIndex(L, a),
                               loop1 = {b <- integer(length(a)); for(i in seq_along(a)) b[i] <- L[[i]][a[i]]},
                               loop2 = {b <- integer(); for(i in seq_along(a)) b[i] <- L[[i]][a[i]]})

#> Unit: milliseconds
#>    expr      min       lq      mean    median       uq      max neval
#> sapply 102.4199 113.72450 125.21764 119.72455 130.41480 291.5465   100
#> vapply  97.8447 107.33390 116.41775 112.33445 119.01680 189.9191   100
#>   purr 226.9039 241.02305 258.34032 246.81175 257.87370 502.3446   100
#> unlist  29.4186  29.97935  32.05529  30.86130  33.02160  44.6751   100
#>   rcpp  22.3468  22.78460  25.47667  23.48495  26.63935  37.2362   100
#>  loop1  25.5240  27.34865  28.94650  28.02920  29.32110  42.9779   100
#>  loop2  41.4726  46.04130  52.58843  51.00240  56.54375  88.3444   100

*我无法使用 akrun 的 dplyr 解决方案来处理更大的向量。