2012-03-23 69 views
11

我想「循環」data.table的行並計算每行的平均值。平均應該基於下面的機理來計算:「循環遍歷」data.table來計算條件平均值

  1. 裏查找行中的識別符ID I(ID(i))的
  2. 查找T2的值在第i行(T2(i))的
  3. 計算平均通過Data1值中的所有行j,滿足這兩個標準:ID(j) = ID(i)T1(j) = T2(i)
  4. 在行的列數據2輸入所計算出的平均I

    DF = data.frame(ID=rep(c("a","b"),each=6), 
          T1=rep(1:2,each=3), T2=c(1,2,3), Data1=c(1:12)) 
    DT = data.table(DF) 
    DT[ , Data2:=NA_real_] 
        ID T1 T2 Data1 Data2 
    [1,] a 1 1  1 NA 
    [2,] a 1 2  2 NA 
    [3,] a 1 3  3 NA 
    [4,] a 2 1  4 NA 
    [5,] a 2 2  5 NA 
    [6,] a 2 3  6 NA 
    [7,] b 1 1  7 NA 
    [8,] b 1 2  8 NA 
    [9,] b 1 3  9 NA 
    [10,] b 2 1 10 NA 
    [11,] b 2 2 11 NA 
    [12,] b 2 3 12 NA 
    

對於這個簡單的例子的結果應該是這樣的:

 ID T1 T2 Data1 Data2 
[1,] a 1 1  1 2 
[2,] a 1 2  2 5 
[3,] a 1 3  3 NA 
[4,] a 2 1  4 2 
[5,] a 2 2  5 5 
[6,] a 2 3  6 NA 
[7,] b 1 1  7 8 
[8,] b 1 2  8 11 
[9,] b 1 3  9 NA 
[10,] b 2 1 10 8 
[11,] b 2 2 11 11 
[12,] b 2 3 12 NA 

,我認爲這樣做是通過行循環的一種方式,但我認爲這是低效的。我查看了apply()函數,但我確定它是否能解決我的問題。如果這樣可以使效率更高或更容易,我也可以使用data.frame而不是data.table。真實的數據集包含大約100萬行。

+2

您編寫的規範看起來很難實現,但您的示例建議在每個ID組中您想要某些值組的值的平均值,其中T2在T1的值範圍內。但是,當試圖弄清楚爲什麼第二行中的Data2應該是5時,即使這種解釋也會崩潰。 – 2012-03-23 17:48:38

+0

@DWin這是因爲平均值是在'Data1'列上完成的。 'Data2 [2]'等於5,因爲5是'(4,5,6)'的平均值。 – ulidtko 2012-03-23 17:57:29

回答

10

經驗法則是首先彙總,然後加入。

agg = DT[,mean(Data1),by=list(ID,T1)] 
setkey(agg,ID,T1) 
DT[,Data2:={JT=J(ID,T2);agg[JT,V1][[3]]}] 
     ID T1 T2 Data1 Data2 
[1,] a 1 1  1  2 
[2,] a 1 2  2  5 
[3,] a 1 3  3 NA 
[4,] a 2 1  4  2 
[5,] a 2 2  5  5 
[6,] a 2 3  6 NA 
[7,] b 1 1  7  8 
[8,] b 1 2  8 11 
[9,] b 1 3  9 NA 
[10,] b 2 1 10  8 
[11,] b 2 2 11 11 
[12,] b 2 3 12 NA 

正如你可以看到它在這種情況下有點醜(但會很快)。它計劃添加drop這將避免[[3]]位,也許我們可以提供一種方式告訴[.data.table在調用範圍內(即不自聯接)評估i,這將避免JT=位,這是因爲IDaggDT

keyby已被添加到R-Forge的v1.8.0,以避免需要setkey

+0

謝謝馬修。這非常快。在創建時,是否有可能將'agg'中的'V1'列賦予一個自定義名稱,以避免對列名造成混淆? – Cake 2012-03-23 20:24:31

+1

嘗試'DT [,list(myname = mean(Data1)),by = list(ID,T1)]'。另請參閱[data.table wiki](http://rwiki.sciviews.org/doku.php?id=packages:cran:data.table)第3點,以便在這種情況下進一步加速。 – 2012-03-23 21:01:42

+0

我用'DT [,Data2:= {agg [J(ID,T2)] [[3]]}]'取代了你的第三行,並得到了相同的結果。即我**避免了'JT ='位(以及',V1')。對我而言,這些不好的做法是? – 2012-03-23 22:49:50

2

迭代行更快一些的替代方法是使用矢量化的解決方案。

R> d <- data.frame(ID=rep(c("a","b"),each=6), T1=rep(1:2,each=3), T2=c(1,2,3), Data1=c(1:12)) 
R> d 
    ID T1 T2 Data1 
1 a 1 1  1 
2 a 1 2  2 
3 a 1 3  3 
4 a 2 1  4 
5 a 2 2  5 
6 a 2 3  6 
7 b 1 1  7 
8 b 1 2  8 
9 b 1 3  9 
10 b 2 1 10 
11 b 2 2 11 
12 b 2 3 12 

R> rowfunction <- function(i) with(d, mean(Data1[which(T1==T2[i] & ID==ID[i])])) 
R> d$Data2 <- sapply(1:nrow(d), rowfunction) 
R> d 
    ID T1 T2 Data1 Data2 
1 a 1 1  1  2 
2 a 1 2  2  5 
3 a 1 3  3 NaN 
4 a 2 1  4  2 
5 a 2 2  5  5 
6 a 2 3  6 NaN 
7 b 1 1  7  8 
8 b 1 2  8 11 
9 b 1 3  9 NaN 
10 b 2 1 10  8 
11 b 2 2 11 11 
12 b 2 3 12 NaN 

而且,我寧願對數據進行預處理之前得到它爲R.即如果您從SQL服務器檢索數據,那麼讓服務器計算平均值可能是更好的選擇,因爲它很可能會在這方面做得更好。

R實際上不是很擅長數字運算,原因有幾個。但是在對已經預處理的數據進行統計時非常出色。

1

使用的另一個最近的文章tapply和部分:

DF = data.frame(ID=rep(c("a","b"),each=6), T1=rep(1:2,each=3), T2=c(1,2,3), Data1=c(1:12)) 

編輯:其實,最原始的功能是多餘的,是爲了別的東西。這裏,簡化:

ansMat <- tapply(DF$Data1, DF[, c("ID", "T1")], mean) 

i <- cbind(match(DF$ID, rownames(ansMat)), match(DF$T2, colnames(ansMat))) 

DF<-cbind(DF,Data2 = ansMat[i]) 


# ansMat<-tapply(seq_len(nrow(DF)), DF[, c("ID", "T1")], function(x) { 
# curSub <- DF[x, ] 
# myIndex <- which(DF$T2 == curSub$T1 & DF$ID == curSub$ID) 
# meanData1 <- mean(curSub$Data1) 
# return(meanData1 = meanData1) 
# }) 

訣竅是在ID和T1,而不是ID和T2 tapply。更快嗎?