2012-03-23 33 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。更快吗?