让我们先从“列”开始,看看发生了什么,然后再回到行。
R 版本data.frame当你对它们进行操作时。例如:
## R v3.0.3
df <- data.frame(x=1:5, y=6:10)
dplyr:::changes(df, transform(df, z=11:15)) ## requires dplyr to be available
# Changed variables:
# old new
# x 0x7ff9343fb4d0 0x7ff9326dfba8
# y 0x7ff9343fb488 0x7ff9326dfbf0
# z <added> 0x7ff9326dfc38
# Changed attributes:
# old new
# names 0x7ff934170c28 0x7ff934308808
# row.names 0x7ff934551b18 0x7ff934308970
# class 0x7ff9346c5278 0x7ff935d1d1f8
您可以看到添加“新”列已生成“旧”列的副本(地址不同)。属性也被复制。最令人印象深刻的是这些副本是深拷贝,相对于浅拷贝.
浅拷贝只复制列指针的向量,而不是整个数据,而深拷贝则复制所有内容(这里是不必要的)。
然而,在 R v3.1.0 中,出现了一些令人欢迎的变化,即“旧”列不再是deep复制的。所有功劳都归功于 R 核心开发团队。
## R v3.1.0
df <- data.frame(x=1:5, y=6:10)
dplyr:::changes(df, transform(df, z=11:15)) ## requires dplyr to be available
# Changed variables:
# old new
# z <added> 0x7f85d328dda8
# Changed attributes:
# old new
# names 0x7f85d1459548 0x7f85d297bec8
# row.names 0x7f85d2c66cd8 0x7f85d2bfa928
# class 0x7f85d345cab8 0x7f85d2d6afb8
您可以看到这些列x
and y
根本没有改变(因此不存在于输出中changes
函数调用)。这是一个巨大的(并且受欢迎的)改进!
到目前为止,我们研究了在 R
现在,回到你的问题:那么,“行”呢?我们首先考虑旧版本的 R,然后再回到 R v3.1.0。
## R v3.0.3
df <- data.frame(x=1:5, y=6:10)
df.old <- df
df$y[1L] <- -6L
dplyr:::changes(df.old, df)
# Changed variables:
# old new
# x 0x7f968b423e50 0x7f968ac6ba40
# y 0x7f968b423e98 0x7f968ac6bad0
#
# Changed attributes:
# old new
# names 0x7f968ab88a28 0x7f968abca8e0
# row.names 0x7f968abb6438 0x7f968ab22bb0
# class 0x7f968ad73e08 0x7f968b580828
我们再次看到不断变化的专栏y
导致复制列x
在旧版本的 R 中也是如此。
## R v3.1.0
df <- data.frame(x=1:5, y=6:10)
df.old <- df
df$y[1L] <- -6L
dplyr:::changes(df.old, df)
# Changed variables:
# old new
# y 0x7f85d3544090 0x7f85d2c9bbb8
#
# Changed attributes:
# old new
# row.names 0x7f85d35a69a8 0x7f85d35a6690
我们看到 R v3.1.0 中的出色改进,从而产生了just column y
。 R v3.1.0 再次有了巨大的改进! R 的修改时复制变得更加明智。
但仍然使用data.table
's 通过引用赋值语义上,我们可以做得更好——甚至不复制y
列与 R v3.1.0 中的情况相同。
这个想法是:只要您分配给特定索引处的列的对象类型不改变(这里,列y
是整数 - 所以只要你分配一个整数回y
), we really无需通过修改复制即可做到in-place(引用)。
为什么?因为我们不必在这里分配/重新分配任何东西。例如,如果您分配了双精度/数字类型,则需要 8 字节的存储空间,而不是整数列的 4 字节存储空间y
,然后我们要创建一个新列y
并将值复制回来。
也就是说,我们可以通过引用进行子分配 using data.table
。我们可以使用:=
or set()
去做这个。我将演示使用set()
here.
现在,这是与基础 R 和data.table
分别针对 R v3.0.3 和 v3.1.0,对包含 2,000 到 20,000,000 行(10 的倍数)的数据进行分析。您可以在这里找到代码 https://gist.github.com/arunsrinivasan/91b9951a57ce85090104.
与 R v3.0.3 的比较图:
Plot for comparison against R v3.1.0:
R v3.0.3、R v3.1.0 和 data.table 在 2000 万行(10 次复制)上的最小值、中值和最大值为:
type min median max
base_3.0.3 10.05 10.70 18.51
base_3.1.0 1.67 1.97 5.20
data.table 0.04 0.04 0.05
注意:您可以在以下位置查看完整的时间安排这个要点 https://gist.github.com/arunsrinivasan/91b9951a57ce85090104.
这清楚地显示了 R v3.1.0 中的改进,但也表明正在更改的列仍在复制,并且仍然消耗一些时间,这是通过以下方式克服的通过引用进行子分配 in data.table
.
HTH