2

I am trying to refer to a column of a data attribute of a spatial object using a string value of its name in R. I have no problem referring to the object only using eval(), but I could not refer to the attributes within the object using the same process.

So:

summary(eval(as.symbol(paste0("layerName", "_p", "5"))))

refers to the object layerName_p5 with no problems. However

summary(eval(as.symbol(paste0("layerName", "_p", "5", "@data$colName"))))

does not work, throwing the following error:

Error in summary(eval(as.symbol(paste0("layerName", "_p", "5", "@data$colName")))) : error in evaluating the argument 'object' in selecting a method for function 'summary': Error in eval(expr, envir, enclos) : object 'layerName_p5@data$colName' not found

I’ll note here that @data is used to point to the data attribute within the spatial object layerName_p5, and $colName is a column within that data attribute. I know this exists, because calling

summary(eval(as.symbol(paste0("opNeon", "_p", "5")))@data$patRoute)

does work. But I would ideally like to be able to refer to the second part of the call using strings as well.

So ultimately my question is why I cannot refer to attributes within an object using a string of its name with eval() (or get() either)?

Things I’ve tried include a nested eval() statement (which I may or may not have gone about using correctly…) and attempting to access attributes using square brackets. Both attempts with relevant error messages below:

eval(eval(as.symbol(paste0("layerName", "_p", "5"))),"@data$colName")

Error in eval(eval(as.symbol(paste0("layerName", "_p", "5"))), "@data$colname") : invalid 'envir' argument of type 'character'

eval(as.symbol(paste0("layerName", "_p", "5")))[eval(as.symbol("@data$colName"))]

Error in eval(expr, envir, enclos) : object '@data$colName' not found

I was wondering if anyone has come across this issue, and if yes if there are any solutions or if I'm just doing something wrong maybe? Any help would be very much appreciated!

Matt Dowle
  • 58,872
  • 22
  • 166
  • 224
Rekarrr
  • 356
  • 3
  • 11

1 Answers1

3

Using the names in your working example, either

eval(call("$",
          eval(call("@",
                    as.symbol(paste0("opNeon", "_p", "5")),
                    as.symbol("data"))),
          as.symbol("patRoute")))

or

eval(call("@",
          as.symbol(paste0("opNeon", "_p", "5")),
          as.symbol("data")))[["patRoute"]]

would work.

Partial matching

If you want to match partial names for the data.frame column, the first option works as such. For the second option, you would need to add exact = FALSE as an option to [[. For example,

eval(call("@",
          as.symbol(paste0("opNeon", "_p", "5")),
          as.symbol("data")))[["patR", exact=FALSE]]

would return the same result using "patR" as a short form of "patRoute", assuming there is no other column name in opNeon_p5@datastarting with the string "patR".

I guess there is little to no need for partial matching in your use case (or non-interactive use in general). Type ?Extract in the R prompt for more information on partial matching.

Why the initial attempts failed

Now let's switch to shorter identifiers a, b and c for the remaining examples. The reason why eval(as.symbol("a@b")), eval(as.symbol("a$c")) or eval(as.symbol("a@b$c")) will not work is that symbols a.k.a. names refer to variables, but `@` and `$` as operators are not part of the variable name.

The small print

Another option that would work is eval(parse(text="a@b$c")), where text can be constructed by whatever means. This is safe as long as you are sure about the contents of text, but evaluating arbitrary expressions is not advisable.

mvkorpel
  • 526
  • 6
  • 10
  • Ah here I was thinking I 'anonymised' my variable names throughout... ANYWAY thank you very much! Both of these work for my purposes, but just a quick question (and sorry if it's obvious) what do you mean by partial name for the column? – Rekarrr Dec 14 '15 at 08:37
  • Thank you very much for the update and the detail, this is great! Thanks again!!! – Rekarrr Dec 17 '15 at 09:33
  • Hello! How about when the column being called is to be created at this step? What I mean is my_data$newcolumn <- 1 works in normal conditions, but eval(as.symbol(df))$newcolumn <- 1 with df = "my_data" doesn't work ! – Parisa Apr 16 '19 at 12:44
  • @Parisa That would be `eval(call("<-", call("$", as.symbol(df), as.symbol("newcolumn")), 1))`. – mvkorpel Apr 21 '19 at 13:39
  • @mvkorpel, Thank you very much! I also found another way to work around this : `eval(parse(text = paste0(df,"$newcolumn<- 1")))` – Parisa Apr 23 '19 at 08:46