如何在向量列表中查找向量的特定索引,其中索引在向量中给出? (没有 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
很长时有效地执行此操作。
您可以使用 Map
或 mapply
。由于 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
解决方案已正确初始化。
使用 unlist
、cumsum
和 lengths
:
的基础 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
解决方案来处理更大的向量。
我想找到一种高效的操作来在列表中进行以下查找:
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
很长时有效地执行此操作。
您可以使用 Map
或 mapply
。由于 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
解决方案已正确初始化。
使用 unlist
、cumsum
和 lengths
:
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
解决方案来处理更大的向量。