我想用data.table
創建一個兄弟網絡。使用data.table的兄弟網絡
我的數據是這樣的
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))
這是
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
三列分別代表ID,母親的父親和id的ID。 0
或NA
表示不可用。因此,在上面的數據中,人3和4是全兄弟姐妹(他們都有父親1
和母親2
),而12和13是半兄弟姐妹(他們有不同的父親,但是同母親,6
)。
對於數據框中的每一行,我想要一個人的兄弟姐妹列表(讓我們先考慮一下兄弟姐妹)。我理想的最終結果將是
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
在最後一列,sibs
,是一個列表或向量(和它不必是數據集的一部分)。
粗版本,以獲得下面
# 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)
在使用輸出基礎R被賦予要獲得的兄弟姐妹通過每個ID運行。找到自己的父親和母親,並從以前的列表
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)
結合兩種相關的條目這將產生
這是所需的輸出。現在上面的代碼不是很快或很漂亮,我真的很想看看我能不能看到一個漂亮的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
看起來像這樣
mid yidx
1: NA 2
2: 0 1
3: 2 3,4
4: 6 5,6,7
5: 8 8
酷似moffspring
以上不同在於它含有的指數,而不是標籤。但是,這並不是一個真正的問題。然後,我想表合併到一起
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
運行了三種不同的建議,看看這三種方法之間是否存在時間差異。 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