Addressing to subsequent objects in loop function in R - r

I need to create a loop function in which I need to address to subsequent objects which names end with numbers i.e. object1, object 2, object3. So the code should look like this:
object1 <- c(1,2,3,4,5)
object2 <- c(2,3,4,5,6)
object3 <- c(3,4,5,6,7)
for (i in 1:3) {
assign (paste0("new_object",i), mean(object???))
}
So I need a equivalent to just typing
new_object1 <- mean(object1)
new_object2 <- mean(object2)
new_object3 <- mean(object3)
Many thanks in advance!

It would be get to return the values of that object by pasteing the 'i' with the 'object' string
for (i in 1:3) {
assign(paste0("new_object",i), mean(get(paste0('object', i)))
}
But, it is not a recommended way as it is creating new objects in the global env.
Instead, if the intention is to get the mean of all the 'object's,
sapply(mget(paste0("object", 1:3)), mean)
Or if there are more than three, use ls with pattern
sapply(mget(ls(pattern = '^object\\d+$')), mean)
Here, mget returns the value of more than one objects in a list, loop through the list with sapply and apply the mean function on the list element.
Creating objects can also be done from the list with list2env
out <- lapply( mget(ls(pattern = '^object\\d+$')), mean)
names(out) <- paste0('new_', names(out))
list2env(out, .GlobalEnv) # not recommended based on the same reason above

Related

How to use lapply to size objects in environment?

I want to make a table with all the dim() of objects in the environment. ls() returns a list of characters which lapply will not take as object names and operate properly on. What to do? Using R.
> lapply(ls(), dim)
just returns
[[1]]
NULL
You may use parse and eval to change the string vector to env objects and then use dim.
lapply(ls(), function(x) dim(eval(parse(text=x))))
1) Use eapply and then optionally use Filter to remove components with no dim. This creates a named list with the dimensions in the corresponding components.
Filter(length, eapply(.GlobalEnv, dim))
2) A variation would be to create a matrix result such that the row names are the variable names.
do.call("rbind", eapply(.GlobalEnv, dim))
3) or to restrict the output to data frames and not arrays (as arrays might cause problems if there are arrays not of two dimensions) then:
df_dim <- function(x) if (is.data.frame(x)) dim(x)
do.call("rbind", eapply(.GlobalEnv, df_dim))
4) or to restrict it to objects having 2 dimensions including both data frames and arrays:
two_dim <- function(x, dimx = dim(x)) if (length(dimx) == 2) dimx
do.call("rbind", eapply(.GlobalEnv, two_dim))
or
do.call("rbind", Filter(function(x) length(x) == 2, eapply(.GlobalEnv, dim)))

remove names of several objects in R

I have many irregularly named objects whose names, in order to be able to use some other package, I need to set to NULL.
E.g.,
v <- 1
w <- 2
names(v) <- "hello"
names(w) <- "world"
I can write
names(v) <- names(w) <- NULL
but for succinctness I would prefer something like
names(c(v,w)) <- NULL
which however does not work ("Error in names(c(v, w)) <- NULL : could not find function "c<-"). This is not unexpected, of course - from ?names: it is a function "to get or set the names of an object".
One option is to place it in a list and set the names to NULL. It is better not to have multiple objects in the global environment
lst1 <- lapply(list(v = v, w = w), setNames, NULL)
Also, as #joran mentioned, unname can be used as well or as.vector (which remove the attributes)
lst1 <- lapply(list(v = v, w = w), unname)
If the intention is to change the already existing objects,
list2env(lst1, envir = .GlobalEnv)
v
#[1] 1
It is better not to create multiple objects in the global env

Loop over elements in vector, and elements are matrices

I started using R today, so I apologize if this is too basic.
First I construct 2 matrices, and construct a vector, whose entries are these matrices. Then, I try to loop over the elements of the vector, i.e. the matrices. However, when I do, I get a "argument of length zero" error.
cam <- 1:12
ped <- 13:24
dim(cam) <- c(3,4)
dim(ped) <- c(4,3)
mats <- c('cam','ped')
for (i in 1:2) {
rownames(mats[i]) <- LETTERS[1:dim(mats[i])[1]]
colnames(mats[i]) <- LETTERS[1:dim(mats[i])[2]]
}
The error text is as follows:
Error in 1:dim(mats[i])[1] : argument of length 0
The question: how to loop over elements of a vector, these elements being matrices? (I'm guessing I'm not calling the elements correctly). Thank you for patience.
The go-to option in R is to use lists:
cam <- 1:12 ; dim(cam) <- c(3,4)
# same as matrix(1:12, nrow = 3, ncol = 4)
ped <- 13:24 ; dim(ped) <- c(4,3)
# save the list ( '=' sign for naming purposes only here)
mats <- list(cam = cam, ped = ped)
# notice the double brackets '[[' which is used for picking the list
for (i in 1:length(mats) {
rownames(mats[[i]]) <- LETTERS[1:dim(mats[[i]])[1]]
colnames(mats[[i]]) <- LETTERS[1:dim(mats[[i]])[2]]
}
# finally you can call the whole list at once as follows:
mats
# or seperately using $ or [[
mats$cam # mats[['cam']]
mats$ped # mats[['ped']]
ALTERNATIVELY
If you really want to get crazy you can take advantage of the get() and assign() functions. get() calls an object by character, and assign() can create one.
mats <- c('cam','ped')
mats.new <- NULL # initialize a matrix placeholder
for (i in 1:length(mats)) {
mats.new <- get(mats[i]) # save as a new matrix each loop
# use dimnames with a list input to do both the row and column at once
dimnames(mats.new) <- list(LETTERS[1:dim(mats.new)[1]],
LETTERS[1:dim(mats.new)[2]])
assign(mats[i],mats.new) # create (re-write) the matrix
}
If the datasets are placed in a list we can use lapply
lst <- lapply(mget(mats), function(x) {
dimnames(x) <- list(LETTERS[seq_len(nrow(x))], LETTERS[seq_len(ncol(x))])
x})
It is better to keep it in a list. In case the original objects needs to be changed
list2env(lst, envir = .GlobalEnv)

R function to use passed object name+ another name inside

I want to build a function that calls another object that name is related to the main object name.
For example, Main object is 'VCU_Players' and the other object is 'VCU_Players_opp'
in my function i need to use both objects in my calculations.
So i am trying to do
my_function<- function(x) {
y<-deparse(substitute(x))
z<-"_opp"
y<- paste(y,z,sep = "")
#My Calculations
x$newfield<- x$pts+ y$pts
Return(x)
}
Now i want to pass the object VCU_Players to the function
my_function(VCU_Players)
But the function doesn't figure the VCU_Players_opp object
Consider passing string literals and using get() to retrieve corresponding object:
teams <- c("Team1", "Team2", "Team3", "Team4", "Team5", "Team6",
"Team7", "Team8", "Team9", "Team10", "Team11", "Team12")
my_function <- function(i) {
x <- get(paste0(i, "_players"))
y <- get(paste0(i, "_opp"))
# My Calculations
x$newfield <- x$pts + y$pts
return(x)
}
dfList <- lapply(teams, my_function)
Ideally, however is working with a few lists of many objects, and not separate multiple objects in your global environment. Try importing from your data source (i.e., Excel) multiple objects into single lists:
teamdfs <- c(Team1_players, Team2_players, Team3_players, Team4_players, Team5_players, Team6_players,
Team7_players, Team8_players, Team9_players, Team10_players, Team11_players, Team12_players)
team_oppdfs <- c(Team1_opp, Team2_opp, Team3_opp, Team4_opp, Team5_opp, Team6_opp,
Team7_opp, Team8_opp, Team9_opp, Team10_opp, Team11_opp, Team12_opp)
my_function <- function(x, y) {
# My Calculations
x$newfield <- x$pts + y$pts
return(x)
}
dfList <- mapply(my_function, teamdfs, team_oppdfs, SIMPLIFY = FALSE)
# EQUIVALENT TO Map(my_function, teamdfs, team_oppdfs)

Positional Matching Trickery

I wrote this nifty function to apply a function for every combination of vectorized arguments:
require(plyr)
require(ggplot2)
###eapply accepts a function and and a call to expand grid
###where columns created by expand.grid must correspond to arguments of fun
##each row created by expand.grid will be called by fun independently
###arguments
##fun either a function or a non-empty character string naming the function to be called.
###... vectors, factors, or a list containing thse
###value
###a data frame
##Details
##at this time, elements of ... must be at least partially named to match args of fun
##positional matching does not work
###from the ddply documentation page:
###The most unambiguous behaviour is achieved when fun returns a data frame - in that case pieces will
###be combined with rbind.fill. If fun returns an atomic vector of fixed length, it will be rbinded
###together and converted to a data frame. Any other values will result in an error.
eapply <- function(fun,...){
if(!is.character(fun)) fun <- as.character(substitute(fun))
adply(
expand.grid(...),
1,
function(x,fun) do.call(fun,x),
fun
)
}
##example use:
m <- function(n,visit.cost){
if(n*visit.cost < 250){
c("total.cost"=n*visit.cost)
}else{
c("total.cost"=250 + (n*visit.cost-250)*.25)
}
}
d <- eapply(m, n=1:30, visit.cost=c(40,60,80,100))
ggplot(d,aes(x=n,y=total.cost,color=as.factor(visit.cost),group=visit.cost)) + geom_line()
How can I rewrite the function such that the arguments passed to expand.grid need not be named:
d <- eapply(m, 1:30, c(40,60,80,100))
Alternatively, are there any existing functions that have similar functionality?
Not the most elegant but this works. Most importantly, it allows you to pass variables to expand.grid without naming them.
eeyore <- function(fun, ...){
if(!is.character(fun)) fun <- as.character(substitute(fun))
f <- match.fun(fun)
args <- as.list(substitute(list(...)))[-1]
foo <- expand.grid(llply(args, eval))
foo$F <- apply(foo, 1, function(x) { f(x[[1]], x[[2]])})
foo
}
d <- eeyore(m, 1:30, c(40,60,80,100))

Resources