Download the notebook `here`

!
Interactive online version:

# Qualitative sensitivity analysis¶

## Morris Method¶

We showcase the use of `econsa`

for qualitative sensitivity analysis.

```
[1]:
```

```
from econsa.morris import elementary_effects
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
```

The module `morris`

implements the extended Morris method as proposed by Ge & Menendez (2017). They extend the Morris method in the sense, that their algorithm takes dependency among inputs into account.

For illustration purposes consider the Morris method for independent inputs only.

Let \(x = \{x_1, \dots, x_k\}\) denote a sample of values assigned to the \(X_i\)’s. \(f(x)\) is then the model output obtained for the values in \(x\). Now consider a second sample \(x_{\Delta_i} = \{x_1, \dots, x_{i-1}, x_i + \Delta, x_{i+1}, \dots, x_k\}\) that is identical to \(x\) up to input \(x_i\) which is varied by \(\Delta\). Then, one elementary effect for input \(i\) is derived by

The above elementary effect is computed \(N\) times, each for a varying \(\Delta\). The actual sensitivity measures resulting from the Morris method are the mean, denoted by \(\mu^\ast_i\), and the standard deviation, denoted by \(\sigma_i\), taken from the \(N\) elementary effects per input \(i\).

The derivation of the extended Morris indices is more complicated and we get *four* sensitivity indices indstead of only two: *independent* and *full* Morris indices, \((\mu_i^{\ast,\ ind}, \mu_i^{\ast,\ full}, \sigma_i^{ind}, \sigma_i^{full})\), which are computed analogously to the Morris indices under input independence, but are based on different elementary effects:

\(EE_i^{ind}\) denotes

*independent*elementary effects for input \(i\), effects that exclude the contributions attributable to the dependence between input \(X_i\) and \(X_j\) for \(i \neq j\), and\(EE_i^{full}\) denotes

*full*elementary effects for input \(i\), that include the effects due to correlation with other inputs.

The implementation of the algorithm used in `econsa`

uses the *radial design* and the *inverse Nataf transformation* as described in Ge & Menendez (2017).

For applying the Morris method, we need to specify the following arguments:

`func`

: The model for which we want to calculate the Morris indices. Note how the data needs to be accessed within the function. See below example.`params`

: The mean values of the inputs.`cov`

: The variance-covariance matrix of the inputs.`n_draws`

: Number of draws, which corresponds to \(N\) above.

Additional arguments are optional.

Note that the current implementation of the Morris method in `econsa`

does allow for Gaussian (i.e. normally distributed) inputs only.

### The `func`

argument¶

`func`

is the implementation of the model we want to conduct sensitivity analysis for. The Morris method can be applied to all models that return a unique value for a given set of realisations of the model inputs.

The model implemented by `func`

needs to access the inputs in the following way, if the input names are specified in `params`

and `cov`

:

`m = x["value"]["m"]`

`c = x["value"]["c"]`

`s = x["value"]["s"]`

.

Alternatively we can access them via the index as well:

`m = x["value"][0]`

`c = x["value"][1]`

`s = x["value"][2]`

.

```
[2]:
```

```
def eoq_model_morris(x, r=0.1):
"""EOQ Model that accesses data as expected by elementary_effects."""
m = x["value"]["m"]
c = x["value"]["c"]
s = x["value"]["s"]
# Need to ensure that there exists a solution (i.e. no NaNs).
if m < 0:
m = 0
elif c < 0:
raise ValueError
elif s < 0:
s = 0
else:
pass
return np.sqrt((24 * m * s) / (r * c))
```

### The `params`

and `cov`

arguments¶

Specify the input names in the data frames `params`

and `cov`

to display the input names in the output of `elementary_effects`

. `params`

is a vector of means of the normally distributed model inputs. `params`

needs to be a `pandas.DataFrame`

with a colum called `"value"`

, which contains the means of the inputs.

`cov`

is the corresponding variance-covariance matrix. The variance-covariance matrix describes the dependence structure of the inputs. As `params`

, `cov`

needs to be a `pandas.DataFrame`

. Indices need to be the same as in `params`

.

```
[3]:
```

```
names = ['m', 'c', 's']
params = pd.DataFrame(data=np.array([5.345, 0.0135, 2.15]), columns=['value'], index=names)
cov = pd.DataFrame(data=np.diag([1, 0.000001, 0.01]), columns=names, index=names)
```

### The `n_draws`

argument¶

`n_draws`

is the number of elementary effects we want to use for the computation of the Morris indices. The total computational cost of the extended Morris method amounts to \(3kN\), where \(k\) denotes the number of inputs and \(N\) the number of draws (`n_draws`

).

```
[4]:
```

```
n_draws = 100
```

### The `sampling_scheme`

and `seed`

arguments¶

By specifying `sampling_scheme`

we can choose how uniformly distributed samples are drawn. The uniformly distributed samples are then transfomed to dependently and normally distributed samples. “sobol” is used to sample from a low-discrepancy seuqence which generates more evenly distributed samples. When using “random”, we get pseudo-random samples. `seed`

denotes the corresponding seed when generating random numbers. The default is that `sampling_scheme`

\(=\) “sobol” and `seed`

\(=1\).

### The `n_cores`

argument¶

Parallelising code is done by the Python built-in `multiprocessing`

module, where `n_cores`

is the number of cores employed. The default is that that `n_cores`

is set to \(1\).

```
[5]:
```

```
results = elementary_effects(eoq_model_morris, params, cov, n_draws)
```

### The output¶

The output of `elementary_effects`

is a dictionary containing the four sensitivity indices derived from the `n_draws`

elementary effects: \((\mu_i^{\ast,\ ind}, \mu_i^{\ast,\ full}, \sigma_i^{ind}, \sigma_i^{full})\). The Morris indices are accessed as shown below.

#### Independent Morris indices \((\mu_i^{\ast,\ ind}, \sigma_i^{ind})\)¶

```
[6]:
```

```
morris_ind = pd.DataFrame(pd.concat((results['mu_ind'], results['sigma_ind']), axis=1))
morris_ind.columns = ['mu', 'sigma']
morris_ind
```

```
[6]:
```

mu | sigma | |
---|---|---|

m | 152.932694 | 54.844400 |

c | 57.953365 | 17.314969 |

s | 33.104776 | 7.963128 |

#### Full Morris indices \((\mu_i^{\ast,\ full}, \sigma_i^{full})\)¶

```
[7]:
```

```
morris_full = pd.DataFrame(pd.concat((results['mu_corr'], results['sigma_corr']), axis=1))
morris_full.columns = ['mu', 'sigma']
morris_full
```

```
[7]:
```

mu | sigma | |
---|---|---|

m | 2369.122666 | 2523.492389 |

c | 3270.066313 | 8210.375153 |

s | 2888.326540 | 3814.720109 |

### Plotting the results¶

The input ranking is conducted based on \((\mu_i^{\ast,\ ind}, \mu_i^{\ast,\ full})\).

```
[8]:
```

```
def plot_morris_indices(morris_full, morris_ind):
fig, ax = plt.subplots(2, 1)
sns.set_style("whitegrid")
sns.scatterplot(x=morris_full['mu'], y=morris_full['sigma'], data=morris_full, ax=ax[0])
sns.scatterplot(x=morris_ind['mu'], y=morris_ind['sigma'], data=morris_full, ax=ax[1])
ax[0].set_title('Full Morris indices')
ax[0].text(x=morris_full['mu'].iloc[0] + 20, y=morris_full['sigma'].iloc[0], s='m')
ax[0].text(x=morris_full['mu'].iloc[1] + 20, y=morris_full['sigma'].iloc[1], s='c')
ax[0].text(x=morris_full['mu'].iloc[2] + 20, y=morris_full['sigma'].iloc[2], s='s')
ax[1].set_title('Independent Morris indices')
ax[1].text(x=morris_ind['mu'].iloc[0] + 2, y=morris_ind['sigma'].iloc[0], s='m')
ax[1].text(x=morris_ind['mu'].iloc[1] + 2, y=morris_ind['sigma'].iloc[1], s='c')
ax[1].text(x=morris_ind['mu'].iloc[2] + 2, y=morris_ind['sigma'].iloc[2], s='s')
plt.tight_layout()
plt.show()
```

```
[9]:
```

```
plot_morris_indices(morris_full, morris_ind)
```

### Interpretation¶

The input ranking based on \((\mu_i^{\ast,\ ind}, \mu_i^{\ast,\ full})\) differs when independent or full indices are considered.

The ranking in ascending order according to *full* indices is \(c - s - m\), whereas the ranking based on *independent* inidces is \(m - c - s\). For input \(m\) this means that the variance contribution due to the isolated effect of \(m\) is much larger than the contribution due to dependence with other inputs. Inputs \(c\) and \(s\), though, exhibit large effects due to dependence.

Since none of the inputs is close to zero, we can conclude that all three inputs are important in terms of their output variance contribution.

```
[ ]:
```

```
```