我想“遍历”data.table的行并计算每行的平均值。平均值应根据以下机制计算:
- 在第i行中查找标识符ID(ID(i))
- 在第i行中查找T2的值(T2(i))
- 计算平均值
Data1
所有行中的值 j
,符合这两个标准: ID(j) = ID(i)
和 T1(j) = T2(i)
在第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万行。
经验法则是首先聚合,然后加入到那里。
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
也是。
迭代行的更快的替代方案将是采用向量化的解决方案。
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实际上并不擅长数字运算。但在对已经预处理的数据进行统计时,它非常出色。
使用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。还有什么比这更快的?