Why is it not possible to assign contrasts using with() or transform() in R?

StackOverflow https://stackoverflow.com/questions/23521863

  •  17-07-2023
  •  | 
  •  

Pergunta

I've been trying to learn more about environments in R. Through reading, it seemed that I should be able to use functions like with() and transform() to modify variables in a data.frame as if I was operating within that object's environment. So, I thought the following might work:

X <- expand.grid(
    Cond=c("baseline","perceptual","semantic"),
    Age=c("child","adult"),
    Gender=c("male","female")
)

Z <- transform(X,
    contrasts(Cond) <- cbind(c(1,0,-1)/2, c(1,-2,1))/4,
    contrasts(Age) <- cbind(c(-1,1)/2),
    contrasts(Gender) <- cbind(c(-1,1)/2)
)
str(Z)
contrasts(Z$Cond)

But it does not. I was hoping someone could explain why. Of course, I understand that contrasts(X$Cond) <- ... would work, but I'm curious about why this does not.

In fact, this does not work either [EDIT: false, this does work. I tried this quickly before posting originally and did something wrong]:

attach(X)
    contrasts(Cond) <- cbind(c(1,0,-1)/2, c(1,-2,1))/4
    contrasts(Age) <- cbind(c(-1,1)/2)
    contrasts(Gender) <- cbind(c(-1,1)/2)
detach(X)

I apologize if this is a "RTFM" sort of thing... it's not that I haven't looked. I just don't understand. Thank you!

[EDIT: Thank you joran---within() instead of with() or transform() does the trick! The following syntax worked.]

Z <- within(X, {
    contrasts(Cond) <- ...
    contrasts(Age) <- ...
    contrasts(Gender) <- ...
  }
)
Foi útil?

Solução

transform is definitely the wrong tool, I think. And you don't want with, you probably want within, in order to return the entire object:

X <- within(X,{contrasts(Cond) <- cbind(c(1,0,-1)/2, c(1,-2,1))/4
                contrasts(Age) <- cbind(c(-1,1)/2)
                contrasts(Gender) <- cbind(c(-1,1)/2)})

The only tricky part here is to remember the curly braces to enclose multiple lines in a single expression.

Your last example, using attach, works just fine for me.

transform is only set up to evaluate expressions of the form tag = value, and because of the way it evaluates those expressions, it isn't really set up to modify attributes of a column. It is more intended for direct modifications to the columns themselves. (Scaling, taking the log, etc.)

The difference between with and within is nicely summed up by the Value section of ?within:

Value For with, the value of the evaluated expr. For within, the modified object.

So with only returns the result of the expression. within is for modifying an object and returning the whole thing.

Outras dicas

While I agree with @Jornan that within is the best strategy here, I will point out it is possible to use transform you just need to do so in a different way

Z <- transform(X,
    Cond = `contrasts<-`(Cond, value=cbind(c(1,0,-1)/2, c(1,-2,1))/4),
    Age = `contrasts<-`(Age, value=cbind(c(-1,1)/2)),
    Gender= `contrasts<-`(Gender, value=cbind(c(-1,1)/2))
)

Here we are explicitly calling the magic function that is used when you run contrasts(a)=b. This actually returns a value that can be used with the a=b format that transform expects. And of course it leaves X unchanged.

The within solution looks much cleaner of course.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top