R Function - assign LMER to dynamic variable name - r

To create a more compact script, I am trying to create my first function.
The general function is:
f.mean <- function(var, fig, datafile){
require(lme4)
change <- as.symbol(paste("change", var, sep=""))
base <- as.symbol(paste("baseline", var, sep = ""))
x <- substitute(lmer(change ~ base + (1|ID), data=datafile))
out<-eval(x)
name <- paste(fig,".", var, sep="")
as.symbol(name) <- out
}
}
The purpose of this function is to input var, fig and datafile and to output a new variable named fig.var containing out (eval of LMER).
Apparently it is difficult to 'change' the variable name on the left side of the <-.
What we have tried so far:
- assign(name, out)
- as.symbol(name) <<- out
- makeActive Binding("y",function() x, .GlobalEnv)
- several rename options to rename out to the specified var name
Can someone help me to assign the out value to this 'run' specific variable name? All other suggestions are welcome as well.

As #Roland comments, in R (or any) programming one should avoid indirect environment manipulators such as assign, attach, list2env, <<-, and others which are difficult to debug and break the flow of usual programming involving explicitly defined objects and methods.
Additionally, avoid flooding your global environment of potentially hundreds or thousands of similarly structured objects that may require environment mining such as ls, mget, or eapply. Simply, use one large container like a list of named elements which is more manageable and makes code more maintainable.
Specifically, be direct in assigning objects and pass string literals (var, fig) or objects (datafile) as function parameters and have function return values. And for many inputs, build lists with lapply or Map (wrapper to mapply) to retain needed objects. Consider below adjustment that builds a formula from string literals and passes into your model with results to be returned at end.
f.mean <- function(var, fig, datafile){
require(lme4)
myformula <- as.formula(paste0("change", var, " ~ baseline", var, " + (1|ID)"))
x <- lmer(myformula, data=datafile)
return(x)
}
var_list <- # ... list/vector of var character literals
fig_list <- # ... list/vector of fig character literals
# BUILD AND NAME LIST OF LMER OUTPUTS
lmer_list <- setNames(Map(f.mean, var_list, fig_list, MorArgs=df),
paste0(fig_list, ".", var_list))
# IDENTIFY NEEDED var*fig* BY ELEMENT NAME OF LARGER CONTAINER
lmer_list$fig1.var1
lmer_list$fig2.var2
lmer_list$fig3.var3

Related

Creating a function for GWR maps

I have created a function for GWR maps and I have run the code without it being in the function and it works well. However, when I create into a function I get an error. I was wondering if anyone could help, thank you!
#a=polygonshapefile
#b= Dependent variabable of shapefile
#c= Explantory variable 1
#d= Explantory vairbale 2
GWR_map <- function(a,b,c,d){
GWRbandwidth <- gwr.sel(a$b ~ a$c+a$d, a,adapt=T)
gwr.model = gwr(a$b ~ a$c+a$d, data = a, adapt=GWRbandwidth, hatmatrix=TRUE, se.fit=TRUE)
gwr.model
}
GWR_map(OA.Census,"Qualification", "Unemployed", "White_British")
The above code produces the following error:
Error in model.frame.default(formula = a$b ~ a$c + a$d, data = a, drop.unused.levels = TRUE) :
invalid type (NULL) for variable 'a$b'
You can't use function parameters with the $. Try changing your function to use the [[x]] notation instead. It should look like this:
GWR_map <- function(a,b,c,d){
GWRbandwidth <- gwr.sel(a[[b]] ~ a[[c]]+a[[d]], a,adapt=T)
gwr.model = gwr(a[[b]] ~ a[[c]]+a[[d]], data = a, adapt=GWRbandwidth, hatmatrix=TRUE, se.fit=TRUE)
gwr.model
}
The R help docs (section 6.2 on lists) explain this difference well:
Additionally, one can also use the names of the list components in double square brackets,
i.e., Lst[["name"]] is the same as Lst$name. This is especially useful, when the name of the component to be extracted is stored in another variable as in
x <- "name"; Lst[[x]] It is very important to distinguish Lst[[1]] from Lst[1]. ‘[[...]]’ is the operator used to select a single element, whereas ‘[...]’ is a general subscripting operator. Thus the former is the first object in the list Lst, and if it is a named list the name is not included. The latter
is a sublist of the list Lst consisting of the first entry only. If it is a named list, the names are transferred to the sublist.

Parameterize name of output dataframe in global environment, assigned to from a function

Trying to pass into a function what I want it to name the dataframe it creates, then save it to global environment.
I am trying to automate creating dataframes that are subsets of other dataframes by filtering for a value; since I'm creating 43 of these, I'm writing a function that can automatically:
a) subset rows containing a certain string into it's own data.frame then
b) name a dataframe after that string and save it to my global environment. (The string in a) is also the suffix I want it to name the data.frame after in b))
I can do a) fine but am having trouble with b).
Say I have a dataset which includes a column named "Team" (detailing whose team that member belongs to):
original.df <- read_csv("../original_data_set")
I create a function to split that dataset according to values in one of its columns...
split.function <- function(string){
x <- original.df
as.name(string) <<- filter(x, str_detect(`Team`, string))
}
... then save the dataframe with the name:
split.by.candidate('Team.Curt')
I keep getting:
> Error in as.name(x) <<- filter(y, str_detect(`Receiving Committee`, x)) :
object 'x' not found
But I just want to see Team.Curt saved as a data.frame in my global environment when I do this with rows including the term Team.Curt
You can use assign to create objects based on a string:
split.function <- function(string){
x <- original.df
assign(string, filter(x, str_detect(`Team`, string)), envir = .GlobalEnv)
}
Here, envir = .GlobalEnv is used to assign the value to the global environment.
Both <- and <<- assignments require that the statement hardcodes the object name. Since you want to parameterize the name, as in your cases, you must use assign().
<<- is merely a variant of <- that can be used inside a function, and does a bottom-up search of environments until it either reaches the top (.GlobalEnv) or finds an existing object of that name. In your case that's unnecessary and slightly dangerous, since if an object of that name existed in some environment halfway up the hierarchy, you'd pick it up and assign to it instead.
So just use assign(..., envir = .GlobalEnv) instead.
But both <<- or assigning directly into .GlobalEnv within functions are strongly discouraged as being disasters in waiting, or "life by a volcano" (burns-stat.com/pages/Tutor/R_inferno.pdf). See the caveats at Assign multiple objects to .GlobalEnv from within a function. tidyverse is probably a better approach for managing multiple dataframes.

R: substitute pattern in formula for a variable name

I have a general function that calls an expression that uses a formula and I would like to pass this functions to various environments that store some specific variables and modify parts of a formula designated by a specific pattern.
Here is an example:
# Let's assume I have an environment storing a variable
env <- new.env()
env$..M.. <- "Sepal.Length"
# And a function that calls an expression
func <- function() summary(lm(..M.. ~ Species, data = iris))$r.squared
# And let's assume I am trying to evaluate it within the environment
environment(func) <- env
# And I would like to have some method that makes it evaluate as:
summary(lm(Sepal.Length ~ Species, data = iris))$r.squared
So far I came up with a very dirty solution based on deparsing the function down to string, greping and then parsing it back. It goes like this:
tfunc <- paste(deparse(func), collapse = "")
tfunc <- gsub("\\.\\.M\\.\\.", ..M.., tfunc, perl = TRUE)
tfunc <- eval(parse(text = tfunc))
So yes, it works, but I would like to find a cleaner method, that would somewhat magically substitute this ..M.. pattern into Sepal.Length without a need for all this parsing and deparsing.
So I would really appreciate some help and hints for that problem.

Name of a list not the contents

I have a list containing 18 elements called bx2.
I want to use bx2 in a function,
XlsMaker <- function(x) {
library("XLConnect")
a <- length(x)
b <- paste0(x,".xlsx")
for (i in 1:a){
writeWorksheetToFile(b, data = x[[i]], sheet = names(x[i]))
}
}
but when put I bx2 into the function it pulls in all the elements of the list rather than just the name of the list.
Is it possible to re-write the function so that b becomes bx2.xlsx?
The line b <- paste0(x,".xlsx") is wrong. That calls paste0 on the object x itself which is not at all what you want to do. You want to call it on the name of the object.
This in general opens a can of worms because objects can have two different names in two different places. Consider: the object named bx2 in your global environment is now named x within the function's scope. If you only want to call this function from the top level (e.g. for interactive use), you can safely get the name of the object from the parent environment (the environment you called the function from) by replacing that line with:
x_name <- deparse(substitute(x))
b <- paste0(x_name, ".xlsx")
The substitute function gets the name of x in the parent environment, as a special name object. The deparse function converts this name into a character vector of length one.
The reason I said this is only safe to use at the top level is that substitute can return surprising or unintended results if not used carefully. Hadley Wickham goes into detail on this point in his book.
I think you just want to deparse the parameter name
XlsMaker <- function(x) {
varname <- deparse(substitute(x))
library("XLConnect")
a <- length(x)
b <- paste0(varname ,".xlsx")
for (i in 1:a){
writeWorksheetToFile(b, data = x[[i]], sheet = names(x[i]))
}
}
bx2 <-list(1:3, 4:6)
XlsMaker(bx2)

Function argument as name of variable/array in R

I'd like to be able to create a vector with R but determine its name within the function call. In SAS I would use the macro language to perform a loop but with R, I can't find how to refer to a variable by name e.g. (Obviously this does not work but it describes what I'd like to do)
fun <- function(X, vectorName) {
paste(vectorName) <- 1:X
}
I'd like to be able to call fun(5, v) and get a vector v = c(1,2,3,4,5) out the end.
Although this is possible, it's not something you should do. A function should only have a return value, which you then can assign, e.g.:
v <- seq_len(5)
Or if you have to pass a variable name programmatically:
myname <- "w"
assign(myname, seq_len(5))
(Though I can't think of a reason why you'd need that.)

Resources