0

I'd like to be able use for() loop to automate the same operation that runs over many variables modifying them.

Here's simplest example to could design:

varToChange = list( 1:10, iris$Species[1:10], letters[1:10]) # assume that it has many more than just 3 elements
varToChange
for (i in varToChange ) {
  if (is.character(y)) i <- as.integer(as.ordered(i))
  if (is.factor(y)) i <- as.integer(i)
}
varToChange # <-- Here I want to see my elements as integers now

Here's actual example that led me to this question - taken from: Best way to plot automatically all data.table columns using ggplot2

In the following function

f <- function(dt, x,y,k) {
  if (is.numeric(x)) x <-  names(dt)[x]
  if (is.numeric(y)) y <-  names(dt)[y]
  if (is.numeric(k)) k <-  names(dt)[k]

 ggplot(dt, aes_string(x,y, col=k)) + geom_jitter(alpha=0.1)
}
f(diamonds, 1,7,2)

instead of brutally repeating the same line many times, as a programmer, I would rather have a loop to repeat this line for me. Something like this one:

for (i in c(x,y,k)) {
  if (is.numeric(i)) i <-  names(dt)[i]
 }

In C/C++ this would have been done using pointers. In R - is it all possible?

UPDATE: Very nice idea to use Map below. However it does not work for this example

getColName <- function(dt, x) { 
  if (is.numeric(x)) { 
    x <-  names(dt)[x]
  }  
  x  
}

f<- function(dt, x,y,k) {
  list(x,y,k) <- Map(getColName, list(x,y,k), dt)
  # if (is.numeric(x)) x <-  names(dt)[x]
  # if (is.numeric(y)) y <-  names(dt)[y]
  # if (is.numeric(k)) k <-  names(dt)[k]

  ggplot(dt, aes_string(x,y, col=k)) + geom_jitter(alpha=0.1)
}
f(diamonds, 1,7,2) # Brrr..
IVIM
  • 2,167
  • 1
  • 15
  • 41

2 Answers2

3

No need for for loop, just Map a function over each of your list items

varToChange = list( 1:10, iris$Species[1:10], letters[1:10])     
myfun <- function(y) {
  if (is.character(y)) y <- as.integer(as.ordered(y))
  if (is.factor(y)) y <- as.integer(y)
  y
}
varToChange <- Map(myfun, varToChange)

UPDATE: Map never modifies variables in place, This is simply not done in R. Use the new values returned by Map

f<- function(dt, x, y, k) {
  args <- Map(function(x) getColName(dt, x), list(x=x,y=y,k=k))

  ggplot(dt, aes_string(args$x,args$y, col=args$k)) + geom_jitter(alpha=0.1)
}
f(diamonds, 1,7,2)
MrFlick
  • 195,160
  • 17
  • 277
  • 295
1

You have two choices for iteration in R, iterate over variables themselves, or over their indices. I generally recommend iterating over indices. This case illustrates a strong advantage of that because your question is a non-issue if you are using indices.

varToChange = list( 1:10, iris$Species[1:10], letters[1:10])
for (i in seq_along(varToChange)) {
  if (is.character(varToChange[[i]])) varToChange[[i]] <- as.integer(as.factor(varToChange[[i]]))
  if (is.factor(varToChange[[i]])) varToChange[[i]] <- as.integer(varToChange[[i]])
}

I also replaced as.ordered() with as.factor() - the only difference between an ordered factor and a regular factor are the default contrasts used in modeling. As you are just coercing to integer, it doesn't matter.

Gregor Thomas
  • 136,190
  • 20
  • 167
  • 294