Skip to main content
added 166 characters in body
Source Link
Jeffrey Evans
  • 32k
  • 2
  • 48
  • 97
system.time({
  v.raster <- unlist(lapply(raster::extract(r, nc)
    lapply(v.raster, mean))
})

system.time({
  v.raster <- raster::extract(r, nc, fun=mean)
})

terra::extract using lapplytapply on resulting object and terra::extract using an internal function call. Note that terra is now returning a data.frame with a unique ID denoting the polygons (probably indicating rownames). As such, we use tapply rather than apply. This allows us to aggregate as stastic by a unique ID.

system.time({
  v.terra <- terra::extract( rast(r), vect(nc))
    tapply(v.terra[,"layer"], v.terra[,"ID"], mean) 
})

system.time({
  v.terra <- terra::extract( rast(r), vect(nc), fun=mean)
})
system.time({
  v.exact <- exactextractr::exact_extract(r, nc) 
    unlist(lapply(v.exact, function(x) mean(x[,"value"]) ))
})

system.time({
  v.exact <- exactextractr::exact_extract(r, nc, "mean") 
})

All of these approaches result in a vector that can be joined back to the polygon data. You can see that the exactextractr::exact is the fastest approach. For both raster::extract and terra::extract you may observe that it can be faster to create an object and then use lapply, as opposed to calling a function internally. This is even more noticeable when it is a custom function (notably so). The exactextractr::exact function has several moment statistics available that are calculated in C++ rather than calling an R function. This speeds up things quite a bit compariredcompared to creating an object and using lapply however, this is not the case for custom function and I have noticed that even with this function with non-C++ function it is faster to create an object and use lapply rather than passing the function using the fun argument.

system.time({
  v.raster <- raster::extract(r, nc)
    lapply(v.raster, mean)
})

system.time({
  v.raster <- raster::extract(r, nc, fun=mean)
})

terra::extract using lapply on resulting object and terra::extract using an internal function call

system.time({
  v.terra <- terra::extract( rast(r), vect(nc))
    tapply(v.terra[,"layer"], v.terra[,"ID"]) 
})

system.time({
  v.terra <- terra::extract( rast(r), vect(nc), fun=mean)
})
system.time({
  v.exact <- exactextractr::exact_extract(r, nc) 
    unlist(lapply(v.exact, function(x) mean(x[,"value"]) ))
})

system.time({
  v.exact <- exactextractr::exact_extract(r, nc, "mean") 
})

All of these approaches result in a vector that can be joined back to the polygon data. You can see that the exactextractr::exact is the fastest approach. For both raster::extract and terra::extract you may observe that it can be faster to create an object and then use lapply, as opposed to calling a function internally. This is even more noticeable when it is a custom function (notably so). The exactextractr::exact function has several moment statistics available that are calculated in C++ rather than calling an R function. This speeds up things quite a bit comparired to creating an object and using lapply however, this is not the case for custom function and I have noticed that even with this function with non-C++ function it is faster to create an object and use lapply rather than passing the function using the fun argument.

system.time({
  unlist(lapply(raster::extract(r, nc), mean))
})

system.time({
  raster::extract(r, nc, fun=mean)
})

terra::extract using tapply on resulting object and terra::extract using an internal function call. Note that terra is now returning a data.frame with a unique ID denoting the polygons (probably indicating rownames). As such, we use tapply rather than apply. This allows us to aggregate as stastic by a unique ID.

system.time({
  v.terra <- terra::extract( rast(r), vect(nc))
    tapply(v.terra[,"layer"], v.terra[,"ID"], mean) 
})

system.time({
  terra::extract( rast(r), vect(nc), fun=mean)
})
system.time({
  v.exact <- exactextractr::exact_extract(r, nc) 
    unlist(lapply(v.exact, function(x) mean(x[,"value"]) ))
})

system.time({
  exactextractr::exact_extract(r, nc, "mean") 
})

All of these approaches result in a vector that can be joined back to the polygon data. You can see that the exactextractr::exact is the fastest approach. For both raster::extract and terra::extract you may observe that it can be faster to create an object and then use lapply, as opposed to calling a function internally. This is even more noticeable when it is a custom function (notably so). The exactextractr::exact function has several moment statistics available that are calculated in C++ rather than calling an R function. This speeds up things quite a bit compared to creating an object and using lapply however, this is not the case for custom function and I have noticed that even with this function with non-C++ function it is faster to create an object and use lapply rather than passing the function using the fun argument.

Source Link
Jeffrey Evans
  • 32k
  • 2
  • 48
  • 97

Let's benchmark some different approaches.

Here is some example data. Please note that we explode the MULTIPOLYGON geometry to simplify matters a bit.

library(sf)
library(raster)
library(terra)
library(exactextractr)

nc <- st_read(system.file("shape/nc.shp", package="sf"))
  nc <- st_cast(nc, "POLYGON")

r <- raster(extent(nc), res=0.05)
  r[] <- runif(ncell(r))
    proj4string(r) <- st_crs(nc)$proj4string

  plot(r)
    plot(nc[,1], add=TRUE)

raster::extract using lapply on resulting object and raster::extract using an internal function call

system.time({
  v.raster <- raster::extract(r, nc)
    lapply(v.raster, mean)
})

system.time({
  v.raster <- raster::extract(r, nc, fun=mean)
})

terra::extract using lapply on resulting object and terra::extract using an internal function call

system.time({
  v.terra <- terra::extract( rast(r), vect(nc))
    tapply(v.terra[,"layer"], v.terra[,"ID"]) 
})

system.time({
  v.terra <- terra::extract( rast(r), vect(nc), fun=mean)
})

exactextractr::exact using lapply on resulting object and exactextractr::exact using an internal function call

system.time({
  v.exact <- exactextractr::exact_extract(r, nc) 
    unlist(lapply(v.exact, function(x) mean(x[,"value"]) ))
})

system.time({
  v.exact <- exactextractr::exact_extract(r, nc, "mean") 
})

All of these approaches result in a vector that can be joined back to the polygon data. You can see that the exactextractr::exact is the fastest approach. For both raster::extract and terra::extract you may observe that it can be faster to create an object and then use lapply, as opposed to calling a function internally. This is even more noticeable when it is a custom function (notably so). The exactextractr::exact function has several moment statistics available that are calculated in C++ rather than calling an R function. This speeds up things quite a bit comparired to creating an object and using lapply however, this is not the case for custom function and I have noticed that even with this function with non-C++ function it is faster to create an object and use lapply rather than passing the function using the fun argument.

Here is a simple example of calculating the proportion of value >= to 0.25 and assigning results to the polygon data.

m <- function(x, p=0.25) {
  nrow(x[x[,1] >= p,]) / nrow(x) 
}
v.exact <- exactextractr::exact_extract(r, nc) 
  nc$p025 <- unlist(lapply(v.exact, m))

plot(nc[,"p025"])