R 将函数应用于data.table子集中的所有值
我有一个成对的值表,我正试图找到最快的方法,将一些函数应用到这个表的各个子集。我正在试验data.table,看看它是否适合我的需要 例如,我从这个数据点向量开始,将其转换为成对距离矩阵R 将函数应用于data.table子集中的所有值,r,data.table,R,Data.table,我有一个成对的值表,我正试图找到最快的方法,将一些函数应用到这个表的各个子集。我正在试验data.table,看看它是否适合我的需要 例如,我从这个数据点向量开始,将其转换为成对距离矩阵 dat <- c(spA = 4, spB = 10, spC = 8, spD = 1, spE = 5, spF = 9) pdist <- as.matrix(dist(dat)) pdist[upper.tri(pdist, diag = TRUE)] <- NA 将此表转换为dat
dat <- c(spA = 4, spB = 10, spC = 8, spD = 1, spE = 5, spF = 9)
pdist <- as.matrix(dist(dat))
pdist[upper.tri(pdist, diag = TRUE)] <- NA
将此表转换为data.table
library(data.table)
pdist <- as.data.table(pdist, keep.rownames=TRUE)
setkey(pdist, rn)
> pdist
rn spA spB spC spD spE spF
1: spA NA NA NA NA NA NA
2: spB 6 NA NA NA NA NA
3: spC 4 2 NA NA NA NA
4: spD 3 9 7 NA NA NA
5: spE 1 5 3 4 NA NA
6: spF 5 1 1 8 4 NA
现在,我如何应用一个函数,例如取这个子集中所有值的平均值(但可能是一个自定义函数)?我可以这样做,但我想知道是否有更好的方法来处理data.table操作
> mean(unlist(pdist[.(sub), sub, with=FALSE]), na.rm=TRUE)
[1] 6
更新
接下来,我决定看看矩阵方法与data.table方法的性能有多大不同:
dat <- runif(1000)
names(dat) <- paste0('sp', 1:1000)
spSub <- replicate(10000, sample(names(dat), 100), simplify=TRUE)
# calculate pairwise distance matrix
pdist <- as.matrix(dist(dat))
pdist[upper.tri(pdist, diag = TRUE)] <- NA
# convert to data.table
pdistDT <- as.data.table(pdist, keep.rownames='sp')
setkey(pdistDT, sp)
matMethod <- function(pdist, sub) {
return(mean(pdist[sub, sub], na.rm=TRUE))
}
dtMethod <- function(pdistDT, sub) {
return(mean(unlist(pdistDT[.(sub), sub, with=FALSE]), na.rm=TRUE))
}
> system.time(q1 <- lapply(spSub, function(x) matMethod(pdist, x)))
user system elapsed
18.116 0.154 18.317
> system.time(q2 <- lapply(spSub, function(x) dtMethod(pdistDT, x)))
user system elapsed
795.456 13.357 806.820
dat请参阅此处发布的解决方案,了解更多通用解决方案。这也可能有助于:
要应用该功能,可以执行以下操作:
第一部分。逐步解决办法
(1.a)将数据转换成数据表格式:
第二部分。数据表方法的一些优点
因为您似乎熟悉矩阵方法,所以我只想指出保留data.table方法的一些优点
(2.a)使用“by=”在组内应用函数
相对于矩阵的一个优点是,您仍然可以使用“by=”参数在组内应用函数
在这里的示例中,我假设您有一个名为“Grp”的变量
使用by=Grp
行,规范化现在在组内
pdist[, unlist(.SD) %>% normalize(), .SDcols = sub, by=Grp]
(2.b)另一个优点是,您可以保留其他标识信息,例如,如果每行都有一个“参与者标识符”P.Id,您希望保留并重复:
在第一步中,在代码的这一部分中完成:pdist[,(Combined.Data=unlist(.SD)),.SDcols=sub,by=p.Id]
首先,我们为“sub”中标识的所有三列中的数据创建一个名为“Combined.Data”的新列
在组合数据的每一行旁边,相应的参与者Id将在P.Id列中重复
在第二步中,在代码的这一部分中完成:
[,(P.Id,Normalized=normalize(Combined.Data),Combined.Data)]
我们可以创建一个名为normalize的新列来存储应用函数normalize()
此外,我们还可以包括Combined.Data列
因此,通过这一行:
pdist[,(Combined.Data=unlist(.SD)),.SDcols=sub,by=P.Id][order(P.Id),(P.Id,Transformed=normalize(Combined.Data),Combined.Data)]
- 我们需要一组列
- 跨子集折叠数据
- 即使在折叠时,也要跟踪每个基准(P.Id)的标识符
- 对整个折叠的数据应用转换,然后
- 最后以数据表的形式输出一个整洁的结果,数据表有3列:(1)P.Id,(2)Transformed,&(3)Combined.data(原始值)
- 而且,
顺序(P.Id)
允许输出显示有意义的顺序
使用矩阵方法也可以做到这一点,但会更加麻烦,需要更多的代码行
数据表允许对数据进行强大的操作和管理,特别是当您开始将操作链接在一起时
(2.c)最后,如果您只希望将行信息保留为简单的row.number,则可以使用data.table包的.I功能:
此功能非常有用,尤其是当您没有本质上有意义的参与者或行标识符时
第三部分。缺点:时间成本
我重新创建了上面显示的校正时间成本,数据表的解决方案确实需要更长的时间
dat <- runif(1000)
names(dat) <- paste0('sp', 1:1000)
spSub <- replicate(10000, sample(names(dat), 100), simplify=TRUE)
# calculate pairwise distance matrix
pdist <- as.matrix(dist(dat))
pdist[upper.tri(pdist, diag = TRUE)] <- NA
# convert to data.table
pdistDT <- as.data.table(pdist, keep.rownames='sp')
# pdistDT$sp %<>% as.factor()
setkey(pdistDT, sp)
matMethod <- function(pdist, sub) {
return(mean(pdist[sub, sub], na.rm=TRUE))
}
dtMethod <- function(pdistDT, sub) {
return(pdistDT[sub, sub, with = FALSE] %>%
unlist(., recursive = FALSE, use.names = FALSE) %>%
mean(., na.rm = TRUE))
}
dtMethod1 <- function(pdistDT, sub) {
return(pdistDT[sub, sub, with = FALSE] %>%
melt.data.table(., measure.vars = sub, na.rm=TRUE) %$%
mean(value))
}
system.time(q1 <- apply(spSub, MARGIN = 2, function(x) matMethod(pdist, x)))
# user system elapsed
# 2.86 0.00 3.27
system.time(q2 <- apply(spSub, MARGIN = 2, function(x) dtMethod(pdistDT, x)))
# user system elapsed
# 57.20 0.02 57.23
system.time(q3 <- apply(spSub, MARGIN = 2, function(x) dtMethod1(pdistDT, x)))
# user system elapsed
# 62.78 0.06 62.91
dat获得平均值的方法已经很好了,我认为你应该坚持使用一个矩阵:平均值(m[sub,sub],na.rm=TRUE)
。我认为将其放入data.table中不会有任何好处。如果您想要提高性能,请查看RcppArmadillo
或RcppEigen
中有关对矩阵子集进行操作的内容。我试图解决您看到的速度差异,并复制上面的精确代码。据我所知-矩阵版本不起作用q1完全由NAN组成。所以,这也许可以解释为什么它看起来要快得多。它实际上并没有做它应该做的事如果我遗漏了什么,请告诉我。
dat <- runif(1000)
names(dat) <- paste0('sp', 1:1000)
spSub <- replicate(10000, sample(names(dat), 100), simplify=TRUE)
# calculate pairwise distance matrix
pdist <- as.matrix(dist(dat))
pdist[upper.tri(pdist, diag = TRUE)] <- NA
# convert to data.table
pdistDT <- as.data.table(pdist, keep.rownames='sp')
setkey(pdistDT, sp)
matMethod <- function(pdist, sub) {
return(mean(pdist[sub, sub], na.rm=TRUE))
}
dtMethod <- function(pdistDT, sub) {
return(mean(unlist(pdistDT[.(sub), sub, with=FALSE]), na.rm=TRUE))
}
> system.time(q1 <- lapply(spSub, function(x) matMethod(pdist, x)))
user system elapsed
18.116 0.154 18.317
> system.time(q2 <- lapply(spSub, function(x) dtMethod(pdistDT, x)))
user system elapsed
795.456 13.357 806.820
library(data.table)
library(magrittr) #for access to pipe operator
pdist <- as.data.table(pdist, keep.rownames=TRUE)
setkey(pdist, rn)
# Get the list of names
sub <- c('spB', 'spF', 'spD')
#Define the function you wish to apply
# Where, normalize is just a function as defined in the question:
normalize <- function(X, X.mean = mean(X, na.rm=T), X.sd = sd(X, na.rm=T)){
X <- (X - X.mean) / X.sd
return(X)}
# Voila:
pdist[, unlist(.SD, use.names = FALSE), .SDcols = sub] %>% normalize()
#Or, you can apply the function inside the [], as below:
pdist[, unlist(.SD, use.names = FALSE) %>% normalize(), .SDcols = sub]
# Or, if you prefer to do it without the pipe operator:
pdist[, normalize(unlist(.SD, use.names = FALSE)), .SDcols = sub]
pdist[, unlist(.SD) %>% normalize(), .SDcols = sub, by=Grp]
pdist[, .(Combined.Data = unlist(.SD)), .SDcols = sub, by=P.Id][order(P.Id),.(P.Id, Transformed = normalize(Combined.Data), Combined.Data)]
pdist[, .(.I, normalize(unlist(.SD)), .SDcols = sub]
dat <- runif(1000)
names(dat) <- paste0('sp', 1:1000)
spSub <- replicate(10000, sample(names(dat), 100), simplify=TRUE)
# calculate pairwise distance matrix
pdist <- as.matrix(dist(dat))
pdist[upper.tri(pdist, diag = TRUE)] <- NA
# convert to data.table
pdistDT <- as.data.table(pdist, keep.rownames='sp')
# pdistDT$sp %<>% as.factor()
setkey(pdistDT, sp)
matMethod <- function(pdist, sub) {
return(mean(pdist[sub, sub], na.rm=TRUE))
}
dtMethod <- function(pdistDT, sub) {
return(pdistDT[sub, sub, with = FALSE] %>%
unlist(., recursive = FALSE, use.names = FALSE) %>%
mean(., na.rm = TRUE))
}
dtMethod1 <- function(pdistDT, sub) {
return(pdistDT[sub, sub, with = FALSE] %>%
melt.data.table(., measure.vars = sub, na.rm=TRUE) %$%
mean(value))
}
system.time(q1 <- apply(spSub, MARGIN = 2, function(x) matMethod(pdist, x)))
# user system elapsed
# 2.86 0.00 3.27
system.time(q2 <- apply(spSub, MARGIN = 2, function(x) dtMethod(pdistDT, x)))
# user system elapsed
# 57.20 0.02 57.23
system.time(q3 <- apply(spSub, MARGIN = 2, function(x) dtMethod1(pdistDT, x)))
# user system elapsed
# 62.78 0.06 62.91