遍历数据表以计算条件平均值

11

我想要“循环遍历”data.table的行,并为每一行计算平均值。平均值应该基于以下机制计算:

  1. 查找行 i 中的标识符ID (ID(i))
  2. 查找行 i 中T2的值(T2(i))
  3. 计算符合这两个条件的所有行jData1值的平均值: ID(j) = ID(i)T1(j) = T2(i)
  4. 将计算出的平均值输入到行 i 的Data2列中。

  5.  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。 - IRTFM
@DWin 这是因为平均值是在 Data1 列上计算的。Data2[2] 等于 5,因为 (4, 5, 6) 的平均值是 5。 - ulidtko
3个回答

11

经验法则是先进行聚合,然后再与之连接。

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=的出现。这里需要这样做是因为ID既在agg中又在DT中。

keyby已经在R-Forge的v1.8.0版本中添加,因此不需要使用setkey


谢谢你,Matthew。这非常快。在创建时是否有可能为agg中的V1列指定自定义名称,以避免对列名称的任何混淆? - Cake
1
尝试使用DT[,list(myname=mean(Data1)),by=list(ID,T1)]。此外,参见data.table wiki的第3点,以进一步提高速度。 - Matt Dowle
我用 DT[,Data2:={agg[J(ID, T2)][[3]]}] 替换了你的第三行代码,并获得了相同的结果。也就是说,我确实避免了 JT= 部分(以及 ,V1)。这两个部分是否都是我的不良实践? - Josh O'Brien
@Josh 你好。我尝试了那行代码,但结果似乎不同。J() 函数内的 ID 是从 agg 中获取的,为了匹配 T2 的长度(因为 T2 不在 agg 中),所以会混淆 ab。但在这种情况下避免使用 V1 是可以的,甚至更有效率。 - Matt Dowle

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上执行tapply,而不是在ID和T2上执行。有更快的方法吗?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接