data.table
を使用して兄弟ネットワークを作成します。data.tableを使用する兄弟ネットワーク
私のデータは
id fid mid
1 1 NA 0
2 2 9 NA
3 3 1 2
4 4 1 2
5 12 7 6
6 13 5 6
7 14 5 6
8 15 5 8
3つの列は、それぞれ、母の父とIDのIDをIDを表している。この
indata <-
structure(list(id = c(1L, 2L, 3L, 4L, 12L, 13L, 14L, 15L), fid = c(NA,
9L, 1L, 1L, 7L, 5L, 5L, 5L), mid = c(0L, NA, 2L, 2L, 6L, 6L,
6L, 8L)), .Names = c("id", "fid", "mid"), class = "data.frame", row.names =
c(NA, -8L))
のように見えます。 0
またはNA
は利用できません。したがって、上記のデータでは、3人と4人は完全兄弟です(両者とも父親1
と母親2
)。一方、12歳と13歳は半分の兄弟です(父親は同じですが母親は6
)。
データフレームの各行について、私はその人の兄弟のリストを望んでいます(半分の兄弟から始めてみましょう)。私の理想的な最終結果は最後の列、sibs
は、リストまたはベクトルである(そして、それは、データセットの一部である必要はありません)
id fid mid sibs
1 1 NA 0 NA
2 2 9 NA NA
3 3 1 2 4
4 4 1 2 3
5 12 7 6 13, 14
6 13 5 6 12, 14, 15
7 14 5 6 12, 13, 15
8 15 5 8 13, 14
だろう。基地Rを使用して出力を得るため
粗バージョンは兄弟がそれぞれのIDを介して実行取得する
# get a list of offspring for each father id
foffspring <- by(indata, indata$fid, function(i) { i$id }, simplify=FALSE)
# and mother id
moffspring <- by(indata, indata$mid, function(i) { i$id }, simplify=FALSE)
以下に示します。これは、所望の出力だった
を生成
sibs <- sapply(1:nrow(indata), function(i) {
res <- c()
if(!is.na(indata$fid[i]))
res <- c(res, unlist(foffspring[paste0(indata$fid[i])]))
if(!is.na(indata$mid[i]))
res <- c(res, unlist(moffspring[paste0(indata$mid[i])]))
unique(res[res != indata$id[i]])
}, simplify=TRUE)
に彼らの父と母を見つけて、以前のリストから2つの関連エントリを兼ね備えています。上のコードは速くてもかわいいものではありません。私は実際には
data.table
のファンシーなバージョンが得られるかどうか確認したいと思います。しかし、私の
data.table
-fuは欠けているようです。
library(data.table)
DT <- data.table(indata)
# Create lists with the _indices_ of the offsprings
FT <- DT[ , list(yidx = list(.I)) , by = fid ]
MT <- DT[ , list(yidx = list(.I)) , by = mid ]
MT
それがインデックスではなくラベルを含む以外は、上記まさにmoffspring
のようなこの
mid yidx
1: NA 2
2: 0 1
3: 2 3,4
4: 6 5,6,7
5: 8 8
のように見えます。しかし、それは実際問題ではありません。その後、私は一緒に
setkey(DT, fid)
setkey(FT, fid)
setkey(MT, mid)
# Inner join
P1 <- DT[FT]
# And inner join on mother
setkey(P1, mid)
P1[MT]
テーブルをマージしたいのですが、現在は最終的な結果は、これはほとんどがあるこの
id fid mid yidx i.yidx
1: 2 9 NA 2 2
2: 1 NA 0 1 1
3: 3 1 2 3,4 3,4
4: 4 1 2 3,4 3,4
5: 13 5 6 6,7,8 5,6,7
6: 14 5 6 6,7,8 5,6,7
7: 12 7 6 5 5,6,7
8: 15 5 8 6,7,8 8
のように見えます。今度は、yidx
とi.yidx
の行連合を取ると、私は半分の同胞(自分自身を含む)のリストを取得し、行方向の交差は完全な兄弟をもたらすでしょう。インデックスはDT
のインデックスを指し、最終的なインデックスはdata.table
ではありませんが、これも修正できます。
しかし、私はこれのようなものがdata.table
コードと "手の優しい波"の数行ではるかに効率的に行えると感じていました。誰かが私を正しい方向に向けることができますか?下の回答に基づいて
更新
[超ロングポストのため申し訳ありません]。それを楽しむために、私はmicrobenchmark
で3つの異なる提案を実行し、3つのアプローチの間にタイミングの違いがあるかどうかを確認しました。 f1()
は@Frankの提案であり、f2()
は@mtotoの解決策であり、f3
は@ amatsuo_netのアプローチです。長さ1000のベクトルを試してみました。ここに出力があります。
Unit: milliseconds
expr min lq mean median uq max neval cld
f1() 4020.8112 4387.7950 4614.7896 4498.8043 4770.1184 6837.672 100 c
f2() 656.9575 685.7706 727.5191 710.3003 735.2832 1080.423 100 a
f3() 1637.8927 1706.7528 1789.1794 1739.4428 1814.7776 2403.474 100 b
かなりの違いがあります。私は700万IDのデータセットでそれを実行する必要があるので、確かに顕著な影響があります。皆さんありがとう!
すばらしい解決策。そして驚くほど速い! – ekstroem