{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "68607c77",
   "metadata": {},
   "source": [
    "<p align=\"center\">\n",
    "    <img src=\"https://github.com/GeostatsGuy/GeostatsPy/blob/master/TCG_color_logo.png?raw=true\" width=\"220\" height=\"240\" />\n",
    "\n",
    "</p>\n",
    "\n",
    "## Interactive Demonstration of Marginal, Joint and Conditional Probabilities and Distributions \n",
    "\n",
    "### Michael J. Pyrcz, Professor, The University of Texas at Austin \n",
    "\n",
    "*Novel Data Analytics, Geostatistics and Machine Learning Subsurface Solutions*"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eaf4d272",
   "metadata": {},
   "source": [
    "### Joint and Conditional Probabilities and Distributions\n",
    "\n",
    "First, let's brielfy recall some basic concepts, but for a more complete discussion, please see the following lectures with linked Python demonstrations.\n",
    "\n",
    "I have recorded a walk-through of this interactive dashboard in my [Data Science Interactive Python Demonstrations](https://www.youtube.com/playlist?list=PLG19vXLQHvSDy26fM3hDLg3VCU7U5BGZl) series on my [YouTube](https://www.youtube.com/@GeostatsGuyLectures) channel.\n",
    "\n",
    "* Join me for walk-through of this dashboard [Chapter 01: Marginal, Joint and Conditional Probabilities and Distributions](https://www.youtube.com/watch?v=4eh8mTJf3O4&t=25s). I'm stoked to guide you, share observations and things to try out!\n",
    "\n",
    "* I have a lecture on [Probability](https://www.youtube.com/watch?v=IGPayWv1BBM&t=140s) as part of my [Data Analytics and Geostatistics](https://www.youtube.com/playlist?list=PLG19vXLQHvSB-D4XKYieEku9GQMQyAzjJ). Note, for all my recorded lecture the interactive and well-documented workflow demononstrations are available on my GitHub repository [GeostatsGuy's Python Numerical Demos](https://github.com/GeostatsGuy/PythonNumericalDemos).\n",
    "\n",
    "* Also, I have a lecture on [Histograms, PDFs and CDFs](https://www.youtube.com/watch?v=TbqaMXdSV4I&list=PLG19vXLQHvSB-D4XKYieEku9GQMQyAzjJ&index=9) as part of my [Data Analytics and Geostatistics](https://www.youtube.com/playlist?list=PLG19vXLQHvSB-D4XKYieEku9GQMQyAzjJ). Note, for all my recorded lecture the interactive and well-documented workflow demononstrations are available on my GitHub repository [GeostatsGuy's Python Numerical Demos](https://github.com/GeostatsGuy/PythonNumericalDemos).\n",
    "\n",
    "##### Probability \n",
    "\n",
    "Measure of the likelihood that an event will occur.  For random experiments and well-defined settings (such as coin tosses): \n",
    "\n",
    "\\begin{equation}\n",
    "Prob(𝐴)=P(𝐴)=lim_{𝑛\\rightarrow \\infty} \\frac{𝑛(𝐴)}{𝑛(Ω)} \n",
    "\\end{equation}\n",
    "\n",
    "$n(A) =$ number of times event $A$ occurred $n(\\Omega) =$ number of trails\n",
    "\n",
    "##### Distribution\n",
    "\n",
    "For a variable / feature a description of the likelihood or probability of occurrence over the range of possible values.\n",
    "\n",
    "What do we get from a statistical distribution?\n",
    "\n",
    "* what is the minimum and maximum?\n",
    "\n",
    "* do we have a lot of low values?\n",
    "\n",
    "* do we have a lot of high values?\n",
    "\n",
    "* do we have outliers (values that don’t make sense and need explaining)?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "86a14a55",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 61540 characters]",
      "text/plain": [
       "<Figure size 640x480 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np; import matplotlib.pyplot as plt               \n",
    "Y = np.random.normal(loc = 0.2,scale=0.03,size=200)\n",
    "plt.subplot(121)\n",
    "plt.hist(Y,color='darkorange',edgecolor='black'); plt.xlabel('Feature'); plt.ylabel('Frequency'); plt.title('Histogram')\n",
    "plt.subplot(122)\n",
    "plt.scatter(np.sort(Y),np.arange(1,len(Y)+1,1)/len(Y),color='darkorange',edgecolor='black'); plt.xlabel('Feature'); plt.ylabel('Cumulative Probability'); plt.title('Cumulative Distribution Function'); plt.ylim([0,1])\n",
    "plt.subplots_adjust(left=0.0,bottom=0.0,right=2.0,top=1.1); plt.show() # set plot size"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "71cb158f",
   "metadata": {},
   "source": [
    "##### Maginal, Joint or Conditional Probility or Distribution\n",
    "\n",
    "**Marginal** - related to a single event $A$ or a single feature, probability $P(A)$ or distribution $f_X(x)$ \n",
    "\n",
    "**Joint** - related to more than one event $A$ or feature, probability $P(A,B,C)$ or distribution $f_{X,Y,Z}(x,y,z)$ \n",
    "\n",
    "**Conditional** - given a condition, the probability $P(A|B)$ [read as probability A given B] or distribution $f_{X|Y}(x|y)$ \n",
    "\n",
    "##### Pearson Product Moment Correlation Coefficient (Correlation Coefficient)\n",
    "\n",
    "The correlation coefficient measures the linear relationship between two features:\n",
    "\n",
    "\\begin{equation}\n",
    "\\rho_{𝑋,𝑌}= \\sum_{𝑖=1}^𝑛 \\frac{(𝑥_𝑖 - \\overline{x})(y_i - \\overline{y})} {(𝑛−1) \\sigma_𝑥 \\sigma_𝑦 }, \\quad −1.0 \\le \\rho_{X,Y} \\le 1.0\n",
    "\\end{equation}\n",
    "\n",
    "#### Getting Started\n",
    "\n",
    "Here's the steps to get setup in Python with the GeostatsPy package:\n",
    "\n",
    "1. Install Anaconda 3 on your machine (https://www.anaconda.com/download/). \n",
    "\n",
    "That's all!\n",
    "\n",
    "#### Load and Configure the Required Libraries\n",
    "\n",
    "The following code loads the required libraries and sets a plotting default."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "a0b427ac",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os                                       # operating system\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt                 # plotting\n",
    "from matplotlib.ticker import (MultipleLocator, AutoMinorLocator, AutoLocator) # control of axes ticks\n",
    "plt.rc('axes', axisbelow=False)                 # set axes and grids in the background for all plots\n",
    "import matplotlib.patches as patches\n",
    "from ipywidgets import interactive              # widgets and interactivity\n",
    "from ipywidgets import widgets                            \n",
    "from ipywidgets import Layout\n",
    "from ipywidgets import Label\n",
    "from ipywidgets import VBox, HBox\n",
    "import warnings\n",
    "warnings.filterwarnings('ignore')               # supress warnings"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "af526285",
   "metadata": {},
   "source": [
    "#### Calculate a Synthetic Mining Grade Bivariate Dataset\n",
    "\n",
    "Let's make a bivariate distribution for Gold and Silver grades with the following steps:\n",
    "\n",
    "1. draw values from a bivariate Guassian distribution with some correlation\n",
    "2. impose a nonlinear relationship with a specified weight\n",
    "3. correct the mean and standard deviations of each feature\n",
    "\n",
    "Then we scatter plot to check the data visually."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "266c7f76",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 323324 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "seed = 73073; n = 5000\n",
    "Ag_mean = 125; Au_mean = 8\n",
    "Ag_stdev = 20; Au_stdev = 1; wt_poly = 0.002\n",
    "np.random.seed(seed=seed)\n",
    "X = np.random.multivariate_normal([Au_mean,Ag_mean],[[9, 0],[0, 400]],size = n)\n",
    "X[:,0] = (1.0-wt_poly)*X[:,0] + wt_poly*(-0.5*np.power(X[:,1]-200,2) + 10)\n",
    "X[:,0] = (X[:,0] - np.mean(X[:,0])) * Au_stdev/np.std(X[:,0]) + Au_mean\n",
    "\n",
    "plt.subplot(111)\n",
    "plt.scatter(X[:,1],X[:,0],c='darkorange',s = 20,alpha=0.2,edgecolor='black')\n",
    "plt.annotate('Correlation = ' + str(np.round(np.corrcoef(X[:,1],X[:,0])[0,1],3)),[70,10.5])\n",
    "plt.xlabel('Silver, Ag, Grade (g/t)'); plt.ylabel('Gold, Au, Grade (g/t)'); plt.title('Mineral Grades')\n",
    "plt.xlim([60,200]); plt.ylim([4,11])\n",
    "\n",
    "plt.gca().grid(True, which='major',linewidth = 1.0); plt.gca().grid(True, which='minor',linewidth = 0.2) # add y grids\n",
    "plt.gca().tick_params(which='major',length=7); plt.gca().tick_params(which='minor', length=4)\n",
    "plt.gca().xaxis.set_minor_locator(AutoMinorLocator()); plt.gca().yaxis.set_minor_locator(AutoMinorLocator()) # turn on minor ticks\n",
    "plt.subplots_adjust(left=0.0,bottom=0.0,right=1.0,top=1.1); plt.show() # set plot size"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "14640512",
   "metadata": {},
   "source": [
    "#### Build the Interactive Dashboard to Demonstrate Joint Probability\n",
    "\n",
    "The following code:\n",
    "\n",
    "* take the previous synthetic dataset and specifiy events:\n",
    "    * A - silver grade within a bin\n",
    "    * B - gold grade within a bin\n",
    "    \n",
    "* calculate conditional probability, $P\\{B , A\\}$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "689ab6e1",
   "metadata": {},
   "outputs": [],
   "source": [
    "l = widgets.Text(value='                               Joint Probability Demo, Prof. Michael Pyrcz, The University of Texas at Austin',\n",
    "                 layout=Layout(width='750px', height='30px'))\n",
    "\n",
    "n = widgets.IntSlider(min=0, max = 5000, value=1000, step = 100, description = '$n$',orientation='horizontal', style = {'description_width': 'initial'}, continuous_update=False)\n",
    "ix = widgets.IntSlider(min=0, max = 6, value=1, step = 1, description = r'Silver Bin, $i_{\\theta_{Ag}}$',orientation='horizontal', style = {'description_width': 'initial'}, continuous_update=False)\n",
    "iy = widgets.IntSlider(min=0, max = 6, value=1, step = 1, description = r'Gold Bin, $i_{\\theta_{Au}}$',orientation='horizontal', style = {'description_width': 'initial'}, continuous_update=False)\n",
    "\n",
    "ui = widgets.HBox([n,ix,iy],)\n",
    "ui2 = widgets.VBox([l,ui],)\n",
    "\n",
    "def run_plot(n,ix,iy):\n",
    "    seed = 73073\n",
    "    Ag_mean = 125; Au_mean = 8\n",
    "    Ag_stdev = 20; Au_stdev = 1; wt_poly = 0.002\n",
    "    np.random.seed(seed=seed)\n",
    "    X = np.random.multivariate_normal([Au_mean,Ag_mean],[[9, 0],[0, 400]],size = n)\n",
    "    X[:,0] = (1.0-wt_poly)*X[:,0] + wt_poly*(-0.5*np.power(X[:,1]-200,2) + 10)\n",
    "    X[:,0] = (X[:,0] - np.mean(X[:,0])) * Au_stdev/np.std(X[:,0]) + Au_mean\n",
    "      \n",
    "    xsiz = 20; ysiz=1;\n",
    "    x_start = ix*xsiz + 60; x_end = (ix+1)*xsiz + 60\n",
    "    y_start = iy*ysiz + 4; y_end = (iy+1)*ysiz + 4\n",
    "    \n",
    "    count = ((y_start < X[:,0]) & (X[:,0] < y_end) & (x_start < X[:,1]) & (X[:,1] < x_end)).sum()\n",
    "    \n",
    "    Xsub = X[((y_start < X[:,0]) & (X[:,0] < y_end) & (x_start < X[:,1]) & (X[:,1] < x_end))]\n",
    "    \n",
    "    fig, axs = plt.subplots(1,2,figsize=(16,7), gridspec_kw={'width_ratios': [0.8, 1]})\n",
    "    \n",
    "    axs[0].scatter(X[:,1],X[:,0],c='r',s = 20,alpha=0.2,edgecolor='black')\n",
    "    axs[0].scatter(Xsub[:,1],Xsub[:,0],c='black',s = 20,alpha=0.6,edgecolor='black')\n",
    "    axs[0].set_xlabel('Silver, Ag, Grade (g/t)'); axs[0].set_ylabel('Gold, Au, Grade (g/t)'); axs[0].set_title('Mineral Grades Scatter Plot')\n",
    "    axs[0].set_xlim([60,200]); axs[0].set_ylim([4,11])\n",
    "    \n",
    "    axs[0].grid(True, which='major',linewidth = 1.0); axs[0].grid(True, which='minor',linewidth = 0.2) # add y grids\n",
    "    axs[0].tick_params(which='major',length=7); axs[0].tick_params(which='minor', length=4)\n",
    "    axs[0].xaxis.set_minor_locator(AutoMinorLocator(n=10)); axs[0].yaxis.set_minor_locator(AutoMinorLocator(n=10)) # turn on minor ticks\n",
    "    axs[0].xaxis.set_major_locator(MultipleLocator(xsiz)); axs[0].yaxis.set_major_locator(MultipleLocator(ysiz)) # turn on minor ticks\n",
    "    \n",
    "    rect = patches.Rectangle((x_start, 4), xsiz, 7*ysiz, linewidth=3, edgecolor='green', facecolor='none'); axs[0].add_patch(rect)\n",
    "    rect = patches.Rectangle((60, y_start), 7*xsiz, ysiz, linewidth=3, edgecolor='blue', facecolor='none'); axs[0].add_patch(rect)\n",
    "    rect = patches.Rectangle((x_start, y_start), xsiz, ysiz, linewidth=5, edgecolor='black', facecolor='none'); axs[0].add_patch(rect)\n",
    "    \n",
    "    axs[0].annotate(r'A = [' + str(x_start) + r' ≤ $\\bf{\\theta_{Ag}}$ ≤ ' + str(x_end) + ']',\n",
    "                    (150,4.6),color='green',weight=\"bold\")\n",
    "    \n",
    "    axs[0].annotate(r'B = [' + str(y_start) + r' ≤ $\\bf{\\theta_{Au}}$ ≤ ' + str(y_end) + ']',\n",
    "                    (150,4.3),color='blue',weight=\"bold\")\n",
    "    \n",
    "    axs[0].annotate(r'$\\bf{A}$',(x_start+0.4*xsiz,10.7),size=15,color='green')\n",
    "    axs[0].annotate(r'$\\bf{B}$',(61.5,y_start+0.4*ysiz),size=15,color='blue')\n",
    "    axs[0].annotate(r'$\\bf{A \\bigcap B}$',(x_start+0.2*xsiz,y_start+0.4*ysiz),size=10,color='black')\n",
    "    \n",
    "    if ix <= 3:\n",
    "        axs[0].annotate(r'n{A $\\bigcap$ B} = ' + str(count),\n",
    "                    (x_end+0.2*xsiz,y_start+ysiz*0.5),color='black',weight=\"bold\")\n",
    "    else:\n",
    "        axs[0].annotate(r'n{A $\\bigcap$ B} = ' + str(count),\n",
    "                    (x_end-2.8*xsiz,y_start+ysiz*0.5),color='black',weight=\"bold\")\n",
    "    \n",
    "    hist2d = axs[1].hist2d(X[:,1],X[:,0], bins=7, range=[[60,200],[4,11]], density=False, weights=None, cmin=None, cmax=None,\n",
    "                       cmap=plt.cm.Reds)\n",
    "    axs[1].set_xlabel('Silver, Ag, Grade (g/t)'); axs[1].set_ylabel('Gold, Au, Grade (g/t)'); axs[1].set_title('Mineral Grades Frequency Table')\n",
    "    axs[1].set_xlim([60,200]); axs[1].set_ylim([4,11])\n",
    "    \n",
    "    cbar = plt.colorbar(hist2d[3], orientation=\"vertical\")\n",
    "    cbar.set_label('Frequency', rotation=270, labelpad=20)\n",
    "    \n",
    "    axs[1].grid(True, which='major',linewidth = 1.0); axs[1].grid(True, which='minor',linewidth = 0.2) # add y grids\n",
    "    axs[1].tick_params(which='major',length=7); axs[1].tick_params(which='minor', length=4)\n",
    "    axs[1].xaxis.set_minor_locator(AutoMinorLocator(n=10)); axs[1].yaxis.set_minor_locator(AutoMinorLocator(n=10)) # turn on minor ticks\n",
    "    axs[1].xaxis.set_major_locator(MultipleLocator(xsiz)); axs[1].yaxis.set_major_locator(MultipleLocator(ysiz)) # turn on minor ticks\n",
    "    \n",
    "    rect = patches.Rectangle((x_start, 4), xsiz, 7*ysiz, linewidth=3, edgecolor='green', facecolor='none'); axs[1].add_patch(rect)\n",
    "    rect = patches.Rectangle((60, y_start), 7*xsiz, ysiz, linewidth=3, edgecolor='blue', facecolor='none'); axs[1].add_patch(rect)\n",
    "    rect = patches.Rectangle((x_start, y_start), xsiz, ysiz, linewidth=5, edgecolor='black', facecolor='none'); axs[1].add_patch(rect)\n",
    "    \n",
    "    axs[1].scatter(X[:,1],X[:,0],c='black',s = 20,alpha=0.1,edgecolor='black')\n",
    "    axs[1].scatter(Xsub[:,1],Xsub[:,0],c='black',s = 20,alpha=0.3,edgecolor='black')\n",
    "    \n",
    "    axs[1].annotate(r'A = [' + str(x_start) + r' ≤ $\\bf{\\theta_{Ag}}$ ≤ ' + str(x_end) + ']',\n",
    "                    (150,4.6),color='green',weight=\"bold\")\n",
    "    \n",
    "    axs[1].annotate(r'$\\bf{A}$',(x_start+0.4*xsiz,10.7),size=15,color='green')\n",
    "    axs[1].annotate(r'$\\bf{B}$',(61.5,y_start+0.4*ysiz),size=15,color='blue')\n",
    "    axs[1].annotate(r'$\\bf{A \\bigcap B}$',(x_start+0.2*xsiz,y_start+0.4*ysiz),size=10,color='black')\n",
    "    \n",
    "    axs[1].annotate(r'B = [' + str(y_start) + r' ≤ $\\bf{\\theta_{Au}}$ ≤ ' + str(y_end) + ']',\n",
    "                    (150,4.3),color='blue',weight=\"bold\")\n",
    "    \n",
    "    if ix <= 3:\n",
    "        z = axs[1].annotate(r'P{A $\\bigcap$ B} = ' + str(count) + '/' + str(n),\n",
    "                    (x_end+0.2*xsiz,y_start+ysiz*0.5),color='black',weight=\"bold\")\n",
    "    else:\n",
    "        z = axs[1].annotate(r'P{A $\\bigcap$ B} = ' + str(count) + '/' + str(n),\n",
    "                    (x_end-3.5*xsiz,y_start+ysiz*0.5),color='black',weight=\"bold\")\n",
    "\n",
    "# connect the function to make the samples and plot to the widgets    \n",
    "interactive_plot = widgets.interactive_output(run_plot, {'n':n,'ix':ix,'iy':iy,})\n",
    "interactive_plot.clear_output(wait = True)               # reduce flickering by delaying plot updating   "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "99d7bf3a",
   "metadata": {},
   "source": [
    "### Interactive Joint Probability Demonstation \n",
    "\n",
    "#### Michael Pyrcz, Professor, The University of Texas at Austin \n",
    "\n",
    "Change the number of sample data, select silver and gold bins.\n",
    "\n",
    "### The Inputs\n",
    "\n",
    "* **n** - number of data, **$i_{\\theta_{Ag}}$** - silver bin, and **$i_{\\theta_{Au}}$** - gold bin "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "29a2bd4b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "13bcd658c92343d89b7323277db7ec7e",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "VBox(children=(Text(value='                               Joint Probability Demo, Prof. Michael Pyrcz, The Uni…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "2a1b5916ac904b6a99841bfe6f909b1a",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Output()"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(ui2, interactive_plot)                           # display the interactive plot"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2221bc84",
   "metadata": {},
   "source": [
    "#### Build the Interactive Dashboard to Demonstrate Conditional Probability\n",
    "\n",
    "The following code:\n",
    "\n",
    "* take the previous synthetic dataset and specifiy events:\n",
    "    * A - silver grade within a bin\n",
    "    * B - gold grade within a bin\n",
    "    \n",
    "* calculate conditional probability, $P\\{B | A\\}$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "b314a066",
   "metadata": {},
   "outputs": [],
   "source": [
    "l = widgets.Text(value='                               Conditional Probability Demo, Prof. Michael Pyrcz, The University of Texas at Austin',\n",
    "                 layout=Layout(width='750px', height='30px'))\n",
    "\n",
    "n = widgets.IntSlider(min=0, max = 5000, value=1000, step = 100, description = '$n$',orientation='horizontal', style = {'description_width': 'initial'}, continuous_update=False)\n",
    "ix = widgets.IntSlider(min=0, max = 6, value=1, step = 1, description = r'Silver Bin, $i_{\\theta_{Ag}}$',orientation='horizontal', style = {'description_width': 'initial'}, continuous_update=False)\n",
    "iy = widgets.IntSlider(min=0, max = 6, value=1, step = 1, description = r'Gold Bin, $i_{\\theta_{Au}}$',orientation='horizontal', style = {'description_width': 'initial'}, continuous_update=False)\n",
    "\n",
    "ui = widgets.HBox([n,ix,iy],)\n",
    "ui3 = widgets.VBox([l,ui],)\n",
    "\n",
    "def run_plot(n,ix,iy):\n",
    "    seed = 73073\n",
    "    Ag_mean = 125; Au_mean = 8\n",
    "    Ag_stdev = 20; Au_stdev = 1; wt_poly = 0.002\n",
    "    np.random.seed(seed=seed)\n",
    "    X = np.random.multivariate_normal([Au_mean,Ag_mean],[[9, 0],[0, 400]],size = n)\n",
    "    X[:,0] = (1.0-wt_poly)*X[:,0] + wt_poly*(-0.5*np.power(X[:,1]-200,2) + 10)\n",
    "    X[:,0] = (X[:,0] - np.mean(X[:,0])) * Au_stdev/np.std(X[:,0]) + Au_mean\n",
    "      \n",
    "    xsiz = 20; ysiz=1;\n",
    "    x_start = ix*xsiz + 60; x_end = (ix+1)*xsiz + 60\n",
    "    y_start = iy*ysiz + 4; y_end = (iy+1)*ysiz + 4\n",
    "    \n",
    "    count = ((y_start < X[:,0]) & (X[:,0] < y_end) & (x_start < X[:,1]) & (X[:,1] < x_end)).sum()\n",
    "    count_a = ((x_start < X[:,1]) & (X[:,1] < x_end)).sum()\n",
    "    count_b = ((y_start < X[:,0]) & (X[:,0] < y_end)).sum()\n",
    "    \n",
    "    Xsub_a = X[((x_start < X[:,1]) & (X[:,1] < x_end))]\n",
    "    Xsub = X[((y_start < X[:,0]) & (X[:,0] < y_end) & (x_start < X[:,1]) & (X[:,1] < x_end))]\n",
    "    \n",
    "    fig, axs = plt.subplots(1,2,figsize=(16,7), gridspec_kw={'width_ratios': [0.8, 1]})\n",
    "    \n",
    "    axs[0].scatter(Xsub_a[:,1],Xsub_a[:,0],c='black',s = 20,alpha=0.4,edgecolor='black')\n",
    "    axs[0].scatter(X[:,1],X[:,0],c='r',s = 20,alpha=0.2,edgecolor='black')\n",
    "    axs[0].scatter(Xsub[:,1],Xsub[:,0],c='black',s = 20,alpha=0.8,edgecolor='black')\n",
    "    axs[0].set_xlabel('Silver, Ag, Grade (g/t)'); axs[0].set_ylabel('Gold, Au, Grade (g/t)'); axs[0].set_title('Mineral Grades Scatter Plot')\n",
    "    axs[0].set_xlim([60,200]); axs[0].set_ylim([4,11])\n",
    "    \n",
    "    axs[0].grid(True, which='major',linewidth = 1.0); axs[0].grid(True, which='minor',linewidth = 0.2) # add y grids\n",
    "    axs[0].tick_params(which='major',length=7); axs[0].tick_params(which='minor', length=4)\n",
    "    axs[0].xaxis.set_minor_locator(AutoMinorLocator(n=10)); axs[0].yaxis.set_minor_locator(AutoMinorLocator(n=10)) # turn on minor ticks\n",
    "    axs[0].xaxis.set_major_locator(MultipleLocator(xsiz)); axs[0].yaxis.set_major_locator(MultipleLocator(ysiz)) # turn on minor ticks\n",
    "    \n",
    "    rect = patches.Rectangle((x_start, 4), xsiz, 7*ysiz, linewidth=3, edgecolor='green', facecolor='none'); axs[0].add_patch(rect)\n",
    "    rect = patches.Rectangle((60, y_start), 7*xsiz, ysiz, linewidth=3,ls='--',edgecolor='blue', alpha=0.4, facecolor='none'); axs[0].add_patch(rect)\n",
    "    rect = patches.Rectangle((x_start, y_start), xsiz, ysiz, linewidth=5, edgecolor='black', facecolor='none'); axs[0].add_patch(rect)\n",
    "    \n",
    "    axs[0].annotate(r'A = [' + str(x_start) + r' ≤ $\\bf{\\theta_{Ag}}$ ≤ ' + str(x_end) + ']',\n",
    "                    (150,4.6),color='green',weight=\"bold\")\n",
    "    \n",
    "    axs[0].annotate(r'B = [' + str(y_start) + r' ≤ $\\bf{\\theta_{Au}}$ ≤ ' + str(y_end) + ']',\n",
    "                    (150,4.3),color='blue',weight=\"bold\")\n",
    "    \n",
    "    axs[0].annotate(r'$\\bf{A}$',(x_start+0.4*xsiz,10.7),size=15,color='green')\n",
    "    axs[0].annotate(r'$\\bf{B}$',(61.5,y_start+0.4*ysiz),size=15,color='blue')\n",
    "    axs[0].annotate(r'$\\bf{A \\bigcap B}$',(x_start+0.2*xsiz,y_start+0.4*ysiz),size=10,color='black')\n",
    "    \n",
    "    if ix <= 3:\n",
    "        axs[0].annotate(r'n{A $\\bigcap$ B} = ' + str(count),\n",
    "                    (x_end+0.2*xsiz,y_start+ysiz*0.6),color='black',weight=\"bold\")\n",
    "        axs[0].annotate(r'n{A} = ' + str(count_a),\n",
    "                    (x_end+0.2*xsiz,y_start+ysiz*0.2),color='black',weight=\"bold\")\n",
    "    else:\n",
    "        axs[0].annotate(r'n{A $\\bigcap$ B} = ' + str(count),\n",
    "                    (x_end-2.8*xsiz,y_start+ysiz*0.5),color='black',weight=\"bold\")\n",
    "    \n",
    "    hist2d = axs[1].hist2d(X[:,1],X[:,0], bins=7, range=[[60,200],[4,11]], density=False, weights=None, cmin=None, cmax=None,\n",
    "                       cmap=plt.cm.Reds)\n",
    "    axs[1].set_xlabel('Silver, Ag, Grade (g/t)'); axs[1].set_ylabel('Gold, Au, Grade (g/t)'); axs[1].set_title('Mineral Grades Conditional Frequency Table')\n",
    "    axs[1].set_xlim([60,200]); axs[1].set_ylim([4,11])\n",
    "    \n",
    "    cbar = plt.colorbar(hist2d[3], orientation=\"vertical\")\n",
    "    cbar.set_label('Frequency', rotation=270, labelpad=20)\n",
    "    \n",
    "    axs[1].grid(True, which='major',linewidth = 1.0); axs[1].grid(True, which='minor',linewidth = 0.2) # add y grids\n",
    "    axs[1].tick_params(which='major',length=7); axs[1].tick_params(which='minor', length=4)\n",
    "    axs[1].xaxis.set_minor_locator(AutoMinorLocator(n=10)); axs[1].yaxis.set_minor_locator(AutoMinorLocator(n=10)) # turn on minor ticks\n",
    "    axs[1].xaxis.set_major_locator(MultipleLocator(xsiz)); axs[1].yaxis.set_major_locator(MultipleLocator(ysiz)) # turn on minor ticks\n",
    "    \n",
    "    rect = patches.Rectangle((60, 4), xsiz*ix, 7*ysiz, linewidth=0, edgecolor='none', facecolor='white'); axs[1].add_patch(rect)\n",
    "    rect = patches.Rectangle((60+xsiz*(ix+1), 4), 60+xsiz*(7 - (ix+1)), 7*ysiz, linewidth=0, edgecolor='none', facecolor='white'); axs[1].add_patch(rect)\n",
    "    \n",
    "    rect = patches.Rectangle((x_start, 4), xsiz, 7*ysiz, linewidth=3, edgecolor='green', facecolor='none'); axs[1].add_patch(rect)\n",
    "    rect = patches.Rectangle((60, y_start), 7*xsiz, ysiz, linewidth=3,ls='--',edgecolor='blue', alpha=0.4, facecolor='none'); axs[1].add_patch(rect)\n",
    "    rect = patches.Rectangle((x_start, y_start), xsiz, ysiz, linewidth=5, edgecolor='black', facecolor='none'); axs[1].add_patch(rect)\n",
    "    \n",
    "    axs[1].scatter(Xsub_a[:,1],Xsub_a[:,0],c='black',s = 20,alpha=0.2,edgecolor='black')\n",
    "    axs[1].scatter(X[:,1],X[:,0],c='black',s = 20,alpha=0.03,edgecolor='black')\n",
    "    axs[1].scatter(Xsub[:,1],Xsub[:,0],c='black',s = 20,alpha=0.6,edgecolor='black')\n",
    "    \n",
    "    axs[1].annotate(r'A = [' + str(x_start) + r' ≤ $\\bf{\\theta_{Ag}}$ ≤ ' + str(x_end) + ']',\n",
    "                    (150,4.6),color='green',weight=\"bold\")\n",
    "    \n",
    "    axs[1].annotate(r'$\\bf{A}$',(x_start+0.4*xsiz,10.7),size=15,color='green')\n",
    "    axs[1].annotate(r'$\\bf{B}$',(61.5,y_start+0.4*ysiz),size=15,color='blue')\n",
    "    axs[1].annotate(r'$\\bf{A \\bigcap B}$',(x_start+0.2*xsiz,y_start+0.4*ysiz),size=10,color='black')\n",
    "    \n",
    "    axs[1].annotate(r'B = [' + str(y_start) + r' ≤ $\\bf{\\theta_{Au}}$ ≤ ' + str(y_end) + ']',\n",
    "                    (150,4.3),color='blue',weight=\"bold\")\n",
    "    \n",
    "    if ix <= 3:\n",
    "        z = axs[1].annotate(r'P{B | A} = ' + str(count) + '/' + str(count_a),\n",
    "                    (x_end+0.2*xsiz,y_start+ysiz*0.5),color='black',weight=\"bold\")\n",
    "    else:\n",
    "        z = axs[1].annotate(r'P{B | A} = ' + str(count) + '/' + str(count_a),\n",
    "                    (x_end-3.5*xsiz,y_start+ysiz*0.5),color='black',weight=\"bold\")\n",
    "        \n",
    "# connect the function to make the samples and plot to the widgets    \n",
    "interactive_plot2 = widgets.interactive_output(run_plot, {'n':n,'ix':ix,'iy':iy,})\n",
    "interactive_plot2.clear_output(wait = True)               # reduce flickering by delaying plot updating   "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2563998",
   "metadata": {},
   "source": [
    "### Interactive Conditional Probability Demonstation \n",
    "\n",
    "#### Michael Pyrcz, Professor, The University of Texas at Austin \n",
    "\n",
    "Change the number of sample data, select silver and gold bins.\n",
    "\n",
    "### The Inputs\n",
    "\n",
    "* **n** - number of data, **$i_{\\theta_{Ag}}$** - silver bin, and **$i_{\\theta_{Au}}$** - gold bin "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "adc93d16",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "67d88f046c2348afbed83d22639eb08e",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "VBox(children=(Text(value='                               Conditional Probability Demo, Prof. Michael Pyrcz, T…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "ec16f0a0dcd24b979180aaaf3c192a19",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Output()"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(ui3, interactive_plot2)                           # display the interactive plot"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "88a29876",
   "metadata": {},
   "source": [
    "#### Build the Interactive Dashboard to Demonstrate Conditional Distributions\n",
    "\n",
    "The following code:\n",
    "\n",
    "* take the previous synthetic dataset and specifiy events:\n",
    "    * A - silver grade within a bin\n",
    "    * B - gold grade within a bin\n",
    "    \n",
    "* calculate conditional distributions, $f_{(B | A)}$ and $f_{(A | B)}$, as histograms and compare to the marginals, $f_B$ and $f_A$ respectively."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "b124a8cf",
   "metadata": {},
   "outputs": [],
   "source": [
    "l = widgets.Text(value='                               Conditional Distributions Demo, Prof. Michael Pyrcz, The University of Texas at Austin',\n",
    "                 layout=Layout(width='750px', height='30px'))\n",
    "\n",
    "n = widgets.IntSlider(min=0, max = 5000, value=1000, step = 100, description = '$n$',orientation='horizontal', style = {'description_width': 'initial'}, continuous_update=False)\n",
    "ix = widgets.IntSlider(min=0, max = 6, value=1, step = 1, description = r'Silver Bin, $i_{\\theta_{Ag}}$',orientation='horizontal', style = {'description_width': 'initial'}, continuous_update=False)\n",
    "iy = widgets.IntSlider(min=0, max = 6, value=1, step = 1, description = r'Gold Bin, $i_{\\theta_{Au}}$',orientation='horizontal', style = {'description_width': 'initial'}, continuous_update=False)\n",
    "\n",
    "ui = widgets.HBox([n,ix,iy],)\n",
    "ui5 = widgets.VBox([l,ui],)\n",
    "\n",
    "def run_plot5(n,ix,iy):\n",
    "    seed = 73073\n",
    "    Ag_mean = 125; Au_mean = 8;\n",
    "    Ag_stdev = 20; Au_stdev = 1; wt_poly = 0.002\n",
    "    np.random.seed(seed=seed)\n",
    "    X = np.random.multivariate_normal([Au_mean,Ag_mean],[[9, 0],[0, 400]],size = n)\n",
    "    X[:,0] = (1.0-wt_poly)*X[:,0] + wt_poly*(-0.5*np.power(X[:,1]-200,2) + 10)\n",
    "    X[:,0] = (X[:,0] - np.mean(X[:,0])) * Au_stdev/np.std(X[:,0]) + Au_mean\n",
    "      \n",
    "    xsiz = 20; ysiz=1;\n",
    "    x_start = ix*xsiz + 60; x_end = (ix+1)*xsiz + 60\n",
    "    y_start = iy*ysiz + 4; y_end = (iy+1)*ysiz + 4\n",
    "    \n",
    "    count = ((y_start < X[:,0]) & (X[:,0] < y_end) & (x_start < X[:,1]) & (X[:,1] < x_end)).sum()\n",
    "    count_a = ((x_start < X[:,1]) & (X[:,1] < x_end)).sum()\n",
    "    count_b = ((y_start < X[:,0]) & (X[:,0] < y_end)).sum()\n",
    "    \n",
    "    Xsub_a = X[((x_start < X[:,1]) & (X[:,1] < x_end))]\n",
    "    Xsub_b = X[((y_start < X[:,0]) & (X[:,0] < y_end))]\n",
    "    Xsub = X[((y_start < X[:,0]) & (X[:,0] < y_end) & (x_start < X[:,1]) & (X[:,1] < x_end))]\n",
    "        \n",
    "    plt_scatter = plt.subplot2grid((3, 3), (1, 0), rowspan=2, colspan=2)\n",
    "    plt_x1 = plt.subplot2grid((3, 3), (0, 0), colspan=2,\n",
    "                               sharex=plt_scatter)\n",
    "    plt_x2 = plt.subplot2grid((3, 3), (1, 2), rowspan=2,\n",
    "                               sharey=plt_scatter)    \n",
    "    \n",
    "    #plt.plot([0,0],[1.0,1.0],color = 'black')\n",
    "    plt_scatter.scatter(Xsub_a[:,1],Xsub_a[:,0],color = 'none',edgecolor='green',alpha=0.9)\n",
    "    plt_scatter.scatter(Xsub_b[:,1],Xsub_b[:,0],color = 'none',edgecolor='blue',alpha=0.9)\n",
    "    plt_scatter.scatter(X[:,1],X[:,0],color = 'red',edgecolor='black',alpha=0.1,label='samples')\n",
    "    #plt_scatter.scatter(sample[:,0],sample[:,1],color = 'red',alpha = 0.8,edgecolors='black',label = 'Samples')\n",
    "    plt_scatter.legend(loc='upper left')  \n",
    "    plt_scatter.set_xlabel('Silver, Ag, Grade (g/t)'); plt_scatter.set_ylabel('Gold, Au, Grade (g/t)'); \n",
    "    plt_scatter.set_title('Mineral Grades Conditional Frequency Table')\n",
    "    plt_scatter.set_xlim([60,200]); plt_scatter.set_ylim([4,11]); plt_scatter.legend(loc='upper right')\n",
    "    \n",
    "    plt_scatter.grid(True, which='major',linewidth = 1.0); plt_scatter.grid(True, which='minor',linewidth = 0.2) # add y grids\n",
    "    plt_scatter.tick_params(which='major',length=7); plt_scatter.tick_params(which='minor', length=4)\n",
    "    plt_scatter.xaxis.set_minor_locator(AutoMinorLocator(n=10)); plt_scatter.yaxis.set_minor_locator(AutoMinorLocator(n=10)) # turn on minor ticks\n",
    "    plt_scatter.xaxis.set_major_locator(MultipleLocator(xsiz)); plt_scatter.yaxis.set_major_locator(MultipleLocator(ysiz)) # turn on minor ticks\n",
    "    \n",
    "    #rect = patches.Rectangle((60, 4), xsiz*ix, 7*ysiz, linewidth=0, edgecolor='none', facecolor='white'); plt_scatter.add_patch(rect)\n",
    "    #rect = patches.Rectangle((60+xsiz*(ix+1), 4), 60+xsiz*(7 - (ix+1)), 7*ysiz, linewidth=0, edgecolor='none', facecolor='white'); plt_scatter.add_patch(rect)\n",
    "    \n",
    "    rect = patches.Rectangle((x_start, 4), xsiz, 7*ysiz, linewidth=3, edgecolor='green', facecolor='none'); plt_scatter.add_patch(rect)\n",
    "    rect = patches.Rectangle((60, y_start), 7*xsiz, ysiz, linewidth=3,edgecolor='blue', alpha=0.4, facecolor='none'); plt_scatter.add_patch(rect)\n",
    "    \n",
    "    plt_scatter.annotate(r'A = [' + str(x_start) + r' ≤ $\\bf{\\theta_{Ag}}$ ≤ ' + str(x_end) + ']',\n",
    "                        (140,4.6),color='green',weight=\"bold\")\n",
    "        \n",
    "    plt_scatter.annotate(r'B = [' + str(y_start) + r' ≤ $\\bf{\\theta_{Au}}$ ≤ ' + str(y_end) + ']',\n",
    "                        (140,4.2),color='blue',weight=\"bold\")\n",
    "    plt_scatter.annotate(r'$\\bf{A}$',(x_start+0.4*xsiz,10.6),size=15,color='green')\n",
    "    plt_scatter.annotate(r'$\\bf{B}$',(61.5,y_start+0.4*ysiz),size=15,color='blue')\n",
    "    \n",
    "    plt_x1.hist(X[:,1],density = True,color='red',alpha=0.3,edgecolor='black',bins=np.linspace(60,200,30),label='marginal')\n",
    "    plt_x1.hist(Xsub_b[:,1],density = True,color='blue',alpha=0.3,edgecolor='black',bins=np.linspace(60,200,30),label='conditional')\n",
    "    \n",
    "    plt_x1.set_xlim([60,200]); plt_x1.legend(loc='upper right')\n",
    "    #plt_x1.set_xlabel('Silver, Ag, Grade (g/t)'); plt_x1.set_ylabel(r'Density')\n",
    "    plt_x1.set_title(r'Marginal and Conditional Distributions')\n",
    "    \n",
    "    plt_x2.hist(X[:,0],orientation='horizontal',density = True,color='red',alpha=0.3,edgecolor='black',bins=np.linspace(4,11,30),label='marginal')\n",
    "    plt_x2.hist(Xsub_a[:,0],orientation='horizontal',density = True,color='green',alpha=0.3,edgecolor='black',bins=np.linspace(4,11,30),label='conditional')\n",
    "    \n",
    "    plt_x2.set_ylim([4,11]); plt_x2.legend(loc='upper right')\n",
    "    #plt_x2.set_ylabel('Gold, Au, Grade (g/t)'); plt_x2.set_xlabel(r'Density')\n",
    "    \n",
    "    plt.subplots_adjust(left=0.0, bottom=0.0, right=1., top=1.2, wspace=0.3, hspace=0.4); plt.show()\n",
    "    \n",
    "# connect the function to make the samples and plot to the widgets    \n",
    "interactive_plot5 = widgets.interactive_output(run_plot5, {'n':n,'ix':ix,'iy':iy,})\n",
    "interactive_plot5.clear_output(wait = True)               # reduce flickering by delaying plot updating"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9c14021d",
   "metadata": {},
   "source": [
    "### Interactive Conditional Distribution Demonstration \n",
    "\n",
    "#### Michael Pyrcz, Professor, The University of Texas at Austin \n",
    "\n",
    "Change the number of sample data, select silver and gold bins.\n",
    "\n",
    "### The Inputs\n",
    "\n",
    "* **n** - number of data, **$i_{\\theta_{Ag}}$** - silver bin, and **$i_{\\theta_{Au}}$** - gold bin "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "ad8e0c81",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f0d28720b1ca446abedbbb96b9574fcf",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "VBox(children=(Text(value='                               Conditional Distributions Demo, Prof. Michael Pyrcz,…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "a444e5f9f2284aa1a66cf7ace3dc9f70",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Output()"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(ui5, interactive_plot5)                           # display the interactive plot"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6d2dfdf7",
   "metadata": {},
   "source": [
    "#### Comments\n",
    "\n",
    "This was a basic demonstration marginal, joint and conditional probabilities. I have many other demonstrations and even basics of working with DataFrames, ndarrays, univariate statistics, plotting data, declustering, data transformations and many other workflows available at https://github.com/GeostatsGuy/PythonNumericalDemos and https://github.com/GeostatsGuy/GeostatsPy. \n",
    "  \n",
    "#### The Author:\n",
    "\n",
    "### Michael Pyrcz, Professor, The University of Texas at Austin \n",
    "*Novel Data Analytics, Geostatistics and Machine Learning Subsurface Solutions*\n",
    "\n",
    "With over 17 years of experience in subsurface consulting, research and development, Michael has returned to academia driven by his passion for teaching and enthusiasm for enhancing engineers' and geoscientists' impact in subsurface resource development. \n",
    "\n",
    "For more about Michael check out these links:\n",
    "\n",
    "#### [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1)\n",
    "\n",
    "#### Want to Work Together?\n",
    "\n",
    "I hope this content is helpful to those that want to learn more about subsurface modeling, data analytics and machine learning. Students and working professionals are welcome to participate.\n",
    "\n",
    "* Want to invite me to visit your company for training, mentoring, project review, workflow design and / or consulting? I'd be happy to drop by and work with you! \n",
    "\n",
    "* Interested in partnering, supporting my graduate student research or my Subsurface Data Analytics and Machine Learning consortium (co-PIs including Profs. Foster, Torres-Verdin and van Oort)? My research combines data analytics, stochastic modeling and machine learning theory with practice to develop novel methods and workflows to add value. We are solving challenging subsurface problems!\n",
    "\n",
    "* I can be reached at mpyrcz@austin.utexas.edu.\n",
    "\n",
    "I'm always happy to discuss,\n",
    "\n",
    "*Michael*\n",
    "\n",
    "Michael Pyrcz, Ph.D., P.Eng. Professor, Cockrell School of Engineering and The Jackson School of Geosciences, The University of Texas at Austin\n",
    "\n",
    "#### More Resources Available at: [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b87dae84",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.11.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
