1

I'm trying to get a for loop to generate multiple smaller dataframes from a large dataframe:

Year <- rep(1995:2012,4)
Name <- rep(LETTERS[1:4],18)
abc <- data.frame(Year,Name)


for (year in 1995:2012) {
  assign(paste("dff",year,sep=""), abc[abc$Year == year,])
  #paste("dff",year,sep="") <- paste("dff",year,sep="")[with(paste("dff",year,sep=""), order(Name)), ]
}

Problem is the last (commented) line which when uncommented throws an error:

invalid 'envir' argument of type 'character'.

What can I do to fix this? I'm basically trying to order the dataframe by the column Name.

wwl
  • 2,025
  • 2
  • 30
  • 51
  • You can't make a `<-` assignment to a character string resulting from `paste`. (You didn't do it in the line above---you used `assign` for that purpose!) A couple ideas: (a) sort the data frame by `Name` before the loop. (b) Use a list of data frames instead: `abc_list = split(abc, abc$Year)` and then to sort: `abc_list = lapply(abc_list, function(x) x[order(x$Name), ])`. See, e.g., [how to make a list of data frames](http://stackoverflow.com/questions/17499013/how-do-i-make-a-list-of-data-frames). – Gregor Thomas Feb 23 '17 at 22:57

2 Answers2

2

Either you use list() or adapt your code the following way:

Year <- rep(1995:2012,4)
Name <- rep(LETTERS[1:4],18)
abc <- data.frame(Year,Name)


for (year in 1995:2012) {
  assign(paste("dff",year,sep=""), abc[abc$Year == year,])
  assign((paste("dff",year,sep="")), get(paste("dff",year,sep=""))[with(get(paste("dff",year,sep="")), order(Name)), ])
}
Tonio Liebrand
  • 17,189
  • 4
  • 39
  • 59
2

Just in the interest of providing a couple of extra options, or If you were interested in using lists, and or avoiding using assign we could do the following:

library(data.table)

Year <- rep(1995:2012,4)
Name <- rep(LETTERS[1:4],18)
abc <- data.frame(Year,Name)

abc <- data.table(abc)
my_list <- split(abc, Year)
my_list <- lapply(my_list, function(x){x[order(Name)]})

Pretty simple, avoids using get and assign. split gets rid of the need to loop things to split the data table up as well. Might be easier to read/understand too.

If you wanted to stick to something using a for loop, i.e. what you had initially, you could do:

empty_list <- list()

for(i in unique(abc[,Year])){
  empty_list[[paste0(i)]] <- abc[Year == i,]
  empty_list[[paste0(i)]] <- empty_list[[paste0(i)]][order(Name)]
}

Someone might be able to write a cleaner loop than above, but it works!

Gin_Salmon
  • 837
  • 1
  • 7
  • 19