Referring to an object within a loop in R - r

I'm sure there is an easy answer. I have a loop, where for each iteration, I create a new vector to store the results. I do this by pasting a name together and then assigning that name to an empty vector.
for (i in seq(1, 50)) {
current_iteration = i
x = paste0("resultsVec", current_iteration)
assign(x, rep(NA, 43))
paste0("resultsVec", i)
for (j in seq(1, 100))
{
resultsVeci[j] = j * j # <- problem here
}
}
However, you obviously can't refer to 'resultsVeci' - so how to I refer to the iteration specific vector each loop?
If you do paste0("resultsVec", i), where i=2 for example, it returns a string "resultsVec2", rather than the object resultsVec2. How do I refer to the object rather than the string?
Thanks.

It really isn't a good idea to use get() and assign() with most R code. (Why is using assign bad?). Better to just use a list. A simple lapply would work here.
resultsVec<-lapply(1:50, function(i) (1:100)*(1:100))
and then you can get the values with reusltsVec[[1]], resultsVec[[2]], etc

Related

R function to reverse a survey item produces NULL

I'm still new to writing my own functions. As an exercise and because I use it alot, I want to write a flexible function to easily reverse survey response scales. This is what I came up with:
rev_scale = function(var, new_var, scale){
for (i in 1:length(abs(var))){
new_var[i] = scale-abs(var[i])+1
}
}
Info on code
var = variable I want to reverse.
new_var = new column with the reversed variable
scale = how many points in the scale (eg. 5 for a 5-point scale)
The reason why I use 'abs' instead of just 'var' is that some dataframes also return value-labels, and I only want the values in this function.
Question
When applying this new function on a variable, R returns "NULL". However, if I run the for-loop separately, with the arguments 'imputed', my new variable is properly reversed.
Any ideas on what is happening here?
Thanks in advance!
### Example of the (working) for-loop with arguments 'imputed' ###
df <- data.frame(matrix(ncol = 1, nrow = 4))
df$var = c(1,2,3,4)
for (i in 1:length(abs(df$var))){
df$var_rev[i] = 4-abs(df$var[i])+1
}
df$var_rev
OUTPUT:
[1] 4 3 2 1
R does not use reference-variables (think pointers)*. So your new_var outside of your function does not get updated when refered to inside a function. Instead, R creates a new copy of new_var and updates that.
You should instead return the new value from your function. I.e.
rev_scale = function(var, scale){
res <- vector('numeric', length(var))
for (i in 1:length(abs(var))){
res[i] = scale-abs(var[i])+1
}
return(res)
}
Also note that I have removed new_var from the function's arguments. In other words, I have completely separated the functions input-arguments from its output.
The reason you get a NULL from the function is that in R, all functions returns somethings. If not specified, the function will return the last value of the last statement, except when the last statement is a control structure (ifs, loops) - then it defaults to a NULL.
* There are a couple of exceptions and work-arounds, but I will not go into that here.
Edit:
As benimwolfspelz noted, you do not need to explicitly iterate over each element in var, as R does this implicitly. Your entire function could be reduced to:
rev_scale = function(var, scale) {
scale-abs(var)+1
}
Secondly, in your for-loop, your can simplify length(abs(var)) to length(var) as abs(var) does not change the length of the vector.

Query for loops in R

I am working out with a data in R.
a<-rep(NA,400)
for(i in 1:10){for(j in 0:40){print(dat$V2[i]-j)}}
Instead of printing, I want to add that value into an empty array (a). I would be thankful if someone help me with the same.
In a case like this (nested loops), often the easiest way is to add a counter to keep track of positions in the array:
a <- rep(NA, 400)
counter <- 1
for(i in 1:10){
for(j in 0:40){
a[counter] <- dat$V2[i]-j
counter <- counter + 1
}
}
Here is one way:
#a<-rep(NA,400)
#for(i in 1:10){for(j in 0:40){print(dat$V2[i]-j)}}
a <- as.numeric( sapply( 1:10, function(i){
sapply( 0:40, function(j) {
dat$V2[i]-j
})
}))
sapply is useful here because countrary to a for loop it returns something with each loop. So in this case I first loop over 1:10, like your for loop, with the useful difference that it actually returns something each time.
What it does in each iteration is to run a new nested block, also using sapply, this time looping over 0:40, also this time returning something, this time its the expression you have innermost in your for loop.
So for 10 times, for each of 1:10, it will loop over 0:40, each time calculating your expression and returning it, which should result in it getting calculated and returned as you want.
as.numeric is wrapped around it to make sure it stays as one long vector, which seems to be what you want.
A way only using loops:
for(i in 1:10){
for(j in 0:39){
print(i*40 - 39 + j)
a[i*40 - 39 + j] = dat$V2[i]-j}}
PS: as you want to create a vector with 400 observations, and i goes from 1 to 10, j needs to have "length" 40, so you want it to be 0:39.
When you want to create a vector (1 dimentional) with a doble loop, the following formula normally applies:
index of the vector = i*length(j) - (length(j)-1) + j

Generate Vectors With For Loop in R

Incredibly basic question. I'm brand new to R. I feel bad for asking, but also like someone will crush it:
I'm trying to generate a number of vectors with a for loop. Each with an unique name, numbered by iteration. The code I'm attaching throws an error, but I think it explains what I'm trying to do in principle fairly well.
Thanks in advance.
vectorBuilder <- function(num){
for (x in num){
paste0("vec",x) <- rnorm(10000, mean = 0, sd = 1)}
}
numSeries <- 1:10
vectorBuilder(numSeries)
You can write the function to return a named list :
create_vector <- function(n) {
setNames(replicate(n, rnorm(10000), simplify = FALSE),
paste0('vec', seq_len(n)))
}
and call it as :
data <- create_vector(10)
data will have list of length 10 with each element having a vector of size 10000. It is better to keep data in this list instead of creating lot of vectors in global environment. However, if you still want separate vectors you can use list2env :
list2env(data, .GlobalEnv)

Apply some function to a variable from its string name

I've got a list of strings list = c("string_1", "string_2", ...) and, based on this vector, I know there are dataframes named df_string_1, df_string_2, ... in my environment.
My code is like:
for (i in 1:length(list)){
res = function(i) // dataframe depending on i, same ncol as df_string_i
rbind(df_list[i],res) // that's the line I don't know how to code
}
I can't find a way to get the dataframe df_string_i at each iteration.
My attempt was to get its name with paste("df_",list[i],sep="") but then, what can I do with this string as I need the variable to be in the rbind?
Thanks for your help!
If you have a situation with data.frames named df_string_1, ..., df_string_n, you would be better off, in the long run, storing these in a list and using tools such as lapply. To solve the current issue, use get:
for (i in 1:length(list)){
res = function(i) // dataframe depending on i, same ncol as df_string_i
rbind(get(paste0("df_",list[i])),res)
}

How to use extract function in a for loop?

I am using the extract function in a loop. See below.
for (i in 1:length(list_shp_Tanzania)){
LU_Mod2000<- extract(x=rc_Mod2000_LC, y=list_shp_Tanzania[[i]], fun=maj)
}
Where maj function is:
maj <- function(x){
y <- as.numeric(names(which.max(table(x))))
return(y)
}
I was expecting to get i outputs, but I get only one output once the loop is done. Somebody knows what I am doing wrong. Thanks.
One solution in this kind of situation is to create a list and then assign the result of each iteration to the corresponding element of the list:
LU_Mod2000 <- vector("list", length(list_shp_Tanzania))
for (i in 1:length(list_shp_Tanzania)){
LU_Mod2000[[i]] <- extract(x=rc_Mod2000_LC, y=list_shp_Tanzania[[i]], fun=maj)
}
Do not do
LU_Mod2000 <- c(LU_Mod2000, extract(x=rc_Mod2000_LC, y=list_shp_Tanzania[[i]], fun=maj))
inside the loop. This will create unnecessary copies and will take long to run. Use the list method, and after the loop, convert the list of results to the desired format (usually using do.call(LU_Mod2000, <some function>))
Alternatively, you could substitute the for loop with lapply, which is what many people seem to prefer
LU_Mod2000 <- lapply(list_shp_Tanzania, function(z) extract(x=rc_Mod2000_LC, y=z, fun=maj))

Resources