问题
I have a raster stack and 100 points. For each raster I want to extract the value and do so using three different scales/buffers.
First, here are three rasters combined into a stack
library(raster)
# Make rasters and combine into stack
set.seed(123)
r1 = raster(ncol=1000, nrow=1000, xmn=0, xmx=1000, ymn=0, ymx=1000)
values(r1) = round(runif(ncell(r1),1,100))
r2 = raster(ncol=1000, nrow=1000, xmn=0, xmx=1000, ymn=0, ymx=1000)
values(r2) = round(seq(1:ncell(r1)))
r3 = raster(ncol=1000, nrow=1000, xmn=0, xmx=1000, ymn=0, ymx=1000)
values(r3) = round(runif(ncell(r1),1,5))
RasterStack <- stack(r1, r2, r3)
I then generate 100 points as a SpatialPoints
object
#make points
Points <- SpatialPoints(data.frame(xPoints = sample(1:1000, 100),
yPoints = sample(1:1000, 100)))
Next, I define the three buffers that I want to loop through
Scales <- c(60, 500)
To better describe the desired outcome, I will first use only a single raster, not the RasterStack. The code below defines a matrix (output) which is populated in the loop with each column being the extracted values of r1
at the two different Scales
. The columns are then labeled outside of the loop.
output <- matrix(ncol = length(Scales), nrow = length(Points))
for( i in 1:length(Scales)) {
output[, i] <- extract(r1, Points, method='simple', buffer=Scales[i], fun=mean)
}
colnames(output) <- paste("r1", Scales, sep = "_" )
> head(output)
r1_60 r1_500
[1,] 50.67339 50.42280
[2,] 50.42401 50.42335
[3,] 49.96709 50.44288
[4,] 50.65492 50.52634
[5,] 50.60678 50.43535
[6,] 50.52477 50.48277
I want this same output, but rather than calling a single raster (e.g. r1 above), I want to do this for each raster in the RasterStack
. The final result would be a matrix (or data.frame) that has two columns for each raster (r1:r3). As in the example, labeling would correspond to the respective scale so that the columns were labeled r1_60, r1_500, r2_60, ... , r3_500.
I think a nested for
loop would work where I loop through the RasterStack
and through the Scales
but suspect there might be a better way.
For the real data I have 20 rasters that are 1541 by 1293 and around 30,000 locations. I also have 5 different scales so a nested for
loop will take a very long time to run.
Addition Taking a different approach, I can use the following code to create a list of data frames, each of which corresponds to the extracted values of each layer using a given buffer.
output <- list()
for(i in 1:length(Scales)){
output[[i]] <- extract(RasterStack, Points, method='simple', buffer = Scales[i], fun = mean)
names(output)[[i]] <- paste("Buffer", Scales[i], sep = "_")
}
From this output, how can I make a single 6 by 100 data frame where each column would be labeled as the "layer_buffer number". For example, layer.1_60, layer.2_60, ... , layer.2_500, layer.3_500.
I can also post a new question of preferred.
回答1:
There appears to be a bug in the raster
package that causes an error to be thrown when extracting values from a RasterStack
if the distance represented by buffer
is smaller than the grid resolution. This is also referred to here.
For example,
extract(RasterStack, Points, buffer=0, fun=mean)
## Error in apply(x, 2, fun2) : dim(X) must have a positive length
The workaround is a little messy:
# Just the first 10 points, for the example
Points <- Points[1:10, ]
dat <- do.call(cbind, lapply(Scales, function(b) {
out <- do.call(rbind, lapply(extract(RasterStack, Points, buffer=b),
function(x) if(is.matrix(x)) colMeans(x) else x))
colnames(out) <- paste(colnames(out), b, sep='_')
out
}))
This produces:
dat
## layer.1_0 layer.2_0 layer.3_0 layer.1_60 layer.2_60 layer.3_60 layer.1_500 layer.2_500 layer.3_500
## [1,] 48 409158 4 50.67339 408657.5 3.013623 50.42280 435485.7 2.999983
## [2,] 80 450287 1 50.42401 449786.5 2.990888 50.42335 460519.9 2.999632
## [3,] 89 987912 3 49.96709 968829.9 2.995279 50.44288 775273.5 3.002715
## [4,] 65 119952 5 50.65492 119448.9 3.009086 50.52634 273116.8 3.000364
## [5,] 99 142320 4 50.60678 141819.5 2.998585 50.43535 289803.0 2.999054
## [6,] 64 394804 3 50.52477 394303.5 2.984253 50.48277 426887.0 3.000055
## [7,] 61 580925 2 50.96037 580424.5 3.001769 50.50032 559294.6 2.999218
## [8,] 47 84918 3 50.83050 84417.5 2.998585 50.51135 258470.6 2.999923
## [9,] 8 750667 4 50.16003 750166.5 2.987969 50.41984 655768.4 3.000635
## [10,] 88 273369 5 50.30219 272868.5 2.981157 50.44709 354833.6 2.999274
回答2:
For the sake of closure, I am posting the solution that worked best for me. In light of the raster package bug, I did not extract values to points using the 0 buffer.
Scales <- c(60, 500)
Then, using the first 10 points,
Points <- Points[1:10]
I created a list for each buffer level using the following code.
output <- list()
for(i in 1:length(Scales)){
output[[i]] <- extract(RasterStack, Points, method='simple', buffer = Scales[i], fun = mean)
names(output)[[i]] <- paste("Buffer", Scales[i], sep = "_")
}
Then, following the post linked here, I used the following code to combine the list of data frames into a single data frame.
do.call(cbind,lapply(names(output),function(x){
res <- output[[x]]
colnames(res) <- paste(colnames(res),x,sep="_")
res
}))
The head
of the returned df is below.
layer.1_Buffer_60 layer.2_Buffer_60 layer.3_Buffer_60 layer.1_Buffer_500
[1,] 50.67339 408657.5 3.013623 50.42280
[2,] 50.42401 449786.5 2.990888 50.42335
[3,] 49.96709 968829.9 2.995279 50.44288
[4,] 50.65492 119448.9 3.009086 50.52634
[5,] 50.60678 141819.5 2.998585 50.43535
[6,] 50.52477 394303.5 2.984253 50.48277
layer.2_Buffer_500 layer.3_Buffer_500
[1,] 435485.7 2.999983
[2,] 460519.9 2.999632
[3,] 775273.5 3.002715
[4,] 273116.8 3.000364
[5,] 289803.0 2.999054
[6,] 426887.0 3.000055
来源:https://stackoverflow.com/questions/34619218/extract-raster-values-from-stack-to-points-in-for-loop