8

I'm having issues fully understanding terra:extract. I wish to extract average raster values for administrative GADM polygons. My raster has one single value per country. I would expect that each administrative polygon within a particular country has the same value, and some polygons that include some country border be allocated area weighted averages. Unfortunately, this is not the case with my current script. raster::extract seems to be giving sensible results but not terra:extract (see my sample code below - providing outputs with different values). Could someone kindly explain me why, in light of my code below? Thank you very much.

## libraries
library(terra)
library(raster)

#===============================================    
## sample example - provides results as expected (1.333, that is (2*0.5+1*1)/1.5)

# sample raster and SpatialPolygons
r <- raster(ncol=2, nrow=3, xmn= 0, ymn= 0, xmx = 30,ymx = 30)
r[] <- c(2, 2, 2, 1, NA, NA)
cds <- rbind(c(7.5,0), c(7.5,20), c(30, 20),c(30,10))
library(sp)
p = Polygon(cds)
ps = Polygons(list(p),1)
sps = SpatialPolygons(list(ps))
plot(r)
plot(sps, add=T)

enter image description here

# test raster package
test1 <- raster::extract(r , sps, fun=mean, na.rm=T,  weights=TRUE) 
test1 # I get 1.333333 which is what I would expect

# test terra package
sps.spatv <- vect(sps)
r.spatR <-  rast(r) #conversion to SpatRaster class

test2 <- terra::extract(r.spatR, sps.spatv, fun=mean, na.rm=T,  weights=TRUE, exact=TRUE, touches=TRUE) 
test2 # I get 1.333333 which is what I would expect  

#===============================================    
## sample code that leads to different results between raster and terra packages - I wish to understand why such difference.
# sample SpatialPolygonsDataFrame 
ETH <- getData("GADM", country = 'ETH', level = 2)
SOM <- getData("GADM", country = 'SOM', level = 2)
sps <- bind(ETH, SOM)

# sample raster stack
ra <- raster(ncol=31, nrow=24, xmn= 33.3, ymn=  3.67, xmx = 47.5, ymx = 14.65, crs=crs(sps) )
ra[] <- rep(10, 24*31)
ra2 <- raster(ncol=31, nrow=24, xmn= 33.3, ymn= -7.31 , xmx = 47.5, ymx = 3.67, crs=crs(sps) )
ra2[] <- rep(20, 24*31)
ra3 <- merge(ra, ra2)

rb <- raster(ncol=31, nrow=24, xmn= 33.3, ymn=  3.67, xmx = 47.5, ymx = 14.65, crs=crs(sps) )
rb[] <- rep(35, 24*31)
rb2 <- raster(ncol=31, nrow=24, xmn= 33.3, ymn= -7.31 , xmx = 47.5, ymx = 3.67, crs=crs(sps) )
rb2[] <- rep(45, 24*31)
rb3 <- merge(rb, rb2)

stack.r <- stack(ra3, rb3)
names(stack.r) <- c("ra3", "rb3")

plot(stack.r[[1]])
plot(sps, add=T)

# raster::extract
rastR <- raster::extract(stack.r, sps, fun=mean, na.rm=T,  weights=TRUE)

# > head(rastR)
# [,1] [,2]
# [1,]   10   35
# [2,]   10   35
# [3,]   10   35
# [4,]   10   35
# [5,]   10   35
# [6,]   10   35

rastR2 <- rastR %>%
  cbind(sps@data["GID_2"]) # add ID

# terra::extract
sps.spatv <- vect(sps)
stack.r.spatR <-  rast(stack.r) 
rastT <- terra::extract(stack.r.spatR, sps.spatv, fun=mean, na.rm=T,  exact=TRUE)
# > head(rastT)
# ID ra3 rb3
# [1,]  1  10  10
# [2,]  2  10  10
# [3,]  3  10  10
# [4,]  4  10  10
# [5,]  5  10  10
# [6,]  6  10  10
rastT2 <- rastT %>%
  cbind(sps@data["GID_2"]) # add ID
Cecile
  • 527
  • 5
  • 22

1 Answers1

11

Thank you for the expanded question, and for insisting. There was a bug in terra that has now been fixed:

Your simplified example data

library(raster)
library(terra)
#terra version 1.7.29

sp <- getData("GADM", country = 'ETH', level = 2)[1:3,]
sv <- vect(sp)

ra <- raster(ncols=31, nrows=24, xmn= 33.3, ymn=  3.67, xmx = 47.5, ymx = 14.65, crs=crs(sp), vals=rep(10, 24*31))
rb <- raster(ncols=31, nrows=24, xmn= 33.3, ymn=  3.67, xmx = 47.5, ymx = 14.65, crs=crs(sv), vals=rep(35, 24*31))

r_raster <- stack(ra, rb)
names(r_raster) <- c("ra", "rb")
r_terra <-  rast(r_raster) 

Testing without weights and small=FALSE for raster

extract(r_raster, sp, fun=mean, na.rm=T, small=FALSE)
#     [,1] [,2]
#[1,]   NA   NA
#[2,]   10   35
#[3,]   10   35

extract(r_terra, sv, fun=mean, na.rm=T)
#  ID  ra  rb
#1  1  10  35
#2  2  10  35
#3  3  10  35

Note that terra also returns a value for a polygon that does not cover any cell center. In effect the option touches=TRUE is always used for such polygons to avoid returning NA.

Testing without weights and small=TRUE for raster (default)

extract(r_raster, sp, fun=mean, na.rm=T)
#      ra rb
#[1,] 10 35
#[2,] 10 35
#[3,] 10 35

extract(r_terra, sv, fun=mean, na.rm=T, touches=TRUE)
#  ID ra rb
#1  1 10 35
#2  2 10 35
#3  3 10 35
 

Testing with weights

extract(r_raster, sp, fun=mean, na.rm=T,  weights=TRUE)
#     ra rb
#[1,] 10 35
#[2,] 10 35
#[3,] 10 35

extract(r_terra, sv, fun=mean, na.rm=T,  weights=TRUE)
#     ID ra rb
#[1,]  1 10 35
#[2,]  2 10 35
#[3,]  3 10 35
Robert Hijmans
  • 40,301
  • 4
  • 55
  • 63
  • 1
    Thank you very much, I've added a bit more details to my question. Your example makes sense to me. Your results are what I'd expect. I'm then really wondering why my two output tibbles Nadmin2R from raster extract and Nadmin2T from terra extract provide such different results. Nadmin2R seems right but Nadmin2T contains values I cannot explain... I might be misunderstanding something – Cecile May 24 '21 at 03:53
  • 2
    Can you simplify your code, by just showing the output of the two extracts for a few areas in Ethiopia? All the other code makes it too difficult to see what is going on. Or better, make a reproducible example starting with something like `eth <- getData("GADM", country="ETH", level=1")` – Robert Hijmans May 24 '21 at 04:05
  • 1
    Thank you for the suggestion. I have revised my question to include a reproducible example. – Cecile May 24 '21 at 23:06
  • 1
    Dear Robert, at this stage, my understanding is that either I do not use the terra::extract function properly on raster stacks, or that terra::extract cannot be used on raster stacks. Should you come across this post again, I'm keen on hearing your thoughts. Thank you – Cecile Jun 02 '21 at 23:32
  • 1
    Thanks for insisting and apologies for the long time it took me to understand what you were reporting – Robert Hijmans Jun 04 '21 at 00:00
  • Thanks a lot! Also, it now looks likes there is an issue when dim(stack.r)[3]=1 (e.g. stack.r <- stack(ra3) in my example). Error: Error in dimnames(x) <- dn : length of 'dimnames' [2] not equal to array extent. If this helps.... – Cecile Jun 10 '21 at 07:10
  • I do not see that. Perhaps you did not load the raster package? – Robert Hijmans Jun 10 '21 at 18:36