Simplify raster values in terra to category

62 Views Asked by At

I have a large raster with many unique values and a complex attribute table. I would like to create a much simpler raster that only has the values of one level, a level which only has values 0-2.

Say I have a raster, but am really only interested in the variable y:

library(terra)

r <- rast(nrows=3, ncols=3)
values(r) <- 1:9

y_table <- data.frame(id=1:9, y=c(0,0,1,0,1,1,2,2,2))
levels(r) <- y_table

Is there a way to create a new raster that effectively substitutes the y level for the values? So rather than

levels(r)
  id y
1  1 0
2  2 0
3  3 1
4  4 0
5  5 1
6  6 1
7  7 2
8  8 2
9  9 2

values(r)
      y
 [1,] 1
 [2,] 2
 [3,] 3
 [4,] 4
 [5,] 5
 [6,] 6
 [7,] 7
 [8,] 8
 [9,] 9

I get to

levels(r_converted)
[1] ""

values(r_converted)
      lyr.1
 [1,]     0
 [2,]     0
 [3,]     1
 [4,]     0
 [5,]     1
 [6,]     1
 [7,]     2
 [8,]     2
 [9,]     2

I clumsily have tried both classify and subst:

testc <- classify(r, rcl=matrix(c(0:2, 0:2), ncol=2), right=NA)
tests <- subst(r, 0:2, 0:2)

but neither changes the values themselves.

3

There are 3 best solutions below

0
Chris On

Using r and r2, y_table and y_table2, but think this is what you're after:

 r <- rast(nrows=3, ncols=3)
values(r) <- 1:9
y_table <- data.frame(id=1:9, y=c(0,0,1,0,1,1,2,2,2))

r2 = r
y_table2 <- data.frame(id=1:9, y=c(0,0,1,0,1,1,2,2,2))
y_table2$y <- as.factor(y_table2$y)
values(r2) = y_table2$y
r2
class       : SpatRaster 
dimensions  : 3, 3, 1  (nrow, ncol, nlyr)
resolution  : 120, 60  (x, y)
extent      : -180, 180, -90, 90  (xmin, xmax, ymin, ymax)
coord. ref. : lon/lat WGS 84 (CRS84) (OGC:CRS84) 
source(s)   : memory
categories  : label 
name        : lyr.1 
min value   :     0 
max value   :     2

is.factor(r2)
[1] TRUE
is.factor(r)
[1] FALSE

see ?terra::factors and your categories become multidimensional, your df has move than 2 columns.

Edit. I think both @grzegorz-sapjaszko and @geoffery both serve you better in advising classify as your rasters are likely far larger than your reprex. You don't say above that you 'need' factors and it seems more a way of simplifiying. This could readily be achieved by preparing a 'from-to' matrix that represents your many classes. So a bigger raster and classify

r9k = disagg(r, 9000, filename = 'terra_sof_78154418.tif')
rclmtx = matrix(c(1,0,2,0,3,1,4,0,5,1,6,1,7,2,8,2,9,2), nrow = 9, byrow = TRUE)
rclmtx
      [,1] [,2]
 [1,]    1    0
 [2,]    2    0
 [3,]    3    1
 [4,]    4    0
 [5,]    5    1
 [6,]    6    1
 [7,]    7    2
 [8,]    8    2
 [9,]    9    2
r9k_cl = classify(r9k, rcl=rclmtx, filename = 'terra_rcl_r9k_sof_78154418.tif')
r9k_cl
class       : SpatRaster 
dimensions  : 27000, 27000, 1  (nrow, ncol, nlyr)
resolution  : 0.01333333, 0.006666667  (x, y)
extent      : -180, 180, -90, 90  (xmin, xmax, ymin, ymax)
coord. ref. : lon/lat WGS 84 (CRS84) (OGC:CRS84) 
source      : terra_rcl_r9k_sof_78154418.tif 
name        : lyr.1 
min value   :     0 
max value   :     2 

It really comes down to your data and needs. If you already have factors you'd use terra::subst.

0
Grzegorz Sapijaszko On

Use your data.frame as clasifification matrix, like:

r <- terra::rast(nrows=3, ncols=3)
terra::values(r) <- 1:9

terra::plot(r)


y_table <- data.frame(id=1:9, y=c(0,0,1,0,1,1,2,2,2))

s <- terra::classify(r, y_table)
terra::plot(s)

Created on 2024-03-13 with reprex v2.1.0

0
Geoffery On

I'll try to synthesize the two answers to make it easier to solve the problem.

In the code in question, you are using the levels function to set the level of r. This does convert r to factor but this does not change the value of r.

is.factor(r)
[1] TRUE
values(r)
      y
 [1,] 1
 [2,] 2
 [3,] 3
 [4,] 4
 [5,] 5
 [6,] 6
 [7,] 7
 [8,] 8
 [9,] 9

I would recommend you use the classify function, which can handle large rasters better compared to values. However, it is important to note that the classified r will not be a factor, and you can use the as.factor function to convert it to a factor type.

r <- classify(r, y_table)
is.factor(r)
[1] FALSE

r <- as.factor(r)
is.factor(r)
[1] TRUE

levels(r)
  ID lyr.1
1  0     0
2  1     1
3  2     2

names(r) <- "y"
values(r)
      y
 [1,] 0
 [2,] 0
 [3,] 1
 [4,] 0
 [5,] 1
 [6,] 1
 [7,] 2
 [8,] 2
 [9,] 2