"""
Created on 30 Jun 2015
@author: will2
"""
import numpy as np
from . import common_args
import pandas as pd
from types import MethodType
from SALib.util import read_param_file, ResultDict
from SALib.sample.ff import generate_contrast, extend_bounds
[docs]
def analyze(problem, X, Y, second_order=False, print_to_console=False, seed=None):
"""Perform a fractional factorial analysis
Returns a dictionary with keys 'ME' (main effect) and 'IE' (interaction
effect). The techniques bulks out the number of parameters with dummy
parameters to the nearest 2**n. Any results involving dummy parameters
could indicate a problem with the model runs.
Notes
-----
Compatible with:
`ff` : :func:`SALib.sample.ff.sample`
Examples
--------
>>> X = sample(problem)
>>> Y = X[:, 0] + (0.1 * X[:, 1]) + ((1.2 * X[:, 2]) * (0.2 + X[:, 0]))
>>> analyze(problem, X, Y, second_order=True, print_to_console=True)
Parameters
----------
problem: dict
The problem definition
X: numpy.matrix
The NumPy matrix containing the model inputs
Y: numpy.array
The NumPy array containing the model outputs
second_order: bool, default=False
Include interaction effects
print_to_console: bool, default=False
Print results directly to console
seed : int
Seed to generate a random number
Returns
-------
Si: dict
A dictionary of sensitivity indices, including main effects ``ME``,
and interaction effects ``IE`` (if ``second_order`` is True)
References
----------
1. Saltelli, A., Ratto, M., Andres, T., Campolongo, F.,
Cariboni, J., Gatelli, D.,
Saisana, M., Tarantola, S., 2008.
Global Sensitivity Analysis: The Primer.
Wiley, West Sussex, U.K.
http://doi.org/10.1002/9780470725184
"""
if seed:
np.random.seed(seed)
problem = extend_bounds(problem)
num_vars = problem["num_vars"]
X = generate_contrast(problem)
main_effect = (1.0 / (2 * num_vars)) * np.dot(Y, X)
Si = ResultDict((k, [None] * num_vars) for k in ["names", "ME"])
Si["ME"] = main_effect
Si["names"] = problem["names"]
if second_order:
interaction_names, interaction_effects = interactions(problem, Y)
Si["interaction_names"] = interaction_names
Si["IE"] = interaction_effects
Si.to_df = MethodType(to_df, Si)
if print_to_console:
for S in Si.to_df():
print(S)
return Si
[docs]
def to_df(self):
"""Conversion method to Pandas DataFrame. To be attached to ResultDict.
Returns
-------
main_effect, inter_effect: tuple
A tuple of DataFrames for main effects and interaction effects.
The second element (for interactions) will be `None` if not available.
"""
names = self["names"]
main_effect = self["ME"]
interactions = self.get("IE", None)
inter_effect = None
if interactions:
interaction_names = self.get("interaction_names")
names = [name for name in names if not isinstance(name, list)]
inter_effect = pd.DataFrame({"IE": interactions}, index=interaction_names)
main_effect = pd.DataFrame({"ME": main_effect}, index=names)
return main_effect, inter_effect
[docs]
def interactions(problem, Y):
"""Computes the second order effects
Computes the second order effects (interactions) between
all combinations of pairs of input factors
Parameters
----------
problem: dict
The problem definition
Y: numpy.array
The NumPy array containing the model outputs
Returns
-------
ie_names: list
The names of the interaction pairs
IE: list
The sensitivity indices for the pairwise interactions
"""
names = problem["names"]
num_vars = problem["num_vars"]
X = generate_contrast(problem)
ie_names = []
IE = []
for col in range(X.shape[1]):
for col_2 in range(col):
x = X[:, col] * X[:, col_2]
var_names = (names[col_2], names[col])
ie_names.append(var_names)
IE.append((1.0 / (2 * num_vars)) * np.dot(Y, x))
return ie_names, IE
[docs]
def cli_parse(parser):
parser.add_argument(
"-X",
"--model-input-file",
type=str,
required=True,
default=None,
help="Model input file",
)
parser.add_argument(
"--max-order",
type=int,
required=False,
default=2,
choices=[1, 2],
help="Maximum order of sensitivity \
indices to calculate",
)
return parser
[docs]
def cli_action(args):
problem = read_param_file(args.paramfile)
Y = np.loadtxt(
args.model_output_file, delimiter=args.delimiter, usecols=(args.column,)
)
X = np.loadtxt(args.model_input_file, delimiter=args.delimiter, ndmin=2)
if len(X.shape) == 1:
X = X.reshape((len(X), 1))
analyze(problem, X, Y, (args.max_order == 2), print_to_console=True, seed=args.seed)
if __name__ == "__main__":
common_args.run_cli(cli_parse, cli_action)