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.