Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assignment to a data.frame with `with`

Tags:

r

Here's an example that assigns in two different ways, one which works and one which doesn't:

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" 

I've grown accustomed to this behavior. Perhaps more surprising is that test2 just disappears (instead of winding up in the base environment, as I'd expect):

> ls(pattern="test")
character(0)

Note that with is a fairly simple^H^H^H^H^H^H short function:

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

First let's replicate with's functionality:

eval( substitute(Time+weight), envir=dat, enclos=parent.frame() )

Now test with a different enclosure:

testEnv <- new.env()
eval( substitute(test3 <- Time+weight), envir=dat, enclos=testEnv )
ls( envir=testEnv )

Which still doesn't assign anywhere. This disproves my hunch that it was related to the enclosing environment being discarded, and rather points to something more fundamental to the ,enclos argument not doing what I think it does.

I'm curious about the mechanics of why this is going on and if there's an alternative which allows assignment.

like image 544
Ari B. Friedman Avatar asked Sep 16 '26 22:09

Ari B. Friedman


1 Answers

Change with to within. with is only for making variables available, not changing them.

Edit: To elaborate, I believe that both with and within create a new environment and populate it with the given list-like object (such as a data frame), and then evaluate the given expression within that environhment. The difference is that with returns the result of the expression and discards the environment, while within returns the environment (converted back to whatever class it originally was, e.g. data.frame). Either way, any assignments made within the expression are presumably performed inside the created environment, which is discarded by with. This explains why test2 is nowhere to be found after doing with(dat, test2 <- Time + weight).

Note that since within returns the modified environment instead of editing it in place (i.e. call-by-value semantics), you need to do dat <- within(dat, test2 <- Time + weight).

If you want a function to do assignment to the current environment (or any specified environment), look at assign.

Edit 2: The modern answer is to embrace the tidyverse and use magrittr & dplyr:

library(datasets)
library(dplyr)
library(magrittr)
dat <- as.data.frame(ChickWeight)
dat %<>% mutate(test1 = Time + weight)

The last line is equivalent to

dat <- dat %>% mutate(test1 = Time + weight)

which is in turn equivalent to

dat <- mutate(dat, test1 = Time + weight)

Use whichever of the last 3 lines makes the most sense to you.

like image 108
Ryan C. Thompson Avatar answered Sep 18 '26 18:09

Ryan C. Thompson



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!