Mic*_*ael 37 r data.table
在" 数据分析软件:使用R编程 "一书中,John Chambers强调通常不应该为其副作用编写函数; 相反,函数应返回一个值而不修改其调用环境中的任何变量.相反,使用data.table对象编写好的脚本应该特别避免使用对象赋值<-,通常用于存储函数的结果.
首先,是一个技术问题.想象一个被调用的R函数proc1,它接受一个data.table对象x作为它的参数(除了,可能还有其他参数).proc1返回NULL但x使用修改:=.根据我的理解,proc1调用只是因为承诺的工作方式而proc1(x=x1)制作副本x1.但是,如下所示,原始对象x1仍然被修改proc1.为什么/这是怎么回事?
> require(data.table)
> x1 <- CJ(1:2, 2:3)
> x1
V1 V2
1: 1 2
2: 1 3
3: 2 2
4: 2 3
> proc1 <- function(x){
+ x[,y:= V1*V2]
+ NULL
+ }
> proc1(x1)
NULL
> x1
V1 V2 y
1: 1 2 2
2: 1 3 3
3: 2 2 4
4: 2 3 6
>
Run Code Online (Sandbox Code Playgroud)
此外,似乎使用proc1(x=x1)并不比直接在x上执行过程慢,这表明我对promises的模糊理解是错误的,并且它们以传递方式工作:
> x1 <- CJ(1:2000, 1:500)
> x1[, paste0("V",3:300) := rnorm(1:nrow(x1))]
> proc1 <- function(x){
+ x[,y:= V1*V2]
+ NULL
+ }
> system.time(proc1(x1))
user system elapsed
0.00 0.02 0.02
> x1 <- CJ(1:2000, 1:500)
> system.time(x1[,y:= V1*V2])
user system elapsed
0.03 0.00 0.03
Run Code Online (Sandbox Code Playgroud)
因此,假设将data.table参数传递给函数不会增加时间,这使得编写data.table对象的过程成为可能,同时结合了data.table的速度和函数的普遍性.但是,考虑到John Chambers所说的那些函数不应该有副作用,在R中编写这种类型的过程编程真的"没问题"吗?为什么他认为副作用是"坏"?如果我不理会他的建议,我应该注意哪些陷阱?我该怎么做才能写出"好"的data.table程序?
flo*_*del 27
是的,data.tables中的列的添加,修改和删除都是通过reference.从某种意义上说,这是一个很好的事情,因为data.table通常持有大量的数据,这将是非常内存和费时的所有更改它是由每次重新分配它.另一方面,这是一件坏事,因为它违背了no-side-effectR试图pass-by-value通过默认使用来提升的函数式编程方法.使用无副作用编程时,几乎不用担心何时调用函数:您可以放心,您的输入或环境不会受到影响,您可以专注于函数的输出.它很简单,因此很舒服.
如果你知道自己在做什么,当然可以忽略约翰·钱伯斯的建议.关于编写"好"的data.tables程序,如果我是你,我会考虑一些规则,作为限制复杂性和副作用数量的方法:
do.something.to(table)而不是table <- do.something.to(table).相反,如果函数有另一个("真实")输出,那么在调用时result <- do.something.to(table),很容易想象你如何将注意力集中在输出上并忘记调用函数会对你的表产生副作用.虽然"一个输出/无副作用"功能是R中的标准,但上述规则允许"一个输出或副作用".如果你同意副作用在某种程度上是一种输出形式,那么你会同意我并没有过多地遵守R的单输出函数式编程风格而过分夸大规则.允许函数具有多个副作用会稍微延伸一点; 并不是说你不能这样做,但如果可能的话我会尽量避免它.
Mat*_*wle 17
文档可以改进(非常受欢迎的建议),但目前这里有什么.或许它应该说"甚至在功能内"?
在?":=":
data.tables不会被以下内容复制:=,setkey或任何其他set*函数.见副本.
通过引用修改DT并返回新值.如果您需要副本,请先复制一份(使用DT2 = copy(DT)).回想一下,这个包是对于大型数据,其中通过参考更新可以是许多数量级不是复制整个表更快(混合列类型,具有多列密钥).
和?copy(但我意识到这与setkey混淆):
输入通过引用修改,并返回(不可见),因此可以在复合语句中使用; 例如,setkey(DT,a)[J("foo")].如果您需要副本,请先复制一份(使用DT2 = copy(DT)).copy()有时可能在以下之前有用:=用于通过引用子列分配给列.看?复制.请注意,setattr也在包位中.两个软件包仅在C级别公开R的内部setAttrib函数,但返回值不同.bit :: setattr返回NULL(不可见)以提醒您该函数用于其副作用.data.table :: setattr返回已更改的对象(不可见),以便在复合语句中使用.
bit::setattr有趣的是,最后两句话与flodel的第2点有关.
另请参阅以下相关问题:
准确理解data.table何时是对另一个data.table的引用(vs副本).
通过引用传递:data.table包中的:=运算符data.table
1.8.1.:"DT1 = DT2"不是与DT1 = copy(DT2)相同?
我非常喜欢你的这部分问题:
这使得编写data.table对象的过程成为可能,同时兼顾了data.table的速度和函数的普遍性.
是的,这绝对是其中一个意图.考虑数据库的工作原理:许多不同的用户/程序通过引用(插入/更新/删除)更改数据库中的一个或多个(大)表.这在数据库领域工作得很好,更像是data.table的思维方式.因此,主页上的svSocket视频,insert以及delete(通过引用,仅动词,副作用功能)的愿望.
| 归档时间: |
|
| 查看次数: |
6726 次 |
| 最近记录: |