0

I was wondering if there is a way that G in my code below could work after $ just like D$post == 1 works?

D <- data.frame(post = 1:10, out = 2:11)

G <- substitute(post == 1)

D$G    ## can we make `G` to work like `D$post`?

D$post == 1  ## Works
  • @joran, I know that but that doesn't fit my purpose. –  Aug 08 '19 at 21:48
  • 2
    it might help to give a little bit more context about your problem. The specific short-cut you want (macro-substitution) is going to be very hard to get working, I think, but if you give a more general goal someone might be able to provide a reasonable solution ... – Ben Bolker Aug 08 '19 at 21:56
  • e.g. with tidyverse, `library(dplyr); f <- function(d,x) {filter(d,{{x}})}` gets you a generic selection function, so `f(D,post==1)` or `D %>% f(post==1)` works (although here almost trivially) – Ben Bolker Aug 08 '19 at 21:59

2 Answers2

2

You can do this:

G <- substitute(post == 1)
E <- substitute(D$G, list(G = G))
#D$post == 1

That expression looks like what you want, right? Well, it isn't, as you can see when you try to evaluate it:

eval(E)
#Error in D$post == 1 : invalid subscript type 'language'

Let's inspect the expression in more detail:

as.list(E)
#[[1]]
#`$`
#
#[[2]]
#D
#
#[[3]]
#post == 1

OK, we have one function call (to $) with two arguments (D and post == 1). The second argument is an expression whereas $ expects a name.

Let's compare this with how it should look like:

as.list(quote(D$post == 1))
#[[1]]
#`==`
#
#[[2]]
#D$post
#
#[[3]]
#[1] 1

as.list(quote(D$post == 1)[[2]])
#[[1]]
#`$`
#
#[[2]]
#D
#
#[[3]]
#post

So, D$post == 1 is actually a call to two nested functions and get's parsed to this:

`==`(`$`(D, post), 1)

I hope this clarifies why "[w]orking with substitute after $ sign" is not so simple.

Just to show that is still possible, if you understand how the expression is parsed:

E <- quote(D$x)
E[[3]] <- G[[2]]
G[[2]] <- E
eval(G)
#[1]  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE

However, such code is really hard to maintain and debug. Don't do this.

As @joran shows, you can use functions like with that evaluate the expression post == 1 within the data.frame, which is basically just a wrapper for eval(G, D, parent.frame()). However, that's a dangerous and slippery path that can lead to dragons. Let me quote the relevant warning from help("subset") here:

This is a convenience function intended for use interactively. For programming it is better to use the standard subsetting functions like [, and in particular the non-standard evaluation of argument subset can have unanticipated consequences.

Roland
  • 127,288
  • 10
  • 191
  • 288
1

Maybe you want something more like this:

q <- quote(post == 1)
with(D,eval(q))
joran
  • 169,992
  • 32
  • 429
  • 468