CODE:

Get the code used for this section here

Example of two-step digital soil mapping

Pre-reading background

The motivation for this section is to gain some insights into a digital soil mapping approach that uses a combination of both continuous and categorical attribute modeling. Subsequently, we will build on the efforts of the training materials that dealt with each of these type of modeling approaches separately.

There are some situations where, a combinatorial modeling approach might be suitable in a digital soil mapping work flow. An example of such a workflow is in Malone et al. (2015) in regards to the prediction of soil thickness. The context behind that approach was that often lithic contact was not achieved during the soil survey activities, effectively indicating soil depth was greater than the soil probing depth (which was 1.5 m). Where lithic contact was found, the resulting depth was recorded. The difficulty in using this data in the raw form was that there were many sites where soil depth was greater than 1.5 m together with actual recorded soil depth measurements. The nature of this type of data is likened to a zero-inflated distribution, where many zero observations are recorded among actual measurements (Sileshi 2008).

In Malone et al. (2015) the zero observations were attributed to soil depth being greater than 1.5m. They therefore performed the modeling in two stages. First modeling involved a categorical or binomial model of soil depth being greater than 1.5 m or not. This was followed by continuous attribute modeling of soil depth using the observations where lithic contact was recorded. While the approach was a reasonable solution, it may be the case that the frequency of recorded measurements is low, meaning that the spatial modeling of the continuous attribute is made under considerable uncertainty, as was the case in Malone et al. (2015) with soil depth and other environmental variables spatially modeled in that study; for example, the frequency of winter frosts.

Another interesting example of a combinatorial DSM work flow was described in Gastaldi et al. (2012) for the mapping of occurrence and thickness of soil horizons. There they used a multinomial logistic model to predict the presence or absence of the given soil horizon class, followed by continuous attribute modeling of the horizon depths. For the purposes a demonstrating the work flow of this combinatorial or two-stage DSM, we will re-visit the approach that is described by Gastaldi et al. (2012) and work through the various steps needed to perform it within R.

The data we will use comes from 1342 soil profile and core descriptions from the Lower Hunter Valley, NSW Australia. These data have been collected on an annual basis since 2001 to present. These data are distributed across the 220km2 area as shown in Figure below. The intention is to use these data first to predict the occurrence of given soil horizon classes (following the nomenclature of the Australian Soil Classification (Isbell 2002). Specifically we want to prediction the spatial distribution of the occurrence of A1, A2, AP, B1, B21, B22, B23, B24, BC, and C horizons, and then where those horizons occur, predict their depth.

Back to top

Data preparation

First lets perform some data discovery both in terms of the soil profile data and spatial covariates to be used as predictor variables and to inform the spatial mapping.

The point data used for this exercise can be downloaded from here and then loaded into the R environment as below.

You will notice the soil profile data datis arranged in a flat file where each row is a soil profile. There are many columns of information which include profile identifier and spatial coordinates. Then there are 11 further columns that are binary indicators of whether a horizon class is present or not (indicated as 1 and 0 respectively). The following 11 columns after the binary columns indicate the horizon depth for the given horizon class.

# load libraries that are used in this exercise
library(ithir)
library(sf)
library(terra)
library(aqp)
library(nnet)
library(MASS)
library(quantregForest)

# data
load("HV_horizons.rda")
str(dat)

## 'data.frame':    1342 obs. of  25 variables:
##  $ FID : Factor w/ 1342 levels "1","10","100",..: 1 2 3 4 5 6 7 8 9 10 ...
##  $ e   : num  338014 338183 341609 341352 339736 ...
##  $ n   : num  6370646 6370550 6370437 6370447 6370439 ...
##  $ A1  : int  1 0 1 1 1 1 1 0 1 1 ...
##  $ A2  : int  1 0 0 0 0 1 1 0 0 0 ...
##  $ AP  : int  0 1 0 0 0 0 0 1 0 0 ...
##  $ B1  : int  0 0 1 1 1 0 0 0 1 0 ...
##  $ B21 : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ B22 : int  1 0 1 0 1 1 1 1 1 1 ...
##  $ B23 : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ B24 : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ B3  : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ BC  : int  0 0 0 0 0 0 0 0 1 1 ...
##  $ C   : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ A1d : num  21 NA 17 45 20 25 13 NA 10 44 ...
##  $ A2d : num  27 NA NA NA NA 15 13 NA NA NA ...
##  $ APd : num  NA 40 NA NA NA NA NA 35 NA NA ...
##  $ B1d : num  NA NA 25 25 30 NA NA NA 30 NA ...
##  $ B21d: num  26 60 26 30 30 25 58 40 20 38 ...
##  $ B22d: num  26 NA 32 NA 20 20 16 25 25 18 ...
##  $ B23d: int  NA NA NA NA NA NA NA NA NA NA ...
##  $ B24d: int  NA NA NA NA NA NA NA NA NA NA ...
##  $ B3d : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ BCd : int  NA NA NA NA NA NA NA NA 15 NA ...
##  $ Cd  : int  NA NA NA NA NA NA NA NA NA NA ...

# Change cases of very thin A1 horizons to having no A1 horizon needed for
# purposes of demonstrating workflow rather than advancing scientific inquiry
dat$A1[dat$A1d <= 15] <- 0

# convert point to spatial object
dat <- sf::st_as_sf(x = dat, coords = c("e", "n"))

At our disposal are several covariates that have been derived entirely from a digital elevation model. These data are available from the ithir package and can be loaded as below.

# grids (covariate rasters from ithir package)
hv.rasters <- list.files(path = system.file("extdata/", package = "ithir"), pattern = "hunterCovariates_A", full.names = T)

# read them into R as SpatRaster objects
hv.rasters <- terra::rast(hv.rasters)
## raster properties
hv.rasters

## class       : SpatRaster 
## dimensions  : 860, 675, 8  (nrow, ncol, nlyr)
## resolution  : 25, 25  (x, y)
## extent      : 334497.3, 351372.3, 6362628, 6384128  (xmin, xmax, ymin, ymax)
## coord. ref. :  
## sources     : hunterCovariates_A_AACN.tif  
##               hunterCovariates_A_elevation.tif  
##               hunterCovariates_A_Hillshading.tif  
##               ... and 5 more source(s)
## names       : hunte~_AACN, hunte~ation, hunte~ading, hunte~ation, hunte~MRVBF, hunte~Slope, ... 
## min values  :      0.0000,     27.3035,      0.0000,    988.5308,    0.000000,     0.00100, ... 
## max values  :    211.1496,    322.1442,     62.6977,   1969.7966,    6.975086,    36.21008, ...

For a quick check, lets overlay the soil profile points onto the DEM. You will notice on the Figure below the area of concentrated soil survey (which represents locations of annual survey) within the extent of a regional scale soil survey across the whole study area.

## Overlay points onto raster
plot(hv.rasters[[2]])
points(dat, pch = 20)
rconsole
Hunter Valley soil profiles locations overlaying digital elevation model.

The last preparatory step we need to take is the covariate intersection of the soil profile data, and remove any sites that are outside the mapping extent or simply do not have the full complement of covariate data.

# Covariate extract for points
ext <- terra::extract(x = hv.rasters, y = dat, bind = T, method = "simple")
dsm.dat <- as.data.frame(ext)

# remove sites with missing covariates
dsm.dat <- dsm.dat[complete.cases(dsm.dat[, 24:31]), ]

Back to top

Two-stage model fitting and evaluation

The two-stage modeling work flow for the A1 horizon modelling is stepped through below. This process would be repeated for the other horizon occurrences and their thicknesses. Some model evaluations and model outcomes will be provided below to advance the general discussion and assess how outputs might be combined to look at full soil profiles and their horizon compositions. Moreover, the applicability the estimated soil profile compositions can also be compared against their observed counterparts for the purposes of evaluation.

First we want to subset 75% of the data for calibrating models, and keeping the rest aside for out-of-bag model evaluation.

## Set target variable and subset data for validation A1 Horizon
dsm.dat$A1 <- as.factor(dsm.dat$A1)
summary(dsm.dat$A1)

##   0   1 
## 712 619

# random subset
set.seed(123)
training <- sample(nrow(dsm.dat), 0.75 * nrow(dsm.dat))
# calibration dataset
dsm.cal <- dsm.dat[training, ]
# out-of-bag model evaluation data
dsm.val <- dsm.dat[-training, ]

Back to top

Model for predicting horizon occurence

We first want to model the presence/absence in this case of the A1 horizon. We will use a multinomial model, followed up with a stepwise regression procedure in order to remove potentially redundant predictor variables.

# A1 presence or absence model
mn1 <- nnet::multinom(formula = A1 ~ hunterCovariates_A_AACN + hunterCovariates_A_elevation + hunterCovariates_A_Hillshading + hunterCovariates_A_light_insolation + hunterCovariates_A_MRVBF + hunterCovariates_A_Slope + hunterCovariates_A_TRI + hunterCovariates_A_TWI, data = dsm.cal)

# stepwise variable selection
mn2 <- stepAIC(mn1, direction = "both", trace = FALSE)

summary(mn2)

## Call:
## nnet::multinom(formula = A1 ~ hunterCovariates_A_elevation + 
##     hunterCovariates_A_MRVBF, data = dsm.cal)
## 
## Coefficients:
##                                    Values   Std. Err.
## (Intercept)                  -0.880315066 0.308536659
## hunterCovariates_A_elevation  0.004513106 0.002350626
## hunterCovariates_A_MRVBF      0.175753835 0.054693734
## 
## Residual Deviance: 1366.553 
## AIC: 1372.553

We use the goofcat function from ithir to evaluate the model quality both in terms of the calibration and out-of-bag data.

## model Goodness of fit calibration
mod.pred <- predict(mn2, newdata = dsm.cal, type = "class")
goofcat(observed = dsm.cal$A1, predicted = mod.pred)

## $confusion_matrix
##     0   1
## 0 441 347
## 1  98 112
## 
## $overall_accuracy
## [1] 56
## 
## $producers_accuracy
##  0  1 
## 82 25 
## 
## $users_accuracy
##  0  1 
## 56 54 
## 
## $kappa
## [1] 0.06479926

# validation
val.pred <- predict(mn2, newdata = dsm.val, type = "class")
goofcat(observed = dsm.val$A1, predicted = val.pred)

## $confusion_matrix
##     0   1
## 0 143 117
## 1  30  43
## 
## $overall_accuracy
## [1] 56
## 
## $producers_accuracy
##  0  1 
## 83 27 
## 
## $users_accuracy
##  0  1 
## 56 59 
## 
## $kappa
## [1] 0.097328

It is clear that the mn2 model appears to be moderately effective to distinguish sites where the A1 horizon is absent with the given data and predictive covariates.

Back to top

Model for predicting horizon thickness

What we want to do now is to model the A1 horizon depth.

We will be using an alternative model to those that have been examined in this book so far. The is a quantile regression forest, which is a generalized implementation of the random forest model from Breiman (2001).

The algorithm is available via the quantregForest package, and further details about the model can be found at Meinshausen (2006). The caret package also interfaces with this model too.

Fundamentally, random forests are integral to the quantile regression algorithm. However, the useful feature and advancement from normal random forests is the ability to infer the full conditional distribution of a response variable. This facility is useful for building non-parametric prediction intervals for any given level of confidence information and also the ability to detect outliers in the data easily.

Quantile regression used via the quanregForest algorithm is implemented in the chapter simply to demonstrate the wide availability of prediction models and machine learning methods that can be used in digital soil mapping.

Getting the model initiated we first need to perform some preparatory tasks. Namely the removal of missing data from the available data set i.e those cases where no A1 horizon exists as the horizon thickness data is currently give the value NA.

# Remove missing values calibration
mod.dat <- dsm.cal[!is.na(dsm.cal$A1d), ]

# out of bag cases
val.dat <- dsm.val[!is.na(dsm.val$A1d), ]

It is useful to check the inputs required for the quantile regression forests (using the help file); however its parameterization is largely similar to other models that have been used already in this book, particularly those for the random forest models.

# Fit quantile regression forest model
qrf <- quantregForest(x = mod.dat[, 24:31], y = mod.dat$A1d, importance = TRUE)
qrf

## 
##  Call:
##  quantregForest(x = mod.dat[, 24:31], y = mod.dat$A1d, importance = TRUE) 
## 
##                      Number of trees: 500
## No. of variables tried at each split: 2

# variable importances
importance(qrf)

##                                       %IncMSE IncNodePurity
## hunterCovariates_A_AACN              6.626844      12234.84
## hunterCovariates_A_elevation        14.208151      12891.47
## hunterCovariates_A_Hillshading      15.045393      10780.55
## hunterCovariates_A_light_insolation  8.411560      12251.45
## hunterCovariates_A_MRVBF            16.520712      13731.85
## hunterCovariates_A_Slope            13.848516      10840.27
## hunterCovariates_A_TRI              17.047087      11984.96
## hunterCovariates_A_TWI               7.802111      11952.38

Naturally, the best test of the model is to evaluate it against out-of-bag cases. In addition to our normal validation procedure we can also derive the PICP for the validation data too.

## model Goodness of fit Calibration
quant.cal <- predict(qrf, newdata = mod.dat[, 24:31], all = T)
goof(observed = mod.dat$A1d, predicted = quant.cal[, 2], plot.it = T)

##          R2 concordance      MSE     RMSE       bias
## 1 0.8969432   0.9040645 18.42857 4.292851 -0.7730415

# out of bag cases
quant.val <- predict(qrf, newdata = val.dat[, 24:31], all = T)
goof(observed = val.dat$A1d, predicted = quant.val[, 2], plot.it = T)

##           R2 concordance     MSE     RMSE      bias
## 1 0.03717959  0.08385013 156.605 12.51419 -3.398577

# Prediction interval coverage probability
sum(quant.val[, 1] <= val.dat$A1d & quant.val[, 2] >= val.dat$A1d)/nrow(val.dat)

## [1] 0.366548

Based on the outputs above, the calibration model seems a reasonable outcome for the model, but is proven to be considerably overfitted when it is evaluated aginst the out-of-bag cases. We should also be expecting a PICP close to 90%, but this is clearly not the case above.

What has been covered above for the two-stage modeling is repeated for all the other soil horizons, with the main results displayed in the Table below. These statistics are reported based on the out-of-bag data.

It clear that there is a considerable amount of uncertainty overall in the various soil horizon models. For some horizons the results are a little encouraging; for example the model to predict the presence of a BC horizon is quite good. It is clear however that distinguishing between different B2 horizons is challenging. However predicting the presence or absence of a B22 horizons seems acceptable.

rconsole
Selected model validation diagnostics returned for each horizon class and associated depth model.

Back to top

Soil profile reconstructions

Another way to assess the quality of the two-stage modeling is to assess first the number of soil profile that have matching sequences of soil horizon types. The following are two associated data sets. The firstare out-of-bag observed soil profiles with their horizon designations and associated thicknesses. The other are the corresponding profiles that have been predicted using the above two-step modeling processes.

# observed soil profiles
obs.profs <- read.table(file = "validation_obs.txt", sep = ",", header = T)

# associated predicted soil profiles
pred.profs <- read.table(file = "validation_outs.txt", sep = ",", header = T)

Below we can compare row for row the observed vs. the predicted in very much a manual way. Using the match function we can quantify horizon and horizon matches, or as below full sol profile soil horizon sequence matching.

# Validation data horizon observations (1st 3 rows)
obs.profs[1:3, c(1, 4:14)]

##    FID A1 A2 AP B1 B21 B22 B23 B24 B3 BC C
## 1  101  1  0  0  1   1   0   0   0  0  0 0
## 2 1022  0  0  1  0   1   1   0   0  0  0 0
## 3 1026  1  0  0  0   1   1   0   0  0  0 0

# Associated model predictions (1st 3 rows)
pred.profs[1:3, 1:12]

##   dat.V.FID a1 a2 ap b1 b21 b22 b23 b24 b3 bc c
## 1       101  1  0  0  0   1   1   0   0  0  0 0
## 2      1022  1  0  0  0   1   1   0   0  0  0 0
## 3      1026  1  0  0  0   1   1   0   0  0  0 0

# matched soil profiles
sum(obs.profs$A1 == pred.profs$a1 & obs.profs$A2 == pred.profs$a2 & obs.profs$AP ==
pred.profs$ap & obs.profs$B1 == pred.profs$b1 & obs.profs$B21 == pred.profs$b21 &
obs.profs$B22 == pred.profs$b22 & obs.profs$B23 == pred.profs$b23 & obs.profs$B24 == pred.profs$b24 & obs.profs$B3 == pred.profs$b3 & obs.profs$BC == pred.profs$bc & obs.profs$C == pred.profs$c)/nrow(obs.profs)

## [1] 0.2222222

The result above indicates that just over 20% of soil profiles have matched sequences of horizons. We can examine visually a few of these matched profiles to examine whether there is much coherence in terms of observed and associated predicted horizon depths. We will select out two soil profiles: One with an AP horizon, and the other with an A1 horizon. We can demonstrate this using the aqp package, which is a dedicated R package for handling soil profile data collections.

# Subset of matching data (observations)
match.dat <- obs.profs[which(obs.profs$A1 == pred.profs$a1 & obs.profs$A2 == pred.profs$a2 & obs.profs$AP == pred.profs$ap & obs.profs$B1 == pred.profs$b1 & obs.profs$B21 == pred.profs$b21 & obs.profs$B22 == pred.profs$b22 & obs.profs$B23 == pred.profs$b23 & obs.profs$B24 == pred.profs$b24 & obs.profs$B3 == pred.profs$b3 & obs.profs$BC == pred.profs$bc & obs.profs$C == pred.profs$c), ]

# Subset of matching data (predictions)
match.dat.P <- pred.profs[which(obs.profs$A1 == pred.profs$a1 & obs.profs$A2 == pred.profs$a2 & obs.profs$AP == pred.profs$ap & obs.profs$B1 == pred.profs$b1 & obs.profs$B21 == pred.profs$b21 & obs.profs$B22 == pred.profs$b22 & obs.profs$B23 == pred.profs$b23 & obs.profs$B24 == pred.profs$b24 & obs.profs$B3 == pred.profs$b3 & obs.profs$BC == pred.profs$bc & obs.profs$C == pred.profs$c), ]

Now we just select any row where we know there is and AP horizon

match.dat[49, ]  #observation

##     FID      e       n A1 A2 AP B1 B21 B22 B23 B24 B3 BC C A1d A2d APd B1d B21d
## 195 642 338096 6372259  0  0  1  0   1   1   0   0  0  1 0  NA  NA  10  NA   30
##     B22d B23d B24d B3d BCd 

match.dat.P[49, ]  #prediction

##     dat.V.FID a1 a2 ap b1 b21 b22 b23 b24 b3 bc c a1d a2d      apd b1.1 b21d
## 195       642  0  0  1  0   1   1   0   0  0  1 0  18  16 21.92308   18   31
##     b22d b23d     b24d b3d bcd cd
## 195   27   20 15.41176  NA  32 NA

We can see in these two profiles, the sequence of horizons is AP, B21, B22, BC. Now we just need to upgrade the data to a soil profile collection.

Using the horizon classes together with the associated depths, we want to plot both soil profiles for comparison. First we need to create a data frame of the relevant data then upgrade to a soilProfileCollection, then finally plot.

The script below demonstrates this for the soil profile with the AP horizon. The same can be done with the associated soil profile with the A1 horizon. The plot of the is shown in the Figure below.

# Horizon classes
H1 <- c("AP", "B21", "B22", "BC")

# Extract horizon depths then combine to create soil profiles
p1 <- c(22, 31, 27, 32)
p2 <- c(10, 30, 15, 45)
p1u <- c(0, (0 + p1[1]), (0 + p1[1] + p1[2]), (0 + p1[1] + p1[2] + p1[3]))
p1l <- c(p1[1], (p1[1] + p1[2]), (p1[1] + p1[2] + p1[3]), (p1[1] + p1[2] + p1[3] + p1[4]))
p2u <- c(0, (0 + p2[1]), (+p2[1] + p2[2]), (+p2[1] + p2[2] + p2[3]))
p2l <- c(p2[1], (p2[1] + p2[2]), (p2[1] + p2[2] + p2[3]), (p2[1] + p2[2] + p2[3] +
p2[4]))

# Upper depths
U1 <- c(p1u, p2u)
# Lower depths
L1 <- c(p1l, p2l)

# Soil profile names
S1 <- c("predicted profile", "predicted profile", "predicted profile", "predicted profile", "observed profile", "observed profile", "observed profile", "observed profile")

# Random soil colors selected to distinguish between horizons
hue <- c("10YR", "10R", "10R", "10R", "10YR", "10R", "10R", "10R")
val <- c(4, 5, 7, 6, 4, 5, 7, 6)
chr <- c(3, 8, 8, 1, 3, 8, 8, 1)

# Combine all the data
TT1 <- data.frame(S1, U1, L1, H1, hue, val, chr)

# Convert munsell colors to rgb
TT1$soil_color <- with(TT1, munsell2rgb(hue, val, chr))

# Upgrade to soil profile collection
aqp::depths(TT1) <- S1 ~ U1 + L1

# Plot observed and associated predicted soil profiles
plot(TT1, name = "H1", colors = "soil_color")
title("Selected soil with AP horizon", cex.main = 0.75)
rconsole
Examples of observed soil profiles with associated predicted profiles from the two-stage horizon class and horizon depth model.

Soil is very complex, and while there is a general agreement between observed and associated predicted soil profiles, the power of the models used in this two-stage example has certainly been challenged. Recreating the arrangement of soil horizons together with maintenance of their depth properties is an interesting problem for pedometric studies and one that is likely to be pursued with vigor as better methods become available.

Back to top

Spatial extension of the two-stage soil horizon occurrence and depth model

We will recall from previous chapters the process for applying prediction models across a mapping extent.

In the case of the two-stage model the mapping work flow if first creating the map of horizon presence/occurrence.

Then we apply the horizon depth model.

In order to ensure that the depth model is not applied to the areas where a particular soil horizon is predicted as being absent, those areas are masked out.

Maps for the presence of the A1 and AP horizons and their respective depths are displayed in the Figure below. The following scripts show the process of applying the two-stage model for the A1 horizon.

## MAPPING Apply A1 horizon presence/absence model spatially
A1.class <- terra::predict(object = hv.rasters, model = mn2)
A1.class
plot(A1.class, main = "A1-horizon occurence")

# Apply A1 horizon depth model spatially get covariates into a table
tempD <- data.frame(cellNos = seq(1:terra::ncell(hv.rasters)))
vals <- as.data.frame(terra::values(hv.rasters))
tempD <- cbind(tempD, vals)
tempD <- tempD[complete.cases(tempD), ]
cellNos <- c(tempD$cellNos)
gXY <- data.frame(terra::xyFromCell(hv.rasters, cellNos))
tempD <- cbind(gXY, tempD)
str(tempD)

# extend model to covariates
A1.depth <- predict(newdata = tempD, object = qrf)
# append coordinates
A1.depth.map <- cbind(data.frame(tempD[, c("x", "y")]), A1.depth)
# create raster
A1.depth.map <- terra::rast(x = A1.depth.map[, c(1, 2, 4)], type = "xyz")
plot(A1.depth.map, main = "A1-horizon thickness (unmasked)")

# Mask out areas where horizon is absent
msk <- terra::ifel(A1.class != 1, NA, 1)
plot(msk)
A1.depth.masked <- terra::mask(A1.depth.map, msk, inverse = T)
plot(A1.depth.masked, main = "A1-horizon thickness (masked)")
rconsole
Predicted occurrence of AP and A1 horizons, and their respective depths in the Lower Hunter Valley, NSW..

This figure displays an interesting pattern whereby AP horizons occur where A1 horizons do not. This makes reasonable sense. The spatial pattern of the AP horizon coincides generally with the distribution of vineyards across the study area, where soils are often cultivated and consequently removing the presence of the A1 horizon. Increased depth of A1 horizons also appears to be the case too in lower lying and stream channel catchments of the study area.

Back to top

References

Breiman, L. 2001. “Random Forests.” Machine Learning 41: 5–32.

Gastaldi, G., Budiman Minasny, and Alex B. McBratney. 2012. “Mapping the Occurrence and Thickness of Soil Horizons.” Edited by Budiman Minasny, Brendan P. Malone, and Alex B. McBratney. London: Taylor & Francis.

Isbell, R. F. 2002. The Australian Soil Classification: Revised Edition. Collingwood, VIC: CSIRO Publishing.

Malone, Brendan P., Darren B. Kidd, Budiman Minasny, and Alex B. McBratney. 2015. “Taking Account of Uncertainties in Digital Land Suitability Assessment.” PeerJ, 3:e1366.

Meinshausen, Nicolai. 2006. “Quantile Regression Forests.” Journal of Machine Learning Research 7: 983–99.

Sileshi, Gudeta. 2008. “The Excess-Zero Problem in Soil Animal Count Data and Choice of Appropriate Models for Statistical Inference.” Pedobiologia 52 (1): 1–17. https://doi.org/http://dx.doi.org/10.1016/j.pedobi.2007.11.003.

Updated on Smart Digital Agriculture