0

I have matlab files with an integer in each name of my variables inside (except the first one). I want to loop to concatenate the name of the integers. There is my code:

library('R.matlab')
mat <- readMat('SeriesContPJM.mat')
#str(mat)
#typeof(mat)
                                        #mat[[1]]
write.csv(mat$vol.PJM$data[[4]][[1]], "PJM.csv")
i = 2
while (i < 7)
{
    write.csv(get(paste("mat$vol.PJM", as.character(i), "$data[[4]][[1]]", sep = "")), paste(paste("PJM", as.character(i), sep="_"), "csv", sep ="."))
    i = i + 1
}

I have write.csv(mat$vol.PJM$data[[4]][[1]], "PJM.csv") which gives me the good ouput. I would like the same for the other variable names in the loop but I get the following ouput:

+ Error in get(paste("mat$vol.PJM", as.character(i), "$data[[4]][[1]]",  (from importpjm.R#10) : 
  objet 'mat$vol.PJM2$data[[4]][[1]]' introuvable

"introuvable" means "not found" in French.

Etienne
  • 108
  • 12

1 Answers1

2

Here you're mixing where you need to use get and where you need to use eval(parse()).

You can use get with a string variable game, e.g., get("mtcars"), but [ is a function that needs to be evaluated.

  • get("mtcars[2, 2]") won't work because you don't have a variable named "mtcars[2, 2]", you have a variable named "mtcars" and a function named "[" that can take arguments 2, 2.

  • eval(parse(text = "mtcars[2, 2]")) will work because it doesn't just look for a variable, it actually evaluates the text string as if you typed it into the command line.

So, you could rewrite your loop, replacing the get(...) with eval(parse(text = ...)) and it would probably work, assuming the strings you've pasted together have the right syntax. But this can be difficult to read and debug. In 6 months, if you look back at this code and need to understand it or modify it, it will be confusing.

Another way to do it would be to use [[ with strings to extract sublists. Rather than mess with eval(parse()) I would do this:

vols = paste0("vol.PJM", 2:7)
for (vol in vols) {
    write.csv(mat[[vol]][["data"]][[4]][[1]],
              paste0(vol, ".csv"))
}

I think it's more readable, and it's easy to debug the vols vector beforehand to make sure all your names are correct. Similarly, if you want to loop over all elements, you could initialize the vols as something like names(mat), or use some sort of regex criteria to extract the appropriate sublists from names(mat).

Gregor Thomas
  • 104,719
  • 16
  • 140
  • 257
  • Thank you very much for this very detailed answer. Your final solution is very elegant. Just a question: what the `[[` changes in comparison to the `$`? – Etienne Jan 18 '17 at 00:51
  • `[[` can be used with strings (and thus with names stored as strings in variables), whereas `$` is a "magical shortcut" that doesn't work with strings. Because `vol` is a string, `mat[[vol]]` works, whereas `mat$vol` won't work. [See this question for more details](http://stackoverflow.com/q/18222286/903061). For the `[["data"]]` bit it doesn't much matter, but since everything else is using `[[` it seemed nice to keep it consistent. – Gregor Thomas Jan 18 '17 at 01:00