为什么从 data.table 中选择列会产生副本?

2023-11-22

看来从 data.table 中选择列[.data.table结果产生基础向量的副本。我说的是非常简单的列选择,按名称,没有要计算的表达式j并且没有要子集的行i。更奇怪的是,data.frame 中的列子集似乎没有创建任何副本。我正在使用 data.table 版本 data.table 1.10.4。下面提供了一个包含详细信息和基准的简单示例。我的问题是:

  • 难道我做错了什么?
  • 这是一个错误还是这是预期的行为?
  • 如果这是有意的,那么按列对 data.table 进行子集化并避免额外复制的最佳方法是什么?

预期的用例涉及大型数据集,因此必须避免额外的副本(特别是因为基础 R 似乎已经支持这一点)。

library(data.table)
set.seed(12345)
cpp_dt <- data.table(a = runif(1e6), b = rnorm(1e6), c = runif(1e6))
cols=c("a","c")

## naive / data.frame style of column selection
## leads to a copy of the column vectors in cols
subset_cols_1=function(dt,cols){
  return(dt[,cols,with=F])
}

## alternative syntax, still results in a copy
subset_cols_2=function(dt,cols){
  return(dt[,..cols])
}

## work-around that uses data.frame column selection,
## appears to avoid the copy
subset_cols_3=function(dt,cols){
  setDF(dt)
  subset=dt[,cols]
  setDT(subset)
  setDT(dt)
  return(subset)
}

## another approach that makes a "shallow" copy of the data.table
## then NULLs the not needed columns by reference
## appears to also avoid the copy
subset_cols_4=function(dt,cols){
  subset=dt[TRUE]
  other_cols=setdiff(names(subset),cols)
  set(subset,j=other_cols,value=NULL)
  return(subset)
}

subset_1=subset_cols_1(cpp_dt,cols)
subset_2=subset_cols_2(cpp_dt,cols)
subset_3=subset_cols_3(cpp_dt,cols)
subset_4=subset_cols_4(cpp_dt,cols)

现在让我们看看内存分配并与原始数据进行比较。

.Internal(inspect(cpp_dt)) # original data, keep an eye on 1st and 3d vector
# @7fe8ba278800 19 VECSXP g1c7 [OBJ,MARK,NAM(2),ATT] (len=3, tl=1027)
#   @10e2ce000 14 REALSXP g1c7 [MARK,NAM(2)] (len=1000000, tl=0) 0.720904,0.875773,0.760982,0.886125,0.456481,...
#   @10f1a3000 14 REALSXP g1c7 [MARK,NAM(2)] (len=1000000, tl=0) -0.947317,-0.636669,0.167872,-0.206986,0.411445,...
#   @10f945000 14 REALSXP g1c7 [MARK,NAM(2)] (len=1000000, tl=0) 0.717611,0.95416,0.191546,0.48525,0.539878,...
# ATTRIB: [removed]

Using [.data.table对列进行子集化的方法:

.Internal(inspect(subset_1)) # looks like data.table is making a copy
# @7fe8b9f3b800 19 VECSXP g0c7 [OBJ,NAM(1),ATT] (len=2, tl=1026)
#   @114cb0000 14 REALSXP g0c7 [MARK,NAM(2)] (len=1000000, tl=0) 0.720904,0.875773,0.760982,0.886125,0.456481,...
#   @1121ca000 14 REALSXP g0c7 [NAM(2)] (len=1000000, tl=0) 0.717611,0.95416,0.191546,0.48525,0.539878,...
# ATTRIB: [removed]

仍在使用的另一个语法版本[.data.table并仍在制作副本:

.Internal(inspect(subset_2)) # same, still copy
# @7fe8b6402600 19 VECSXP g0c7 [OBJ,NAM(1),ATT] (len=2, tl=1026)
#   @115452000 14 REALSXP g0c7 [NAM(2)] (len=1000000, tl=0) 0.720904,0.875773,0.760982,0.886125,0.456481,...
#   @1100e7000 14 REALSXP g0c7 [NAM(2)] (len=1000000, tl=0) 0.717611,0.95416,0.191546,0.48525,0.539878,...
# ATTRIB: [removed]

使用一系列setDF, 其次是[.data.frame and setDT。看,向量a and c不再复制!看来基本 R 方法更高效/内存占用更小?

.Internal(inspect(subset_3)) # "[.data.frame" is not making a copy!!
# @7fe8b633f400 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=1026)
#   @10e2ce000 14 REALSXP g1c7 [MARK,NAM(2)] (len=1000000, tl=0) 0.720904,0.875773,0.760982,0.886125,0.456481,...
#   @10f945000 14 REALSXP g1c7 [MARK,NAM(2)] (len=1000000, tl=0) 0.717611,0.95416,0.191546,0.48525,0.539878,...
# ATTRIB: [removed]

另一种方法是对 data.table 进行浅表复制,然后通过在新 data.table 中引用将所有额外列设为 NULL。同样,没有复制。

.Internal(inspect(subset_4)) # 4th approach seems to also avoid the copy
# @7fe8b924d800 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=1027)
#   @10e2ce000 14 REALSXP g1c7 [MARK,NAM(2)] (len=1000000, tl=0) 0.720904,0.875773,0.760982,0.886125,0.456481,...
#   @10f945000 14 REALSXP g1c7 [MARK,NAM(2)] (len=1000000, tl=0) 0.717611,0.95416,0.191546,0.48525,0.539878,...
# ATTRIB: [removed]

现在让我们看看这四种方法的基准。它看起来像“[.data.frame”(subset_cols_3)是明显的赢家。

microbenchmark({subset_cols_1(cpp_dt,cols)},
               {subset_cols_2(cpp_dt,cols)},
               {subset_cols_3(cpp_dt,cols)},
               {subset_cols_4(cpp_dt,cols)},
               times=100)

# Unit: microseconds
#                                 expr      min        lq      mean   median        uq       max neval
#  {     subset_cols_1(cpp_dt, cols) } 4772.092 5128.7395 8956.7398 7149.447 10189.397 53117.358   100
#  {     subset_cols_2(cpp_dt, cols) } 4705.383 5107.1690 8977.1816 6680.666  9206.164 53523.191   100
#  {     subset_cols_3(cpp_dt, cols) }  148.659  177.9595  285.4926  250.620   283.414  4422.968   100
#  {     subset_cols_4(cpp_dt, cols) }  193.912  241.9010  531.8308  336.467   384.844 20061.864   100

我已经有一段时间没有思考这个问题了,但现在还是这样。

好问题。但是为什么你需要子集adata.table像那样?我们真的需要看看你在做什么next: 更大的图景。对于这个更大的图景,我们在 data.table 中可能有与基本 R 习惯用法不同的方式。

粗略地说明可能是一个不好的例子:

DT[region=="EU", lapply(.SD, sum), .SDcols=10:20]

而不是采用 R 语言的基本惯用法,即获取一个子集,然后执行下一步操作(此处,apply)在外面的结果上:

apply(DT[DT$region=="EU", 10:20], 2, sum)

总的来说,我们希望鼓励在一个空间内做尽可能多的事情[...]这样 data.table 就会看到i, j and by合而为一[...]操作并可以优化组合。当您对列进行子集化,然后在外部执行下一操作时,需要更多的软件复杂性来优化。在大多数情况下,大部分计算成本都在第一个[...]这减少到相对较小的尺寸。

话虽如此,除了弗兰克的评论之外shallow,我们也在等待,看看如何阿尔特雷普项目平移出来。这改进了基 R 中的引用计数,并且可能使:=可靠地知道正在操作的列是否需要首先进行写时复制。现在,:=总是通过引用更新,因此如果选择某些整个列没有进行深层复制,它会更新两个 data.table(出于这个原因,它是故意进行复制的)。如果:=里面没有使用[...] then [...]始终返回可以安全使用的新结果:=上,目前这是一个相当简单的规则。即使您所做的只是出于某种原因选择了几个整列。

我们确实需要看到更大的图景:您之后在列的子集上做了什么。明确这一点将有助于提高调查 ALTREP 或对此案例进行我们自己的参考计数的优先级。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么从 data.table 中选择列会产生副本? 的相关文章

  • R:变换不规则时间字符串

    我有两个不同的时间序列 来自不同的数据帧 具有不同的不规则格式 但问题是相同的 我只想提取小时 分钟 秒和毫秒 时代系列看起来像这样 ts1 08 27 23 445 08 27 24 280 08 27 25 115 I tried st
  • 从 R 主题模型中的 DocumentTermMatrix 中删除空文档?

    我正在使用 R 中的 topicmodels 包进行主题建模 我正在创建一个 Corpus 对象 进行一些基本的预处理 然后创建一个 DocumentTermMatrix corpus lt Corpus VectorSource vec
  • 根据值的运行总计创建组

    我的数据在一个变量 Y 上是唯一的 另一个变量 Z 告诉我每个 Y 中有多少人 我的问题是我想从这些 Y 和 Z 创建 45 人的组 我的意思是 每当运行总计Z 达到 45 创建一组 然后代码继续创建下一组 我的数据看起来像这样 ID X
  • Quarto/Rmarkdown 中的美人鱼图:狭窄且模糊

    我正在尝试生成 pdf 格式的四开文档 稍后会生成 word 格式 我遇到了美人鱼图的问题 请在下面找到一个示例 qmd 文件来说明该问题 所以首先它应该支持 mermaid 标签 但当我这样做时 我无法在 rstudio 中 运行 单元
  • 有效地将环境从内部功能转移到全局环境

    我有一个在其中创建环境的函数 我希望将该环境分配给全局环境 目前我通过将环境分配给来做到这一点globalenv 作为最后一步 如下 funfun lt function inc 1 dataEnv lt new env dataEnv d
  • ggplot 中跨组的连续线

    我有一个数据时间序列 其中观察了一些数据 模拟了一些数据 我想生成整个数据系列随时间变化的图 其中颜色表示数据源 但是 我只能弄清楚如何使 ggplot 中的 geom line 连接同一组中的点 这是一个例子来说明 Create samp
  • readRDS() 加载额外的包

    什么情况下会出现readRDS R 中的函数尝试加载包 命名空间 我很惊讶地在新的 R 会话中看到以下内容 gt loadedNamespaces 1 base datasets graphics grDevices methods sta
  • r Shiny 中的 fileInput 函数没有响应

    我是 R 和 R闪亮的新手 一直致力于构建一个统计应用程序 该应用程序将允许用户导入文件 然后对数据运行不同的统计程序 直到最近 fileData 函数一直对我来说运行良好 现在每当我尝试上传文件时 都不会打开任何内容 我已尝试了所有我能想
  • R:使用管道将单个参数传递到函数中的多个位置

    我试图专门使用管道来重写以下代码 使用babynames包中的babynames数据 library babynames library dplyr myDF lt babynames gt group by year gt summari
  • R:在 Shiny 中,如何修复应用于“反应性”类对象的“xtable”没有适用的方法

    我收到此错误 Error in UseMethod xtable no applicable method for xtable applied to an object of class reactive UI R library shi
  • 在 R 中修改传单弹出窗口

    我想修改 R 中传单弹出窗口的外观 帮助文件指出 in the popupOptions 函数需要 传递给底层 Javascript 对象构造函数的额外选项 In 这个例子 https rstudio github io leaflet p
  • R 中带有边缘箱线图的直方图

    如何使直方图中的 X 轴与边缘箱线图匹配 data lt rnorm 1000 nf lt layout mat matrix c 1 2 2 1 byrow TRUE height c 1 3 layout show nf par mar
  • 如何使用r中的dplyr在特定位置插入空白行

    我想在数据框中的特定位置插入空白行 我的数据框是这样的 dat lt data frame group c rep A 1 rep B 4 rep C 2 rep D 2 group 1 A 2 B 3 B 4 B 5 B 6 C 7 C
  • 如何在 R 中查找平衡面板数据(又名,如何查找面板中的哪些条目在给定窗口内完整)

    我有来自 Compustat 的大量数据 我向其中添加了一些手工收集的数据 认真地从一堆旧书中手工收集 但我不想手工收集整个面板 只想随机选择一个子集 为了找到更大的集合 我从中随机选择 我想从 Compustat 的平衡面板开始 我看到p
  • 创建序列组合

    我正在尝试解决以下问题 考虑 5 个简单序列 0 100 100 0 rep 0 101 rep 50 101 rep 100 101 我需要 3 个数字变量的集合 它们的所有组合都具有上述序列 由于有 5 个序列和 3 个变量 因此可以有
  • 如何将 mcmc.list 转换为 bugs 对象?

    我正在使用rjagsR 库 功能coda samples产生一个mcmc list 例如 来自example coda samples library rjags data LINE LINE recompile LINE out lt c
  • Sql批量复制截断小数

    当我使用批量复制将十进制值从 C DataTable 插入 Sql Server 2005 时 值会被截断而不是四舍五入 DataTable 中的数据类型为 Decimal 数据库中的数据类型为Decimal 19 3 数据表中的值为 1
  • 将 csv 文件上传到shinyApps.io

    我的应用程序在本地运行良好 并且我能够成功地将应用程序部署到shinyapps io 服务器 但是当我尝试使用shinyapps URL 在浏览器中加载应用程序时 收到以下错误消息 错误对象 数据 不是成立 我认为这是因为 data 变量从
  • 如何处理包内部的 R 数据?

    我正在开发的 R 包需要多个 R 数据对象 例如预先计算的模型和参数 目前 我将包的 数据 目录中的每个对象放在单独的 RData 文件中 使用该包时 用户可以使用 数据 功能将这些对象附加到他们的环境中 我想要的行为是 在加载包时 数据对
  • 使用插入符和方法 = gamLoess 进行训练时 R 崩溃

    当我运行下面的代码时 R 崩溃了 如果我在训练调用中注释掉tuneGrid行 就不会发生崩溃 我已经用另一个数据集尝试过此操作 但仍然使 R 崩溃 崩溃消息是 R 会话中止 R遇到致命错误 会话被终止 开始新会话 代码是 library s

随机推荐

  • 为什么阻塞系统调用会阻塞用户级线程的整个过程?

    我不明白以下内容 用户级线程需要非阻塞系统调用 即多线程内核 否则 即使进程中还有可运行的线程 整个进程也会在内核中阻塞 内核线程如何处理阻塞系统调用 在用户级线程中 当一个线程正在进行阻塞系统调用 例如读取 时 为什么其他线程不能继续其工
  • 如何获取 LAN 上的活动 IP 地址、MAC 地址和 NetBIOS 名称的列表?

    如何获取活动 IP 地址 MAC 地址和NetBIOS局域网上的名称 我想获取 NetBIOS 名称 IP 和MAC地址对于 LAN 上的每台主机 最好不必亲自走到每台 PC 处记录这些内容 如何做到这一点Windows 脚本宿主 Powe
  • npm 警告 npm npm 不支持 Node.js v9.1.0

    我更新到最新的节点 v9 1 0 现在 npm 不起作用 npm 警告 npm npm 不支持 Node js v9 1 0 事实证明 当前安装的npm与新的不兼容node并且很难更新 解决办法就是卸载npm然后重新安装node其中将包含正
  • 关于iPhone上URL中NSString字符无效的问题

    我的一些http请求的URL中有 空格 但iPhone无法识别它 每当网址中存在 空格 时 我就必须将 空格 更改为 20 现在我必须将用户输入的消息发送到服务器 并且它可以具有用户喜欢的任意多个 空格 看起来我必须把它们全部替换掉 str
  • 使用 google plus 登录时出现“无法创建与服务器的可靠连接”错误

    我正在开发一个应用程序 我想在其中使用 google plus 登录 我已按照以下步骤操作https developers google com mobile android sign in 但是当我尝试使用 google api 示例项目
  • 使用库 mouse() 中的估算数据集来拟合 R 中的多级模型

    我是包装新手mice但我试图从 R 中估算 5 个数据集popmis然后拟合一个lmer model with 每一个和最后pool 穿过他们 我觉得pool 函数于mice 不适用于lmer 来电来自lme4包 对吗 如果是这样的话 有没
  • 如何获取用户的所有 github `PushEvents` 列表

    我可以查看特定用户的所有事件 例如这里是我的 https api github com users arasbm events 但我只对特定类型的事件感兴趣 type PushEvent 如何获取这些数据而不必处理所有事件的列表 这可能很慢
  • 关于 cudaMemcpyAsync 函数

    我有一些疑问 最近在用CUDA做一个程序 在我的程序中 Host 上有一个用 std map string vector int 编程的大数据 通过使用这些数据 一些向量 int 被复制到GPU全局内存并在GPU上处理 处理后 在 GPU
  • 逆透视 SQL 表中的所有列

    我有一个包含 30 列的表 我想轻松地取消透视所有列 我知道我可以使用这个策略 SELECT col value INTO New Table FROM SELECT FROM Test Data p UNPIVOT value FOR c
  • 在页面加载时使用 javascript(无 jquery)自行调整窗口大小并居中

    这不是我们通常会做的事情 但我们正在尝试将视频加载到由 clickTag 来自横幅广告 打开的空白窗口中 并使其尽可能感觉像模态窗口 是否可以使用javascript自行调整clickTag打开的空白窗口的大小并将其置于屏幕中央 我们无法对
  • Flutter Facebook 网页登录

    我已经尝试过Flutter Facebook 登录包 它在 android 中工作正常 但在 web 中我没有被重定向到 Facebook 进行身份验证 尝试过这个软件包的人可以帮忙吗 该插件不支持网页 但有人更新了代码以支持 romulo
  • 从列表集合中删除项目而不删除

    我正在制作一个集合 我需要从集合中删除一项并使用过滤 删除的集合 这是我的代码 public class Emp public int Id get set public string Name get set List
  • 在视频 FFmpeg 上添加图像叠加

    我有一个视频 test mp4 和一个图像 test png test png是一个黑色图像 中心有一个透明的 洞 我想使用 FFmpeg 显示test png在视频上方 所以我只能看到一圈视频 如果可能的话 调整大小test png适应视
  • USSD 消息中的最大字符数是多少?

    据我了解 USSD 消息由 160 个字节组成 对于 7 位数据编码方案 最大字符数为 160 8 7 即 182 个字符 我不清楚 UCS2 编码的最大字符数是多少 通常情况下 它会类似于 160 2 但我对此有一些复杂的信息 USSD
  • SSO - SAML,成功登录后将用户重定向到指定的登录页面

    我正在实施 SSO 其中我是身份提供商 现在我能够成功登录服务提供商 但它带我到主页 我想在发布响应时指定着陆页 URL 进行了很多搜索 但找不到任何令人信服的东西 不太清楚 SAML 响应的哪个元素带有登陆页面 URL 或者是我必须指定的
  • r - 投资组合优化 -solve.QP - 约束不一致

    我正在尝试使用solve QP来解决投资组合优化问题 二次问题 总计 3 项资产 有4个限制 权重之和等于 1 投资组合预期回报率为 5 2 每项资产权重大于0 每个资产权重小于 0 5 Dmat 是协方差矩阵 Dmat lt matrix
  • 使用 HTTPBuilder POST -> NullPointerException?

    我正在尝试发出一个简单的 HTTP POST 请求 但我不知道为什么以下失败 我尝试按照示例进行操作here 我不明白我哪里错了 例外 java lang NullPointerException at groovyx net http H
  • $(document).ready() 之后立即调用什么 jQuery 事件?

    我生成了很多 HTML document ready 我有一个简单的窗口系统 但它不仅是在 document ready 还有一些 HTML 元素 不同的 JS 文件将内容放入 document ready 我希望我的窗口系统在之后生成 d
  • SQL Max(日期) 不带分组依据

    我有下表 MemberID ServDate 001 12 12 2015 001 12 13 2015 001 12 15 2015 002 11 30 2015 002 12 04 2015 我想让它看起来像这样 MemberID Se
  • 为什么从 data.table 中选择列会产生副本?

    看来从 data table 中选择列 data table结果产生基础向量的副本 我说的是非常简单的列选择 按名称 没有要计算的表达式j并且没有要子集的行i 更奇怪的是 data frame 中的列子集似乎没有创建任何副本 我正在使用 d