Sampling

Currently, there are 9 functions associated with the sample verb in the sgsR package:

Algorithm Description Reference
sample_srs() Simple random
sample_systematic() Systematic
sample_strat() Stratified Queinnec, White, & Coops (2021)
sample_sys_strat() Systematic Stratified
sample_nc() Nearest centroid Melville & Stone (2016)
sample_clhs() Conditioned Latin hypercube Minasny & McBratney (2006)
sample_balanced() Balanced sampling Grafström, A. Lisic, J (2018)
sample_ahels() Adapted hypercube evaluation of a legacy sample Malone, Minasny, & Brungard (2019)
sample_existing() Sub-sampling an existing sample

sample_srs

We have demonstrated a simple example of using the sample_srs() function in vignette("sgsR"). We will demonstrate additional examples below.

raster

The input required for sample_srs() is a raster. This means that sraster and mraster are supported for this function.

#--- perform simple random sampling ---#
sample_srs(
  raster = sraster, # input sraster
  nSamp = 200, # number of desired sample units
  plot = TRUE
) # plot

#> Simple feature collection with 200 features and 0 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431110 ymin: 5337730 xmax: 438550 ymax: 5343230
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>                  geometry
#> 1  POINT (435810 5340510)
#> 2  POINT (432050 5341690)
#> 3  POINT (437250 5341110)
#> 4  POINT (432290 5341950)
#> 5  POINT (437730 5342770)
#> 6  POINT (435010 5342030)
#> 7  POINT (434310 5338910)
#> 8  POINT (438390 5341730)
#> 9  POINT (438130 5342050)
#> 10 POINT (431670 5340470)
sample_srs(
  raster = mraster, # input mraster
  nSamp = 200, # number of desired sample units
  access = access, # define access road network
  mindist = 200, # minimum distance sample units must be apart from one another
  buff_inner = 50, # inner buffer - no sample units within this distance from road
  buff_outer = 200, # outer buffer - no sample units further than this distance from road
  plot = TRUE
) # plot

#> Simple feature collection with 200 features and 0 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431150 ymin: 5337750 xmax: 438530 ymax: 5343230
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>                  geometry
#> 1  POINT (432550 5340830)
#> 2  POINT (432370 5343070)
#> 3  POINT (434530 5341390)
#> 4  POINT (433630 5342370)
#> 5  POINT (433310 5341330)
#> 6  POINT (434850 5341110)
#> 7  POINT (435070 5340470)
#> 8  POINT (432010 5342690)
#> 9  POINT (436570 5342950)
#> 10 POINT (434650 5339490)

sample_systematic

The sample_systematic() function applies systematic sampling across an area with the cellsize parameter defining the resolution of the tessellation. The tessellation shape can be modified using the square parameter. Assigning TRUE (default) to the square parameter results in a regular grid and assigning FALSE results in a hexagonal grid.

The location of sample units can also be adjusted using the locations parameter, where centers takes the center, corners takes all corners, and random takes a random location within each tessellation. Random start points and translations are applied when the function is called.

#--- perform grid sampling ---#
sample_systematic(
  raster = sraster, # input sraster
  cellsize = 1000, # grid distance
  plot = TRUE
) # plot

#> Simple feature collection with 34 features and 0 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431177.9 ymin: 5337728 xmax: 438230.1 ymax: 5343163
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>                    geometry
#> 1  POINT (438023.6 5338175)
#> 2  POINT (436613.2 5338278)
#> 3  POINT (438126.9 5339585)
#> 4  POINT (434445.9 5337728)
#> 5  POINT (435202.7 5338381)
#> 6  POINT (435959.6 5339035)
#> 7  POINT (436716.4 5339689)
#> 8  POINT (437473.3 5340342)
#> 9  POINT (438230.1 5340996)
#> 10 POINT (433035.5 5337831)
#--- perform grid sampling ---#
sample_systematic(
  raster = sraster, # input sraster
  cellsize = 500, # grid distance
  square = FALSE, # hexagonal tessellation
  location = "random", # randomly sample within tessellation
  plot = TRUE
) # plot

#> Simple feature collection with 172 features and 0 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431129 ymin: 5337727 xmax: 438509.6 ymax: 5343213
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>                    geometry
#> 1  POINT (438496.9 5339306)
#> 2    POINT (438341 5338594)
#> 3  POINT (438509.6 5342444)
#> 4  POINT (438495.3 5340861)
#> 5  POINT (438352.6 5339871)
#> 6  POINT (438249.8 5339080)
#> 7  POINT (437936.1 5338264)
#> 8    POINT (438381 5341850)
#> 9  POINT (438313.6 5341127)
#> 10 POINT (438034.6 5340412)
sample_systematic(
  raster = sraster, # input sraster
  cellsize = 500, # grid distance
  access = access, # define access road network
  buff_outer = 200, # outer buffer - no sample units further than this distance from road
  square = FALSE, # hexagonal tessellation
  location = "corners", # take corners instead of centers
  plot = TRUE
)

#> Simple feature collection with 627 features and 0 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431286.9 ymin: 5337751 xmax: 438549.8 ymax: 5343238
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>                    geometry
#> 1  POINT (438549.8 5340764)
#> 2  POINT (438455.6 5339034)
#> 3  POINT (438471.3 5339322)
#> 4  POINT (438408.6 5338170)
#> 5  POINT (438424.3 5338458)
#> 6  POINT (438323.7 5341210)
#> 7  POINT (438549.8 5340764)
#> 8  POINT (438276.6 5340345)
#> 9  POINT (438549.8 5340764)
#> 10 POINT (438471.3 5339322)

sample_strat

The sample_strat() contains two methods to perform sampling:

  • "Queinnec" - Hierarchical sampling using a focal window to isolate contiguous groups of stratum pixels, which was originally developed by Martin Queinnec.

  • "random" - Traditional stratified random sampling. This method ignores much of the functionality of the algorithm to allow users the capability to use standard stratified random sampling approaches without the use of a focal window to locate contiguous stratum cells.

method = "Queinnec"

Queinnec, M., White, J. C., & Coops, N. C. (2021). Comparing airborne and spaceborne photon-counting LiDAR canopy structural estimates across different boreal forest types. Remote Sensing of Environment, 262(August 2020), 112510.

This algorithm uses moving window (wrow and wcol parameters) to filter the input sraster to prioritize sample unit allocation to where stratum pixels are spatially grouped, rather than dispersed individuals across the landscape.

Sampling is performed using 2 rules:

  • Rule 1 - Sample within spatially grouped stratum pixels. Moving window defined by wrow and wcol.

  • Rule 2 - If no additional sample units exist to satisfy desired sample size(nSamp), individual stratum pixels are sampled.

The rule applied to a select each sample unit is defined in the rule attribute of output samples. We give a few examples below:

#--- perform stratified sampling random sampling ---#
sample_strat(
  sraster = sraster, # input sraster
  nSamp = 200
) # desired sample size # plot
#> Simple feature collection with 200 features and 3 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431170 ymin: 5337730 xmax: 438490 ymax: 5343210
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>    strata type  rule               geometry
#> x       1  new rule1 POINT (433630 5341130)
#> x1      1  new rule1 POINT (434630 5341910)
#> x2      1  new rule1 POINT (433690 5341410)
#> x3      1  new rule1 POINT (437010 5337730)
#> x4      1  new rule1 POINT (438250 5340930)
#> x5      1  new rule1 POINT (436610 5339330)
#> x6      1  new rule1 POINT (433910 5340870)
#> x7      1  new rule1 POINT (437710 5341010)
#> x8      1  new rule1 POINT (435530 5342290)
#> x9      1  new rule1 POINT (434450 5342430)

In some cases, users might want to include an existing sample within the algorithm. In order to adjust the total number of sample units needed per stratum to reflect those already present in existing, we can use the intermediate function extract_strata().

This function uses the sraster and existing sample units and extracts the stratum for each. These sample units can be included within sample_strat(), which adjusts total sample units required per class based on representation in existing.

#--- extract strata values to existing samples ---#
e.sr <- extract_strata(
  sraster = sraster, # input sraster
  existing = existing
) # existing samples to add strata value to

TIP!

sample_strat() requires the sraster input to have an attribute named strata and will give an error if it doesn’t.

sample_strat(
  sraster = sraster, # input sraster
  nSamp = 200, # desired sample size
  access = access, # define access road network
  existing = e.sr, # existing sample with strata values
  mindist = 200, # minimum distance sample units must be apart from one another
  buff_inner = 50, # inner buffer - no sample units within this distance from road
  buff_outer = 200, # outer buffer - no sample units further than this distance from road
  plot = TRUE
) # plot

#> Simple feature collection with 400 features and 3 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431130 ymin: 5337770 xmax: 438530 ymax: 5343210
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>    strata     type     rule               geometry
#> 1       1 existing existing POINT (433950 5341310)
#> 2       1 existing existing POINT (438530 5338290)
#> 3       1 existing existing POINT (434190 5340910)
#> 4       1 existing existing POINT (433770 5340970)
#> 5       1 existing existing POINT (438030 5341010)
#> 6       1 existing existing POINT (435750 5342190)
#> 7       1 existing existing POINT (433070 5341590)
#> 8       1 existing existing POINT (433810 5340810)
#> 9       1 existing existing POINT (434310 5340790)
#> 10      1 existing existing POINT (434250 5339330)

The code in the example above defined the mindist parameter, which specifies the minimum euclidean distance that new sample units must be apart from one another.

Notice that the sample units have type and rule attributes which outline whether they are existing or new, and whether rule1 or rule2 were used to select them. If type is existing (a user provided existing sample), rule will be existing as well as seen above.

sample_strat(
  sraster = sraster, # input
  nSamp = 200, # desired sample size
  access = access, # define access road network
  existing = e.sr, # existing samples with strata values
  include = TRUE, # include existing sample in nSamp total
  buff_outer = 200, # outer buffer - no samples further than this distance from road
  plot = TRUE
) # plot

#> Simple feature collection with 200 features and 3 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431150 ymin: 5337790 xmax: 438530 ymax: 5343210
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>    strata     type     rule               geometry
#> 1       1 existing existing POINT (433950 5341310)
#> 2       1 existing existing POINT (438530 5338290)
#> 3       1 existing existing POINT (434190 5340910)
#> 4       1 existing existing POINT (433770 5340970)
#> 5       1 existing existing POINT (438030 5341010)
#> 6       1 existing existing POINT (435750 5342190)
#> 7       1 existing existing POINT (433070 5341590)
#> 8       1 existing existing POINT (433810 5340810)
#> 9       1 existing existing POINT (434310 5340790)
#> 10      1 existing existing POINT (434250 5339330)

The include parameter determines whether existing sample units should be included in the total sample size defined by nSamp. By default, the include parameter is set as FALSE.

method = "random

Stratified random sampling with equal probability for all cells (using default algorithm values for mindist and no use of access functionality). In essence this method perform the sample_srs algorithm for each stratum separately to meet the specified sample size.

#--- perform stratified sampling random sampling ---#
sample_strat(
  sraster = sraster, # input sraster
  method = "random", # stratified random sampling
  nSamp = 200, # desired sample size
  plot = TRUE
) # plot

#> Simple feature collection with 200 features and 2 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431270 ymin: 5337890 xmax: 438550 ymax: 5343230
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>    strata type               geometry
#> x       1  new POINT (434110 5340990)
#> x1      1  new POINT (436850 5339230)
#> x2      1  new POINT (435030 5342570)
#> x3      1  new POINT (434310 5343230)
#> x4      1  new POINT (435150 5339170)
#> x5      1  new POINT (435430 5342070)
#> x6      1  new POINT (431390 5340790)
#> x7      1  new POINT (434290 5342430)
#> x8      1  new POINT (434310 5341770)
#> x9      1  new POINT (434350 5339370)

sample_sys_strat

sample_sys_strat() function implements systematic stratified sampling on an sraster. This function uses the same functionality as sample_systematic() but takes an sraster as input and performs sampling on each stratum iteratively.

#--- perform grid sampling on each stratum separately ---#
sample_sys_strat(
  sraster = sraster, # input sraster with 4 strata
  cellsize = 1000, # grid size
  plot = TRUE # plot output
)
#> Warning: [readStart] source already open for reading
#> Processing strata : 1
#> Warning: [extract] source already open for reading
#> Processing strata : 2
#> Warning: [extract] source already open for reading
#> Processing strata : 3
#> Warning: [extract] source already open for reading
#> Processing strata : 4
#> Warning: [extract] source already open for reading

#> Simple feature collection with 24 features and 1 field
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431627.1 ymin: 5337814 xmax: 438425.9 ymax: 5343129
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>    strata                 geometry
#> 1       1 POINT (437546.7 5343129)
#> 2       1 POINT (435457.4 5342332)
#> 3       1 POINT (434462.3 5342431)
#> 4       1 POINT (434363.2 5341436)
#> 5       1 POINT (433368.1 5341535)
#> 6       1 POINT (435259.1 5340342)
#> 7       1 POINT (432273.9 5340640)
#> 8       1   POINT (438046 5338055)
#> 9       1   POINT (437051 5338154)
#> 10      2 POINT (437979.3 5340923)

Just like with sample_systematic() we can specify where we want our samples to fall within our tessellations. We specify location = "corners" below. Note that the tesselations are all saved to a list file when details = TRUE should the user want to save them.

sample_sys_strat(
  sraster = sraster, # input sraster with 4 strata
  cellsize = 500, # grid size
  square = FALSE, # hexagon tessellation
  location = "corners", # samples on tessellation corners
  plot = TRUE # plot output
)
#> Processing strata : 1
#> Warning: [extract] source already open for reading
#> Processing strata : 2
#> Warning: [extract] source already open for reading
#> Processing strata : 3
#> Warning: [extract] source already open for reading
#> Processing strata : 4
#> Warning: [extract] source already open for reading

#> Simple feature collection with 1172 features and 1 field
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431125.5 ymin: 5337716 xmax: 438539.9 ymax: 5343237
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>    strata                 geometry
#> 1       1 POINT (438539.9 5340360)
#> 2       1 POINT (438519.3 5339205)
#> 3       1 POINT (438503.8 5338339)
#> 4       1 POINT (438493.5 5337762)
#> 5       1 POINT (438539.9 5340360)
#> 6       1 POINT (438292.5 5340509)
#> 7       1   POINT (438277 5339643)
#> 8       1 POINT (438519.3 5339205)
#> 9       1 POINT (438261.6 5338777)
#> 10      1 POINT (438519.3 5339205)

This sampling approach could be especially useful incombination with strat_poly() to ensure consistency of sampling accross specific management units.

#--- read polygon coverage ---#
poly <- system.file("extdata", "inventory_polygons.shp", package = "sgsR")
fri <- sf::st_read(poly)
#> Reading layer `inventory_polygons' from data source 
#>   `/tmp/RtmpIs2b2x/Rinst13b9779e3221/sgsR/extdata/inventory_polygons.shp' 
#>   using driver `ESRI Shapefile'
#> Simple feature collection with 632 features and 3 fields
#> Geometry type: MULTIPOLYGON
#> Dimension:     XY
#> Bounding box:  xmin: 431100 ymin: 5337700 xmax: 438560 ymax: 5343240
#> Projected CRS: UTM_Zone_17_Northern_Hemisphere

#--- stratify polygon coverage ---#
#--- specify polygon attribute to stratify ---#
attribute <- "NUTRIENTS"

#--- specify features within attribute & how they should be grouped ---#
#--- as a single vector ---#
features <- c("poor", "rich", "medium")

#--- get polygon stratification ---#
srasterpoly <- strat_poly(
  poly = fri,
  attribute = attribute,
  features = features,
  raster = sraster
)

#--- systematatic stratified sampling for each stratum ---#
sample_sys_strat(
  sraster = srasterpoly, # input sraster from strat_poly() with 3 strata
  cellsize = 500, # grid size
  square = FALSE, # hexagon tessellation
  location = "random", # randomize plot location
  plot = TRUE # plot output
)
#> Processing strata : 1
#> Warning: [extract] source already open for reading
#> Processing strata : 2
#> Warning: [extract] source already open for reading
#> Processing strata : 3
#> Warning: [extract] source already open for reading
#> Simple feature collection with 183 features and 1 field
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431143.4 ymin: 5337750 xmax: 438519.8 ymax: 5343172
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>    strata                 geometry
#> 1       1 POINT (438200.7 5342477)
#> 2       1 POINT (438468.4 5337994)
#> 3       1 POINT (437961.5 5342892)
#> 4       1 POINT (437889.1 5341904)
#> 5       1 POINT (438007.1 5339301)
#> 6       1 POINT (438179.7 5338479)
#> 7       1 POINT (437833.9 5342287)
#> 8       1 POINT (437761.2 5341658)
#> 9       1 POINT (437806.8 5340664)
#> 10      1 POINT (437915.8 5339717)

sample_nc

sample_nc() function implements the Nearest Centroid sampling algorithm described in Melville & Stone (2016). The algorithm uses kmeans clustering where the number of clusters (centroids) is equal to the desired sample size (nSamp).

Cluster centers are located, which then prompts the nearest neighbour mraster pixel for each cluster to be selected (assuming default k parameter). These nearest neighbours are the output sample units.

#--- perform simple random sampling ---#
sample_nc(
  mraster = mraster, # input
  nSamp = 25, # desired sample size
  plot = TRUE
)
#> K-means being performed on 3 layers with 25 centers.

#> Simple feature collection with 25 features and 4 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431190 ymin: 5337910 xmax: 438530 ymax: 5343170
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>        zq90 pzabove2  zsd kcenter               geometry
#> 21644 18.70     87.7 5.09       1 POINT (431290 5342070)
#> 99324 23.50     89.9 6.85       2 POINT (433210 5337910)
#> 16935 15.50     94.8 2.71       3 POINT (434090 5342330)
#> 37247 17.60     72.4 4.86       4 POINT (437490 5341250)
#> 52346  7.27     13.5 1.98       5 POINT (433610 5340430)
#> 39139  8.10     85.4 1.80       6 POINT (438030 5341150)
#> 15080  9.64     64.8 2.46       7 POINT (434290 5342430)
#> 66634  8.75     40.3 2.30       8 POINT (435890 5339670)
#> 44332 12.40     75.2 3.16       9 POINT (437450 5340870)
#> 49010  4.55     30.7 1.02      10 POINT (434030 5340610)

Altering the k parameter leads to a multiplicative increase in output sample units where total output samples = \(nSamp * k\).

#--- perform simple random sampling ---#
samples <- sample_nc(
  mraster = mraster, # input
  k = 2, # number of nearest neighbours to take for each kmeans center
  nSamp = 25, # desired sample size
  plot = TRUE
)
#> K-means being performed on 3 layers with 25 centers.


#--- total samples = nSamp * k (25 * 2) = 50 ---#
nrow(samples)
#> [1] 50

Visualizing what the kmeans centers and sample units looks like is possible when using details = TRUE. The $kplot output provides a quick visualization of where the centers are based on a scatter plot of the first 2 layers in mraster. Notice that the centers are well distributed in covariate space and chosen sample units are the closest pixels to each center (nearest neighbours).

#--- perform simple random sampling with details ---#
details <- sample_nc(
  mraster = mraster, # input
  nSamp = 25, # desired sample number
  details = TRUE
)
#> K-means being performed on 3 layers with 25 centers.

#--- plot ggplot output ---#

details$kplot

sample_clhs

sample_clhs() function implements conditioned Latin hypercube (clhs) sampling methodology from the clhs package.

TIP!

A number of other functions in the sgsR package help to provide guidance on clhs sampling including calculate_pop() and calculate_lhsOpt(). Check out these functions to better understand how sample numbers could be optimized.

The syntax for this function is similar to others shown above, although parameters like iter, which define the number of iterations within the Metropolis-Hastings process are important to consider. In these examples we use a low iter value for efficiency. Default values for iter within the clhs package are 10,000.

sample_clhs(
  mraster = mraster, # input
  nSamp = 200, # desired sample size
  plot = TRUE, # plot
  iter = 100
) # number of iterations

The cost parameter defines the mraster covariate, which is used to constrain the clhs sampling. An example could be the distance a pixel is from road access (e.g. from calculate_distance() see example below), terrain slope, the output from calculate_coobs(), or many others.

#--- cost constrained examples ---#
#--- calculate distance to access layer for each pixel in mr ---#
mr.c <- calculate_distance(
  raster = mraster, # input
  access = access, # define access road network
  plot = TRUE
) # plot

sample_clhs(
  mraster = mr.c, # input
  nSamp = 250, # desired sample size
  iter = 100, # number of iterations
  cost = "dist2access", # cost parameter - name defined in calculate_distance()
  plot = TRUE
) # plot

sample_balanced

The sample_balanced() algorithm performs a balanced sampling methodology from the stratifyR / SamplingBigData packages.

sample_balanced(
  mraster = mraster, # input
  nSamp = 200, # desired sample size
  plot = TRUE
) # plot

#> Simple feature collection with 200 features and 0 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431150 ymin: 5337710 xmax: 438550 ymax: 5343210
#> Projected CRS: +proj=utm +zone=17 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs
#> First 10 features:
#>                  geometry
#> 1  POINT (437450 5343210)
#> 2  POINT (437390 5343150)
#> 3  POINT (435950 5343130)
#> 4  POINT (432190 5343110)
#> 5  POINT (436270 5343110)
#> 6  POINT (432470 5343030)
#> 7  POINT (436630 5343030)
#> 8  POINT (433930 5343010)
#> 9  POINT (434430 5342950)
#> 10 POINT (437070 5342930)
sample_balanced(
  mraster = mraster, # input
  nSamp = 100, # desired sample size
  algorithm = "lcube", # algorithm type
  access = access, # define access road network
  buff_inner = 50, # inner buffer - no sample units within this distance from road
  buff_outer = 200
) # outer buffer - no sample units further than this distance from road
#> Simple feature collection with 100 features and 0 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431270 ymin: 5337750 xmax: 438550 ymax: 5343210
#> Projected CRS: +proj=utm +zone=17 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs
#> First 10 features:
#>                  geometry
#> 1  POINT (434670 5343210)
#> 2  POINT (434710 5343210)
#> 3  POINT (433750 5343190)
#> 4  POINT (437270 5343130)
#> 5  POINT (436010 5343110)
#> 6  POINT (432470 5343070)
#> 7  POINT (435410 5343030)
#> 8  POINT (437350 5343030)
#> 9  POINT (436190 5342990)
#> 10 POINT (434190 5342950)

sample_ahels

The sample_ahels() function performs the adapted Hypercube Evaluation of a Legacy Sample (ahels) algorithm usingexisting sample data and an mraster. New sample units are allocated based on quantile ratios between the existing sample and mraster covariate dataset.

This algorithm was adapted from that presented in the paper below, which we highly recommend.

Malone BP, Minansy B, Brungard C. 2019. Some methods to improve the utility of conditioned Latin hypercube sampling. PeerJ 7:e6451 DOI 10.7717/peerj.6451

This algorithm:

  1. Determines the quantile distributions of existing sample units and mraster covariates.

  2. Determines quantiles where there is a disparity between sample units and covariates.

  3. Prioritizes sampling within those quantile to improve representation.

To use this function, user must first specify the number of quantiles (nQuant) followed by either the nSamp (total number of desired sample units to be added) or the threshold (sampling ratio vs. covariate coverage ratio for quantiles - default is 0.9) parameters.

#--- remove `type` variable from existing  - causes plotting issues ---#

existing <- existing %>% select(-type)

sample_ahels(
  mraster = mraster,
  existing = existing, # existing sample
  plot = TRUE
) # plot
#> Simple feature collection with 285 features and 7 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431150 ymin: 5337750 xmax: 438550 ymax: 5343210
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>      type.x zq90 pzabove2  zsd strata type.y  rule               geometry
#> 1  existing 7.18     27.0 1.86      1    new rule1 POINT (433950 5341310)
#> 2  existing 4.72     12.0 1.11      1    new rule1 POINT (438530 5338290)
#> 3  existing 4.43      7.1 1.03      1    new rule1 POINT (434190 5340910)
#> 4  existing 3.83     28.0 0.78      1    new rule1 POINT (433770 5340970)
#> 5  existing 8.93     70.0 2.20      1    new rule1 POINT (438030 5341010)
#> 6  existing 4.10     24.2 1.03      1    new rule1 POINT (435750 5342190)
#> 7  existing 3.36     32.7 0.60      1    new rule1 POINT (433070 5341590)
#> 8  existing 4.96      4.7 1.30      1    new rule1 POINT (433810 5340810)
#> 9  existing 7.67     94.1 1.67      1    new rule1 POINT (434310 5340790)
#> 10 existing 4.39     21.3 1.10      1    new rule1 POINT (434250 5339330)

TIP!

Notice that no threshold, nSamp, or nQuant were defined. That is because the default setting for threshold = 0.9 and nQuant = 10.

The first matrix output shows the quantile ratios between the sample and the covariates. A value of 1.0 indicates that the sample is representative of quantile coverage. Values > 1.0 indicate over representation of sample units, while < 1.0 indicate under representation.

sample_ahels(
  mraster = mraster,
  existing = existing, # existing sample
  nQuant = 20, # define 20 quantiles
  nSamp = 300
) # desired sample size
#> Simple feature collection with 500 features and 7 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431110 ymin: 5337710 xmax: 438550 ymax: 5343230
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>      type.x zq90 pzabove2  zsd strata type.y  rule               geometry
#> 1  existing 7.18     27.0 1.86      1    new rule1 POINT (433950 5341310)
#> 2  existing 4.72     12.0 1.11      1    new rule1 POINT (438530 5338290)
#> 3  existing 4.43      7.1 1.03      1    new rule1 POINT (434190 5340910)
#> 4  existing 3.83     28.0 0.78      1    new rule1 POINT (433770 5340970)
#> 5  existing 8.93     70.0 2.20      1    new rule1 POINT (438030 5341010)
#> 6  existing 4.10     24.2 1.03      1    new rule1 POINT (435750 5342190)
#> 7  existing 3.36     32.7 0.60      1    new rule1 POINT (433070 5341590)
#> 8  existing 4.96      4.7 1.30      1    new rule1 POINT (433810 5340810)
#> 9  existing 7.67     94.1 1.67      1    new rule1 POINT (434310 5340790)
#> 10 existing 4.39     21.3 1.10      1    new rule1 POINT (434250 5339330)

Notice that the total number of samples is 500. This value is the sum of existing units (200) and number of sample units defined by nSamp = 300.

sample_existing

Acknowledging that existing sample networks are common is important. There is significant investment into these samples, and in order to keep inventories up-to-date, we often need to collect new data for sample units. The sample_existing algorithm provides the user with methods for sub-sampling an existing sample network should the financial / logistical resources not be available to collect data at all sample units. The functions allows users to choose between algorithm types using (type = "clhs" - default, type = "balanced", type = "srs", type = "strat"). Differences in type result in calling internal sample_existing_*() functions (sample_existing_clhs() (default), sample_existing_balanced(), sample_existing_srs(), sample_existing_strat()). These functions are not exported to be used stand-alone, however they employ the same functionality as their sample_clhs() etc counterparts.

While using sample_existing(), should the user wish to specify algorithm specific parameters (e.g. algorithm = "lcube" in sample_balanced() or allocation = "equal" in sample_strat()), they can specify within sample_existing() as if calling the function directly.

I give applied examples for all methods below that are based on the following scenario:

  • We have a systematic sample where sample units are 200m apart.

  • We know we only have resources to sample 300 of them.

  • We have some ALS data available (mraster), which we can use to improve knowledge of the metric populations.

See our existing sample for the scenario below.

#--- generate existing samples and extract metrics ---#
existing <- sample_systematic(raster = mraster, cellsize = 200, plot = TRUE)


#--- sub sample using ---#
e <- existing %>%
  extract_metrics(mraster = mraster, existing = .)

sample_existing(type = "clhs")

The algorithm is unique in that it has two fundamental approaches:

  1. Sample exclusively using existing and the attributes it contains.
#--- sub sample using ---#
sample_existing(existing = e, nSamp = 300, type = "clhs")
#> Simple feature collection with 300 features and 3 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431112.7 ymin: 5337762 xmax: 438550.9 ymax: 5343234
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>      zq90 pzabove2  zsd                 geometry
#> 787 13.40     67.1 4.06 POINT (432110.2 5339843)
#> 28   4.43      4.2 1.06 POINT (438379.9 5340598)
#> 386 21.60     90.3 7.06 POINT (435344.8 5340213)
#> 744 22.40     70.8 5.82 POINT (432594.7 5341011)
#> 740 15.90     91.5 3.66 POINT (432537.7 5340213)
#> 194 10.30     91.1 2.36 POINT (436812.5 5338304)
#> 583  6.06     12.4 1.50 POINT (433777.4 5340726)
#> 32  21.40     77.1 5.76 POINT (438436.9 5341396)
#> 345  6.84     33.5 1.79 POINT (435900.5 5342379)
#> 899 16.30     93.0 4.89 POINT (431141.2 5340313)
  1. Sub-sampling using raster distributions

Our systematic sample of ~900 plots is fairly comprehensive, however we can generate a true population distribution through the inclusion of the ALS metrics in the sampling process. The metrics will be included in internal latin hypercube sampling to help guide sub-sampling of existing.

#--- sub sample using ---#
sample_existing(
  existing = existing, # our existing sample
  nSamp = 300, # desired sample size
  raster = mraster, # include mraster metrics to guide sampling of existing
  plot = TRUE
) # plot
#> Simple feature collection with 300 features and 3 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431126.9 ymin: 5337705 xmax: 438522.4 ymax: 5343234
#> Projected CRS: +proj=utm +zone=17 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs
#> First 10 features:
#>        zq90 pzabove2  zsd                 geometry
#> 91318 11.40     67.9 3.25 POINT (437425.3 5338461)
#> 91435 18.30     80.6 4.31   POINT (436556 5340327)
#> 91406 22.80     89.5 6.99 POINT (437140.2 5342892)
#> 91332 15.10     69.8 4.04 POINT (437653.2 5341653)
#> 91658 19.30     94.6 4.51 POINT (434803.3 5341054)
#> 91376 21.10     83.3 6.02 POINT (437225.7 5341282)
#> 92086 12.90     85.9 2.74 POINT (431454.7 5341895)
#> 91750 13.30     97.4 2.79 POINT (433905.6 5339715)
#> 91329  8.37     49.0 2.22 POINT (437596.2 5340855)
#> 91304  7.23     69.0 1.64 POINT (437824.2 5341239)

The sample distribution again mimics the population distribution quite well! Now lets try using a cost variable to constrain the sub-sample.

#--- create distance from roads metric ---#
dist <- calculate_distance(raster = mraster, access = access)
#--- sub sample using ---#
sample_existing(
  existing = existing, # our existing sample
  nSamp = 300, # desired sample size
  raster = dist, # include mraster metrics to guide sampling of existing
  cost = 4, # either provide the index (band number) or the name of the cost layer
  plot = TRUE
) # plot
#> Simple feature collection with 300 features and 4 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431127 ymin: 5337705 xmax: 438479.7 ymax: 5343234
#> Projected CRS: +proj=utm +zone=17 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs
#> First 10 features:
#>        zq90 pzabove2  zsd dist2access                 geometry
#> 91936 21.90     90.1 5.58   182.13042 POINT (432551.9 5340413)
#> 91659  5.66     62.2 1.30   111.08908 POINT (434817.6 5341254)
#> 91604  6.24     11.9 2.13    24.14474 POINT (435088.3 5339430)
#> 92093 22.00     97.6 4.65   444.52689   POINT (431127 5340113)
#> 91336 10.50     12.5 3.28   131.91483 POINT (437710.2 5342451)
#> 91595 11.20     92.4 2.62    21.53118 POINT (435558.5 5343206)
#> 91594 13.00     34.1 3.94    23.04094 POINT (435544.2 5343006)
#> 91600 21.20     98.5 5.17   187.60277 POINT (435031.4 5338632)
#> 91697  9.87     32.1 2.76    33.09837 POINT (434233.4 5338689)
#> 91734  3.39     11.3 0.74    96.56604 POINT (434219.1 5341296)

Finally, should the user wish to further constrain the sample based on access like other sampling approaches in sgsR that is also possible.

#--- ensure access and existing are in the same CRS ---#

sf::st_crs(existing) <- sf::st_crs(access)

#--- sub sample using ---#
sample_existing(
  existing = existing, # our existing sample
  nSamp = 300, # desired sample size
  raster = dist, # include mraster metrics to guide sampling of existing
  cost = 4, # either provide the index (band number) or the name of the cost layer
  access = access, # roads layer
  buff_inner = 50, # inner buffer - no sample units within this distance from road
  buff_outer = 300, # outer buffer - no sample units further than this distance from road
  plot = TRUE
) # plot
#> Simple feature collection with 300 features and 4 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431184 ymin: 5337705 xmax: 438494 ymax: 5343234
#> Projected CRS: +proj=utm +zone=17 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs
#> First 10 features:
#>        zq90 pzabove2  zsd dist2access                 geometry
#> 91418 19.60     95.8 4.67   222.69940 POINT (434931.6 5340042)
#> 91390  8.11     20.7 2.27   126.86679   POINT (435473 5342009)
#> 91383 27.40     90.6 8.26   116.52442 POINT (435259.3 5339016)
#> 91349  5.23     26.2 1.24   238.13652 POINT (435914.8 5339772)
#> 91599 13.10     61.2 3.63   174.55228 POINT (432323.9 5340028)
#> 91624 16.60     75.3 5.21   113.25232 POINT (431768.3 5337862)
#> 91517 17.90     94.1 2.36   149.59869 POINT (433691.8 5342336)
#> 91265  7.05     77.4 1.51    78.32536 POINT (437396.8 5338062)
#> 91646 19.80     81.9 3.76    43.31871 POINT (431711.1 5342678)
#> 91603 21.30     90.0 4.94    61.51195 POINT (432423.6 5341424)

TIP!

The greater constraints we add to sampling, the less likely we will have strong correlations between the population and sample, so its always important to understand these limitations and plan accordingly.

sample_existing(type = "balanced")

When type = "balanced" users can define all parameters that are found within sample_balanced(). This means that one can change the algorithm, p etc.

sample_existing(existing = e, nSamp = 300, type = "balanced")
#> Simple feature collection with 300 features and 3 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431112.7 ymin: 5337705 xmax: 438550.9 ymax: 5343220
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>     zq90 pzabove2  zsd                 geometry
#> 4  22.40     70.3 6.24 POINT (438422.7 5338389)
#> 7  10.60     28.5 3.41 POINT (438465.5 5338988)
#> 8  17.40     14.8 4.74 POINT (438479.7 5339187)
#> 10  9.84     94.2 2.43 POINT (438508.2 5339586)
#> 12 18.20     89.5 4.21 POINT (438536.7 5339985)
#> 13  7.78     27.8 2.06 POINT (438550.9 5340185)
#> 15  8.26     55.7 2.12 POINT (438194.7 5338005)
#> 16  2.99      8.3 0.68   POINT (438209 5338204)
#> 17  2.84     14.0 0.93 POINT (438223.2 5338404)
#> 20 17.30     91.5 4.29   POINT (438266 5339002)
sample_existing(existing = e, nSamp = 300, type = "balanced", algorithm = "lcube")
#> Simple feature collection with 300 features and 3 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431126.9 ymin: 5337705 xmax: 438550.9 ymax: 5343234
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>     zq90 pzabove2  zsd                 geometry
#> 1   4.77     11.9 1.12   POINT (438380 5337791)
#> 4  22.40     70.3 6.24 POINT (438422.7 5338389)
#> 11 16.10     95.1 3.37 POINT (438522.4 5339786)
#> 12 18.20     89.5 4.21 POINT (438536.7 5339985)
#> 14 13.90     61.2 3.62 POINT (438180.5 5337805)
#> 26 19.00     94.5 4.78 POINT (438351.4 5340199)
#> 27 17.60     81.2 5.15 POINT (438365.7 5340399)
#> 28  4.43      4.2 1.06 POINT (438379.9 5340598)
#> 32 21.40     77.1 5.76 POINT (438436.9 5341396)
#> 36 24.80     89.6 7.75 POINT (438493.9 5342194)

sample_existing(type = "srs")

The simplest, type = srs, randomly selects sample units.

sample_existing(existing = e, nSamp = 300, type = "srs")
#> Simple feature collection with 300 features and 3 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431112.7 ymin: 5337705 xmax: 438536.6 ymax: 5343234
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>     zq90 pzabove2  zsd                 geometry
#> 1  14.90     95.1 2.35 POINT (433364.1 5340555)
#> 2   2.84     14.0 0.93 POINT (438223.2 5338404)
#> 3  10.70     75.9 2.65 POINT (434304.5 5342493)
#> 4  17.60     82.2 4.08 POINT (436769.7 5340513)
#> 5   2.54      4.8 0.37   POINT (437867 5339031)
#> 6  25.20     89.9 9.00 POINT (437353.9 5343078)
#> 7  15.80     91.3 3.96 POINT (436313.8 5339743)
#> 8  16.20     88.6 4.03 POINT (436698.4 5342322)
#> 9  12.50     65.8 3.42 POINT (437154.4 5343092)
#> 10  6.08     45.4 1.49   POINT (436100 5342365)

sample_existing(type = "strat")

When type = "strat", existing must have an attribute named strata (just like how sample_strat() requires a strata layer). If it doesnt exist you will get an error. Lets define an sraster so that we are compliant.

sraster <- strat_kmeans(mraster = mraster, nStrata = 4)

e_strata <- extract_strata(sraster = sraster, existing = e)

When we do have a strata attribute, the function works very much the same as sample_strat() in that is allows the user to define the allocation method ("prop" - defaults, "optim", "manual", "equal").

#--- proportional stratified sampling of existing ---#
sample_existing(existing = e_strata, nSamp = 300, type = "strat", allocation = "prop")
#> Simple feature collection with 300 features and 4 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431112.7 ymin: 5337705 xmax: 438494 ymax: 5343234
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>    strata  zq90  pzabove2  zsd                 geometry
#> 1       4  2.73  2.700000 0.44 POINT (438080.7 5339216)
#> 2       4 10.30  6.100000 2.76 POINT (435430.3 5338603)
#> 3       4  5.71 54.700001 1.26 POINT (432751.3 5343206)
#> 4       4  2.60  3.300000 0.41 POINT (438408.5 5338190)
#> 5       4  2.95  3.800000 0.52 POINT (434475.6 5339273)
#> 6       4  4.42  5.300000 0.93 POINT (437938.1 5342835)
#> 7       4  4.33 26.200001 1.34 POINT (434019.6 5341311)
#> 8       4  5.20  9.400001 1.28 POINT (435658.3 5341795)
#> 9       4  3.21 15.200000 0.61 POINT (434489.8 5342279)
#> 10      4  6.49 34.200001 1.74 POINT (434318.8 5342693)

TIP!

Remember that when allocation = "equal", the nSamp value will be allocated for each strata.

We get 400 sample units in our output below because we have 4 strata and nSamp = 100.

#--- equal stratified sampling of existing ---#
sample_existing(existing = e_strata, nSamp = 100, type = "strat", allocation = "equal")
#> Simple feature collection with 400 features and 4 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431141.2 ymin: 5337705 xmax: 438536.7 ymax: 5343234
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>    strata  zq90  pzabove2  zsd                 geometry
#> 1       4  5.46 40.400002 1.38 POINT (435330.5 5342821)
#> 2       4 10.50  7.000000 2.69 POINT (435359.1 5340413)
#> 3       4  2.72 16.600000 0.43 POINT (437752.9 5343049)
#> 4       4  4.25  5.400000 0.97 POINT (433563.6 5340541)
#> 5       4  5.20  9.400001 1.28 POINT (435658.3 5341795)
#> 6       4  2.84 14.000000 0.93 POINT (438223.2 5338404)
#> 7       4  3.08  1.800000 0.57 POINT (433919.8 5342721)
#> 8       4  0.00  0.000000 0.00 POINT (436014.6 5338361)
#> 9       4 11.40 27.800001 3.08 POINT (435230.9 5338617)
#> 10      4  2.86  8.500000 0.47 POINT (436142.7 5342963)
#--- manual stratified sampling of existing with user defined weights ---#
s <- sample_existing(existing = e_strata, nSamp = 100, type = "strat", allocation = "manual", weights = c(0.2, 0.6, 0.1, 0.1))

We can check the proportion of samples from each strata with:

#--- check proportions match weights ---#
table(s$strata) / 100
#> 
#>   1   2   3   4 
#> 0.2 0.6 0.1 0.1

Finally, type = "optim allows for the user to define a raster metric to be used to optimize within strata variances.

#--- manual stratified sampling of existing with user defined weights ---#
sample_existing(existing = e_strata, nSamp = 100, type = "strat", allocation = "optim", raster = mraster, metric = "zq90")
#> Simple feature collection with 100 features and 4 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 431198.3 ymin: 5337805 xmax: 438522.4 ymax: 5343177
#> Projected CRS: UTM Zone 17, Northern Hemisphere
#> First 10 features:
#>    strata  zq90  pzabove2  zsd                 geometry
#> 1       4 10.80 19.700001 3.03   POINT (438095 5339415)
#> 2       4  3.20  6.000000 0.58 POINT (438280.2 5339202)
#> 3       4  7.08 36.400002 1.78 POINT (436613.1 5338318)
#> 4       4  5.20  9.400001 1.28 POINT (435658.3 5341795)
#> 5       4  5.56 16.100000 1.37   POINT (435701 5342394)
#> 6       4  3.18  2.800000 0.64 POINT (435045.6 5338831)
#> 7       4  9.18 29.400000 2.46 POINT (433079.1 5342180)
#> 8       4  6.32 51.200001 1.68 POINT (435116.8 5342636)
#> 9       4  3.27 21.400000 0.59 POINT (433036.4 5341581)
#> 10      4  0.00  0.000000 0.00 POINT (435829.3 5338575)

We see from the output that we get 300 sample units that are a sub-sample of existing. The plotted output shows cumulative frequency distributions of the population (all existing samples) and the sub-sample (the 300 samples we requested).