{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Elementary effects sensitivity analysis method" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " \n", "\n", "This notebook is an element of the [risk-engineering.org courseware](https://risk-engineering.org/). It is distributed under the terms of the [Creative Commons Attribution-ShareAlike licence](https://creativecommons.org/licenses/by-sa/4.0/).\n", "\n", "Author: Eric Marsden \n", "\n", "---\n", "\n", "This notebook contains an introduction to the Elementary Effects method for sensitivity analysis, using Python and [SciPy](https://scipy.org/). It uses some Python3 features. Consult the [accompanying slides on risk-engineering.org](https://risk-engineering.org/sensitivity-analysis/) for details of the applications of sensitivity analysis and some intuition and theory of the technique, and to download this content as a Jupyter/Python notebook." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import numpy\n", "import scipy.stats\n", "import matplotlib.pyplot as plt\n", "plt.style.use(\"bmh\")\n", "%config InlineBackend.figure_formats=[\"svg\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let us consider the following function of 6 parameters, $x_1$ to $x_6$. \n", "\n", "$y(\\textbf{x}) = 1.0 + 1.5x_2 + 1.5x_3 + 0.6x_4 + 1.7x_4^2 + 0.7x_5 + 0.8x_6 + 0.5(x_5 x_6)$\n", "\n", "The parameters are stored in a Python vector. " ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "def y(x) -> float:\n", " return 1.0 + 1.5*x + 1.5*x + 0.6*x + 1.7*x**2 + 0.7*x + 0.8*x + 0.5*(x*x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We are interested in studying the behaviour of $y$ over the domain \n", "\n", "- $0 ≤ x_1, x_2, x_4, x_5, x_6 ≤ 1$\n", "\n", "- $0 ≤ x_3 ≤ 5$\n", "\n", "where we assume the input variables all follow a uniform distribution." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We start by defining a function which will generate $\\Delta\\textbf{e}_i$, the unit vector in direction of the $i$-th axis. We choose to use a $\\Delta$ of 10% of the range of each variable, so 0.1 for most variables and 0.5 for $x_3$." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "def delta(i) -> float:\n", " x = numpy.zeros(7)\n", " x = 1 # this is not used\n", " x[i] = 0.1\n", " if i == 3: x = 0.5\n", " return x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then a function to generate a random variate for $\\textbf{x}$, a randomly selected point in our input space." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "def random_x() -> float:\n", " x = numpy.ones(7)\n", " x = numpy.random.uniform(0, 1)\n", " x = numpy.random.uniform(0, 1)\n", " x = numpy.random.uniform(0, 5)\n", " x = numpy.random.uniform(0, 1)\n", " x = numpy.random.uniform(0, 1)\n", " x = numpy.random.uniform(0, 1)\n", " return x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We need a function to calculate the elementary effect of variable number $i$ at point $\\textbf{x}$. Recall that the *elementary effect* for the $i$-th input variable at $\\mathbf{x} ∈ [0,1]^k$ is the first difference approximation to the derivative of $f(·)$ at $\\mathbf{x}$:\n", " \n", "$EE_i(\\mathbf{x}) = \\frac{f(\\textbf{x} + ∆\\textbf{e}_i) − f(\\textbf{x})}{∆}$\n", " \n", "where $\\textbf{e}_i$ is the unit vector in the direction of the $i$-th axis\n", "\n", "**Intuition**: it’s the slope of the secant line parallel to the input axis." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "def EE(i, x) -> float:\n", " return (y(x+delta(i)) - y(x))/0.1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To estimate the **sensitivity** (the relative influence) of variable number $i$, we calculate the average of $EE_i(\\textbf{x})$ for various points $\\textbf{x}$ randomly selected from the input domain, using our random_x function defined above:\n", "\n", "$μ_i = \\frac{1}{r} ∑_{j=1}^{r} \\left|EE_i(x_j)\\right|$" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def sensitivity(i) -> float:\n", " sample = numpy.zeros(100)\n", " for j in range(100):\n", " sample[j] = EE(i, random_x())\n", " return numpy.absolute(sample).mean()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let’s plot the results for our function $y$ defined above." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n" ], "text/plain": [ "