rpattern-matchinggreplterra

Partial matching of raster values in R


I have a raster with text values in cells, and I need to find cells where a certain word occurs. For a vector, I would use grepl, however applying it to the raster cell values returns all FALSE.

library(terra)

x <- rast(ncol = 10, nrow = 10)
x[] <- sample(c("AA", "AB", "BB"), size = 100, replace = TRUE)

a <- app(x, fun = \(i) grepl("A", i))
a
# class       : SpatRaster 
# dimensions  : 10, 10, 1  (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
# name        : lyr.1 
# min value   :     0 
# max value   :     0 

I could mimic the desired behaviour with:

A <- (x == "AA") + (x == "AB")
A <- A > 0

But my data do not allow full matches. How can I find raster cell values that contain a word in a character string?


Solution

  • SpatRasters with character cell values are categorical rasters: the raster itself stores an index into a list of categories (just like a factor works). That's why you can't apply a function like grepl() directly on the raster values (the app() function currently doesn't support that, apparently). You should work with the category labels and values instead.

    To demonstrate what I mean:

    > as.vector(x)
      [1] 1 3 2 2 3 2 2 3 3 2 1 3 1 2 3 2 2 2 1 3 3 3 1 1 2 2 2 1 3 2 3 3 1 2 2 1
     [37] 1 2 1 2 3 2 2 2 3 2 1 3 2 3 1 2 3 1 2 1 2 3 2 2 3 2 1 3 3 3 2 3 1 1 3 1
     [73] 1 3 1 1 3 1 3 3 1 3 3 2 1 2 1 2 1 1 2 3 2 3 3 3 3 2 3 1
    

    That's just the values stored in the raster, not the character labels that they represent. The categories are returned as a list with an element for each layer in the SpatRaster, each being a data.frame:

    > cats(x)
    [[1]]
      value label
    1     1    AA
    2     2    AB
    3     3    BB
    

    So what you want to do is in the first layer find the values that correspond to the label matching your search:

    > hits <- cats(x)[[1]]$value[grepl("A", cats(x)[[1]]$label)]
    > hits
    [1] 1 2
    

    And then apply those hits to your raster:

    > a <- app(x, fun = \(i) i %in% hits)
    > a
    class       : SpatRaster 
    dimensions  : 10, 10, 1  (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
    name        : lyr.1 
    min value   :     0 
    max value   :     1 
    > plot(a)
    

    enter image description here