由于内存(和速度)问题,我希望在data.table中进行一些计算,而不是在data.table中进行。
以下代码具有100.000行,但我正在处理4000万行。
from functools import wraps
def reversed_args(f):
@wraps(f)
def g(*args):
return f(*args[::-1])
return g
所有变体的输出是相同的,并且符合预期。 基准是:
26秒 72秒 38秒 105秒 ,所以我看不到使用data.table内的函数或使用mapply的优势。
我主要关心的是内存,而future_map2解决方案无法解决该问题。
我现在正在使用Windows,所以我希望找到除mclapply之外的其他速度解决方案,也许是一些data.table技巧,我没看到(列表不支持键输入)
答案 0 :(得分:5)
这实际上是有关内存和数据存储类型的问题。我所有的讨论都将针对100,000个数据元素,以便不会陷入困境。
让我们研究一个长度为100,000的向量与一个包含100,000个独立元素的列表的情况。
object.size(rep(1L, 1E5))
#400048 bytes
object.size(replicate(1E5, 1, simplify = F))
#6400048 bytes
仅仅通过不同的方式存储数据,我们从0.4 MB增长到6.4 MB!将其应用于函数Map(veryfing_function, ...)
和仅1E5元素时:
dt <- data.table(letters = replicate(1e5, sample(letters[1:5], 3, TRUE), simplify = FALSE),
numbers = replicate(1e5, sample(letters[6:10], 3, TRUE), simplify = FALSE))
tic()
result2 <- Map(veryfing_function, dt[['letters']], dt[['numbers']])
toc()
# 11.93 sec elapsed
object.size(result2)
# 109,769,872 bytes
#example return:
[[1000]]
[[1000]]$`1`
[1] "cg" "bg" "cg"
[[1000]]$`2`
[1] "ch" "bh" "ch"
[[1000]]$`3`
[1] "ch" "bh" "ch"
我们可以对您的函数进行简单的修改,以返回未命名列表而不是拆分,并且由于split()
似乎提供了命名列表,因此我们节省了一些内存,我认为我们不需要这个名称:
verifying_function2 <- function(vec1, vec2) {
vector <- outer(vec1, vec2, paste0) #not as.vector
lapply(seq_len(ncol(vector)), function(i) vector[, i]) #no need to split, just return a list
}
tic()
result2_mod <- Map(verifying_function2, dt[['letters']], dt[['numbers']])
toc()
# 2.86 sec elapsed
object.size(result2_mod)
# 73,769,872 bytes
#example_output
[[1000]]
[[1000]][[1]]
[1] "cg" "bg" "cg"
[[1000]][[2]]
[1] "ch" "bh" "ch"
[[1000]][[3]]
[1] "ch" "bh" "ch"
下一步就是为什么要完全返回列表列表。我在修改后的函数中使用lapply()
只是获得您的输出。相反,松开lapply()
会列出一系列我认为会有所帮助的矩阵:
tic()
result2_mod2 <- Map(function(x,y) outer(x, y, paste0), dt[['letters']], dt[['numbers']])
toc()
# 1.66 sec elapsed
object.size(result2_mod2)
# 68,570,336 bytes
#example output:
[[1000]]
[,1] [,2] [,3]
[1,] "cg" "ch" "ch"
[2,] "bg" "bh" "bh"
[3,] "cg" "ch" "ch"
最后一个逻辑步骤是只返回一个矩阵。请注意,在这整个过程中,我们一直在与mapply(..., simplify = F)
等效的Map()
进行简化。
tic()
result2_mod3 <- mapply(function(x,y) outer(x, y, paste0), dt[['letters']], dt[['numbers']])
toc()
# 1.3 sec elapsed
object.size(result2_mod3)
# 7,201,616 bytes
如果需要某种尺寸,可以将大矩阵转换为3D数组:
tic()
result2_mod3_arr <- array(as.vector(result2_mod3), dim = c(3,3,1E5))
toc()
# 0.02 sec elapsed
result2_mod3_arr[,,1000]
[,1] [,2] [,3]
[1,] "cg" "ch" "ch"
[2,] "bg" "bh" "bh"
[3,] "cg" "ch" "ch"
object.size(result2_mod3_arr)
# 7,201,624 bytes
我还查看了@marbel的答案-它更快,并且只占用更多的内存。通过将初始dt
列表尽快转换为其他列表,我的方法可能会受益。
tic()
dt1 = as.data.table(do.call(rbind, dt[['letters']]))
dt2 = as.data.table(do.call(rbind, dt[['numbers']]))
res = data.table()
combs = expand.grid(names(dt1), names(dt2), stringsAsFactors=FALSE)
set(res, j=paste0(combs[,1], combs[,2]), value=paste0( dt1[, get(combs[,1])], dt2[, get(combs[,2])] ) )
toc()
# 0.14 sec elapsed
object.size(res)
# 7,215,384 bytes
tl; dr -将您的对象转换为矩阵或data.frame,使其更易于存储。同样有意义的是,函数的data.table
版本需要更长的时间-可能比直接应用mapply()
的开销更大。
答案 1 :(得分:3)
这是解决问题的另一种方法,但我相信它会很有用。
输出是不同的,所以我不确定没有更多信息是否可以解决您的具体问题,但是在这里,希望对您有所帮助!
时间是1.165秒,而mapply是87秒。
vec1 = replicate(1e6, sample(letters[1:5], 3, TRUE), simplify = FALSE)
vec2 = replicate(1e6, sample(letters[6:10], 3, TRUE), simplify = FALSE)
dt <- data.table(v1 = vec1, v2 = vec2)
dt1 = as.data.table(do.call(rbind, vec1))
dt2 = as.data.table(do.call(rbind, vec2))
res = data.table()
tic()
cols1 = names(dt1)
cols2 = names(dt2)
combs = expand.grid(cols1, cols2, stringsAsFactors=FALSE)
for(i in 1:nrow(combs)){
vars = combs[i, ]
set(res, j=paste0(vars[,1], vars[,2]), value=paste0( dt1[, get(vars[,1])], dt2[, get(vars[,2])] ) )
}
toc()