Skip to content

Commit

Permalink
Merge pull request Netflix-Skunkworks#20 from Netflix-Skunkworks/add_…
Browse files Browse the repository at this point in the history
…sample_notebook

sample interactive notebook
  • Loading branch information
snkilmartin authored Oct 8, 2020
2 parents 19351ad + 54523d4 commit 481a456
Showing 1 changed file with 339 additions and 0 deletions.
339 changes: 339 additions & 0 deletions riskquant_demo.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,339 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Simple Risk Quantification\n",
"\n",
"Please see the Riskquant blog if you want detailed info on the simulation code: \n",
"https://medium.com/@NetflixTechBlog/open-sourcing-riskquant-a-library-for-quantifying-risk-6720cc1e4968\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Step 1:\n",
"\n",
"## Update the Loss Fields\n",
"\n",
"These are sample values only. Update these fields with values from your internal loss tables."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"# Dollar value of one hour of one employee\n",
"EMPLOYEE_HOURLY_COST = 50\n",
"\n",
"# Lowest cost to respond to an incident\n",
"LOW_RESPONSE_COST = 100\n",
"# Highest cost to respond to an incident\n",
"HIGH_RESPONSE_COST = 10000\n",
"\n",
"# Lowest cost of PII data loss\n",
"LOW_PII_COST = 10000\n",
"# Highest cost of PII data loss\n",
"HIGH_PII_COST = 1000000\n",
"\n",
"# Lowest cost of PCI data loss\n",
"LOW_PCI_COST = 100000\n",
"# Highest cost of PII data loss\n",
"HIGH_PCI_COST = 10000000\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Step 2\n",
"\n",
"## Run the notebook\n",
"\n",
" 1. Click Cell > Run All\n",
" 2. Scroll down to the bottom of this notebook\n",
" 3. Under **Risk Quantification** make selections based on your scenario, and click Simulate Risk"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# If you have not yet installed riskquant, install it\n",
"# %pip install --upgrade riskquant"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"text/markdown": [
"# Risk Quantification\n",
"\n",
"**This is a starting point. Please work with your Risk team for a detailed Risk Quantification.**\n",
"\n",
"**This is only a simulation based on the loss information entered.**\n",
"\n",
"**Instructions:**\n",
"- Select a Loss Event Frequency: The frequency within the next year that loss will materialize from a threat agent’s action.\n",
"- Enter the number of employees involved in responding to the scenario.\n",
"- Enter the number of hours each employee will spend responding to the scenario.\n",
"- Select if the scenario includes PCI and/or PII data.\n",
"- Click **Simulate Risk**"
],
"text/plain": [
"<IPython.core.display.Markdown object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "54f168bdce2342ffbf663d5d0d0879e0",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(Dropdown(description='Loss Event Frequency:', layout=Layout(width='500px'), options=('Once per …"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# You can change the code here but you shouldn't need to... \n",
"# Cnce you've clicked Cell > Run all, scroll to the bottom of this cell for the interactive section\n",
"from IPython.display import display, Markdown, clear_output\n",
"import ipywidgets as widgets\n",
"from matplotlib import pyplot as plt\n",
"from matplotlib import ticker as mtick\n",
"import numpy as np\n",
"from riskquant import pertloss\n",
"\n",
"def loss_exceedance_curve(simulate,\n",
" title=\"Aggregated Loss Exceedance\",\n",
" xlim=[100000, 1000000000]):\n",
" losses = np.array([np.percentile(simulate, x) for x in range(1, 100, 1)])\n",
" percentiles = np.array([float(100 - x) / 100.0 for x in range(1, 100, 1)])\n",
" _ = plt.figure()\n",
" ax = plt.gca()\n",
" ax.plot(losses, percentiles)\n",
" plt.title(title)\n",
" ax.set_xscale(\"log\")\n",
" ax.set_ylim(0.0, percentiles[np.argmax(losses > 0.0)] + 0.05)\n",
" ax.set_xlim(xlim[0], xlim[1])\n",
" xtick = mtick.StrMethodFormatter('${x:,.0f}')\n",
" ax.xaxis.set_major_formatter(xtick)\n",
" ytick = mtick.StrMethodFormatter('{x:.000%}')\n",
" ax.yaxis.set_major_formatter(ytick)\n",
" plt.grid(which='both')\n",
" plt.show()\n",
"\n",
"# Frequency widgets\n",
"frequency_menu = widgets.Dropdown(\n",
" options=['Once per Day',\n",
" 'Once per Week',\n",
" 'Once per Month',\n",
" 'Once per Three Months',\n",
" 'Once per Year',\n",
" 'Once per Two Years',\n",
" 'Once per Ten Years',\n",
" 'Once per One Hundred Years'],\n",
" style={'description_width': '300px'},\n",
" layout={'width': '500px'},\n",
" description='Loss Event Frequency:')\n",
"\n",
"# Magnitude widgets\n",
"emp = widgets.Text(value='0',description='Number of employees resolving scenario', \n",
" style={'description_width': '300px'},\n",
" layout={'width': '400px'} )\n",
"emp_hours = widgets.Text(value='0',description='Number of hours each employee will spend',\n",
" style={'description_width': '300px'},\n",
" layout={'width': '400px'} )\n",
"pci_checkbox = widgets.Checkbox(description='Scenario includes loss of PCI data',\n",
" layout={'width': '400px'} )\n",
"pii_checkbox = widgets.Checkbox(description='Scenario includes loss of PII data',\n",
" layout={'width': '400px'})\n",
"\n",
"risk_button = widgets.Button(description='Simulate Risk')\n",
"\n",
"out = widgets.Output()\n",
"\n",
"def on_button_clicked(b):\n",
" with out:\n",
" clear_output() \n",
" freq_input = frequency_menu.value\n",
"\n",
" if freq_input == 'Once per Day':\n",
" minimum_frequency = 310\n",
" maximum_frequency = 420\n",
" most_likely_frequency = 365\n",
" elif freq_input == 'Once per Week':\n",
" minimum_frequency = 44\n",
" maximum_frequency = 60\n",
" most_likely_frequency = 52 \n",
" elif freq_input == 'Once per Month':\n",
" minimum_frequency = 8\n",
" maximum_frequency = 16\n",
" most_likely_frequency = 12 \n",
" elif freq_input == 'Once per Three Months':\n",
" minimum_frequency = 2\n",
" maximum_frequency = 6\n",
" most_likely_frequency = 4 \n",
" elif freq_input == 'Once per Year':\n",
" minimum_frequency = 0.5\n",
" maximum_frequency = 2\n",
" most_likely_frequency = 1 \n",
" elif freq_input == 'Once per Two Years':\n",
" minimum_frequency = 0.3\n",
" maximum_frequency = 1\n",
" most_likely_frequency = 0.5 \n",
" elif freq_input == 'Once per Ten Years':\n",
" minimum_frequency = 0.05\n",
" maximum_frequency = 0.25\n",
" most_likely_frequency = 0.1 \n",
" elif freq_input == 'Once per One Hundred Years':\n",
" minimum_frequency = 0.005\n",
" maximum_frequency = 0.015\n",
" most_likely_frequency = 0.01 \n",
" else:\n",
" minimum_frequency = 0.05\n",
" maximum_frequency = 0.25\n",
" most_likely_frequency = 0.1 \n",
"\n",
" if pci_checkbox.value and pii_checkbox.value:\n",
" low_loss = LOW_PCI_COST + LOW_PII_COST \n",
" high_loss = HIGH_PCI_COST + HIGH_PII_COST\n",
" elif pci_checkbox.value and pii_checkbox.value == False:\n",
" low_loss = LOW_PCI_COST\n",
" high_loss = HIGH_PCI_COST \n",
" elif pci_checkbox.value == False and pii_checkbox.value:\n",
" low_loss = LOW_PII_COST\n",
" high_loss = HIGH_PII_COST \n",
" else:\n",
" low_loss = LOW_RESPONSE_COST\n",
" high_loss = HIGH_RESPONSE_COST \n",
"\n",
"\n",
" try:\n",
" emp_int = int(emp.value)\n",
" except:\n",
" print('Please enter an integer for number of employees')\n",
"\n",
" try:\n",
" emp_hours_int = int(emp_hours.value)\n",
" except:\n",
" print('Please enter an integer for number of employees')\n",
"\n",
" emp_hour_cost = emp_hours_int * EMPLOYEE_HOURLY_COST\n",
" emp_cost = emp_hour_cost * emp_int\n",
"\n",
" high_loss += emp_cost\n",
"\n",
" p = pertloss.PERTLoss(low_loss=low_loss, \n",
" high_loss=high_loss, \n",
" min_freq=minimum_frequency, \n",
" max_freq=maximum_frequency, \n",
" most_likely_freq=most_likely_frequency)\n",
" simulate = p.simulate_years(50000)\n",
" loss_summary = p.summarize_loss(simulate)\n",
" annualized_loss_exposure = loss_summary['ninetieth_percentile'] * most_likely_frequency\n",
" print('Annualized Loss Exposure (worst case):\\n')\n",
" ale = round((most_likely_frequency * loss_summary['ninetieth_percentile']), -3)\n",
" print('$', ale )\n",
" print('\\nLoss Event Frequency:\\n')\n",
" print(freq_input)\n",
" print('\\nLoss summary:')\n",
" print(loss_summary)\n",
" loss_exceedance_curve(simulate)\n",
"\n",
"\n",
"risk_button.on_click(on_button_clicked)\n",
"info = Markdown(\"\"\"# Risk Quantification\n",
"\n",
"**This is a starting point. Please work with your Risk team for a detailed Risk Quantification.**\n",
"\n",
"**This is only a simulation based on the loss information entered.**\n",
"\n",
"**Instructions:**\n",
"- Select a Loss Event Frequency: The frequency within the next year that loss will materialize from a threat agent’s action.\n",
"- Enter the number of employees involved in responding to the scenario.\n",
"- Enter the number of hours each employee will spend responding to the scenario.\n",
"- Select if the scenario includes PCI and/or PII data.\n",
"- Click **Simulate Risk**\"\"\")\n",
"\n",
"grapher = widgets.VBox([frequency_menu, emp, emp_hours, pci_checkbox, \n",
" pii_checkbox, risk_button, out])\n",
"display(info, grapher)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"hide_input": false,
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.2"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {
"height": "calc(100% - 180px)",
"left": "10px",
"top": "150px",
"width": "165px"
},
"toc_section_display": true,
"toc_window_display": true
}
},
"nbformat": 4,
"nbformat_minor": 4
}

0 comments on commit 481a456

Please sign in to comment.