我有一个成对的值表,我正在尝试找到将一些函数应用于此表的各个子集的最快方法。我正在尝试使用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
看起来像这样:
> pdist
spA spB spC spD spE spF
spA NA NA NA NA NA NA
spB 6 NA NA NA NA NA
spC 4 2 NA NA NA NA
spD 3 9 7 NA NA NA
spE 1 5 3 4 NA NA
spF 5 1 1 8 4 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
如果我有一些要为其提取值的子集,
sub <- c('spB', 'spF', 'spD')
我可以执行以下操作,从而产生我感兴趣的子矩阵:
> pdist[.(sub), sub, with=FALSE]
spB spF spD
1: NA NA NA
2: 1 NA 8
3: 9 NA 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
看来,通过此处的data.table步骤会导致很大的性能成本。
答案 0 :(得分:1)
请参阅此处发布的解决方案,以获取更多常规解决方案。它还可能会帮助: data.table: transforming subset of columns with a function, row by row
要应用此功能,您可以执行以下操作:
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]
由于您似乎熟悉矩阵方法,所以我只想指出保留data.table方法的一些优点
相对于矩阵的一个优势是,您仍然可以使用“ by =“参数在组内应用函数。
在这里的示例中,我假设您有一个名为“ Grp”的变量。
使用by=Grp
行,现在归一化在组内。
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[, .(Combined.Data = unlist(.SD)), .SDcols = sub, by=P.Id]
第二步,在此部分代码中完成:
[,.(P.Id, Normalized = normalize(Combined.Data), Combined.Data)]
normalize()
因此,用以下这一行: pdist [,。(Combined.Data = unlist(.SD)),.SDcols = sub,by = P.Id] [order(P.Id),。(P.Id,Transformed = normalize(Combined.Data),组合数据)]
order(P.Id)
允许输出显示有意义的顺序。使用矩阵方法也可以做到这一点,但麻烦得多,并且需要更多代码行。
数据表允许强大的数据处理和管理,尤其是当您开始将操作链接在一起时。
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