I am looking to make a function that takes a vector as input, does some simple arithmetic with the vector and call the new vector something which consists of a set string (say, "log.") plus the original vector name.
d = c(1 2, 3)
my.function <- function { x
x2 <- log(x)
...
I would like the function to return a vector called log.d (that is, not log.x or something set, but something dependent on the name of the vector input as x).
You can try next:
d = c(1, 2, 3)
my.function <- function(x){
x2 <- log(x)
arg_name <- deparse(substitute(x)) # Get argument name
var_name <- paste("log", arg_name, sep="_") # Construct the name
assign(var_name, x2, env=.GlobalEnv) # Assign values to variable
# variable will be created in .GlobalEnv
}
One way to do this would be to store separately names of all your input vector names and then pass them to assign function. Like assign takes text string for output object name, get looks up object from string.
I will assume your vectors all follow common pattern and start with "d", to make it all as dynamic as possible.
d1 <- c(1,2,3)
d2 <- c(2,3,4)
vec_names <- ls(pattern = "^d")
log_vec <- function(x){
log(x)
}
sapply(vec_names, function(x) assign(paste0("log.", x), log_vec(get(x)), envir = globalenv()))
This should create two new objects "log.d1" and "log.d2".
Related
I am trying to write a function with an unspecified number of arguments using ... but I am running into issues where those arguments are column names. As a simple example, if I want a function that takes a data frame and uses within() to make a new column that is several other columns pasted together, I would intuitively write it as
example.fun <- function(input,...){
res <- within(input,pasted <- paste(...))
res}
where input is a data frame and ... specifies column names. This gives an error saying that the column names cannot be found (they are treated as objects). e.g.
df <- data.frame(x = c(1,2),y=c("a","b"))
example.fun(df,x,y)
This returns "Error in paste(...) : object 'x' not found "
I can use attach() and detach() within the function as a work around,
example.fun2 <- function(input,...){
attach(input)
res <- within(input,pasted <- paste(...))
detach(input)
res}
This works, but it's clunky and runs into issues if there happens to be an object in the global environment that is called the same thing as a column name, so it's not my preference.
What is the correct way to do this?
Thanks
1) Wrap the code in eval(substitute(...code...)) like this:
example.fun <- function(data, ...) {
eval(substitute(within(data, pasted <- paste(...))))
}
# test
df <- data.frame(x = c(1, 2), y = c("a", "b"))
example.fun(df, x, y)
## x y pasted
## 1 1 a 1 a
## 2 2 b 2 b
1a) A variation of that would be:
example.fun.2 <- function(data, ...) {
data.frame(data, pasted = eval(substitute(paste(...)), data))
}
example.fun.2(df, x, y)
2) Another possibility is to convert each argument to a character string and then use indexing.
example.fun.3 <- function(data, ...) {
vnames <- sapply(substitute(list(...))[-1], deparse)
data.frame(data, pasted = do.call("paste", data[vnames]))
}
example.fun.3(df, x, y)
3) Other possibilities are to change the design of the function and pass the variable names as a formula or character vector.
example.fun.4 <- function(data, formula) {
data.frame(data, pasted = do.call("paste", get_all_vars(formula, data)))
}
example.fun.4(df, ~ x + y)
example.fun.5 <- function(data, vnames) {
data.frame(data, pasted = do.call("paste", data[vnames]))
}
example.fun.5(df, c("x", "y"))
I want to extract values from a list of (named) lists in R.
Example
The data looks as follows:
data <- list('1' = list(x = c(1,2,3), y = c(2,3,4), z = c(2,3,7)),
'2' = list(x = c(2,3,4,5), y = c(3,4,5,6), z = c(1,2,3,5)))
From a specified list (e.g., '1'), I would like to extract all the first/second/etc elements from the lists. The choice for the index of the element should be random.
For example, if I want to sample from the first list (i.e., '1'), I generate a random index and extract the x, y, and z values corresponding to that random index. Say the index is 2, then the elements should be x=2, y=3, and z=3.
Approach
I thought a function should be able to do the job. The first step was to call the list from the function:
This works:
x <- function(i){
data$`1`
}
x(1)
But this doesn't:
x <- function(i){
data$`i`
}
x(1)
Question
How do I call a list of named lists from within the function? And what is the most convenient way to sample data corresponding to the selected index?
Do you need something like this ?
get_elements <- function(data, i) {
#select the main list
tmp <- data[[i]]
#Check the length of each sublist, select minimum value
#and sample 1 number from 1 to that number
rand_int <- sample(min(lengths(tmp)), 1)
#select that element from each sub-list
sapply(tmp, `[[`, rand_int)
}
get_elements(data, 1)
If I understood your problem correctly a solution would be with the "purrr" package:
library(purrr)
# list "name"
i <- '1'
# index
j <- 2
# to get the needed info as a list:
purrr::map(data[[i]], ~ .x[j])
# to get the needed info as a data.frame:
purrr::map_df(data[[i]], ~ .x[j])
I have a few data frames with the names:
Meanplots1,
Meanplots2,
Meanplots3 etc.
I am trying to write a for loop to do a series of equations on each data frame.
I am attempting to use the paste0 function.
What I want to happen is for x to be a column of each data set. So the code should work like this line:
x <- Meanplots1$PAR
However, since I want to put this in a for loop I want to format it like this:
for (i in 1:3){
x <- paste0("Meanplots",i,"$PAR")
Dmodel <- nls(y ~ ((a*x)/(b + x )) - c, data = dat, start = list(a=a,b=b,c=c))
}
What this does is it assigns x to the list "Meanplots1$PAR" not the actual column. Any idea on how to fix this?
We can get all the data.frame in a list with mget
lst1 <- mget(ls(pattern = '^MeanPlots\\d+$'))
then loop over the list with lapply and apply the model
DmodelLst <- lapply(lst1, function(dat) nls(y ~ ((a* PAR)/(b + PAR )) - c,
data = dat, start = list(a=a,b=b,c=c)))
Replace 'x' with the column name 'PAR'.
In the OP's loop, create a NULL list to store the output ('Outlst'), get the value of the object from paste0, then apply the formula with the unquoted column name i.e. 'PAR'
Outlst <- vector("list", 3)
ndat <- data.frame(x = seq(0,2000,100))
for(i in 1:3) {
dat <- get(paste0("MeanPlots", i))
modeltmp <- nls(y ~ ((a*PAR)/(b + PAR )) - c,
data = dat, start = list(a=a,b=b,c=c))
MD <- data.frame(predict(modeltmp, newdata = ndat))
MD[,2] <- ndat$x
names(MD) <- c("Photo","PARi")
Outlst[[i]] <- MD
}
Now, we extract the output of each list element
Outlst[[1]]
Outlst[[2]]
instead of creating multiple objects in the global environment
I have a simple question. Suppose that I have this code:
y <- x
name <- "x"
Where x could be any R object.
Is there an a way the variable name to automatically take the string x once I assign x to y ?
If I understand correctly, you want the value of x to be assigned to y, while the variable name itself to be assigned to name as a string. If so, then you can capture x as an unevaluated expression, then 1) evaluate it and store the result to y, and 2) deparse it and store the resulting string to name:
z <- quote(x) # z contains unevaluated expression `x`
y <- eval(z) # evaluates the expression, returning the value of x
name <- deparse(z) # returns the expression as a string
If the question is how to get the name of the variable that was assigned to y then one cannot do that perfectly but a heuristic would be to examine every variable and return the name (or names) of anything with matching value.
If the code you are using is within a function and you are looking for variables that are defined in that function then use e <- environment() in place of the line that defines e below.
# test data
# start a fresh R session
a <- 1
x <- 2
y <- x
e <- .GlobalEnv
setdiff(names(Filter(isTRUE, eapply(e, identical, y))), "y")
## [1] "x"
Note
If the question is how to get value value of x from its name then:
Use get:
# test input
x <- 3
name <- "x"
y <- get(name)
y
## [1] 3
This will also work if x is in the global environment:
y <- .GlobalEnv[[name]]
y
## [1] 3
Would that work for you? I personally would not use anything like this in my scripts, but it accomplishes what you need:
assign_and_save_name <- function(new_var, var, var_name){
var_value <- sapply(ls(envir = parent.frame()), get)[[var]]
assign(new_var, var_value, parent.frame())
assign(var_name, var, parent.frame())
}
x <- 3
assign_and_save_name('y', 'x', 'name_x')
I have a named list of data and a custom function I want to apply to the data:
#Some example data
d.list <- list(a = c(1,2,3), b = c(4,5,6), c = c(7,8,9))
#A simple function to process some data, write an output graph, and return an output
myfun <- function(data, f.name) {
y <- list()
y[1] <- data[1] + data[2] + data[3]
y[2] <- data[3] - data[1]/ data[2]
y[3] <- data[2] * data[3] - data[1]
svg(filename = f.name, width = 7, height = 5, pointsize = 12)
plot.new()
plot(data, y)
dev.off()
return(y)
}
I now want to iterate this over my list using sapply and get a saved image file for each iteration, with the file name set as the list element name (e.g. a.svg, b.svg, c.svg in the example above), along with the data frame containing results of the calculations. When I run this:
#Iterate over the example data using sapply
res <- t(sapply(d.list, function(x) myfun(data=x,
f.name=paste(names(d.list), ".svg", sep = ""))))
I get the expected data frame:
[,1] [,2] [,3]
a 6 2.500 5
b 15 5.200 26
c 24 8.125 65
but I only end up with one file in the target directory: "a.svg"
How can I pass the list element names through correctly as a parameter to the function I'm calling in sapply?
If you need to iterate over two vectors at the same time (both your data and the file names), use mapply (or Map)
res <- t(mapply(myfun, d.list, paste0(names(d.list), ".svg")))
In the OP's post, it looped through each element of the 'd.list', but called names(d.list) in each of the loop i.e. calling a vector (c("a", "b", "c")). We need to loop by the names of the 'd.list'. In that way, we can get the individual names as well as the list elements by subsetting.
lapply(names(d.list), function(x) paste0(x, ".svg"))
If we are using the OP's function
lapply(names(d.list), function(x) myfun(data= d.list[[x]],
f.name = paste0(x, ".svg")))