Extract raster values (from Stack) to points in for loop

我的未来我决定 提交于 2020-04-30 07:48:16

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!