使用`with`分配到data.frame

时间:2013-02-11 19:10:08

标签: r

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

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参数更基本的东西而不是我认为它做的事情。

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

3 个答案:

答案 0 :(得分:4)

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

编辑:详细说明,我相信withwithin都会创建一个新环境,并使用给定的类似列表的对象(例如数据框)填充它,然后评估给定的在那个环境中表达。区别在于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)

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

 dat <- within(dat, newcol <- oldcol1*oldcol2)
 dat$newcol <- with(dat,  oldcol1*oldcol2)