Calculating cumulative sum of rasters in a stack - raster

I am calculating the cumulative sum of rasters in a stack using methods provided by both terra and raster packages
First, I try to do it step by step as follows:
library(terra)
library(raster)
r <- rast(ncols=9, nrows=9)
values(r) <- 1:ncell(r)
s <- c(r, r, r, r, r, r)
s <- s * 1:6
x <- s[[1]]+s[[2]]
x <- x+s[[3]]
x <- x+s[[4]]
x <- x+s[[5]]
rr1 <- x+s[[6]]
When using terra, the output seem not correct
rr2 <- rapp(s, s[[1]], nlyr(s), function(i) max(cumsum(i)))
However, when using raster, the values seem OK.
rs<- raster::stack(s)
rs <- calc(rs, fun = cumsum)
rr3 <- rs[[6]]
What could be the problem?

Your example data
library(terra)
#terra 1.6.30
r <- rast(ncols=9, nrows=9)
values(r) <- 1:ncell(r)
s <- c(r, r, r, r, r, r) * 1:6
With terra 1.6-30 you can use cumsum (there is a bug in the CRAN version that prevents this from working)
cumsum(s)
#class : SpatRaster
#dimensions : 9, 9, 6 (nrow, ncol, nlyr)
#resolution : 40, 20 (x, y)
#extent : -180, 180, -90, 90 (xmin, xmax, ymin, ymax)
#coord. ref. : lon/lat WGS 84
#source(s) : memory
#names : lyr.1, lyr.1, lyr.1, lyr.1, lyr.1, lyr.1
#min values : 1, 3, 6, 10, 15, 21
#max values : 81, 243, 486, 810, 1215, 1701
A work-around is
app(s, cumsum)
#class : SpatRaster
#dimensions : 9, 9, 6 (nrow, ncol, nlyr)
#resolution : 40, 20 (x, y)
#extent : -180, 180, -90, 90 (xmin, xmax, ymin, ymax)
#coord. ref. : lon/lat WGS 84
#source(s) : memory
#names : lyr.1, lyr.1, lyr.1, lyr.1, lyr.1, lyr.1
#min values : 1, 3, 6, 10, 15, 21
#max values : 81, 243, 486, 810, 1215, 1701
But in case you only want the last layer, you can of course do
sum(s)
#class : SpatRaster
#dimensions : 9, 9, 1 (nrow, ncol, nlyr)
#resolution : 40, 20 (x, y)
#extent : -180, 180, -90, 90 (xmin, xmax, ymin, ymax)
#coord. ref. : lon/lat WGS 84
#source(s) : memory
#name : sum
#min value : 21
#max value : 1701
rapp does not apply here, but you could use your formula with app
app(s, \(i) max(cumsum(i)))
You can install terra version 1.6-30 with:
install.packages('terra', repos='https://rspatial.r-universe.dev')

Related

Calculate stats on a group of raster stack layers using {terra}?

I have a raster stack of 4 layers. Two of the layers are from model 1, two of the layers are from model 2. I need to calculate the median, 5th percentile and 95th percentile of each model. Is there some way to do this in one step? i.e. without writing out two intermediate stacks of rasters and then joining them together again. My attempt is below but it doesn't do the function by group.
library("terra")
# Create some toy data
a <- rast(ncol = 10, nrow = 10, vals=rep(5,100), names=1)
b <- rast(ncol = 10, nrow = 10, vals=rep(10,100), names=1)
c <- rast(ncol = 10, nrow = 10, vals=rep(5,100), names=2)
d <- rast(ncol = 10, nrow = 10, vals=rep(10,100), names=2)
z <- c(a, b, c, d)
# Try to write a function to do the work
app(z,
function(x) {
c(median(x), quantile(x, c(0.05, 0.95)))
},
filename = "grouped_stats.tif)
My desired result is a raster stack of 6 layers. Something like this.
class : SpatRaster
dimensions : 10, 10, 6 (nrow, ncol, nlyr)
resolution : 36, 18 (x, y)
extent : -180, 180, -90, 90 (xmin, xmax, ymin, ymax)
coord. ref. : lon/lat WGS 84
sources : memory (3 layers)
memory (3 layers)
names : median_1, q5_1, q95_1, median_2, pc5_2, pc95_2
min values : 7.5, 5.0, 10.0, 7.5, 5.0, 10.0
max values : 7.5, 5.0, 10.0, 7.5, 5.0, 10.0
Any ideas please? Thanks.
EFFORT 1
Inspired by #spacedman I wrote this function but it doesn't quite get me there. Putting it here as possible inspiration for others.
grouped_stats <- function(x) {
layers_names <- unique(names(x))
cell_output <- NA
for (each_layer in layers_names) {
cell_output <- rbind(cell_output,
c(median(x[[each_layer]], na.rm = TRUE),
quantile(x[[each_layer]], 0.05, 0.95)))
names(cell_output) <- glue("{each_layer}_{c('median','pc5','pc95')}")
}
cell_output
}
g <- app(z, fun = grouped_stats)
EFFORT 2
Getting nearer I think, but not quite there.
my_stats_function <- function(x) {c(median(x), quantile(0.05, 0.95))}
app(z,
function(x){
unlist(tapply(x, layer_names, my_stats_function))
})
class : SpatRaster
dimensions : 10, 10, 4 (nrow, ncol, nlyr)
resolution : 36, 18 (x, y)
extent : -180, 180, -90, 90 (xmin, xmax, ymin, ymax)
coord. ref. : lon/lat WGS 84
source : memory
names : 11, 1.95%, 21, 2.95%
min values : 7.50, 0.05, 7.50, 0.05
max values : 7.50, 0.05, 7.50, 0.05
EFFORT 3
Think I'm about there. :-)
my_stats_function <- function(x) {c(median(x), quantile(x, c(0.05, 0.95)))}
app(z,
function(x){
unlist(tapply(x, layer_names, my_stats_function))
})
class : SpatRaster
dimensions : 10, 10, 6 (nrow, ncol, nlyr)
resolution : 36, 18 (x, y)
extent : -180, 180, -90, 90 (xmin, xmax, ymin, ymax)
coord. ref. : lon/lat WGS 84
source : memory
names : 11, 1.5%, 1.95%, 21, 2.5%, 2.95%
min values : 5, 5, 5, 5, 5, 5
max values : 5, 5, 5, 5, 5, 5
Your example data (with changed layer names for clarity)
library(terra)
a <- rast(ncol = 10, nrow = 10, vals=rep(5,100), names="A")
b <- rast(ncol = 10, nrow = 10, vals=rep(10,100), names="A")
c <- rast(ncol = 10, nrow = 10, vals=rep(5,100), names="B")
d <- rast(ncol = 10, nrow = 10, vals=rep(10,100), names="B")
z <- c(a, b, c, d)
What your version of "terra" it was not possible to tapp because it did not handle functions that return multiple numbers. However, you can do that with the current version
f <- function(x) quantile(x, c(0.5, 0.05, 0.95))
x <- tapp(z, names(z), f)
x
#class : SpatRaster
#dimensions : 10, 10, 6 (nrow, ncol, nlyr)
#resolution : 36, 18 (x, y)
#extent : -180, 180, -90, 90 (xmin, xmax, ymin, ymax)
#coord. ref. : lon/lat WGS 84
#source(s) : memory
#names : A_5%, A_50%, A_95%, B_5%, B_50%, B_95%
#min values : 5.25, 7.5, 9.75, 5.25, 7.5, 9.75
#max values : 5.25, 7.5, 9.75, 5.25, 7.5, 9.75
That is the cleanest solution, I think. But in this case, because there is a quantile<SpatRaster> method it might be faster to do
probs <- c(0.05, 0.5, 0.95)
ids <- names(z)
uids <- unique(ids)
x <- lapply(uids, function(i) quantile(z[[ids == i]], probs))
rast(x)
#class : SpatRaster
#dimensions : 10, 10, 6 (nrow, ncol, nlyr)
#resolution : 36, 18 (x, y)
#extent : -180, 180, -90, 90 (xmin, xmax, ymin, ymax)
#coord. ref. : lon/lat WGS 84
#source(s) : memory
#names : q0.05, q0.5, q0.95, q0.05, q0.5, q0.95
#min values : 5.25, 7.5, 9.75, 5.25, 7.5, 9.75
#max values : 5.25, 7.5, 9.75, 5.25, 7.5, 9.75
But I would only even look at that if the rasters are very large.
I assume that in all cases it is more efficient to use
function(x) quantile(x, c(0.5, 0.05, 0.95))
instead of
function(x) c(median(x), quantile(x, c(0.05, 0.95)))
Here is a less efficient solution that uses a loop and app
f <- function(x) quantile(x, c(0.05, 0.5, 0.95))
ids <- names(z)
uids <- unique(ids)
x <- lapply(uids, function(i) app(z[[ids == i]], f))
rast(x)
#class : SpatRaster
#dimensions : 10, 10, 6 (nrow, ncol, nlyr)
#resolution : 36, 18 (x, y)
#extent : -180, 180, -90, 90 (xmin, xmax, ymin, ymax)
#coord. ref. : lon/lat WGS 84
#sources : memory (3 layers)
# memory (3 layers)
#names : 5%, 50%, 95%, 5%, 50%, 95%
#min values : 5.25, 7.5, 9.75, 5.25, 7.5, 9.75
#max values : 5.25, 7.5, 9.75, 5.25, 7.5, 9.75
If you work with the parts within the function you can do this:
First define which layers are which group:
> p1 = c(1,3)
> p2 = c(2,4)
Then subset x and work with that:
> app(z, function(x){c(median(x[p1]), quantile(x[p1], c(0.05, 0.95)), median(x[p2], ), quantile(x[p2], c(0.05, 0.95)))})
class : SpatRaster
dimensions : 10, 10, 6 (nrow, ncol, nlyr)
resolution : 36, 18 (x, y)
extent : -180, 180, -90, 90 (xmin, xmax, ymin, ymax)
coord. ref. : lon/lat WGS 84
source : memory
names : lyr.1, lyr.2, lyr.3, lyr.4, lyr.5, lyr.6
min values : 5, 5, 5, 10, 10, 10
max values : 5, 5, 5, 10, 10, 10
I'm not sure how you get a median of 7.5 in your sample though, so maybe you used mean for that, and maybe your groupings are different...

Plot several raster maps with constant color scale

i have some raster maps that i want to plot with values in every pixel.
Here are the raster layers
class : RasterLayer
dimensions : 54, 72, 3888 (nrow, ncol, ncell)
resolution : 0.04166667, 0.04166667 (x, y)
extent : 7.5, 10.5, 47.54167, 49.79167 (xmin, xmax, ymin, ymax)
crs : +proj=longlat +datum=WGS84 +no_defs
source : ens_T_spad_bw_max10_2_5m_3.img
names : ens_T_spad_bw_max10_2_5m_3
values : 0.01401434, 0.8403213 (min, max)
> enf_ssp585_2021
class : RasterLayer
dimensions : 54, 72, 3888 (nrow, ncol, ncell)
resolution : 0.04166667, 0.04166667 (x, y)
extent : 7.5, 10.5, 47.54167, 49.79167 (xmin, xmax, ymin, ymax)
crs : +proj=longlat +datum=WGS84 +no_defs
source : ensfuture_T_spad_mix_bw_ssp585_2021.img
names : ensfuture_T_spad_mix_bw_ssp585_2021
values : 0.01403303, 0.3395551 (min, max)
> enf_ssp585_2041
class : RasterLayer
dimensions : 54, 72, 3888 (nrow, ncol, ncell)
resolution : 0.04166667, 0.04166667 (x, y)
extent : 7.5, 10.5, 47.54167, 49.79167 (xmin, xmax, ymin, ymax)
crs : +proj=longlat +datum=WGS84 +no_defs
source : ensfuture_T_spad_mix_bw_ssp585_2041.img
names : ensfuture_T_spad_mix_bw_ssp585_2041
values : 0.01417738, 0.1845056 (min, max)
When i plot the rasters with the plot function, the colorpalete changes for every raster/map.
I want a constant color palete to make the maps comparable.
How do I do this?
plot(enf_ssp126_2041, col=cl(200))

change extent in map from 0, 360, 0, 300 to -180, 180, -90, 90

how can I change the extent of a netcdf file from 0, 360, 0, 300 to -180, 180, -90, 90
Would the solution be the same if the original extents are
0, 320, 0, 384
0, 362, 0, 294
0, 720, 0, 576
0, 362, 0, 332
0, 360, 0, 256
0, 802, 0, 404
class : RasterLayer
dimensions : 300, 360, 108000 (nrow, ncol, ncell)
resolution : 1, 1 (x, y)
extent : 0, 360, 0, 300 (xmin, xmax, ymin, ymax)
crs : NA
source : memory
names : layer
values : -1.728468, 35.60058 (min, max)
You can change the extent of raster data with the raster package like this
extent(x) <- c(0,1,0,1)
or with the terra package like this
ext(x) <- c(0,1,0,1)
What you are showing looks like row and column numbers, not coordinates. So how do you get these extents in the first place? How do you read the ncdf files?
Generally, the easiest way would be
library(terra)
x <- rast("ncdffile.nc")
if longitude is between 0 and 360 instead of -180 and 180, you can then do
y <- rotate(x)

How to get the raster() function in R to round to the outside of a specified extent?

How can I get the raster() function to round to the outside of the specified x/y min/max? For example, what I see:
> raster(xmn = 0, xmx = 1.01,
+ ymn = 0, ymx = 1.01,
+ res = 1)
class : RasterLayer
dimensions : 1, 1, 1 (nrow, ncol, ncell)
resolution : 1, 1 (x, y)
extent : 0, 1, 0.01, 1.01 (xmin, xmax, ymin, ymax)
crs : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
and what I want to see:
> raster(xmn = 0, xmx = 1.01,
+ ymn = 0, ymx = 1.01,
+ res = 1)
class : RasterLayer
dimensions : 2, 2, 4 (nrow, ncol, ncell)
resolution : 1, 1 (x, y)
extent : 0, 2, 0, 2 (xmin, xmax, ymin, ymax)
crs : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
or anything that includes the specified xmin, xmax, ymin, and ymax within the resulting raster object. I realize this is an easy example but I'm looking for general code that can be used for many different values of xmn, xmx, ymn, ymx, and res.
Thanks in advance! (:
Not sure if this exactly answers your question, what you need may depend a bit on your actual workflow, but it seems that what you are looking for is floor(extent).
library(raster)
r <- raster(xmn = 0, xmx = 1.01, ymn = 0, ymx = 1.01)
e <- floor(extent(r))
rr <- raster(e, res=1, crs=crs(r))
rr
#class : RasterLayer
#dimensions : 2, 2, 4 (nrow, ncol, ncell)
#resolution : 1, 1 (x, y)
#extent : 0, 2, 0, 2 (xmin, xmax, ymin, ymax)
#crs : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
Note that the floor method for Extent objects rounds min x and y down, but rounds max x and y up. There is also a ceiling method that does the opposite, and a round method that goes to the nearest integer.
In other cases (depending if there is data you want to keep, and whether the resolution needs to change or not) you may want one of these
x <- extend(r, e)
y <- setExtent(r, e)
extent(r) <- e
If you need to align an extent with a Raster*, you can do
alignExtent(e, r, snap='near')
You can also do things like
e <- round(e + 15)

Stackapply error in a sum function with several rasters

I created a function to sum raster by monthyear and It works good, nevertheless when I run the code in other computer the stack of mes_sum has values 0 for all images. I can't understand the error because in other computers works great. I think the error can be a memory error, however R dont't show the message "allocate error in the vector..."
mes_año<- format(as.Date(dias, format = "%Y-%m-%d"), format = "%Y%m")
mes_año<- as.integer(mes_año);mes_año
mes_sum<-stackApply(stack(files), indices=mes_año, fun=sum, na.rm=T)
Error:
> mes_sum
class : RasterBrick
dimensions : 63, 52, 3276, 58 (nrow, ncol, ncell, nlayers)
resolution : 0.08983153, 0.08983153 (x, y)
extent : -75.72798, -71.05674, -49.22768, -43.56829 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0
data source : C:\Users\TPPC\AppData\Local\Temp\RtmpKolenM\raster\r_tmp_2019-07-13_032823_15524_68776.grd
names : index_201403, index_201404, index_201405, index_201406, index_201407, index_201408, index_201409, index_201410, index_201411, index_201412, index_201501, index_201502, index_201503, index_201504, index_201505, ...
min values : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
max values : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
Good ejecution (same code, different computer:
> mes_sum
class : RasterBrick
dimensions : 63, 52, 3276, 58 (nrow, ncol, ncell, nlayers)
resolution : 0.08983153, 0.08983153 (x, y)
extent : -75.72798, -71.05674, -49.22768, -43.56829 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0
data source : in memory
names : index_201403, index_201404, index_201405, index_201406, index_201407, index_201408, index_201409, index_201410, index_201411, index_201412, index_201501, index_201502, index_201503, index_201504, index_201505, ...
min values : 0.60000002, 2.40125140, 7.95000005, 15.93932509, 13.05649427, 6.75000021, 10.20000026, 0.15000001, 3.30000007, 11.40000042, 0.90000001, 0.35000001, 8.85000017, 32.55000037, 19.86704370, ...
max values : 405.6248, 651.3500, 1057.0718, 732.1000, 998.9205, 1085.4109, 778.4997, 697.0117, 1108.9277, 521.9504, 555.6580, 312.7431, 934.3000, 602.0671, 1185.9418, ...

Resources