Source code for SALib.sample.shapley

from numbers import Integral
from typing import Optional, Union

import numpy as np

from . import common_args
from ..util import handle_seed, read_param_file, scale_samples


[docs] def sample( problem: dict, N: int, seed: Optional[Union[int, np.random.Generator]] = None, ) -> np.ndarray: """Generate inputs for Goda's Shapley effects estimator. The method draws two independent input vectors for each Monte Carlo replication. A random permutation then defines a trajectory that replaces one coordinate at a time from the first vector with the corresponding coordinate from the second. The resulting matrix contains ``N * (D + 1)`` rows and ``D`` columns. Results from these model inputs are intended to be used with :func:`SALib.analyze.shapley.analyze`. Notes ----- Goda's estimator assumes mutually independent input variables. SALib problem definitions specify marginal distributions, so this sampler draws every input independently. Grouped parameters are not supported. Parameters ---------- problem : dict The problem definition. N : int Number of independent Monte Carlo trajectories. The model is evaluated ``N * (D + 1)`` times. seed : {None, int, `numpy.random.Generator`}, optional Seed or random generator used to draw the sample and permutations. Returns ------- numpy.ndarray Model inputs arranged as ``N`` consecutive trajectories. References ---------- 1. Goda, T. (2021). A simple algorithm for global sensitivity analysis with Shapley effects. Reliability Engineering & System Safety, 213, 107702. https://doi.org/10.1016/j.ress.2021.107702 """ if isinstance(N, bool) or not isinstance(N, Integral) or N <= 0: raise ValueError("N must be a positive integer.") _validate_ungrouped(problem) num_vars = problem["num_vars"] if num_vars <= 0: raise ValueError("The problem must contain at least one variable.") rng = handle_seed(seed) independent_pairs = scale_samples(rng.random((2 * N, num_vars)), problem) first, second = np.split(independent_pairs, 2) samples = np.empty((N * (num_vars + 1), num_vars), dtype=float) for trajectory, permutation in enumerate( (rng.permutation(num_vars) for _ in range(N)) ): start = trajectory * (num_vars + 1) samples[start] = first[trajectory] current = first[trajectory].copy() for step, factor in enumerate(permutation, start=1): current[factor] = second[trajectory, factor] samples[start + step] = current return samples
def _validate_ungrouped(problem: dict) -> None: groups = problem.get("groups") if groups and list(groups) != list(problem.get("names", [])): raise ValueError("Goda's Shapley estimator does not support groups.") # No additional CLI options cli_parse = None
[docs] def cli_action(args): """Generate a Shapley trajectory design from command-line arguments.""" problem = read_param_file(args.paramfile) param_values = sample(problem, args.samples, seed=args.seed) np.savetxt( args.output, param_values, delimiter=args.delimiter, fmt="%." + str(args.precision) + "e", )
if __name__ == "__main__": common_args.run_cli(cli_parse, cli_action)