这是一个以两种不同的方式分配的例子,一种有效,另一种无效:
library(datasets)
dat <- as.data.frame(ChickWeight)
dat$test1 <- with(dat, Time + weight)
with(dat, test2 <- Time + weight)
> colnames(dat)
[1] "weight" "Time" "Chick" "Diet" "test1"
我已经习惯了这种行为。也许更令人惊讶的是test2
只是消失了(而不是像我期望的那样在基础环境中消失):
> ls(pattern="test")
character(0)
注意用一个相当简单的^ H ^ H ^ H ^ H ^ H ^ H短函数:
function (data, expr, ...)
eval(substitute(expr), data, enclos = parent.frame())
首先让我们复制一下它的功能:
eval( substitute(Time+weight), envir=dat, enclos=parent.frame() )
现在使用不同的机箱进行测试:
testEnv <- new.env()
eval( substitute(test3 <- Time+weight), envir=dat, enclos=testEnv )
ls( envir=testEnv )
哪个仍然没有分配到任何地方。这反驳了我的预感,它与被抛弃的封闭环境有关,而是指向,enclos
参数更基本的东西而不是我认为它做的事情。
我很好奇机制为什么会这样,以及是否有替代允许分配。
答案 0 :(得分:4)
将with
更改为within
。 with
仅用于使变量可用,而不是更改它们。
编辑:详细说明,我相信with
和within
都会创建一个新环境,并使用给定的类似列表的对象(例如数据框)填充它,然后评估给定的在那个环境中表达。区别在于with
返回表达式的结果并丢弃环境,而within
返回环境(转换回原来的任何类,例如data.frame)。无论哪种方式,表达式中的任何分配都可能在创建的环境中执行,with
将其丢弃。这解释了在执行test2
后无法找到with(dat, test2 <- Time + weight)
的原因。
请注意,由于within
返回已修改的环境而不是在适当的位置进行编辑(即按值调用语义),因此您需要执行dat <- within(dat, test2 <- Time + weight)
。
如果您希望函数对当前环境(或任何指定环境)进行分配,请查看assign
。
编辑2:现代的答案是拥抱tidyverse并使用magrittr&amp; dplyr:
library(datasets)
library(dplyr)
library(magrittr)
dat <- as.data.frame(ChickWeight)
dat %<>% mutate(test1 = Time + weight)
最后一行相当于
dat <- dat %>% mutate(test1 = Time + weight)
又相当于
dat <- mutate(dat, test1 = Time + weight)
使用最后3行中最合适的一行。
答案 1 :(得分:1)
受以下事实的启发:命令行...
eval(substitute(test <- Time + weight, dat))
...我把以下内容放在一起,这似乎有用。
myWith <- function(DAT, expr) {
X <- call("eval",
call("substitute", substitute(expr), DAT))
eval(X, parent.frame())
}
## Trying it out
dat <- as.data.frame(ChickWeight)
myWith(dat, test <- Time + weight)
head(test)
# [1] 42 53 63 70 84 103
(此问题的复杂方面是我们需要substitute()
在一个环境(当前帧)中搜索符号,而“外部”eval()
分配到不同的环境(父框架)。)
答案 2 :(得分:1)
我觉得这太复杂了。 with
和within
都返回由对数据框的命名列的操作计算的值。如果您不将它们分配给任何东西,该值将被垃圾收集。存储tehn的常用方法是使用<-
运算符分配给命名对象或可能是对象的组件。 within
返回整个数据帧,而with
仅返回根据对列名执行的任何操作计算的向量。当然,您可以使用assign
而不是<-
,但我认为过度使用该功能可能会混淆而不是澄清代码。使用上的差异只是分配给一个委托数据帧或只是一列:
dat <- within(dat, newcol <- oldcol1*oldcol2)
dat$newcol <- with(dat, oldcol1*oldcol2)