Morris Method / Elementary Effects#
Morris’ Method of Elementary Effects.
Module includes optimised sampling of trajectories including optional groups as well as calculation of the Morris measures mu, stddev and mu*.
Saltelli, A., Chan, K., & Scott, E. M. (2000). Sensitivity Analysis. Wiley Series in Probability and Statistics, John Wiley & Sons, New York, 1-504, pages 68ff
- Provided functions are:
morris_sampling - Sample trajectories in parameter space
elementary_effects - Calculate Elementary Effects from model output on trajectories
Note that the functions morris_sampling and elementary_effects are wrappers for the functions Optimized_Groups and Morris_Measure_Groups of F. Campolongo and J. Cariboni ported to Python by Stijn Van Hoey.
This module was originally written by Stijn Van Hoey as a translation from an original Matlab code of F. Campolongo and J. Cariboni, JRC - IPSC Ispra, Varese, IT. It was adapted by Matthias Cuntz while at Department of Computational Hydrosystems, Helmholtz Centre for Environmental Research - UFZ, Leipzig, Germany, and continued while at Institut National de Recherche pour l’Agriculture, l’Alimentation et l’Environnement (INRAE), Nancy, France.
- copyright:
Copyright 2012-2022 Stijn Van Hoey, Matthias Cuntz, see AUTHORS.rst for details.
- license:
MIT License, see LICENSE for details.
The following wrappers are provided
|
Sample trajectories in parameter space |
|
Compute the Morris measures given the Morris sample matrix, the output values and the group matrix. |
- History
Written in Matlab by F. Campolongo, J. Cariboni, JRC - IPSC Ispra, Varese, IT. Last Update 15 November 2005 by J. Cariboni: http://sensitivity-analysis.jrc.ec.europa.eu/software/index.htm now at: https://ec.europa.eu/jrc/en/samo/simlab
Translated to Python in May 2012 by Stijn Van Hoey.
Adapted to Python 3, etc., Oct 2013, Matthias Cuntz
Went from exponential time increase with number of trajectories to linear increase by using in Optimised_Groups one call to cdist from scipy.spatial.distance and removed one loop in a loop over total number of trajectories. Several further little improvements on speed, Dec 2017, Matthias Cuntz
Allow single trajectories, Dec 2017, Matthias Cuntz
Catch degenerated case where lower bound==upper bound, return 0, Feb 2018, Matthias Cuntz
Use integer division operator // instead of / for trajectory length r, Jul 2018, Fabio Genaretti
Distance matrix is not done for all trajectories at once because of very large memory requirement, Aug 2018, Matthias Cuntz & Fabio Gennaretti
Changed to Sphinx docstring and numpydoc, Dec 2019, Matthias Cuntz
Distinguish iterable and array_like parameter types, Jan 2020, Matthias Cuntz
Remove np.matrix in Sampling_Function_2, called in Optimized_Groups to remove numpy deprecation warnings, Jan 2020, Matthias Cuntz
Plot diagnostic figures in png files if matplotlib installed, Feb 2020, Matthias Cuntz
Renamed file to morris_method.py, Feb 2020, Matthias Cuntz
Adjusted argument and keyword argument names to be consistent with pyeee, Feb 2020, Matthias Cuntz
Make number of final trajectories an argument instead of a keyword argument and sample default of 10*final trajectories, Feb 2020, Matthias Cuntz
Sample not only from uniform distribution but allow all distributions of scipy.stats, Mar 2020, Matthias Cuntz
More consistent docstrings, Jan 2022, Matthias Cuntz
Raise Error if more than one component changed at once, Jul 2023, Matthias Cuntz
- elementary_effects(nparam, OptMatrix, OptOutVec, Output, nsteps=4, Group=[], Diagnostic=False)[source]#
Compute the Morris measures given the Morris sample matrix, the output values and the group matrix.
- Parameters:
nparam (int) – Number of parameters / factors list [OptMatrix, OptOutVec] with OptMatrix((nparam+1)*nt,nparam) and OptOutVec(nparam*nt)
OptMatrix (ndarray) – ((nparam+1)*nt,nparam) Matrix of the Morris sampled trajectories from morris_sampling
OptOutVec (ndarray) – (nparam*nt,) Matrix with the parameter / factor changings from morris_sampling
Output (ndarray) – ((nparam+1)*nt,) Matrix of the output values of each point of each trajectory
nsteps (int, optional) – Number of levels, i.e. intervals in trajectories (default: 4)
Group (ndarray, optional) – (nparam,NumGroups) Matrix describing the groups. (default: []) Each column represents a group. The elements of each column are zero if the parameter / factor is not in the group, otherwise it is 1.
Diagnostic (bool, optional) – Print out diagnostics if True (default: False)
- Returns:
SA, OutMatrix – SA(nparam*Output.shape[1],N) individual sensitivity measures, OutMatrix(nparam*Output.shape[1], 3) = [Mu*, Mu, StDev] Morris Measures
It gives the three measures of each parameter / factor for each output.
- Return type:
list of ndarrays
References
Saltelli, A., Chan, K., & Scott, E. M. (2000). Sensitivity Analysis. Wiley Series in Probability and Statistics, John Wiley & Sons, New York, 1-504. - on page 68ff
Examples
>>> import numpy as np >>> seed = 1023 >>> np.random.seed(seed=seed) >>> npara = 10 >>> x0 = np.ones(npara)*0.5 >>> lb = np.zeros(npara) >>> ub = np.ones(npara) >>> mask = np.ones(npara, dtype=bool) >>> mask[5::2] = False >>> nmask = np.sum(mask) >>> nt = npara >>> ntotal = max(nt**2, 10*nt) >>> nsteps = 6 >>> tmatrix, tvec = morris_sampling(nmask, lb[mask], ub[mask], nt, ... ntotal=ntotal, nsteps=nsteps, ... Diagnostic=False) >>> # Set input vector to trajectories and masked elements to x0 >>> x = np.tile(x0, tvec.size).reshape(tvec.size, npara) # default to x0 >>> x[:,mask] = tmatrix # replaced unmasked with trajectorie values >>> func = np.sum >>> fx = np.array(list(map(func,x))) >>> out = np.zeros((npara,3)) >>> sa, res = elementary_effects(nmask, tmatrix, tvec, fx, nsteps=nsteps, ... Diagnostic=False) >>> out[mask,:] = res >>> print(out[:,0]) [1. 1. 1. 1. 1. 0. 1. 0. 1. 0.]
- morris_sampling(nparam, lb, ub, nt, nsteps=6, ntotal=None, dist=None, distparam=None, GroupMat=array([], dtype=float64), Diagnostic=0)[source]#
Sample trajectories in parameter space
Optimisation in the choice of trajectories for Morris experiment, that means elementary effects.
- Parameters:
nparam (int) – Number of parameters / factors
lb (array_like) – (nparam,) Lower bound of the uniform distribution for each parameter / factor or lower fraction of percent point function ppf if distribution given. Be aware that the percent point function ppf of most continuous distributions is infinite at 0.
ub (array_like) – (nparam,) Upper bound of the uniform distribution for each parameter / factor or upper fraction of percent point function ppf if distribution given. Be aware that the percent point function ppf of most continuous distributions is infinite at 1.
nt (int) – Final number of optimal trajectories
nsteps (int, optional) – Number of levels, i.e. intervals in trajectories (default: 6)
ntotal (int, optional) – Total number of sampled trajectories. If None: ntotal=10*nt (default: None)
dist (list, optional) – List of None or scipy.stats distribution objects for each factor having the method ppf, Percent Point Function (Inverse of CDF) (default: None) If None, the uniform distribution will be sampled from lower bound lb to upper bound ub. If dist is scipy.stats.uniform, the ppf will be sampled from the lower fraction given in lb and the upper fraction in ub. The sampling interval is then given by the parameters loc=lower and scale=interval=upper-lower in distparam. This means dist=None, lb=a, ub=b corresponds to lb=0, ub=1, dist=scipy.stats.uniform, distparam=[a,b-a]
distparam (list, optional) – List with tuples with parameters as required for dist (default: (0,1)). All distributions of scipy.stats have location and scale parameters, at least. loc and scale are implemented as keyword arguments in scipy.stats. Other parameters such as the shape parameter of the gamma distribution must hence be given first, e.g. (shape,loc,scale) for the gamma distribution. distparam is ignored if dist is None. The percent point function ppf is called like this: dist(*distparam).ppf(x)
GroupMat (ndarray, optional) – (nparam,ngroup) Matrix describing the groups. (default: np.array([])) Each column represents a group. The elements of each column are zero if the parameter / factor is not in the group, otherwise it is 1.
Diagnostic (bool, optional) – Plot the histograms and compute the efficiency of the sampling if True (default: False)
- Returns:
traj – list [OptMatrix, OptOutVec] with OptMatrix((nparam+1)*nt,nparam) and OptOutVec(nparam*nt)
- Return type:
References
Saltelli, A., Chan, K., & Scott, E. M. (2000). Sensitivity Analysis. Wiley Series in Probability and Statistics, John Wiley & Sons, New York, 1-504. - on page 68ff
Examples
>>> import numpy as np >>> seed = 1023 >>> np.random.seed(seed=seed) >>> npara = 10 >>> x0 = np.ones(npara)*0.5 >>> lb = np.zeros(npara) >>> ub = np.ones(npara) >>> mask = np.ones(npara, dtype=bool) >>> mask[5::2] = False >>> nmask = np.sum(mask) >>> nt = npara >>> ntotal = max(nt**2, 10*nt) >>> nsteps = 6 >>> tmatrix, tvec = morris_sampling(nmask, lb[mask], ub[mask], nt, ... nsteps=nsteps, ntotal=ntotal, ... Diagnostic=False) >>> # Set input vector to trajectories and masked elements to x0 >>> x = np.tile(x0, tvec.size).reshape(tvec.size, npara) # default to x0 >>> x[:,mask] = tmatrix # replaced unmasked with trajectorie values >>> print(x[0,:]) [0.6 0.4 0.8 0.6 0.6 0.5 0.4 0.5 0. 0.5]
>>> import scipy.stats as stats >>> seed = 1023 >>> np.random.seed(seed=seed) >>> npara = 10 >>> x0 = np.ones(npara)*0.5 >>> lb = np.zeros(npara) >>> ub = np.ones(npara) >>> dist = [ stats.uniform for i in range(npara) ] >>> distparam = [ (lb[i], ub[i]-lb[i]) for i in range(npara) ] >>> mask = np.ones(npara, dtype=bool) >>> mask[5::2] = False >>> nmask = np.sum(mask) >>> nt = npara >>> ntotal = max(nt**2, 10*nt) >>> nsteps = 6 >>> tmatrix, tvec = morris_sampling(nmask, lb[mask], ub[mask], nt, ... nsteps=nsteps, ntotal=ntotal, ... dist=dist, distparam=distparam, ... Diagnostic=False) >>> # Set input vector to trajectories and masked elements to x0 >>> x = np.tile(x0, tvec.size).reshape(tvec.size, npara) # default to x0 >>> x[:,mask] = tmatrix # replaced unmasked with trajectory values >>> print(x[0,:]) [0.6 0.4 0.8 0.6 0.6 0.5 0.4 0.5 0. 0.5]