using eval() on string to access object attributes in R - r

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!

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.

Related

Problems obtaining the correct object class. R

I created a small function to process a dataframe to be able to use the function:
preprocessCore::normalize.quantiles()
Since normalize.quintles() can only use a matrixc object, and I need to rearrange my data, I create a small function that takes a specific column (variable) in a especific data frame and do the following:
normal<-function(boco,df){
df_p1<-subset(df,df$Plate==1)
df_p2<-subset(df,df$Plate==2)
mat<-cbind(df_p1$boco,df_p2$boco)
norm<-preprocessCore::normalize.quantiles(mat)
df_1<-data.frame(var_1=c(norm[,1],norm[,2]),well=c(df_p1$well,df_p2$well))
return(df_1)
}
However, "mat" should be a matrix, but it seems the cbind() does not do its job since I'm obtaining the following Error:
normal(antitrombina_FI,Six_Plex_IID)
Error in preprocessCore::normalize.quantiles(mat) :
Matrix expected in normalize.quantiles
So, it is clear that the cbind() is not creating a matrix. I don't understand why this is happening.
Most likely you are binding two NULL objects together, yielding NULL, which is not a matrix. If your df objects are data.frame, then df_p1$boco is interpreted as "extract the variable named boco", not "extract the variable whose name is the value of an object having the symbol boco". I suspect that your data does not contain a variable literally named "boco", so df_p1$boco is evaluated as NULL.
If you want to extract the column that is given as the value to the formal argument boco in function normal() then you should use [[, not $:
normal<-function(boco,df){
df_p1<-subset(df,df$Plate==1)
df_p2<-subset(df,df$Plate==2)
mat<-cbind(df_p1[[boco]],df_p2[[boco]])
norm<-preprocessCore::normalize.quantiles(mat)
df_1<-data.frame(var_1=c(norm[,1],norm[,2]),well=c(df_p1$well,df_p2$well))
return(df_1)
}
Thanks for your help bcarlsen. However I have found some errors:
First, I believe you need to introduce quotes in
mat<-cbind(df_p1[["boco"]],df_p2[["boco"]])
If I run this script outside of a function works erally perfectly:
df_p1<-subset(Six_Plex_IID,Six_Plex_IID$Plate==1)
df_p2<-subset(Six_Plex_IID,Six_Plex_IID$Plate==2)
mat<-cbind(df_p1[["antitrombina_FI"]],df_p2[["antitrombina_FI"]])
norm<-preprocessCore::normalize.quantiles(mat)
However If I introduce this now in a function and try to run it like a function:
normal<-function(boco,df){
df_p1<-subset(df,df$Plate==1)
df_p2<-subset(df,df$Plate==2)
mat<-cbind(df_p1[["boco"]],df_p2[["boco"]])
norm<-preprocessCore::normalize.quantiles(mat)
df_1<-data.frame(var_1=c(norm[,1],norm[,2]),well=c(df_p1$well,df_p2$well))
return(df_1)
}
normal(antitrombina_FI,Six_Plex_IID)
I get the same error mesage:
Error in preprocessCore::normalize.quantiles(mat) :
Matrix expected in normalize.quantiles
I'm completely clueless about why this is happening, why outside the function I'm obtaining a matrix and why inside the function not.
Thanks

Remove all objects from R environment whose names DO NOT contain a specific string

I'd like to remove all objects from my RStudio environment where the object names DO NOT contain a given string.
rm(list=ls(pattern!="may19"))
Yet this gives me an error message
Error in as.environment(pos) : no item called "pattern != "may19"" on
the search list
Is there another way I can approach this? Thanks
You can do:
rm(list= names(Filter(function(x) !any(names(x) == "may19"),
mget(ls(),envir = .GlobalEnv))))
Or simply(as suggested by #nicola):
rm(list=grep("may19",ls(),value=TRUE,invert=TRUE))

Remove all objects from environment except those matching a given pattern

I'm trying to remove all objects from my RStudio environment where the object names are NOT equal to a pattern.
rm(list=ls(pattern!="may19"))
However this gives me an error
Error in as.environment(pos) : no item called "pattern != "may19""
on the search list
Is there another way to approach this?
Thanks in advance
We could use one of the following(other variants might exist, you can add all=TRUE or all.names=TRUE) for completeness:
rm(list=setdiff(ls(),"may19"))
rm(list=ls(pattern = "[^may19]"))

save get'd variable (after assign)

Why can't R find this variable?
assign(paste0('my', '_var'), 2)
get(paste0('my', '_var')) ## isn't this returning an object?
save(get(paste0('my', '_var')), file = paste0('my', '_var.RDATA'))
This throws the error:
Error in save(paste0("my", "_var"), file = paste0("my", "_var.RDATA")) :
object ‘paste0("my", "_var")’ not found
From the help page, the save() function expects "the names of the objects to be saved (as symbols or character strings)." Those values are not evaulated, ie you can't put in functions that will eventually return strings or raw values themselves. Use the list= parameter if you want to call a function to return a string the the name of a variable.
save(list=paste0('my', '_var'), file = paste0('my', '_var.RDATA'))
Though using get/assign is often not a good practice in R. They are usually better ways so you might want to rethink your general approach.
And finally, if you are saving a single object, you might want to consider saveRDS() instead. Often that's the behavior people are expecting when they use the save() function.
The documentation for save says that ... should be
the names of the objects to be saved (as symbols or character strings).
And indeed if you type save into the console you can see that the source has the line
names <- as.character(substitute(list(...)))[-1L]
where substitute captures its argument and doesn't evaluate it. So as the error suggests, it is looking for an object with the name paste0('my', '_var'), not evaluating the expressions supplied.

Get a function from a string of the form "package::function"

There has been discussion on how to get a variable from a string. Indeed, get works for, say, the data.table function: get("data.table") returns data.table. However,
> get("data.table::data.table")
Error in get("data.table::data.table") :
object 'data.table::data.table' not found
Is there a way to do this that preserves the reference to the package name? I.e., I do NOT want to simply do a split on "::" and get the second half of the string.
You could just use the envir argument to get the function from the namespace.
get("data.table", envir = getNamespace("data.table"))
Or more simply as #joran notes, getFromNamespace() can be used.
getFromNamespace("data.table", "data.table")

Resources