web-dev-qa-db-ja.com

リスト内の一致のインデックスを取得する高速な方法

等しくない長さのベクトルを含むリストabのベクトルからのいくつかの要素を含むベクトルaが与えられた場合、baの要素が一致するbのインデックスを含む(これは私が知っている悪い説明です)...

次のコードがその役割を果たします。

a <- list(1:3, 4:5, 6:9)
b <- c(2, 3, 5, 8)

sapply(b, function(x, list) which(unlist(lapply(list, function(y, z) z %in% y, z=x))), list=a)
[1] 1 1 2 3

sapplyをforループに置き換えると、もちろん同じことが実現します。

問題は、このコードが長さが1000を超えるリストとベクトルで使用されることです。実際のセットでは、関数は約15秒かかります(forループとsapplyの両方)。

並列アプローチで安全に、これを高速化する方法を誰かが知っていますか?私はベクトル化されたアプローチを見ることができませんでした(そして私はCでプログラムすることはできませんが、おそらくそれが最速でしょう)。

編集:

1667倍のオーダー(15から0.009)の速度増加をもたらしたmatch()を使用したアーロンのエレガントなソリューションを強調します

複数の一致を許可するために少し拡張しました(戻り値はリストになります)

a <- list(1:3, 3:5, 3:7)
b <- c(3, 5)
g <- rep(seq_along(a), sapply(a, length))
sapply(b, function(x) g[which(unlist(a) %in% x)])
[[1]]
[1] 1 2 3

[[2]]
[1] 2 3

このための実行時間は0.169で、これは間違いなくかなり遅いですが、一方でより柔軟です

15
ThomasP85

matchを使用する1つの可能性は次のとおりです。

> a <- list(1:3, 4:5, 6:9)
> b <- c(2, 3, 5, 8)
> g <- rep(seq_along(a), sapply(a, length))
> g[match(b, unlist(a))]
[1] 1 1 2 3

findIntervalは別のオプションです:

> findInterval(match(b, unlist(a)), cumsum(c(0,sapply(a, length)))+1)
[1] 1 1 2 3

リストを返すには、次のことを試してください。

a <- list(1:3, 4:5, 5:9)
b <- c(2,3,5,8,5)
g <- rep(seq_along(a), sapply(a, length))
aa <- unlist(a)
au <- unique(aa)
af <- factor(aa, levels=au)
gg <- split(g, af)
gg[match(b, au)]
16
Aaron

あなたの投稿へのコメントが示唆しているように、それは同じ要素がaの複数のベクトルに現れる場合/いつ何をしたいかに依存します。あなたができる最低のインデックスが欲しいと仮定すると:

apply(sapply(a, function(vec) {b %in% vec}), 1, which.max)
0
ALiX