【问题标题】:"Loop through" data.table to calculate conditional averages“循环”data.table 以计算条件平均值
【发布时间】:2012-03-23 17:27:40
【问题描述】:

我想“遍历”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 列中输入计算的平均值

     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 万行。

【问题讨论】:

  • 您编写的规范似乎难以操作,但您的示例表明,在每个 ID 组中,您需要 T2 在 T1 值范围内的某些值组的平均值。但是,当试图弄清楚为什么第二行中的 Data2 应该是 5 时,即使这种解释也失败了。?????
  • @DWin 这是因为平均是在​​ Data1 列上完成的。 Data2[2] 等于 5,因为 5 是 (4, 5, 6) 的平均值。

标签: r data.table


【解决方案1】:

经验法则是先聚合,然后加入。

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

【讨论】:

  • 谢谢你,马修。这是非常快的。是否有可能在创建 agg 中的 V1 列时给它一个自定义名称以避免列名混淆?
  • 试试DT[,list(myname=mean(Data1)),by=list(ID,T1)]。另请参阅data.table wiki 第 3 点,了解在这种情况下的进一步加速。
  • 我将您的第三行替换为DT[,Data2:={agg[J(ID, T2)][[3]]}],并得到相同的结果。即,我确实避免了JT= 位(以及,V1)。这些都是我的坏习惯吗?
  • @Josh 嗨。我试过那条线,但结果似乎不一样。 J() 中的ID 来自agg,被回收以匹配T2 的长度(来自DT,因为T2 不在agg 中)所以它混淆了a' s 和 b 的。但是在这种情况下避免V1 是可以的,也许效率更高。
【解决方案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.I.e 之前对其进行预处理。如果您从 SQL 服务器检索数据,让服务器计算平均值可能是更好的选择,因为它很可能在这方面做得更好。

R 实际上并不擅长数字运算,原因有几个。但是在对已经预处理的数据进行统计时非常好。

【讨论】:

    【解决方案3】:

    使用 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。有更快的吗?

    【讨论】:

      猜你喜欢
      • 2016-12-22
      • 2014-08-09
      • 2021-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多