Partial matching of raster values in R

66 Views Asked by At

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?

2

There are 2 best solutions below

0
Patrick On BEST ANSWER

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

3
Wimpel On

I rarely use terra, but perhaps this is what you need?

library(tidyverse)
x %>%
  as.data.frame(xy = TRUE) %>%
  mutate(withA = grepl("A", lyr.1)) %>%
  ggplot() + geom_raster(aes(x = x, y = y, fill = withA))

update an other approach

set.seed(123)
library(terra)
library(tidyverse)
x <- rast(ncol = 10, nrow = 10)
x[] <- sample(c("AA", "AB", "BB"), size = 100, replace = TRUE)

# add a new colyum based on the regex "A" output
y <- rast(ncol = 10, nrow = 10)
y[] <- x %>%
  as.data.frame(xy = TRUE) %>%
  mutate(withA = grepl("A", lyr.1)) %>% select(withA) %>% unlist()
add(x) <- y
names(x) <- c("lyr.1", "withA")
plot(x)
  

enter image description here

# or replace original values
x[] <- x %>%
  as.data.frame(xy = TRUE) %>%
  mutate(withA = grepl("A", lyr.1)) %>% select(withA) %>% unlist()
plot(x)