Home > Software design >  R: Problem with raster prediction from a linear model
R: Problem with raster prediction from a linear model

Time:10-16

I am using the function raster::predict to extract the prediction part of a linear model as a raster but I am getting this error:

Error in model.frame.default(Terms, newdata, na.action = na.action, xlev = object$xlevels) : object is not a matrix
In addition: Warning message:
'newdata' had 622 rows but variables found have 91 rows

My data set is a RasterStack of two satellite images (same CRS and data type). I have found this question but I couldn't solve my problem. Here is the code and the data:

library(raster)
ntl = raster ("path/ntl.tif")
vals_ntl <- as.data.frame(values(ntl))
ntl_coords = as.data.frame(xyFromCell(ntl, 1:ncell(ntl)))
combine <- as.data.frame(cbind(ntl_coords,vals_ntl))

ebbi = raster ("path/ebbi.tif")
ebbi <- resample(ebbi, ntl, method = "bilinear")
vals_ebbi <- as.data.frame(values(ebbi))

s = stack(ntl, ebbi)

block.data <- as.data.frame(cbind(combine, vals_ebbi))
names(block.data)[3] <- "ntl"
names(block.data)[4] <- "ebbi"

block.data <- na.omit(block.data)

model <- lm(formula = ntl ~ ebbi, data = block.data)

#predict to a raster
r1 <- raster::predict(s, model, progress = 'text', na.rm = T)
plot(r1)
writeRaster(r1, filename = "path/lm_predict.tif")

The data can be downloaded from here (I don't know if by sharing a smaller dataset the problem would still exist so I decided to share the full dataset which is quite big when using the dput command to copy-paste it)

CodePudding user response:

You are correct that dput is generally not very useful for spatial data; and that you should avoid using it. However, in most cases, there is no need to share data as you can create example data with code, or with data that ships with R, like in most examples in the help files and questions on this site. Saying that "I don't know if by sharing a smaller dataset the problem would still exist" suggests that the first thing you should do is to find out.

If you have a SpatRaster x that you want to reproduce, you can start with as.character(x), which is what I did to get the below.

library(terra)
ntl <- rast(ncols=48, nrows=91, nlyrs=1, xmin=582360, xmax=604440, ymin=1005560, ymax=1047420, names=c('avg_rad'), crs='EPSG:7767')
ebbi <- rast(ncols=48, nrows=91, nlyrs=1, xmin=582360, xmax=604440, ymin=1005560, ymax=1047420, names=c('B6_median'), crs='EPSG:7767')
values(ntl) <- sample(100, ncell(ntl), replace=TRUE)
values(ebbi) <- runif(ncell(ebbi))

Combine, set the names, and get the values into a data.frame. For larger datasets you could take a sample with spatSample(x, type="regular").

x <- c(ntl, ebbi)
names(x) <- c("ntl", "ebbi")  

Fit the model. You can do that in two steps

v <- as.data.frame(x, na.rm=TRUE)
model <- lm(ntl ~ ebbi, data=v)    

Or in one step

model <- lm(ntl ~ ebbi, data=x)    

And now predict (set a filename if you want to save the raster to disk).

p <- predict(x$ebbi, model, filename="")

It is important that the first (SpatRaster) argument to predict has names that match the names in the model. So in this case you can use x$ebbi or x[[2]], but if you use ebbi you get a mysterious error message

 p <- predict(ebbi, model)
 #Error in model.frame.default(Terms, newdata, na.action = na.action, xlev = object$xlevels) : object is not a matrix
 #In addition: Warning message:
 #'newdata' had 48 rows but variables found have 91 rows 

unless you first do

 names(ebbi) <- "ebbi"
 p <- predict(ebbi, model)

CodePudding user response:

Alternative, using the raster package the solution is:

library(raster)

ntl = raster ("path/ntl.tif")

ebbi = raster ("path/ebbi.tif")
ebbi <- resample(ebbi, ntl, method = "bilinear")

s = stack(ntl, ebbi)
names(s) = c('ntl', 'ebbi') # important step in order to run the predict function successfully

block.data = data.frame(na.omit(values(s)))
names(block.data) <- c('ntl', 'ebbi')

model <- lm(formula = ntl ~ ebbi, data = block.data)

#predict to a raster
r1 <- raster::predict(s, model, progress = 'text', na.rm = T)
plot(r1)
writeRaster(r1, filename = "path/lm_predict.tif")

I found the answer based on this post.

  • Related