问题
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