Skip to contents

Sensitivity analysis quantifies how changes in an input affect the model output. In risk analysis, it can be used to identify which uncertain inputs are most important for a decision-relevant output. This helps to understand the model behaviour before running it with real data, to prioritise where to intervene to reduce risk, and to increase efforts to reduce uncertainty in the most sensitive inputs.

In mcmodule, you can perform sensitivity analysis using different methods:

  • Scenario (“what-if”) analysis: Compares output changes under targeted input variations, practical for model runs using real data. It does not produce sensitivity indices, but it can be useful for complex models where the most important inputs depend on context.
  • Correlation (tornado) analysis: Ranking of influential inputs by correlating each input uncertainty with output uncertainty across existing runs. Only captures inputs that are uncertain in those runs. It can be applied to multivariate inputs, but interpretation can be more complex when correlations vary across variates.
  • Sample design based sensitivity analysis: Evaluates the model on a predefined input design (e.g., Morris/Sobol) to estimate sensitivity over a chosen input space using packages such as sensitivity(Iooss et al. 2025) or sensobol (Puy et al. 2022)and yield sensitivity indices. In mcmodule, this method is currently limited to one variate.

Scenario analysis

Scenario analysis (or “what-if analysis”) is a non-statistical but still useful approach to assess input sensitivity. It does not produce sensitivity indices, but it will show how the output changes when you make specific changes to the inputs. This makes it a practical way to compare targeted input changes using real input data. It is especially helpful for complex models where the most important inputs depend on context (for example, when estimating disease-entry risk across countries, where certain pathways will be more important in some countries than in others). Scenario analysis can therefore help you identify the most sensitive inputs for a particular use case. This is covered in the working with what-if scenarios section.

Correlation analysis

mcmodule_corr() ranks inputs by their correlation with a chosen output across the current model runs. It is an extension of mc2d::tornado() from mc2d (Pouillot and Delignette-Muller 2010) for mcmodule objects and multiple variates.

Unlike methods that require a sample design, this approach works directly on an mcmodule built from model input data. Note that mcmodule_corr() only computes correlations for uncertain inputs (stochastic nodes built with func) and, when variates_as_nsv = TRUE, also for multivariate inputs.

When we correlate specific model input data distributions with outputs, the particular input distributions used in each variate do not necessarily capture the full input sample space. As a result, these correlations indicate where output uncertainty “comes from”, rather than which inputs the output is most sensitive to. A weak correlation does not necessarily mean an input is unimportant (e.g., symmetric non-linear effects can cancel out), and a strong correlation may reflect confounding with other correlated inputs.

Here we compute inputs-output correlations using Spearman coefficient:

# Calculate output (expected number of animals not detected)
imports_mcmodule_corr <- trial_totals(
  imports_mcmodule,
  mc_names = c("no_detect"),
  trials_n = "animals_n",
  subsets_n = "farms_n",
  subsets_p = "h_prev",
  mctable = imports_mctable
)

# Calculate correlations using Spearman's method (default)
corr_results <- mcmodule_corr(imports_mcmodule_corr, output = "no_detect_set_n", method = "spearman")
#> 
#> === Correlation Analysis Summary ===
#> 
#> Analysis Parameters:
#> - Analysis type: Global output
#> - Output node: no_detect_set_n
#> - Correlation method(s): spearman
#> - Missing value handling: all.obs
#> 
#> Expression Information:
#> - Module: mcmodule
#>   - Expression: imports
#>     - Input nodes: w_prev, test_sensi, animals_n, h_prev
#>     - Variates analyzed: 6
#> 
#> Results Summary:
#> - Total correlations calculated: 24
#> - Top 4 most correlated inputs (by absolute mean correlation):
#>   1. h_prev: 0.7251
#>   2. w_prev: 0.4256
#>   3. animals_n: 0.3073
#>   4. test_sensi: -0.1436
#> 
#> Input Correlation Strength Distribution:
#> - Very strong: 2 (8.3%)
#> - Strong: 4 (16.7%)
#> - Moderate: 6 (25.0%)
#> - Weak: 5 (20.8%)
#> - Very weak/None: 7 (29.2%)
#> 
#> Inputs by Correlation Strength:
#> - Very strong: h_prev
#> - Strong: w_prev, h_prev, test_sensi
#> - Moderate: animals_n, h_prev, w_prev
#> - Weak: w_prev, animals_n
#> - Very weak/None: test_sensi, animals_n

In this example, h_prev is the main driver of no_detect_set_n (very strong positive association). w_prev and animals_n also show positive correlation, while test_sensi is close to zero or slightly negative across variates.

# View correlation results
head(corr_results)
#>       exp exp_n variate          output      input        value   method
#> 1 imports     1       1 no_detect_set_n     w_prev  0.685231942 spearman
#> 2 imports     1       1 no_detect_set_n test_sensi  0.004825929 spearman
#> 3 imports     1       1 no_detect_set_n  animals_n  0.461132712 spearman
#> 4 imports     1       1 no_detect_set_n     h_prev  0.511269102 spearman
#> 5 imports     1       2 no_detect_set_n     w_prev  0.268298384 spearman
#> 6 imports     1       2 no_detect_set_n test_sensi -0.016015960 spearman
#>       use   module pathogen origin       strength
#> 1 all.obs mcmodule        a   nord         Strong
#> 2 all.obs mcmodule        a   nord Very weak/None
#> 3 all.obs mcmodule        a   nord       Moderate
#> 4 all.obs mcmodule        a   nord       Moderate
#> 5 all.obs mcmodule        a  south           Weak
#> 6 all.obs mcmodule        a  south Very weak/None

Then visualise the ranking with a tornado plot. It can be customised with standard ggplot2 layers.

library(ggplot2)

# Plot tornado plot of correlations
mcmodule_tornado(imports_mcmodule_corr, output = "no_detect_set_n", method = "spearman", print_summary = FALSE)

Each point in the plot represents one input variate. The coloured point marks the variate with the largest absolute correlation for that input, and that value is used to rank the inputs. The black point shows the median correlation across variates, which is often a useful summary when correlations vary across the model. The black horizontal line shows the full range of correlations across variates, so you can see whether an input is consistently influential or only influential in certain variates.

Note that farms_n and test_origin are not shown in this analysis because they are fixed in the original model data and therefore have zero correlation with the output. This is an important limitation of correlation-based methods using real input data: they can only identify influential inputs that vary in the original model runs. To explore uncertainty for fixed inputs, you need a method that evaluates a sample design.

Sensitivity analysis with sample design

Create a sample design

An input sample space is the allowable range of values it can take (or distribution definition). A sample design is a specific set of sampled input combinations used to run the model. The sample space defines where values can come from, while the sample design defines which values are actually used in model runs.

A sample_design in mcmodule can be either:

  • a matrix/data frame, or
  • a sensitivity object from the sensitivity::sensitivity package (for example, a Morris design object) that has an X element containing the sampling matrix.

The sampling matrix must have one column per input node and one row per sample. Each row is one scenario in the mcnode uncertainty dimension. When you provide a sample_design, that uncertainty dimension is no longer sampled from the original model inputs; it is mapped to the values in your design. Currently, sample_design supports one variate.

Inputs used by the model but missing from the sample design are created as fixed nodes, with the same value for every row of the design. The value is taken from mctable$sample_space according to if_not_sampled in eval_module() and trial_totals() ("median" by default, or "mean", "max" and "min"), after applying any transformation. Fixed nodes are flagged with from_sample_design_fixed = TRUE in the node_list.

You can pass the sampling matrix (or sensitivity object) directly to eval_module(), or set it as a global default with set_sample_design(). Using a global design ensures the same sampling matrix is available across all model steps, which is convenient when you run sensitivity analysis after building a complex model. You can also pass the design to each function call, but that is more verbose, and you must remove it later to run the model with the original data inputs.

# Create a sample design as a data frame
X <- data.frame(
  w_prev = runif(10000, 0.15, 0.6),
  h_prev = runif(10000, 0.02, 0.7),
  test_sensi = runif(10000, 0.8, 0.91),
  animals_n = sample(82:176, 10000, replace = TRUE),
  farms_n = sample(5:10, 10000, replace = TRUE),
  test_origin = runif(10000, 0, 1)
)

dim(X)
#> [1] 10000     6
head(X)
#>      w_prev    h_prev test_sensi animals_n farms_n test_origin
#> 1 0.3303008 0.2163915  0.8793875        96       7   0.7406187
#> 2 0.5421882 0.2731196  0.8048914        85       5   0.4972073
#> 3 0.1530968 0.1427685  0.8548849       162       8   0.4201413
#> 4 0.5210398 0.6816595  0.8377471        97       5   0.2843835
#> 5 0.5570636 0.6964915  0.8565773       105       7   0.4106484
#> 6 0.4148973 0.0881092  0.8319424       122       5   0.5431301

# Set global sample design
set_sample_design(X)
#> sample_design set to X

# Retrieve current global sample design
current_design <- set_sample_design()

# Evaluate the module with the global sample design
imports_mcmodule_X <- eval_module(
  exp = imports_exp
)
#> imports_exp evaluated
#> mcmodule created (expressions: imports_exp)

# Calculate output (expected number of animals not detected)
imports_mcmodule_X <- trial_totals(
  imports_mcmodule_X,
  mc_names = c("no_detect"),
  trials_n = "animals_n",
  subsets_n = "farms_n",
  subsets_p = "h_prev"
)

mcmodule_tornado(imports_mcmodule_X, output = "no_detect_set_n", method = "spearman")
#> 
#> === Correlation Analysis Summary ===
#> 
#> Analysis Parameters:
#> - Analysis type: Global output
#> - Output node: no_detect_set_n
#> - Correlation method(s): spearman
#> - Missing value handling: all.obs
#> 
#> Expression Information:
#> - Module: mcmodule
#>   - Expression: imports_exp
#>     - Input nodes: w_prev, test_origin, test_sensi, animals_n, farms_n, h_prev
#>     - Variates analyzed: 1
#> 
#> Results Summary:
#> - Total correlations calculated: 6
#> - Top 5 most correlated inputs (by absolute mean correlation):
#>   1. h_prev: 0.6731
#>   2. test_origin: -0.4779
#>   3. w_prev: 0.3628
#>   4. animals_n: 0.2183
#>   5. farms_n: 0.2153
#> 
#> Input Correlation Strength Distribution:
#> - Very strong: 0 (0.0%)
#> - Strong: 1 (16.7%)
#> - Moderate: 1 (16.7%)
#> - Weak: 3 (50.0%)
#> - Very weak/None: 1 (16.7%)
#> 
#> Inputs by Correlation Strength:
#> - Strong: h_prev
#> - Moderate: test_origin
#> - Weak: w_prev, animals_n, farms_n
#> - Very weak/None: test_sensi


# Reset global sample design
reset_sample_design()
#> sample_design reset

This sample design produces a different ranking from the first tornado analysis, because we’re using the inputs sampling space rather than the original model inputs. Here, h_prev is the main positive driver of no_detect_set_n. test_origin is the next most influential input and is negatively associated with the output. w_prev, animals_n and farms_n have smaller positive effects, while test_sensi is weak or negligible within these bounds.

Define sample space in mctable

For methods that use a sample design, mctable can be used to define how inputs are sampled. In particular, mctable$sample_space stores the bounds or distribution parameters used to generate sensitivity samples. To read more about mctable see the package vignette.

imports_mctable[,c("mcnode","mc_func", "transformation", "sample_space")]
#>            mcnode mc_func
#> 1          h_prev   runif
#> 2          w_prev   runif
#> 3      test_sensi   rpert
#> 4         farms_n    <NA>
#> 5       animals_n   rnorm
#> 6 test_origin_unk    <NA>
#> 7     test_origin    <NA>
#>                                                                                     transformation
#> 1                                                                                             <NA>
#> 2                                                                                             <NA>
#> 3                                                                                             <NA>
#> 4                                                                                             <NA>
#> 5                                                                                             <NA>
#> 6                                                                               value == 'unknown'
#> 7 ifelse(value == 'always', 1, ifelse(value == 'sometimes', 0.5, ifelse(value == 'never', 0, NA)))
#>                          sample_space
#> 1               min = 0.02, max = 0.7
#> 2               min = 0.15, max = 0.6
#> 3 min = 0.8, mode = 0.875, max = 0.91
#> 4                   min = 5, max = 10
#> 5                 min = 82, max = 176
#> 6                                <NA>
#> 7                    min = 0, max = 1

sample_space is interpreted together with mc_func:

  • Bounds, such as "min = 0.02, max = 0.7" or "c(0.02, 0.7)", are sampled uniformly. This is also used for rnorm or rpert inputs whose sample_space only gives min and max (for example animals_n).
  • rnorm inputs can use "mean = 100, sd = 10", and rpert inputs can use "min = 0.8, mode = 0.875, max = 0.91" (with an optional shape). Namespaced functions, such as mc2d::rpert, are also recognised.
  • Categorical values, such as "c('always', 'sometimes', 'never')", are sampled by index and must be mapped to numbers with transformation.
  • A single numeric value, such as "1", "c(1)" or "value = 1", defines a constant input (equivalent to "min = 1, max = 1").

By default (transformation = TRUE), bounds and Sobol matrices are computed on the model scale, after applying transformation. If a transformation returns only NA for the sample space values, it is skipped with a message and sample_space is assumed to be on the model scale already. This is the case for test_origin, whose transformation maps categorical values but whose sample_space is numeric ("min = 0, max = 1"). Transformed ranges are probed on an evenly spaced grid (n_probe points), so results are reproducible and do not use the random number stream.

Two helper functions support common sampling designs from mctable:

Currently, mcmodule only provides direct conversion from mctable to Sobol sampling designs. However, you can use mctable_bounds() to create many other sample designs using different approaches. Your sample design matrix should contain columns for the input nodes you want to vary; any other input node in your mcmodule model needs a sample_space in mctable so it can be created as a fixed node.

Inputs whose sample space has no variation (lower bound equal to upper bound, after any transformation) do not need to be sampled. By default (drop_constant = TRUE), mctable_bounds() and mctable_sobol_matrices() drop them with a message. Dropped inputs are listed in $dropped and attr(X, "dropped"), respectively, and are created as fixed nodes when the model is evaluated.

Morris elementary effects

Morris (Morris 1991) is another screening method. It identifies influential inputs and potential nonlinearity or interactions at moderate computational cost

Start by getting parameter bounds from mctable.

# Get bounds for Morris sampling design
b <- mctable_bounds(imports_mctable)
#> Transformation for 'test_origin' returns NA for all sample_space values; assuming sample_space is already on the model scale and skipping it

test_origin_unk has no sample_space, so it is excluded, and the test_origin transformation is skipped with a message, as explained above. Bounds are returned on the model scale.

Build the Morris design with sensitivity::morris(). The resulting object can be passed directly as sample_design. The arguments factors, r, and design control the the factors (inputs) sampled, the number of repetitions of the design, and the step size between sampled values, respectively. The arguments binf and bsup provide the bounds for each input, and scale = TRUE scales the inputs to [0,1] for better comparability of Morris indices across inputs with different ranges.

To have stable Morris indices in large sample designs (with many inputs), you need to run the model with a large number of repetitions, which can be computationally intensive. Always check the sensitivity indices stability in diferent runs before interpreting them, increase r until rankings stabilise.

library(sensitivity)
#> Registered S3 method overwritten by 'sensitivity':
#>   method    from 
#>   print.src dplyr

# Create Morris design
morris_sa <- sensitivity::morris(
  model = NULL,
  factors = b$factors,
  r = 2000,
  design = list(type = "oat", levels = 4, grid.jump = 2),
  binf = b$binf,
  bsup = b$bsup,
  scale = TRUE
)

# Evaluate the module with that design.
imports_mcmodule_morris <- eval_module(
  exp = imports_exp,
  sample_design = morris_sa,
  mctable = imports_mctable
)
#> imports_exp evaluated
#> mcmodule created (expressions: imports_exp)

# Calculate output (expected number of animals not detected)
imports_mcmodule_morris <- trial_totals(
  imports_mcmodule_morris,
  mc_names = c("no_detect"),
  trials_n = "animals_n",
  subsets_n = "farms_n",
  subsets_p = "h_prev",
  sample_design = morris_sa,
  mctable = imports_mctable
)

Finally, extract the output vector and estimate Morris indices. The tell() function updates the Morris object with the model output, and then you can print and plot the indices.

# Extract the output vector and estimate Morris indices.
y <- unmc(imports_mcmodule_morris$node_list$no_detect_set_n$mcnode)

# Aggregated output used for sensitivity analysis.
sensitivity::tell(morris_sa, y)

# Print Morris indices
morris_sa
#> 
#> Call:
#> sensitivity::morris(model = NULL, factors = b$factors, r = 2000,     design = list(type = "oat", levels = 4, grid.jump = 2), binf = b$binf,     bsup = b$bsup, scale = TRUE)
#> 
#> Model runs: 14000 
#>                      mu    mu.star     sigma
#> h_prev       144.957931 144.957931 128.34699
#> w_prev        90.310164  90.310164  98.98181
#> test_sensi    -7.453771   7.453771  10.82876
#> farms_n       50.487169  50.487169  63.37776
#> animals_n     54.006314  54.006314  67.27486
#> test_origin -112.481620 112.481620 112.62289

# Plot Morris indices (mu.star and sigma)
plot(morris_sa)

In the Morris method, mu ( μ\mu ) is the mean of the elementary effects across trajectories and mu.star ( μ*\mu^* ) is the absolute value of mu, which summarises each input’s overall importance (larger values indicate a stronger average influence on the output, regardless of direction). h_prev has the largest mu.star, making it the most influential input overall, followed by test_origin. w_prev is the next most important, while farms_n and animals_n have more moderate effects, and test_sensi is negligible within the explored bounds (note that its sample space is narrow, 0.8 to 0.91). sigma ( σ\sigma ) is the standard deviation of the elementary effects. Large sigma values indicate that the effect of an input varies substantially across the sampled space, which typically reflects nonlinearity and/or interactions with other inputs.

Sobol indices

Sobol analysis Saltelli (2002) decomposes output variance into first-order and higher-order effects, providing a global sensitivity analysis. It is more computationally intensive than Morris, but it gives a more complete picture of input importance, including interactions. Sobol variance decomposition assumes independent inputs, if inputs are dependent, interpret Sobol indices with care or consider other approaches.

First, generate Sobol sampling matrices from mctable.

mctable_sobol_matrices() creates Sobol design matrices from your mctable definitions. Each column is mapped through the distribution defined by sample_space and mc_func (for example, test_sensi follows its PERT distribution) and, by default, through transformation. Inputs with no variation are dropped, so colnames(X) only contains the sampled inputs, which is what sensobol::sobol_indices() expects in params.

library(sensobol)

N <- 10000
X <- mctable_sobol_matrices(imports_mctable, N = N, order = "second")
#> Transformation for 'test_origin' returns NA for all sample_space values; assuming sample_space is already on the model scale and skipping it
#> Transformation for 'test_origin' returns NA for all sample_space values; assuming sample_space is already on the model scale and skipping it

Evaluate the module, compute the target output as before.

imports_mcmodule_sobol <- eval_module(
  exp = imports_exp,
  data = NULL,
  sample_design = X,
  mctable = imports_mctable
)
#> imports_exp evaluated
#> mcmodule created (expressions: imports_exp)

imports_mcmodule_sobol <- trial_totals(
  mcmodule = imports_mcmodule_sobol,
  mc_names = c("no_detect"),
  trials_n = "animals_n",
  subsets_n = "farms_n",
  subsets_p = "h_prev",
  sample_design = X,
  mctable = imports_mctable
)

Then compute first-order (Si) and total-order (Ti) Sobol indices and print and plot the results.

y <- unmc(imports_mcmodule_sobol$node_list$no_detect_set_n$mcnode)

# Compute Sobol indices
sobol_sa <- sensobol::sobol_indices(Y = y, N = N, params = colnames(X), order = "second", boot = TRUE, R = 1000)

sobol_sa
#> 
#> First-order estimator: saltelli | Total-order estimator: jansen 
#> 
#> Total number of model runs: 230000 
#> 
#> Sum of first order indices: 0.7854101 
#>          original          bias    std.error        low.ci      high.ci
#>             <num>         <num>        <num>         <num>        <num>
#>  1:  3.332841e-01  3.099247e-04 1.503685e-02  0.3035024940 0.3624458577
#>  2:  1.377370e-01  1.211129e-04 1.008895e-02  0.1178419004 0.1573898696
#>  3:  3.909247e-04  8.265513e-06 5.247818e-04 -0.0006458944 0.0014112127
#>  4:  4.305921e-02 -2.353319e-04 5.983032e-03  0.0315680158 0.0550210698
#>  5:  4.684154e-02  2.187602e-04 6.037745e-03  0.0347890206 0.0584565471
#>  6:  2.240974e-01 -1.537965e-04 1.286486e-02  0.1990365095 0.2494658181
#>  7:  4.907211e-01  5.029842e-04 1.219883e-02  0.4663088741 0.5141273963
#>  8:  2.289120e-01  1.664369e-04 6.444912e-03  0.2161137802 0.2413773709
#>  9:  7.520003e-04  1.394706e-06 3.146124e-05  0.0006889427 0.0008122685
#> 10:  7.602997e-02  7.697351e-05 2.190714e-03  0.0716592768 0.0802467186
#> 11:  9.089470e-02  2.002757e-05 2.823669e-03  0.0853403783 0.0964089576
#> 12:  3.490372e-01  1.385059e-04 9.336575e-03  0.3305993629 0.3671980633
#> 13:  4.242323e-02 -2.963187e-04 7.304782e-03  0.0284024408 0.0570366613
#> 14:  2.276340e-04 -1.155691e-05 4.052946e-04 -0.0005551720 0.0010335537
#> 15:  1.158978e-02 -1.280642e-04 4.101501e-03  0.0036790489 0.0197566393
#> 16:  1.559113e-02 -4.947027e-05 4.275942e-03  0.0072599048 0.0240212910
#> 17:  6.621890e-02 -3.913419e-04 8.430482e-03  0.0500868043 0.0831336875
#> 18:  1.986587e-05  4.759990e-07 2.625493e-04 -0.0004951973 0.0005339770
#> 19:  4.869421e-03 -1.756851e-05 2.876097e-03 -0.0007500576 0.0105240368
#> 20:  5.898327e-03  5.372702e-05 2.902011e-03  0.0001567634 0.0115324376
#> 21:  3.005418e-02  3.837472e-05 6.179741e-03  0.0179037310 0.0421278724
#> 22: -5.842551e-06 -2.991494e-07 1.363850e-04 -0.0002728532 0.0002617664
#> 23:  4.855472e-05  8.994945e-06 1.657679e-04 -0.0002853393 0.0003644589
#> 24: -1.751435e-05 -9.572491e-06 3.828920e-04 -0.0007583965 0.0007425128
#> 25:  2.403328e-03  2.917652e-05 1.662299e-03 -0.0008838938 0.0056321974
#> 26:  7.210102e-03 -1.338501e-04 3.454881e-03  0.0005725092 0.0141153942
#> 27:  8.240894e-03 -3.579039e-05 3.807726e-03  0.0008136775 0.0157396907
#>          original          bias    std.error        low.ci      high.ci
#>             <num>         <num>        <num>         <num>        <num>
#>     sensitivity             parameters
#>          <char>                 <char>
#>  1:          Si                 h_prev
#>  2:          Si                 w_prev
#>  3:          Si             test_sensi
#>  4:          Si                farms_n
#>  5:          Si              animals_n
#>  6:          Si            test_origin
#>  7:          Ti                 h_prev
#>  8:          Ti                 w_prev
#>  9:          Ti             test_sensi
#> 10:          Ti                farms_n
#> 11:          Ti              animals_n
#> 12:          Ti            test_origin
#> 13:         Sij          h_prev.w_prev
#> 14:         Sij      h_prev.test_sensi
#> 15:         Sij         h_prev.farms_n
#> 16:         Sij       h_prev.animals_n
#> 17:         Sij     h_prev.test_origin
#> 18:         Sij      w_prev.test_sensi
#> 19:         Sij         w_prev.farms_n
#> 20:         Sij       w_prev.animals_n
#> 21:         Sij     w_prev.test_origin
#> 22:         Sij     test_sensi.farms_n
#> 23:         Sij   test_sensi.animals_n
#> 24:         Sij test_sensi.test_origin
#> 25:         Sij      farms_n.animals_n
#> 26:         Sij    farms_n.test_origin
#> 27:         Sij  animals_n.test_origin
#>     sensitivity             parameters
#>          <char>                 <char>

plot(sobol_sa)

plot(sobol_sa, order = "second")

First-order indices (Si) measure each input’s main effect, the share of output variance attributable to changing that input alone, averaged over uncertainty in all other inputs. Larger Si values indicate the model output is more sensitive to that factor. Here, h_prev is the dominant driver of variance, test_origin is the next most influential, and w_prev also contributes materially; farms_n and animals_n have smaller but non-zero effects, while test_sensi is negligible. Negative estimates can occur due to Monte Carlo error and should be interpreted as ~0 if the confidence interval overlaps 0. If important factors have wide CIs or many negatives appear, increase N (and/or bootstrap replicates) before interpreting small effects.

Total-order indices (Ti) measure each input’s total effect on output variance, including its main effect plus all interaction effects with other inputs. Ti is therefore always at least as large as Si for the same parameter.

Second-order indices (Sij) quantify interaction effects. They measure how much variance is explained only by varying two inputs together beyond what their individual first-order effects (Si) explain. Values near 0 indicate little interaction; larger values indicate the combined effect of the pair is important (e.g., h_prev.test_origin suggests a meaningful interaction between h_prev and test_origin).

Other methods

You can use any method to analyse the importance of your inputs as long as you can build a table/matrix of input values to run the model with, and collect the corresponding model output(s). For example, you could use sensitivity::fast99() or Shapley values (iml::shapley()). The best method will depend on your model, computational resources, and the specific questions you want to answer.

References

Iooss, Bertrand, Sebastien Da Veiga, Alexandre Janon, and Gilles Pujol. 2025. Sensitivity: Global Sensitivity Analysis of Model Outputs and Importance Measures. https://doi.org/10.32614/CRAN.package.sensitivity.
Morris, Max D. 1991. “Factorial Sampling Plans for Preliminary Computational Experiments.” Technometrics 33 (2): 161174. https://doi.org/10.1080/00401706.1991.10484804.
Pouillot, R., and M.-L. Delignette-Muller. 2010. Evaluating Variability and Uncertainty in Microbial Quantitative Risk Assessment Using Two r Packages. 142: 330–40.
Puy, Arnald, Samuele Lo Piano, Andrea Saltelli, and Simon A. Levin. 2022. Sensobol: An r Package to Compute Variance-Based Sensitivity Indices. 102. https://doi.org/10.18637/jss.v102.i05.
Saltelli, Andrea. 2002. “Making Best Use of Model Evaluations to Compute Sensitivity Indices.” Computer Physics Communications 145 (2): 280297. https://doi.org/10.1016/S0010-4655(02)00280-1.
Sobol, I. M. 1993. “Sensitivity Estimates for Nonlinear Mathematical Models.” Mathematical Modelling and Computational Experiments, no. 4: 407414. https://www.andreasaltelli.eu/file/repository/sobol1993.pdf.