# Source code for SALib.analyze.ff

```"""
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.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):
"-X",
"--model-input-file",
type=str,
required=True,
default=None,
help="Model input file",
)
"--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):