exactextractr R Package

repository·master·Indexed 43 days ago

https://github.com/isciences/exactextractr

An R package for fast and accurate zonal statistics that accounts for partially covered grid cells by using coverage fractions. It provides the `exact_extract` function for summarizing raster values over polygonal areas, supporting built-in summary operations (e.g., mean, sum, variety), custom R functions, and weighted zonal statistics. It also includes the `coverage_fraction()` function to rasterize polygons.

Tokens
3.9K
Snippets
9
Records
13
Agent score
55%

What's inside exactextractr

  1. Understand the accuracy of exactextractr

    master

    Unlike other common implementations, exactextractr provides higher accuracy by accounting for raster pixels that are only partially covered by polygons. This is particularly significant for small or irregularly shaped polygons.

    In practical testing (using 5,500 Brazilian municipalities), ignoring partial coverage resulted in errors of less than 1% for 88% of municipalities, with a maximum error of 9%.

  2. Perform weighted zonal statistics

    master

    To perform calculations like population-weighted temperature or area-weighted precipitation, use the weights argument in exact_extract.

    Requirements for the weighting raster:

    1. Must use the same coordinate system as the primary raster.
    2. Must use a compatible grid (the higher resolution grid's cell boundaries must coincide with the lower resolution grid's boundaries).

    Using named weighted operations:

    # Area-weighted mean precipitation
    brazil$mean_dec_prec_weighted <- exact_extract(prec[[12]], brazil, 'weighted_mean', weights = area(prec))

    Using custom functions with weights: When using a custom function with a weighting raster, the function signature must be function(values, coverage_frac, weights).

    # Area-weighted mean using a custom function
    brazil$mean_dec_prec_weighted <- 
      exact_extract(prec[[12]], brazil, function(values, coverage_frac, weights) {
        weighted.mean(values, coverage_frac * weights)
      }, weights = area(prec))
    # Area-weighted mean precipitation
    brazil$mean_dec_prec_weighted <- exact_extract(prec[[12]], brazil, 'weighted_mean', weights = area(prec))
    
    # Area-weighted mean using a custom function
    brazil$mean_dec_prec_weighted <- 
      exact_extract(prec[[12]], brazil, function(values, coverage_frac, weights) {
        weighted.mean(values, coverage_frac * weights)
      }, weights = area(prec))
  3. Return multi-valued results from summary functions

    master

    A summary function can return a one-row data.frame for each input feature. This allows you to return multiple statistics (e.g., mean temperature for different altitude categories) for a single polygon. The results for all features are combined into a single data frame using rbind or dplyr::bind_rows.

    Example: Mean precipitation for different altitude categories:

    altitude <- getData('alt', country = 'BRA')
    
    prec_for_altitude <- exact_extract(prec[[12]], brazil, function(prec, frac, alt) {
      # ignore cells with unknown altitude
      prec <- prec[!is.na(alt)]
      frac <- frac[!is.na(alt)]
      alt <- alt[!is.na(alt)]
      
      low <- !is.na(alt) & alt < 500
      high <- !is.na(alt) & alt >= 500
    
      data.frame(
        prec_low_alt = weighted.mean(prec[low], frac[low]),
        prec_high_alt = weighted.mean(prec[high], frac[high])
      )
    }, weights = altitude)
  4. Install GEOS dependency for exactextractr

    master

    To install exactextractr, you must have version 3.5 or greater of the GEOS geometry processing library installed on your system.

    Installation methods by platform:

    • Windows: GEOS is downloaded automatically during the package installation process.
    • MacOS: Use Homebrew: brew install geos.
    • Linux (Debian/Ubuntu): Use apt-get install libgeos-dev.
    • Linux (CentOS/RedHat): Use yum install libgeos-devel.
    # MacOS
    brew install geos
    
    # Debian/Ubuntu
    apt-get install libgeos-dev
    
    # CentOS/RedHat
    yum install libgeos-devel
  5. Use custom R functions as summary functions

    master

    If built-in operations are insufficient, you can pass an arbitrary R function to the fun parameter. Because exactextractr accounts for partial cell coverage, your custom function must accept two arguments: values (the raster values) and coverage_fraction (the fraction of the cell area covered by the polygon).

    If you also provide a weights argument, your custom function must accept a third argument: weights.

    Examples:

    # Number of cells covered by the polygon (ignoring raster values)
    exact_extract(rast, poly, function(values, coverage_fraction) sum(coverage_fraction))
    
    # Sum of values weighted by coverage fraction
    exact_extract(rast, poly, function(values, coverage_fraction) sum(values * coverage_fraction, na.rm=TRUE))
    
    # Number of distinct values in cells more than 10% covered
    exact_extract(rast, poly, function(values, coverage_fraction) length(unique(values[coverage_fraction > 0.1])))
    # Number of cells covered by the polygon (raster values are ignored)
    exact_extract(rast, poly, function(values, coverage_fraction)
                                sum(coverage_fraction))
    
    # Sum of defined raster values within the polygon, accounting for coverage fraction
    exact_extract(rast, poly, function(values, coverage_fraction)
                                sum(values * coverage_fraction, na.rm=TRUE))
    
    # Number of distinct raster values within the polygon (coverage fractions are ignored)
    exact_extract(rast, poly, function(values, coverage_fraction)
                                length(unique(values)))
    
    # Number of distinct raster values in cells more than 10% covered by the polygon
    exact_extract(rast, poly, function(values, coverage_fraction)
                                length(unique(values[coverage_fraction > 0.1])))
  6. Summarize multiple raster layers simultaneously

    master

    You can implement complex logic involving multiple layers (e.g., calculating average temperature from both tmin and tmax layers) by passing a RasterStack to exact_extract.

    Key Behaviors:

    • If stack_apply = TRUE: The summary function is applied to each layer independently.
    • If stack_apply = FALSE (default): The values from all layers in the stack are provided to the summary function as a data frame.

    Example: Calculating average temperature from min/max stack:

    # temp is a RasterStack of tmin and tmax
    brazil$tavg_dec <- exact_extract(temp, brazil,
      function(values, coverage_fraction, weights) {
        tavg <- 0.5*(values$tmin12 + values$tmax12)
        weighted.mean(tavg, coverage_fraction * weights)
      }, weights = area(prec))
    tmin <- getData('worldclim', var = 'tmin', res = 10)
    tmax <- getData('worldclim', var = 'tmax', res = 10)
    
    temp <- stack(tmin[[12]], tmax[[12]])
    
    brazil$tavg_dec <- exact_extract(temp, brazil,
      function(values, coverage_fraction, weights) {
        tavg <- 0.5*(values$tmin12 + values$tmax12)
        weighted.mean(tavg, coverage_fraction * weights)
      }, weights = area(prec))
  7. Compile exactextract from source

    master

    To build exactextract on Linux, you need a C++14 compiler (gcc 5.0+), CMake 3.8+, GEOS 3.5+, and GDAL 2.0+ (if building the CLI).

    By default, the build process includes the CLI, tests, and documentation. You can control these using CMake flags:

    • BUILD_CLI: Builds the main program (requires GDAL).
    • BUILD_TEST: Builds the catch_test suite.
    • BUILD_DOC: Builds Doxygen documentation.

    To build only the library and test suite (without requiring GDAL for the CLI), use the following command:

    git clone https://github.com/isciences/exactextract
    cd exactextract
    mkdir cmake-build-release
    cd cmake-build-release
    cmake -DBUILD_CLI:=OFF -DBUILD_DOC:=OFF -DCMAKE_BUILD_TYPE=Release ..
    make
    ./catch_tests && sudo make install
  8. Optimize exact_extract performance

    master

    While exactextractr is generally faster than raster and terra, performance can be improved by addressing specific bottlenecks.

    Common Performance Tips:

    • Use SpatRaster: For best performance, use terra::SpatRaster objects instead of raster::RasterLayer.
    • Increase Memory Limits: Increase the max_cells_in_memory parameter.
    • GDAL Cache: Increase the size of the GDAL block cache.
    • Chunking Strategy: If performance is poor, consider rewriting input rasters to use a different chunking scheme (e.g., tiled vs striped).
    • Batch Processing: Process inputs as batches of nearby polygons.

    If progress = TRUE is set, the package will emit messages if it detects situations likely to cause poor performance, such as raster chunk sizes that are too large to allow effective caching between vector features.

  9. Rasterize polygons using coverage_fraction

    master

    You can rasterize polygons by calculating the coverage fraction of each cell. The coverage_fraction() function returns a RasterLayer with values from 0 to 1, representing the fraction of each cell covered by the polygon.

    Warning: This function generates a RasterLayer for each feature in the input dataset. For large datasets, this can consume significant memory. It is recommended to manually loop over features and combine the rasters if memory is a concern.

  10. Use exact_extract for zonal statistics

    master

    The exact_extract function is the primary method for summarizing raster values over polygonal areas (zonal statistics). Unlike standard implementations, it accurately handles grid cells that are partially covered by a polygon by accounting for the coverage fraction. It operates similarly to raster::extract but provides more accurate results for partial overlaps.

    To use it, provide a raster object, a polygon object (e.g., from sf), and a summary operation or function.

    library(exactextractr)
    library(sf)
    
    # Example: Calculate mean precipitation for each municipality in Brazil
    # brazil: sf object
    # prec: raster object
    brazil$mean_dec_prec <- exact_extract(prec[[12]], brazil, 'mean')
  11. Available named summary operations for exact_extract

    master

    You can pass a character vector to the fun parameter of exact_extract to use optimized, built-in summary operations. These are generally faster and more memory-efficient than passing an R function.

    Standard Operations:

    • count: Sum of all cell coverage fractions.
    • majority (or mode): The raster value with the largest sum of coverage fractions.
    • max: Maximum value of cells that intersect the polygon (ignores coverage fractions).
    • mean: Mean value of cells weighted by coverage fraction.
    • median: Median value of cells weighted by coverage fraction.
    • quantile: Arbitrary quantile value of cells weighted by coverage fraction.
    • min: Minimum value of cells that intersect the polygon (ignores coverage fractions).
    • minority: The raster value with the smallest sum of coverage fractions.
    • sum: Sum of values weighted by coverage fraction.
    • variety: Number of distinct raster values in cells wholly or partially covered.
    • variance: Population variance weighted by coverage fraction.
    • stdev: Population standard deviation weighted by coverage fraction.
    • coefficient_of_variation: Population coefficient of variation weighted by coverage fraction.
    • frac: Fraction of covered cells occupied by each distinct raster value.

    Weighted Operations (requires weights argument):

    • weighted_mean: Mean of non-NA cells, weighted by (coverage fraction * weight raster value).
    • weighted_sum: Sum of non-NA values, weighted by (coverage fraction * weight raster value).
    • weighted_variance: Population variance of non-NA values, weighted by (coverage fraction * weight raster value).
    • weighted_stdev: Population standard deviation of non-NA values, weighted by (coverage fraction * weight raster value).
    • weighted_frac: Fraction of covered cells occupied by each distinct value, with coverage fractions multiplied by the weight raster value.
  12. Initialize S4RasterSource for R raster objects

    master

    The S4RasterSource class is used to interface with R S4 raster objects (such as those from the raster or terra packages) within the exactextractr C++ backend. It allows the engine to read raster values from an R-managed object by mapping the raster's extent and resolution to an internal grid.

    There are two ways to initialize this source:

    1. From an R SEXP: Pass the R object directly. The class will attempt to derive the grid from the object.
    2. With explicit extent and resolution: Pass the R object along with explicit extent and resolution vectors to define the grid manually.

    You can also specify a default_value (defaults to NaN) to replace any NaN values found in the raster during reading.

    // Example conceptual usage of S4RasterSource initialization
    // 1. Using the R object directly
    S4RasterSource source(rast_object, default_val);
    
    // 2. Using explicit extent and resolution
    S4RasterSource source(rast_object, extent_vector, res_vector, default_val);