Home > Software engineering >  How can I stack rasters after for loop in R with terra?
How can I stack rasters after for loop in R with terra?

Time:07-27

I have created a for loop to bring in climate data from a series of rasters. Each variable has data for four months with each month having a raster for each of 30 years of data. The loop below finds all rasters with the same month (of the same variable) and calculates the average across all years. Finally, the loop stacks the four monthly averaged rasters under an object with the variable name.

I have more variables than are shown below in clim.vars and I would like to be able to run any combination of variables by only changing the clim.vars variable. This loop does a good job at working through the variables but my end goal is to create a stacked raster that contains all variables and all months with each layer named as "variable_month". This is where I seem to get stuck. I have tried multiple ways to combine all the variables after the loop but I can't quite get it. I think the issue is in the fact that clim.vars is a character vector, not an object vector. I have tried changing it with as.name() and mget()

clim.vars <- c("max_Tmean", "mean_RH", "min_RH", "mean_Tmax")

for(var in clim.vars){

    June      <- rast(list.files(paste0("./Data/", var, "/Rasters"), 
                                 full.names = TRUE,
                                 pattern = glob2rx(paste0(var, "_June_*.tif$")))) %>%
      crop(vect(SA)) %>% mask(vect(SA)) %>% mean() 
    
    July      <- rast(list.files(paste0("./Data/", var, "/Rasters"), 
                                 full.names = TRUE,
                                 pattern = glob2rx(paste0(var, "_July_*.tif$")))) %>%
      crop(vect(SA)) %>% mask(vect(SA)) %>% mean() 
    
    August    <- rast(list.files(paste0("./Data/", var, "/Rasters"), 
                                 full.names = TRUE,
                                 pattern = glob2rx(paste0(var, "_August_*.tif$")))) %>%
      crop(vect(SA)) %>% mask(vect(SA)) %>% mean() 
    
    September <- rast(list.files(paste0("./Data/", var, "/Rasters"), 
                                 full.names = TRUE,
                                 pattern = glob2rx(paste0(var, "_September_*.tif$")))) %>%
      crop(vect(SA)) %>% mask(vect(SA)) %>% mean() 
    
    assign(var, c(June, July, August, September)) %>%
    set.names(c(paste0(var, "_June"),
                paste0(var, "_July"),
                paste0(var, "_August"),
                paste0(var, "_September")))

}

## Combining all rasters (these are my attempts that fail either completely or change 
## layer names to match only the variable name)...
bioclim <- c(clim.vars)
bioclim <- rast(mget(clim.vars))
bioclim <- c(as.name(clim.vars))
bioclim <- c(lapply(clim.vars, as.name))
bioclim <- rast(lapply(clim.vars, as.name))

## This way gets what I want...
# `clim.rast` is defined alongside `clim.vars` at the beginning of my script
clim.rast <- c(max_Tmean, mean_RH, min_RH, mean_Tmax)

# `bioclim` is defined after running the loop above
bioclim <- clim.rast

CodePudding user response:

In situations like this you should use lists. Avoid use assign and get. It is difficult to answer your question as your code is not self-contained (there is no data), but it could look like this:

clim.vars <- c("max_Tmean", "mean_RH", "min_RH", "mean_Tmax")
mnts <- c("_June", "_July", "_August", "_September")
SA <- vect(SA)
x <- list()
for(i in 1:length(clim.vars)) {
    ff <- list.files(paste0("./Data/", clim.vars[i], "/Rasters"), full.names = TRUE)
    m <- list()
    for (j in 1:length(mnts)) {
        m[[j]] <- rast(grep(mnts[j], ff)) |> crop(SA) |> mean() |> mask(SA)
    }
    names(m) <- paste0(clim.vars[i], mnts)
    x[[i]] <- rast(m)
}

z <- rast(x)
  • Related