热门标签 | HotTags
当前位置:  开发笔记 > 开发工具 > 正文

如何在data.table中执行更快的列表列操作

如何解决《如何在data.table中执行更快的列表列操作》经验,为你挑选了1个好方法。

由于内存(和速度)问题,我希望在data.table内进行一些计算,而不是在data.table外进行。

以下代码有100.000行,但我正在处理4000万行。

library(tictoc)
library(data.table) # version 1.11.8
library(purrr)
library(furrr)
plan(multiprocess)

veryfing_function <- function(vec1, vec2){
  vector <- as.vector(outer(vec1, vec2, paste0))
  split(vector, ceiling(seq_along(vector)/length(vec1)))
}


dt <- data.table(letters = replicate(1e6, sample(letters[1:5], 3, TRUE), simplify = FALSE),
                 numbers = replicate(1e6, sample(letters[6:10], 3, TRUE), simplify = FALSE))



tic()
result1 <- future_map2(dt$letters, dt$numbers, veryfing_function)
toc()


tic()
result2 <- mapply(veryfing_function, dt$letters, dt$numbers, SIMPLIFY = FALSE)
toc()



tic()
dt[, result := future_map2(letters, numbers, veryfing_function)]
toc()


tic()
dt[, result2 := mapply(veryfing_function, letters, numbers, SIMPLIFY = FALSE)]
toc()

所有变体的输出都是相同的,并且符合预期。基准是:

26秒72秒38秒105秒,所以我看不到使用data.table内的函数或使用mapply的优势。

我主要关心的是内存,future_map2解决方案无法解决该问题。

我现在正在使用Windows,所以我希望找到除mclapply以外的其他速度解决方案,可能是一些data.table技巧,我没有看到(列表不支持键)



1> Cole..:

这确实是有关内存和数据存储类型的问题。我所有的讨论将针对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()


推荐阅读
  • 使用 Git Rebase -i 合并多个提交
    在开发过程中,频繁的小改动往往会生成多个提交记录。为了保持代码仓库的整洁,我们可以使用 git rebase -i 命令将多个提交合并成一个。 ... [详细]
  • 兆芯X86 CPU架构的演进与现状(国产CPU系列)
    本文详细介绍了兆芯X86 CPU架构的发展历程,从公司成立背景到关键技术授权,再到具体芯片架构的演进,全面解析了兆芯在国产CPU领域的贡献与挑战。 ... [详细]
  • 如何在IE11中启用Edge模式?详细设置指南
    Edge模式是IE11的一项重要更新,旨在提升浏览器的兼容性和互操作性。该模式允许用户以更高标准的Web技术访问网页,类似于WP8.1 GDR1中的IE11特性。 ... [详细]
  • 2020年9月15日,Oracle正式发布了最新的JDK 15版本。本次更新带来了许多新特性,包括隐藏类、EdDSA签名算法、模式匹配、记录类、封闭类和文本块等。 ... [详细]
  • 包含phppdoerrorcode的词条 ... [详细]
  • 高端存储技术演进与趋势
    本文探讨了高端存储技术的发展趋势,包括松耦合架构、虚拟化、高性能、高安全性和智能化等方面。同时,分析了全闪存阵列和中端存储集群对高端存储市场的冲击,以及高端存储在不同应用场景中的发展趋势。 ... [详细]
  • 本文详细介绍了Linux系统中用于管理IPC(Inter-Process Communication)资源的两个重要命令:ipcs和ipcrm。通过这些命令,用户可以查看和删除系统中的消息队列、共享内存和信号量。 ... [详细]
  • A*算法在AI路径规划中的应用
    路径规划算法用于在地图上找到从起点到终点的最佳路径,特别是在存在障碍物的情况下。A*算法是一种高效且广泛使用的路径规划算法,适用于静态和动态环境。 ... [详细]
  • NX二次开发:UFUN点收集器UF_UI_select_point_collection详解
    本文介绍了如何在NX中使用UFUN库进行点收集器的二次开发,包括必要的头文件包含、初始化和选择点集合的具体实现。 ... [详细]
  • 解决SQL Server数据库sa登录名无法连接的问题
    在安装SQL Server数据库后,使用Windows身份验证成功,但使用SQL Server身份验证时遇到问题。本文将介绍如何通过设置sa登录名的密码、启用登录名状态以及开启TCP协议来解决这一问题。 ... [详细]
  • MySQL 数据库连接方法
    本文介绍了如何使用 MySQL 命令行工具连接到指定的数据库。 ... [详细]
  • 我有一个从C项目编译的.o文件,该文件引用了名为init_static_pool ... [详细]
  • 本文详细介绍了如何在PHP中记录和管理行为日志,包括ThinkPHP框架中的日志记录方法、日志的用途、实现原理以及相关配置。 ... [详细]
  • 如何解决8080端口被占用问题
    本文介绍了如何通过命令行和任务管理器查找并终止占用8080端口的进程,以确保该端口能够正常使用。 ... [详细]
  • 如何在Eclipse中配置Python开发环境
    本文详细介绍了如何在Eclipse中配置Python开发环境,包括安装PyDev插件和设置Python解释器的步骤。 ... [详细]
author-avatar
手机用户2502857587
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有