2017-10-12 93 views
3

我的數據是這樣的:獲取和其相應值的變量的所有組合中的一個分組的數據集

mydata <- data.frame(id = c(1,1,1,2,2,3,3,3,3), 
       subid = c(1,2,3,1,2,1,2,3,4), 
       time = c(16, 18, 20, 10, 11, 7, 9, 10, 11)) 

    id subid time 
1 1  1 16 
2 1  2 18 
3 1  3 20 
4 2  1 10 
5 2  2 11 
6 3  1 7 
7 3  2 9 
8 3  3 10 
9 3  4 11 

我的目標是將數據轉化爲:

newdata <- data.frame(id = c(1,1,1,2,3,3,3,3,3,3), 
        subid.1 = c(1,1,2,1,1,1,1,2,2,3), 
        subid.2 = c(2,3,3,2,2,3,4,3,4,4), 
        time.1 = c(16,16,18,10,7,7,7,9,9,10), 
        time.2 = c(18,20,20,11,9,10,11,10,11,11)) 

    id subid.1 subid.2 time.1 time.2 
1 1  1  2  16  18 
2 1  1  3  16  20 
3 1  2  3  18  20 
4 2  1  2  10  11 
5 3  1  2  7  9 
6 3  1  3  7  10 
7 3  1  4  7  11 
8 3  2  3  9  10 
9 3  2  4  9  11 
10 3  3  4  10  11 

所以它不是一個從長到寬的過程進行簡單的重塑:這個想法是,在由id定義的組中,採取所有可能的子組號和它們相應的時間值的組合,並將它們變成一個寬格式。

我知道我可以使用所有可能的組合,例如gtools::combinations。第一組由3排,所以

gtools::combinations(n=3, r=2) 

給了我新的subid.1和subid.2 pair的組ID == 1矩陣:

 [,1] [,2] 
[1,] 1 2 
[2,] 1 3 
[3,] 2 3 

但後來我不t知道如何進行(既不要將id==1重塑爲這種格式的組,也不能如何爲每個組分別做到這一點)。謝謝!

回答

2

與基礎R:

subset(merge(mydata, mydata, by="id", suffix=c(".1",".2")), subid.1 < subid.2) 
# id subid.1 time.1 subid.2 time.2 
# 1 1  1  16  2  18 
# 2 1  1  16  3  20 
# 3 1  2  18  3  20 
# 4 2  1  10  2  11 
# 5 3  1  7  2  9 
# 6 3  1  7  3  10 
# 7 3  1  7  4  11 
# 8 3  2  9  3  10 
# 9 3  2  9  4  11 
# 10 3  3  10  4  11 

dplyr版本:

mydata %>% inner_join(.,.,by="id",suffix=c(".1",".2")) %>% filter(subid.1 < subid.2) 

data.table版本:

setDT(mydata) 
mydata[mydata, on="id", allow.cartesian=TRUE][subid < i.subid] 
#  id subid time i.subid i.time 
# 1: 1  1 16  2  18 
# 2: 1  1 16  3  20 
# 3: 1  2 18  3  20 
# 4: 2  1 10  2  11 
# 5: 3  1 7  2  9 
# 6: 3  1 7  3  10 
# 7: 3  2 9  3  10 
# 8: 3  1 7  4  11 
# 9: 3  2 9  4  11 
# 10: 3  3 10  4  11 

或向右得到您的列名,但它殺死的樂趣短的解決方案:)。

merge(mydata, mydata, by="id", suffix=c(".1",".2"), allow.cartesian=TRUE)[subid.1 < subid.2] 
+0

我覺得你的解決方案最容易理解和讚美,因爲他們認識到「subid.1 graham

+0

不一定,您「僅」會創建兩次太多的數據,並且您可能會通過使用簡單高效的向量化操作來削減其他開銷,您必須測試:)。如果你這樣做,測試我的'data.table'解決方案,我相信'data.table'有一個更有效的合併,至少它在幾年前拯救了我一次。 –

+0

我已經使用microbenchmark和50次運行在更大的數據集(約500k行)上測試過它。我自己的解決方案(我在此期間進行了一些改進)大約需要2分鐘。我不記得你的基礎R解決方案,但我認爲它大約是15秒。您的dplyr解決方案平均耗時790毫秒。兩個data.table解決方案花費了大約相同的時間(可以理解),大約900毫秒。所以對於我的數據來說,dplyr解決方案實際上更快!我想你需要更大的數據集來查看data.table的強大功能。謝謝您的意見! – graham

1

使用data.table -package:

library(data.table) 
setDT(mydata)[, .(subid = c(t(combn(subid, 2)))), by = id 
       ][, grp := rep(1:2, each = .N/2), by = id 
       ][mydata, on = .(id, subid), time := time 
        ][, dcast(.SD, id + rowid(grp) ~ grp, value.var = list('subid','time'), sep = '.')] 

它給你:

id grp subid.1 subid.2 time.1 time.2 
1: 1 1  1  2  16  18 
2: 1 2  1  3  16  20 
3: 1 3  2  3  18  20 
4: 2 4  1  2  10  11 
5: 3 5  1  2  7  9 
6: 3 6  1  3  7  10 
7: 3 7  1  4  7  11 
8: 3 8  2  3  9  10 
9: 3 9  2  4  9  11 
10: 3 10  3  4  10  11 
+0

謝謝@Jaap!我總是很難理解data.table函數(dplyr和reshape有我的偏好,或者只是簡單的應用函數鏈接)。我想知道:這將與成千上萬的數據行一起工作,還是可能會佔用所有RAM?如果是這樣,我寧願使用for循環或應用函數來遍歷每一行數據(和/或我可以並行化以使用所有內核),並逐行構建結果數據幀。但是我聽說data.table非常高效,所以也許這正是它下面的內容。 – graham

+0

@graham我認爲這是一個非常有效的方法。 'data.table'是最高效的數據處理軟件包之一。 for循環或鏈接* apply函數肯定會效率較低。 – Jaap

+0

我會在我的巨大數據集上嘗試一下,看看它是否有效。感謝這個不錯的答案 – graham

1

忘了聲明,我想出了這個相當蹩腳的4個步驟的解決方案:

step1 <- lapply(unique(mydata$id), function(x) { 
    nrows <- nrow(mydata[which(mydata$id == x), ]) 
    combos <- gtools::combinations(n=nrows, r=2) 
    return(as.data.frame(cbind(x, combos))) 
}) 

step2 <- dplyr::bind_rows(step1) 

step3a <- merge(step2, mydata, by.x = c("x", "V2"), by.y = c("id", "subid")) 
step3b <- merge(step3a, mydata, by.x = c("x", "V3"), by.y = c("id", "subid")) 

step4 <- step3b[, c(1, 3, 2, 4, 5)] 
names(step4) <- c("id", "subid.1", "subid.2", "time.1", "time.2") 

這是醜陋的,但工程。

相關問題