我想知道是否有人可以查看以下代码和最小示例并提出改进建议 - 尤其是在处理非常大的数据集时的代码效率方面。
该函数接受data.frame并按分组变量(factor)拆分,然后计算每组中所有行的距离矩阵。
我不需要保留距离矩阵 - 只需要一些统计数据,即均值,直方图......,然后它们就可以被丢弃。
我对内存分配等知之甚少,我想知道最好的方法是什么,因为我将处理每组10.000-100,000个案例。任何想法将不胜感激!
此外,在遇到严重的内存问题时,将bigmemory或其他大型数据处理包包含在函数中的最不痛苦的方法是什么?
FactorDistances <- function(df) {
# df is the data frame where the first column is the grouping variable.
# find names and number of groups in df (in the example there are three:(2,3,4)
factor.names <- unique(df[1])
n.factors <-length(unique(df$factor))
# split df by factor into list - each subset dataframe is one list element
df.l<-list()
for (f in 1:n.factors) {df.l[[f]]<-df[which(df$factor==factor.names[f,]),]}
# use lapply to go through list and calculate distance matrix for each group
# this results in a new list where each element is a distance matrix
distances <- lapply (df.l, function(x) dist(x[,2:length(x)], method="minkowski", p=2))
# again use lapply to get the mean distance for each group
means <- lapply (distances, mean)
rm(distances)
gc()
return(means)
}
df <- data.frame(cbind(factor=rep(2:4,2:4), rnorm(9), rnorm(9)))
FactorDistances(df)
# The result are three average euclidean distances between all pairs in each group
# If a group has only one member, the value is NaN
编辑:我编辑了标题以反映我作为答案发布的分块问题..
答案 0 :(得分:5)
我已经为那些dist()无法处理的超大矩阵提出了一个分块解决方案,我在这里发布以防万一其他人发现它有用(或者发现它有问题,请!)。它明显比dist()慢,但这有点无关紧要,因为它只应在dist()抛出错误时使用 - 通常是以下之一:
"Error in double(N * (N - 1)/2) : vector size specified is too large"
"Error: cannot allocate vector of size 6.0 Gb"
"Error: negative length vectors are not allowed"
该函数计算矩阵的平均距离,但您可以将其更改为其他任何内容,但是如果您想要实际保存矩阵,我相信某种文件备份的bigmemory矩阵是有序的。感谢{{3这个想法和Ari的帮助!
FunDistanceMatrixChunking <- function (df, blockSize=100){
n <- nrow(df)
blocks <- n %/% blockSize
if((n %% blockSize) > 0)blocks <- blocks + 1
chunk.means <- matrix(NA, nrow=blocks*(blocks+1)/2, ncol= 2)
dex <- 1:blockSize
chunk <- 0
for(i in 1:blocks){
p <- dex + (i-1)*blockSize
lex <- (blockSize+1):(2*blockSize)
lex <- lex[p<= n]
p <- p[p<= n]
for(j in 1:blocks){
q <- dex +(j-1)*blockSize
q <- q[q<=n]
if (i == j) {
chunk <- chunk+1
x <- dist(df[p,])
chunk.means[chunk,] <- c(length(x), mean(x))}
if ( i > j) {
chunk <- chunk+1
x <- as.matrix(dist(df[c(q,p),]))[lex,dex]
chunk.means[chunk,] <- c(length(x), mean(x))}
}
}
mean <- weighted.mean(chunk.means[,2], chunk.means[,1])
return(mean)
}
df <- cbind(var1=rnorm(1000), var2=rnorm(1000))
mean(dist(df))
FunDistanceMatrixChunking(df, blockSize=100)
不确定我是否应该将此作为编辑而不是答案发布..它确实解决了我的问题,虽然我没有真正指明这种方式..
答案 1 :(得分:2)
一些想法:
unique(df[1])
可能有效(通过忽略列表的data.frame属性),但让我感到紧张并且难以阅读。 unique(df[,1])
会更好。for (f in 1:n.factors) {df.l[[f]]<-df[which(df$factor==factor.names[f,]),]}
可以使用split
。lapply (df.l, function(x) mean(dist(x[,2:length(x)], method="minkowski", p=2)))
。 如果您需要多个摘要统计信息,请计算两者并返回一个列表:
lapply (df.l, function(x) {
dmat <- dist(x[,2:length(x)], method="minkowski", p=2)
list( mean=mean(dmat), median=median(dmat) )
})
看看是否能让你随处可见。如果没有,您可能需要更专业化(避免lapply
,将您的data.frames存储为矩阵等等。)