3

这是一个以两种不同方式分配的示例,一种有效,一种无效:

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)

注意 with 是一个相当简单的^H^H^H^H^H^H 短函数:

function (data, expr, ...) 
eval(substitute(expr), data, enclos = parent.frame())

首先让我们复制 with 的功能:

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论点,而不是像我认为的那样做。

我很好奇为什么会这样的机制以及是否有允许分配的替代方案。

4

3 回答 3

4

更改withwithinwith仅用于使变量可用,而不是更改它们。

编辑:详细说明,我相信两者都with创建within一个新环境并用给定的类似列表的对象(例如数据框)填充它,然后在该环境中评估给定的表达式。不同之处在于with返回表达式的结果并丢弃环境,而within返回环境(转换回原来的任何类,例如 data.frame)。无论哪种方式,在表达式中进行的任何赋值都可能在创建的环境中执行,该环境被with. 这就解释了为什么test2做了之后就无处可寻with(dat, test2 <- Time + weight)

请注意,由于within返回修改后的环境而不是就地编辑它(即按值调用语义),因此您需要执行dat <- within(dat, test2 <- Time + weight).

如果您想要一个函数对当前环境(或任何指定的环境)进行分配,请查看assign.

编辑 2:现代答案是拥抱 tidyverse 并使用 magrittr & 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 行中对您最有意义的那一行。

于 2013-02-11T19:13:31.877 回答
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()

于 2013-02-11T20:43:54.530 回答
1

我觉得这太复杂了。两者withwithin返回通过对数据帧的命名列的操作计算的值。如果您不将它们分配给任何东西,则该值将被垃圾收集。存储 tehn 的常用方法是使用运算符分配给命名对象或对象的组件<-within返回整个数据框,而with仅返回根据对列名执行的任何操作计算得出的向量。当然,您可以使用assign而不是<-,但我认为过度使用该函数可能会混淆而不是澄清代码。使用的区别只是分配给一个完整的数据框或只是一个列:

 dat <- within(dat, newcol <- oldcol1*oldcol2)
 dat$newcol <- with(dat,  oldcol1*oldcol2)
于 2013-02-11T21:07:19.887 回答