Home > Software engineering >  List elements getting overwritten in for loop R?
List elements getting overwritten in for loop R?

Time:05-17

I have a bunch of csv files that I'm trying to read into R all at once, with each data frame from a csv becoming an element of a list. The loops largely work, but they keep overriding the list elements. So, for example, if I loop over the first 2 files, both data frames in list[[1]] and list[[2]] will contain the data frame for the second file.

#function to open one group of files named with "cores"
open_csv_core<- function(year, orgtype){
  file<- paste(year, "/coreco.core", year, orgtype, ".csv", sep = "")
  df <- read.csv(file)
  names(df) <- tolower(names(df))
  df <- df[df$ntee1 %in% c("C","D"),]
  df<- df[!(df$nteecc %in% c("D20","D40", "D50", "D60", "D61")),]
  return(df)
}

#function to open one group of files named with "nccs"
open_csv_nccs<- function(year, orgtype){
  file2<- paste(year, "/nccs.core", year, orgtype, ".csv", sep="")
  df2 <- read.csv(file2)
  names(df2) <- tolower(names(df2))
  df2 <- df2[df2$ntee1 %in% c("C","D"),]
  df2<- df2[!(df2$nteecc %in% c("D20","D40", "D50", "D60", "D61")),]
  return(df2)
}
#############################################################################
yrpc<- list()
yrpf<- list()
yrco<- list()
fname<- vector()
file_yrs<- as.character(c(1989:2019))
for(i in 1:length(file_yrs)){
  
  fname<- list.files(path = file_yrs[i], pattern = NULL)

#accessing files in a folder and assigning to the proper function to open them based on how the file is named  
for(j in 1:length(fname)){
    
    if(grepl("pc.csv", fname[j])==T) { 
      
      if(grepl("nccs", fname[j])==T){
        a <- open_csv_nccs(file_yrs[j], "pc") 
        yrpc[[paste0(file_yrs[i], "pc")]] <- a
        
      } else {
        b<- open_csv_core(file_yrs[j], "pc")
        yrpc[[paste0(file_yrs[i], "pc")]] <- b
      }
      
    } else if (grepl("pf.csv", fname[j])==T){
      
      if(grepl("nccs", fname[j])==T){
        c <- open_csv_nccs(file_yrs[j], "pf")
        yrpf[[paste0(file_yrs[i], "pf")]] <- c
        
      } else {
        d<- open_csv_core(file_yrs[j], "pf")
        yrpf[[paste0(file_yrs[i], "pf")]] <- d
      }
      
    } else {
      
      if(grepl("nccs", fname[j])==T){
        e<- open_csv_nccs(file_yrs[j], "co")
        yrco[[paste0(file_yrs[i], "co")]] <- e
        
      } else {
        f<- open_csv_core(file_yrs[j], "co")
        yrco[[paste0(file_yrs[i], "co")]] <- f
      }
    }
    
    
  }

}

CodePudding user response:

It seems that in your for(j in 1:length(fname)){... you are creating one of 4 variable a, b, c or d. And you're reusing these variable names, so they are getting overwritten.

The "correct" way to do this is to use lapply in place of the for loop. Pass the list of files, and the required function (i.e. open_csv_core, etc) to lapply, and the return value that you get back is a list of the results.

CodePudding user response:

Actually, both of your csv reading functions do exactly the same, except that the paths are different.

If you find a way to list your files with abstract paths instead of relative paths (just the file names), you wouldn't need to reconstruct the paths like you do. This is possible by full.names = TRUE in list.files().

The second point is, it seems there is never from same year and same type a "nccs.core" file in addition to a "coreco.core" file. So they are mutually exclusive. So then, there is no logics necessary to distinguish those cases, which simplifies our code.

The third point is, you just want to separate the data frames by filetype ("pc", "pf", "co") and years.

Instead of creating 3 lists for each type, I would create one res-ults list, which contains for each type an inner list.

I would solve this like this:

years <- c(1989:2019) 

path_to_type <- function(path) gsub(".*(pc|pf|co)\\.csv", "\\1", path)

res <- list("pc" = list(),
            "pf" = list(),
            "co" = list())

lapply(years, function(year) {
  files <- list.files(path = year, pattern = "\\.csv", full.names = TRUE)
  dfs <- lapply(files, function(path) {
    print(path) # just to signal that the path is getting processed
    df <- read.csv(path)
    file_type <- path_to_type(path)
    names(df) <- tolower(names(df))
    df <- df[df$ntee1 %in% c("C", "D"), ]
    df <- df[!(df$nteecc %in% c("D20", "D40", "D50", "D60", "D61")), ]
    res[[file_type]][[year]] <- df
  })
}) 

Now you can call from result's list by file_type and year e.g.:

res[["co"]][[1995]]
res[["pf"]][[2018]]

And so on.

Actually, the results of the lapply() calls in this case are not interesting. Just the content of res ... (result list).

  • Related