r - data.table 的 `:=` 操作符真的是通过引用来操作的吗?
问题描述
data.table 的:=
运算符记录为:
...通过引用添加或更新或删除列。它根本不复制内存的任何部分。
那么这里会发生什么?
dt <- data.table(a = 1:5, b = 6:10)
address(dt$b)
# [1] "0000021cca78db58"
dt[, b := 2*a]
address(dt$b)
# [1] "0000021cc77ade10"
列的地址怎么b
变了?
我正在使用 R 3.6.1 和 data.table 1.12.8。
解决方案
您(或者也许是专栏)刚刚被打断了;)帮助文本()中对打断行为进行了相当详尽的描述?`:=`
:
不像
<-
_data.frame
,(可能很大)LHS 不会被强制匹配(通常很小)RHS 的类型。相反,如果需要,RHS 会被强制匹配 LHS 的类型。如果这涉及将双精度值强制转换为整数列,则会给出警告(无论小数数据是否被截断)。这样做的动机是效率。最好在前面正确地获取列类型并坚持使用它们。更改列类型是可能的,但故意更难:提供一整列作为 RHS。然后将此 RHS 插入该列槽中,我们称之为 plonk 语法,或者如果您愿意,可以替换列语法。通过需要构造一个新类型的全长向量,作为用户的您更加了解正在发生的事情,并且您的代码的读者更清楚您确实打算更改列类型。
但是,文档中目前没有明确说明plonking 和内存之间的关系(但请参见下文)。因此,像您和其他人这样的问题(在 github 上::= 如果 i 丢失,则不会通过引用现有列更新,并不总是就地分配)。:=
github 帖子中有很多有趣的点,但我不要重复它们,请去那里享受吧!不过,我相信Matt Dowle 的一句话很好地证明了这种笨拙的行为:
而不是 5 列分配,现在只有一个用于
a+a
表达式(RHS,无论如何都会创建),然后通过引用将其插入列槽,即address(DT)
不会改变但address(DT$a)
会改变。这是正确的行为,也是最有效的,可以保存将整个 RHS 复制到现有列中(这只有在它们是相同类型的情况下才有可能)。由于 RHS 与行数一样长,所以它只是被插入。
(免责声明:自那篇文章以来,事情可能已经发生了变化data.table
,R
但我认为主要信息仍然有效。)
关于文档,有一个开放的 PR(更新和澄清 := docs),其中建议对 plonk 和内存进行更明确的描述:
当一列被plonked时,原始列不会通过引用更新,因为这需要更新该列的每个元素。
我被撞了吗?是的!对我来说,这不是内存,而是列类引起了一些头疼,我最终来到这里:Why is data.table cast column classes when I assign all columns by reference。在阅读了您的问题后,我回到了那篇文章,并意识到马特的非常好的回答不仅“”解决了课堂问题,而且还解决了记忆问题。我认为值得在这里重复(我的粗体和评论[]
):
如果然后将RHS
length(RHS) == nrow(DT)
(以及任何类型) 插入该列槽。即使这些长度为 1。如果,列(及其类型)的内存保持在原位[隐含内存没有保持原位,我假设]但 RHS 被强制并回收以替换(子集)项目在那一栏中。length(RHS) < nrow(DT)
length(RHS) == nrow(DT)
如果我需要在大表中更改列的类型,我会写:
DT[, col := as.numeric(col)]
这里
as.numeric
分配一个新向量,将“col”强制到那个新内存中,然后将它插入到列 slot中。它尽可能高效。这是一个笨拙的原因是因为length(RHS) == nrow(DT)
。
推荐阅读
- python - 基于应由部分字符串匹配的公共列合并两个数据框
- python - 即使使用 pd.set_option(),Pandas 也无法打印未截断的数据帧
- elasticsearch - Elastic Search 中的空间处理
- scala - 如何在数据框中获取数组并将其转换为数据框
- python - 如何在列表中进行减法?
- vue.js - 无法在 vuex 中更新购物车商品数量
- javascript - Getting infinite loop on useEffect hook
- xml - 如何使用 xsl:for-each 获取输入
- python - RNN 在最近的数据上具有更多权重
- moodle - How to override Moodle Core functionality?