{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "<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 Variogram Calculation and Modeling, Spatial Estimation with Kriging Activity\n",
    "\n",
    "\n",
    "### Michael Pyrcz, Associate Professor, University of Texas at Austin \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",
    "\n",
    "### The Interactive Workflow\n",
    "\n",
    "Here's an interactive workflow for calculating directional experimental variograms in 2D, modeling the variograms and applying the variogram models to calculate spatial estimation maps with kriging. \n",
    "\n",
    "* Wherever you see -999 values you must update with your own choices.\n",
    "* When you make changes you must rerun the subsuquent code update the results.\n",
    "* When you save your workflow and reload the interactive GUIs are reset so record you parameters before exiting, e.g., screen captures.\n",
    "\n",
    "To complete this workflow you will:\n",
    "\n",
    "1. **Load Spatial Data**, select from available datasets on Dr. Pyrcz's GitHub GeoDataSets repository or load your own dataset. Make sure that the following variables are consistent with the dataset.\n",
    "\n",
    "    - feature and feature_units - the name of the feature in the dataframe and the units of the feature\n",
    "    - vmin and vmax - the minimum and maximum values of the feature\n",
    "    - xmin, xmax, ymin and ymax - the area of interest for plotting and building a kriged map\n",
    "\n",
    "2. **Inspect the Posted Data**, visualize the data location map over the area of interest\n",
    "\n",
    "    - make sure the area of interest covers the data and does not extend too far from data (i.e. extreme spatial extrapolation)\n",
    "    - visually check for obvious sample bias with clustered samples over highs or lows\n",
    "    - visually check for minimum lag spacing and possible major direction of continuity\n",
    "\n",
    "3. **Declustering**,  apply cell beased declustering to the dataset.\n",
    "\n",
    "    - calculate the declustered mean to apply as the stationary mean for simple kriging. Recall away from data the mean gets a weight of $1-\\sum_{\\alpha = 1}^{n} \\lambda_{\\alpha}$\n",
    "    - calculate the declustered variance as the variogram sill and the maximum kriging estimation variance outside the range of all of the data (note ordinary kriging estimation variance can exceed the sill)\n",
    "    - select reasonable parameters for declustering:\n",
    "\n",
    "```python\n",
    "find_minimizing_cell_size = -999\n",
    "number_offsets = -999\n",
    "number_cell_sizes = -999\n",
    "min_cell_size = -999\n",
    "max_cell_size = -999\n",
    "```\n",
    "\n",
    "4. **Calculate Directional Experimental Variograms**, set the directional variogram calculation search template parameters to find the major and minor directions of continuity and produce the more interpretable experimental variograms possible\n",
    "\n",
    "    - use the interactive GUI to calculate the directional variograms\n",
    "\n",
    "5. **Model the Directional Variograms**, use up to 2 nest variogram structures plus nugget if present to model the directional variogram \n",
    "\n",
    "    - rerun the code to update with a new experimental variogram and declustered variance / sill\n",
    "    - use the interactive GUI to fit the directional variogram model\n",
    "\n",
    "6. **Spatial Predictions with Kriging**, use the declustering results and variogram model with the data to calculate spatial estimation maps with kriging\n",
    "\n",
    "    - rerun the code to update with a new variogram model and declustered stationary mean and variance / sill\n",
    "    - set the kriging parameters, note vdmean is the declustered mean, you can reduce ndmax to speed up the run but may add limited search artifacts\n",
    "\n",
    "```python\n",
    "skmean = vdmean                            # simple kriging mean (used if simple kriging is selected below)\n",
    "ktype = 0                                  # kriging type, 0 - simple, 1 - ordinary\n",
    "radius = -999                              # search radius for neighbouring data\n",
    "ndmin = -999; ndmax = -999                 # minimum and maximum data for an estimate\n",
    "tmin =  -1.0e21; tmax = 1.0e21             # data trimming limits, set very small and large to not trim the data\n",
    "```\n",
    "7. **Spatial Prediction with Kriging - One Location with Uncertainty Model**, use the previous kriging estimate and kriging estimation variance maps to calculate the uncertainty model at a single location\n",
    "\n",
    "    - select the location with this code:\n",
    "```python\n",
    "x = 500                                     # location to estimate\n",
    "y = 500\n",
    "```\n",
    "\n",
    "#### Additional Resources\n",
    "\n",
    "I have recorded lectures, code walkthroughs and comprehensive workflows for declustering:\n",
    "\n",
    "* [spatial bias](https://www.youtube.com/watch?v=w0HgVibxpMQ&list=PLG19vXLQHvSB-D4XKYieEku9GQMQyAzjJ&index=25)\n",
    "\n",
    "* [declustering lecture](https://www.youtube.com/watch?v=rN0RKcTIVcI&list=PLG19vXLQHvSB-D4XKYieEku9GQMQyAzjJ&index=26)\n",
    "\n",
    "* [declustering in Python with GeostatsPy](https://github.com/GeostatsGuy/PythonNumericalDemos/blob/master/GeostatsPy_declustering.ipynb)\n",
    "\n",
    "and variogram calculation and modeling:\n",
    "\n",
    "* [variogram introduction lecture](https://youtu.be/jVRLGOsnYuw)\n",
    "\n",
    "* [variogram calculation lecture](https://youtu.be/mzPLicovE7Q)\n",
    "\n",
    "* [variogram calculation search parameters](https://youtu.be/NE4xfhIHAm4)\n",
    "\n",
    "* [variogram calculation in Python walkthough](https://www.youtube.com/watch?v=FugSEcCi2gI&list=PLG19vXLQHvSB-D4XKYieEku9GQMQyAzjJ&index=36)\n",
    "\n",
    "* [directional variogram calculation in Python walkthrough](https://www.youtube.com/watch?v=bryRCrtf3hk&list=PLG19vXLQHvSB-D4XKYieEku9GQMQyAzjJ&index=37)\n",
    "\n",
    "* [variogram interpretation lecture](https://www.youtube.com/watch?v=Li-Xzlu7hvs&list=PLG19vXLQHvSB-D4XKYieEku9GQMQyAzjJ&index=38)\n",
    "\n",
    "* [variogram modeling lecture](https://www.youtube.com/watch?v=-Bi63Y3u6TU&list=PLG19vXLQHvSB-D4XKYieEku9GQMQyAzjJ&index=39)\n",
    "\n",
    "* [variogram modeling in Python walkthrough](https://www.youtube.com/watch?v=bRj3HnEa1Z4&list=PLG19vXLQHvSB-D4XKYieEku9GQMQyAzjJ&index=40)\n",
    "\n",
    "* [experimental variogram calculation in Python with GeostatsPy](https://github.com/GeostatsGuy/PythonNumericalDemos/blob/master/GeostatsPy_variogram_calculation.ipynb)\n",
    "\n",
    "* [determination of major and minor spatial continuity directions in Python with GeostatsPy](https://github.com/GeostatsGuy/PythonNumericalDemos/blob/master/GeostatsPy_spatial_continuity_directions.ipynb)\n",
    "\n",
    "and kriging for spatial estimation:\n",
    "\n",
    "* [kriging lecture](https://www.youtube.com/watch?v=CVkmuwF8cJ8&list=PLG19vXLQHvSB-D4XKYieEku9GQMQyAzjJ&index=42)\n",
    "\n",
    "* [simple kriging in Python walkthrough](https://www.youtube.com/watch?v=adkZAFKLY3s&list=PLG19vXLQHvSB-D4XKYieEku9GQMQyAzjJ&index=44)\n",
    "\n",
    "* [kriging Interactive Demonstration in Python with GeostatsPy](https://github.com/GeostatsGuy/PythonNumericalDemos/blob/master/Interactive_Simple_Kriging.ipynb)\n",
    "\n",
    "* [Complete by-Facies Kriging Workflow for Spatial Estimation in Python with GeostatsPy](https://github.com/GeostatsGuy/PythonNumericalDemos/blob/master/GeostatsPy_kriging.ipynb)\n",
    "\n",
    "Here's some more basic details on each topic for convenience. \n",
    "\n",
    "#### Spatial Continuity \n",
    "\n",
    "**Spatial Continuity** is the correlation between values over distance.\n",
    "\n",
    "* No spatial continuity – no correlation between values over distance, random values at each location in space regardless of separation distance.\n",
    "\n",
    "* Homogenous phenomenon have perfect spatial continuity, since all values as the same (or very similar) they are correlated. \n",
    "\n",
    "We need a statistic to quantify spatial continuity! A convenient method is the Semivariogram.\n",
    "\n",
    "#### The Semivariogram\n",
    "\n",
    "Function of difference over distance.\n",
    "\n",
    "* The expected (average) squared difference between values separated by a lag distance vector (distance and direction), $h$:\n",
    "\n",
    "\\begin{equation}\n",
    "\\gamma(\\bf{h}) = \\frac{1}{2 N(\\bf{h})} \\sum^{N(\\bf{h})}_{\\alpha=1} (z(\\bf{u}_\\alpha) - z(\\bf{u}_\\alpha + \\bf{h}))^2  \n",
    "\\end{equation}\n",
    "\n",
    "where $z(\\bf{u}_\\alpha)$ and $z(\\bf{u}_\\alpha + \\bf{h})$ are the spatial sample values at tail and head locations of the lag vector respectively.\n",
    "\n",
    "* Calculated over a suite of lag distances to obtain a continuous function.\n",
    "\n",
    "* the $\\frac{1}{2}$ term converts a variogram into a semivariogram, but in practice the term variogram is used instead of semivariogram.\n",
    "* We prefer the semivariogram because it relates directly to the covariance function, $C_x(\\bf{h})$ and univariate variance, $\\sigma^2_x$:\n",
    "\n",
    "\\begin{equation}\n",
    "C_x(\\bf{h}) = \\sigma^2_x - \\gamma(\\bf{h})\n",
    "\\end{equation}\n",
    "\n",
    "Note the correlogram is related to the covariance function as:\n",
    "\n",
    "\\begin{equation}\n",
    "\\rho_x(\\bf{h}) = \\frac{C_x(\\bf{h})}{\\sigma^2_x}\n",
    "\\end{equation}\n",
    "\n",
    "The correlogram provides of function of the $\\bf{h}-\\bf{h}$ scatter plot correlation vs. lag offset $\\bf{h}$.  \n",
    "\n",
    "\\begin{equation}\n",
    "-1.0 \\le \\rho_x(\\bf{h}) \\le 1.0\n",
    "\\end{equation}\n",
    "\n",
    "#### Variogram Observations\n",
    "\n",
    "The following are common observations for variograms that should assist with their practical use.\n",
    "\n",
    "##### Observation \\#1 - As distance increases, variability increase (in general).\n",
    "\n",
    "This is common since in general, over greater distance offsets, there is often more difference between the head and tail samples.\n",
    "\n",
    "In some cases, such as with spatial cyclicity of the hole effect variogram model the variogram may have negative slope over somelag distance intervals\n",
    "\n",
    "Negative slopes at lag distances greater than half the data extent are often caused by too few pairs for a reliable variogram calculation\n",
    "\n",
    "##### Observation \\#2 - Calculated with over all possible pairs separated by lag vector, $\\bf{𝐡}$.\n",
    "\n",
    "We scan through the entire data set, searching for all possible pair combinations with all other data.  We then calculate the variogram as one half the expectation of squared difference between all pairs.\n",
    "\n",
    "More pairs results in a more reliable measure.\n",
    "\n",
    "##### Observation \\#3 - Need to plot the sill to know the degree of correlation.\n",
    "\n",
    "**Sill** is the variance, $\\sigma^2_x$\n",
    "\n",
    "Given stationarity of the variance, $\\sigma^2_x$, and variogram $\\gamma(\\bf{h})$:\n",
    "\n",
    "we can define the covariance function:\n",
    "\n",
    "\\begin{equation}\n",
    "C_x(\\bf{h}) = \\sigma^2_x - \\gamma(\\bf{h})\n",
    "\\end{equation}\n",
    "\n",
    "The covariance measure is a measure of similarity over distance (the mirror image of the variogram as shown by the equation above).\n",
    "\n",
    "Given a standardized distribution $\\sigma^2_x = 1.0$, the covariance, $C_x(\\bf{h})$, is equal to the correlogram, $\\rho_x(\\bf{h})$: \n",
    "\n",
    "\\begin{equation}\n",
    "\\rho_x(\\bf{h}) = \\sigma^2_x - \\gamma(\\bf{h})\n",
    "\\end{equation}\n",
    "\n",
    "##### Observation \\#4 - The lag distance at which the variogram reaches the sill is know as the range.\n",
    "\n",
    "At the range, knowing the data value at the tail location provides no information about a value at the head location of the lag distance vector.\n",
    "\n",
    "##### Observation \\#5 - The nugget effect, a discontinuity at the origin\n",
    "\n",
    "Sometimes there is a discontinuity in the variogram at distances less than the minimum data spacing.  This is known as **nugget effect**.\n",
    "\n",
    "The ratio of nugget / sill, is known as relative nugget effect (%). Modeled as a discontinuity with no correlation structure that at lags, $h \\gt \\epsilon$, an infinitesimal lag distance, and perfect correlation at $\\bf{h} = 0$.\n",
    "Caution when including nuggect effect in the variogram model as measurement error, mixing populations cause apparent nugget effect\n",
    "\n",
    "This exercise demonstrates the semivariogram calculation with GeostatsPy. The steps include:\n",
    "\n",
    "1. generate a 2D model with sequential Gaussian simulation\n",
    "2. sample from the simulation\n",
    "3. calculate and visualize experimental semivariograms\n",
    "\n",
    "#### Variogram Calculation Parameters\n",
    "\n",
    "The variogram calculation parameters include:\n",
    "\n",
    "* **azimuth** is the azimuth of the lag vector\n",
    "\n",
    "* **azimuth tolerance** is the maximum allowable departure from the azimuth (isotropic variograms are calculated with an azimuth tolerance of to 90.0)\n",
    "\n",
    "* **unit lag distance** the size of the bins in lag distance, usually set to the minimum data spacing\n",
    "\n",
    "* **lag distance tolerance** - the allowable tolerance in lage distance, commonly set to 50% of unit lag distanceonal smoothing\n",
    "\n",
    "* **number of lags** - set based on the spatial extent of the dataset, we can typically calculate reliable variograms up to 1/2 the extent of the dataset\n",
    "\n",
    "* **bandwidth** is the maximum offset allowable from the lag vector \n",
    "\n",
    "\n",
    "#### Variogram Modeling\n",
    "\n",
    "Spatial continuity can be modeled with nested, positive definate variogram structures:\n",
    "\n",
    "\\begin{equation}\n",
    "\\Gamma_x(\\bf{h}) = \\sum_{i=1}^{nst} \\gamma_i(\\bf{h})\n",
    "\\end{equation}\n",
    "\n",
    "where $\\Gamma_x(\\bf{h})$ is the nested variogram model resulting from the summation of $nst$ nested variograms  $\\gamma_i(\\bf{h})$.\n",
    "\n",
    "The types of structure commonly applied include:\n",
    "\n",
    "* spherical\n",
    "\n",
    "* exponential\n",
    "\n",
    "* gaussian\n",
    "\n",
    "* nugget\n",
    "\n",
    "Other less common models include:\n",
    "\n",
    "* hole effect\n",
    "\n",
    "* dampenned hole effect\n",
    "\n",
    "* power law\n",
    "\n",
    "these will not be covered here.\n",
    "\n",
    "Each one of these variogram structures, $\\gamma_i(\\bf{h})$, is based on a geometric anisotropy model parameterized by the orientation and range in the major and minor directions.  In 2D this is simply an azimuth and ranges, $azi$, $a_{maj}$ and $a_{min}$. Note, the range in the minor direction (orthogonal to the major direction).\n",
    "\n",
    "The geometric anisotropy model assumes that the range in all off-diagonal directions is based on an ellipse with the major and minor axes alligned with and set to the major and minor for the variogram.\n",
    "\n",
    "\\begin{equation}\n",
    "\\bf{h}_i = \\sqrt{\\left(\\frac{r_{maj}}{a_{maj_i}}\\right)^2 + \\left(\\frac{r_{maj}}{a_{maj_i}}\\right)^2}  \n",
    "\\end{equation}\n",
    "\n",
    "Therefore, if we know the major direction, range in major and minor directions, we may completely describe each nested componnent of the complete spatial continuity of the variable of interest, $i = 1,\\dots,nst$.\n",
    "\n",
    "Some comments on modeling nested variograms:\n",
    "\n",
    "* we can capture nugget, short and long range continuity structures\n",
    "\n",
    "* we rely on the geometric anisotropy model, so all structures must inform the same level of contribution (porportion of the sill) in all directions.\n",
    "\n",
    "* the geometric anisotropy model is based on azimuth of the major direction of continuity, range in the major direction and range in the minor direction (orthogonal to the major direction).  The range is interpolated between the major and minor azimuths with a ellipse model\n",
    "\n",
    "* we can vary the type of variogram, direction or azimuth of the major direction, and major and minor ranges by structure\n",
    "\n",
    "#### Spatial Estimation\n",
    "\n",
    "Consider the case of making an estimate at some unsampled location, $𝑧(\\bf{u}_0)$, where $z$ is the property of interest (e.g. porosity etc.) and $𝐮_0$ is a location vector describing the unsampled location.\n",
    "\n",
    "How would you do this given data, $𝑧(\\bf{𝐮}_1)$, $𝑧(\\bf{𝐮}_2)$, and $𝑧(\\bf{𝐮}_3)$?\n",
    "\n",
    "It would be natural to use a set of linear weights to formulate the estimator given the available data.\n",
    "\n",
    "\\begin{equation}\n",
    "z^{*}(\\bf{u}) = \\sum^{n}_{\\alpha = 1} \\lambda_{\\alpha} z(\\bf{u}_{\\alpha})\n",
    "\\end{equation}\n",
    "\n",
    "We could add an unbiasedness constraint to impose the sum of the weights equal to one.  What we will do is assign the remainder of the weight (one minus the sum of weights) to the global average; therefore, if we have no informative data we will estimate with the global average of the property of interest.\n",
    "\n",
    "\\begin{equation}\n",
    "z^{*}(\\bf{u}) = \\sum^{n}_{\\alpha = 1} \\lambda_{\\alpha} z(\\bf{u}_{\\alpha}) + \\left(1-\\sum^{n}_{\\alpha = 1} \\lambda_{\\alpha} \\right) \\overline{z}\n",
    "\\end{equation}\n",
    "\n",
    "We will make a stationarity assumption, so let's assume that we are working with residuals, $y$. \n",
    "\n",
    "\\begin{equation}\n",
    "y^{*}(\\bf{u}) = z^{*}(\\bf{u}) - \\overline{z}(\\bf{u})\n",
    "\\end{equation}\n",
    "\n",
    "If we substitute this form into our estimator the estimator simplifies, since the mean of the residual is zero.\n",
    "\n",
    "\\begin{equation}\n",
    "y^{*}(\\bf{u}) = \\sum^{n}_{\\alpha = 1} \\lambda_{\\alpha} y(\\bf{u}_{\\alpha})\n",
    "\\end{equation}\n",
    "\n",
    "while satisfying the unbaisedness constraint.  \n",
    "\n",
    "#### Kriging\n",
    "\n",
    "Now the next question is what weights should we use?  \n",
    "\n",
    "We could use equal weighting, $\\lambda = \\frac{1}{n}$, and the estimator would be the average of the local data applied for the spatial estimate. This would not be very informative.\n",
    "\n",
    "We could assign weights considering the spatial context of the data and the estimate:\n",
    "\n",
    "* **spatial continuity** as quantified by the variogram (and covariance function)\n",
    "* **redundancy** the degree of spatial continuity between all of the available data with themselves \n",
    "* **closeness** the degree of spatial continuity between the avaiable data and the estimation location\n",
    "\n",
    "The kriging approach accomplishes this, calculating the best linear unbiased weights for the local data to estimate at the unknown location.  The derivation of the kriging system and the resulting linear set of equations is available in the lecture notes.  Furthermore kriging provides a measure of the accuracy of the estimate!  This is the kriging estimation variance (sometimes just called the kriging variance).\n",
    "\n",
    "\\begin{equation}\n",
    "\\sigma^{2}_{E}(\\bf{u}) = C(0) - \\sum^{n}_{\\alpha = 1} \\lambda_{\\alpha} C(\\bf{u}_0 - \\bf{u}_{\\alpha})\n",
    "\\end{equation}\n",
    "\n",
    "What is 'best' about this estimate? Kriging estimates are best in that they minimize the above estimation variance. \n",
    "\n",
    "#### Properties of Kriging\n",
    "\n",
    "Here are some important properties of kriging:\n",
    "\n",
    "* **Exact interpolator** - kriging estimates with the data values at the data locations\n",
    "* **Kriging variance** can be calculated before getting the sample information, as the kriging estimation variance is not dependent on the values of the data nor the kriging estimate, i.e. the kriging estimator is homoscedastic. \n",
    "* **Spatial context** - kriging takes into account, furthermore to the statements on spatial continuity, closeness and redundancy we can state that kriging accounts for the configuration of the data and structural continuity of the variable being estimated.\n",
    "* **Scale** - kriging may be generalized to account for the support volume of the data and estimate. We will cover this later.\n",
    "* **Multivariate** - kriging may be generalized to account for multiple secondary data in the spatial estimate with the cokriging system. We will cover this later.\n",
    "* **Smoothing effect** of kriging can be forecast. We will use this to build stochastic simulations later.\n",
    "\n",
    "In this workflow we will explore methods to:\n",
    "\n",
    "1. decluster the data to calculate a representative mean\n",
    "2. interactively detect directionality from a spatial dataset\n",
    "3. interactively calculate the directional variograms in the major and minor directions \n",
    "4. interactively build a consistent 2D model fit to the major and minor directions\n",
    "5. apply the variogram model for building an spatial estimated map with simple kriging with the representative mean\n",
    "\n",
    "Note, since we are using the variogram for estimation, we will not Gaussian transform the feature first.\n",
    "\n",
    "* the sill of the variogram will be equal to the variance of the data and not necesssarily 1.0 as with the standard normal distribution.\n",
    "\n",
    "#### Objective \n",
    "\n",
    "In the PGE 383: Stochastic Subsurface Modeling class I want to provide hands-on experience with building subsurface modeling workflows. Python provides an excellent vehicle to accomplish this. I have coded a package called GeostatsPy with GSLIB: Geostatistical Library (Deutsch and Journel, 1998) functionality that provides basic building blocks for building subsurface modeling workflows. \n",
    "\n",
    "The objective is to remove the hurdles of subsurface modeling workflow construction by providing building blocks and sufficient examples. This is not a coding class per se, but we need the ability to 'script' workflows working with numerical methods.    \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",
    "2. From Anaconda Navigator (within Anaconda3 group), go to the environment tab, click on base (root) green arrow and open a terminal. \n",
    "3. In the terminal type: pip install geostatspy. \n",
    "4. Open Jupyter and in the top block get started by copy and pasting the code block below from this Jupyter Notebook to start using the geostatspy functionality. \n",
    "\n",
    "You will need to copy the data file to your working directory.  They are available here:\n",
    "\n",
    "* Tabular data - sample_data.csv at https://git.io/fh4gm.\n",
    "\n",
    "There are exampled below with these functions. You can go here to see a list of the available functions, https://git.io/fh4eX, other example workflows and source code. \n",
    "\n",
    "#### Load the required libraries\n",
    "\n",
    "The following code loads the required libraries."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "import geostatspy.GSLIB as GSLIB                       # GSLIB utilies, visualization and wrapper\n",
    "import geostatspy.geostats as geostats                 # GSLIB methods convert to Python    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We will also need some standard packages. These should have been installed with Anaconda 3."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import os                                               # to set current working directory \n",
    "import sys                                              # supress output to screen for interactive variogram modeling\n",
    "import io\n",
    "import numpy as np                                      # arrays and matrix math\n",
    "import pandas as pd                                     # DataFrames\n",
    "import matplotlib.pyplot as plt                         # plotting\n",
    "from matplotlib.pyplot import cm                        # color maps\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",
    "from scipy import stats                                 # summary statistics\n",
    "from statsmodels.stats.weightstats import DescrStatsW   # any weighted statistics\n",
    "from scipy.stats import norm                            # Gaussian distribution\n",
    "import math                                             # square root \n",
    "\n",
    "import warnings                                         # remove warnings\n",
    "warnings.filterwarnings(\"ignore\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you get a package import error, you may have to first install some of these packages. This can usually be accomplished by opening up a command window on Windows and then typing 'python -m pip install [package-name]'. More assistance is available with the respective package docs.  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "#### Set the working directory\n",
    "\n",
    "I always like to do this so I don't lose files and to simplify subsequent read and writes (avoid including the full address each time).  Also, in this case make sure to place the required (see above) GSLIB executables in this directory or a location identified in the environmental variable *Path*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "#os.chdir(\"c:/PGE383\")                                   # set the working directory"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Loading Tabular Data\n",
    "\n",
    "Here's the command to load our comma delimited data file in to a Pandas' DataFrame object. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>X</th>\n",
       "      <th>Y</th>\n",
       "      <th>Facies</th>\n",
       "      <th>Porosity</th>\n",
       "      <th>Perm</th>\n",
       "      <th>AI</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>100.0</td>\n",
       "      <td>900.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>10.131888</td>\n",
       "      <td>1.996868</td>\n",
       "      <td>5590.417154</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>100.0</td>\n",
       "      <td>800.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>14.767608</td>\n",
       "      <td>10.711789</td>\n",
       "      <td>3470.845666</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>100.0</td>\n",
       "      <td>700.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>14.591186</td>\n",
       "      <td>17.818143</td>\n",
       "      <td>3586.988513</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>100.0</td>\n",
       "      <td>600.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>18.616662</td>\n",
       "      <td>217.109365</td>\n",
       "      <td>3732.114787</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>100.0</td>\n",
       "      <td>500.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>14.608824</td>\n",
       "      <td>16.717367</td>\n",
       "      <td>2534.551236</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       X      Y  Facies   Porosity        Perm           AI\n",
       "0  100.0  900.0     0.0  10.131888    1.996868  5590.417154\n",
       "1  100.0  800.0     1.0  14.767608   10.711789  3470.845666\n",
       "2  100.0  700.0     1.0  14.591186   17.818143  3586.988513\n",
       "3  100.0  600.0     1.0  18.616662  217.109365  3732.114787\n",
       "4  100.0  500.0     1.0  14.608824   16.717367  2534.551236"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data = 1\n",
    "\n",
    "if data == 0:\n",
    "    df = pd.read_csv(\"https://raw.githubusercontent.com/GeostatsGuy/GeoDataSets/master/spatial_nonlinear_MV_facies_v6_sand_only.csv\") # load from Prof. Pyrcz's GitHub repository\n",
    "    df = df.rename(columns = {'Por':'Porosity'})            # rename feature(s)\n",
    "    df = df.iloc[:,1:]                                      # remove first column\n",
    "elif data == 1:\n",
    "    df = pd.read_csv(\"https://raw.githubusercontent.com/GeostatsGuy/GeoDataSets/master/sample_data_MV_biased.csv\") # load from Prof. Pyrcz's GitHub repository\n",
    "    df = df.rename(columns = {'Por':'Porosity'})            # rename feature(s)\n",
    "    df['Porosity'] = df['Porosity']*100.0\n",
    "    df = df.iloc[:,1:]                                      # remove first column\n",
    "elif data == 2:\n",
    "    df = pd.read_csv(\"https://raw.githubusercontent.com/GeostatsGuy/GeoDataSets/master/spatial_nonlinear_MV_facies_v3.csv\") # load from Prof. Pyrcz's GitHub repository\n",
    "    df = df.rename(columns = {'Por':'Porosity'})            # rename feature(s)\n",
    "    df = df.iloc[:,1:] \n",
    "elif data == 3:\n",
    "    df = pd.read_csv(\"https://raw.githubusercontent.com/GeostatsGuy/GeoDataSets/master/12_sample_data.csv\") # load from Prof. Pyrcz's GitHub repository\n",
    "    df = df.rename(columns = {'Por':'Porosity'})            # rename feature(s) \n",
    "    df['Porosity'] = df['Porosity']*100.0\n",
    "    df = df.iloc[:,1:] \n",
    "elif data == 4:\n",
    "    df = pd.read_csv(\"https://raw.githubusercontent.com/GeostatsGuy/GeoDataSets/master/spatial_nonlinear_MV_facies_v5_sand_only.csv\") # load from Prof. Pyrcz's GitHub repository\n",
    "    df = df.rename(columns = {'Por':'Porosity'})            # rename feature(s) \n",
    "    df = df.iloc[:,1:] \n",
    "else:\n",
    "    df = pd.read_csv(\"https://raw.githubusercontent.com/GeostatsGuy/GeoDataSets/master/spatial_nonlinear_MV_facies_v1.csv\") # load from Prof. Pyrcz's GitHub repository\n",
    "    df = df.rename(columns = {'Por':'Porosity'})            # rename feature(s)\n",
    "    df = df.iloc[:,1:] \n",
    "    \n",
    "df.head()                                               # we could also use this command for a table preview "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The features:\n",
    "\n",
    "* **X** - x coordinate in meters\n",
    "* **Y** - y coordinate in meters\n",
    "* **Porosity** - rock porosity averaged over a specific rock unit from a vertical well\n",
    "* **Perm** - rock permeability averaged (scaled up) over a specific rock unit from a vertical well \n",
    "* **AI** - acoustic impedance from a seismic cube assigned at a specific rock unit and at the location of a vertical well \n",
    "* **facies** - facies, 0 - shale, 1 - sandstone\n",
    "\n",
    "Concerning facies:\n",
    "\n",
    "We will work with all facies pooled together. I wanted to simplify this workflow and focus more on spatial continuity direction detection. Finally, by not using facies we do have more samples to support our statistical inference. Most often facies are essential in the subsurface model. Don't worry we will check if this is reasonable in a bit.   \n",
    "\n",
    "You are welcome to repeat this workflow on a by-facies basis.  The following code could be used to build DataFrames ('df_sand' and 'df_shale') for each facies.\n",
    "\n",
    "```p\n",
    "df_sand = pd.DataFrame.copy(df[df['Facies'] == 1]).reset_index()  # copy only 'Facies' = sand records\n",
    "df_shale = pd.DataFrame.copy(df[df['Facies'] == 0]).reset_index() # copy only 'Facies' = shale records\n",
    "```\n",
    "\n",
    "Let's look at summary statistics for all facies combined:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>count</th>\n",
       "      <th>mean</th>\n",
       "      <th>std</th>\n",
       "      <th>min</th>\n",
       "      <th>25%</th>\n",
       "      <th>50%</th>\n",
       "      <th>75%</th>\n",
       "      <th>max</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>X</th>\n",
       "      <td>368.0</td>\n",
       "      <td>499.565217</td>\n",
       "      <td>289.770794</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>240.000000</td>\n",
       "      <td>500.000000</td>\n",
       "      <td>762.500000</td>\n",
       "      <td>990.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Y</th>\n",
       "      <td>368.0</td>\n",
       "      <td>520.644022</td>\n",
       "      <td>277.412187</td>\n",
       "      <td>9.000000</td>\n",
       "      <td>269.000000</td>\n",
       "      <td>539.000000</td>\n",
       "      <td>769.000000</td>\n",
       "      <td>999.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Facies</th>\n",
       "      <td>368.0</td>\n",
       "      <td>0.597826</td>\n",
       "      <td>0.491004</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>1.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Porosity</th>\n",
       "      <td>368.0</td>\n",
       "      <td>12.702582</td>\n",
       "      <td>3.064227</td>\n",
       "      <td>4.112152</td>\n",
       "      <td>10.341201</td>\n",
       "      <td>12.584177</td>\n",
       "      <td>14.862292</td>\n",
       "      <td>21.025763</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Perm</th>\n",
       "      <td>368.0</td>\n",
       "      <td>85.617362</td>\n",
       "      <td>228.362654</td>\n",
       "      <td>0.094627</td>\n",
       "      <td>2.297348</td>\n",
       "      <td>10.377292</td>\n",
       "      <td>50.581288</td>\n",
       "      <td>1991.097723</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>AI</th>\n",
       "      <td>368.0</td>\n",
       "      <td>4791.736646</td>\n",
       "      <td>974.560569</td>\n",
       "      <td>1981.177309</td>\n",
       "      <td>4110.728374</td>\n",
       "      <td>4713.325533</td>\n",
       "      <td>5464.043562</td>\n",
       "      <td>7561.250336</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "          count         mean         std          min          25%  \\\n",
       "X         368.0   499.565217  289.770794     0.000000   240.000000   \n",
       "Y         368.0   520.644022  277.412187     9.000000   269.000000   \n",
       "Facies    368.0     0.597826    0.491004     0.000000     0.000000   \n",
       "Porosity  368.0    12.702582    3.064227     4.112152    10.341201   \n",
       "Perm      368.0    85.617362  228.362654     0.094627     2.297348   \n",
       "AI        368.0  4791.736646  974.560569  1981.177309  4110.728374   \n",
       "\n",
       "                  50%          75%          max  \n",
       "X          500.000000   762.500000   990.000000  \n",
       "Y          539.000000   769.000000   999.000000  \n",
       "Facies       1.000000     1.000000     1.000000  \n",
       "Porosity    12.584177    14.862292    21.025763  \n",
       "Perm        10.377292    50.581288  1991.097723  \n",
       "AI        4713.325533  5464.043562  7561.250336  "
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df.describe().transpose()                               # summary table of sand only DataFrame statistics"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Set the Model Parameters\n",
    "\n",
    "See the the following model parameters:\n",
    "\n",
    "* **xmin**, **xmax**, **ymin** and **ymax** - extents of the dataset for plotting\n",
    "* **feature** and **feature_units** - feature of interest and associated units\n",
    "* **vmin** and **vmax** - minimum and maximum of the feature of interest"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "xmin = 0.0; xmax = 1000.0                                # spatial extents in x and y\n",
    "ymin = 0.0; ymax = 1000.0\n",
    "feature = 'Porosity'; feature_units = '%'         # name and units of the feature of interest\n",
    "vmin = 0.0; vmax = 22.0                                  # min and max of the feature of interest\n",
    "cmap = plt.cm.inferno                                    # set the color map"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Inspection of Posted Data\n",
    "\n",
    "Data visualization is very useful to detect patterns. Our brains are very good at pattern detection. I promote quantitative methods and recognize issues with cognitive bias, but it is important to recognize the value is expert intepretation based on data visualization.\n",
    "\n",
    "* Look for clustering of samples over regions of high or low values.\n",
    "\n",
    "* This data visualization will also be important to assist with parameter selection for the variogram calculation search template.\n",
    "\n",
    "Let's plot the location maps of the original feature and the normal score transforms of the feature."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 100892 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.subplot(111)                                        # location map of normal score transform of porosity\n",
    "GSLIB.locmap_st(df,'X','Y',feature,0,1000,0,1000,vmin,vmax,feature,'X (m)','Y (m)',feature,cmap)\n",
    "\n",
    "plt.subplots_adjust(left=0.0, bottom=0.0, right=1.0, top=1.1, wspace=0.5, hspace=0.3)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Look carefully, and you'll notice the the spatial samples are more dense in the high porosity regions and lower in the low porosity regions.  There is preferential sampling.  We cannot use the naive statistics to represent this region.  We have to correct for the clustering of the samples in the high porosity regions. \n",
    "\n",
    "Let's try cell declustering. We can interpret that we will want to minimize the declustering mean and that get a sense of a range of possible optimal cell sizes based on 'an ocular' estimate of the largest average spacing in the sparsely sampled regions.   \n",
    "\n",
    "Let's check out the declus program reimplimented from GSLIB."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function geostatspy.geostats.declus(df, xcol, ycol, vcol, iminmax, noff, ncell, cmin, cmax)>"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "geostats.declus"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can now populate the parameters. The parameters are:\n",
    "\n",
    "* **df** - DataFrame with the spatial dataset\n",
    "* **xcol** - column with the x coordinate\n",
    "* **ycol** - column with the y coordinate\n",
    "* **vcol** - column with the feature value\n",
    "* **iminmax** - if 1 use the cell size that minimizes the declustered mean, if 0 the cell size that maximizes the declustered mean\n",
    "* **noff** - number of cell mesh offsets to average the declustered weights over\n",
    "* **ncell** - number of cell sizes to consider (between the **cmin** and **cmax**)\n",
    "* **cmin** - minimum cell size\n",
    "* **cmax** - maximum cell size\n",
    "\n",
    "We will run a very wide range of cell sizes, from 10m to 2,000m ('cmin' and 'cmax') and take the cell size that minimizes the declustered mean ('iminmax' = 1 minimize, and = 0 maximize). Multiple offsets (number of these is 'noff') uses multiple grid origins and averages the results to remove sensitivity to grid position.  The ncell is the number of cell sizes.\n",
    "\n",
    "The output from this program is:\n",
    "\n",
    "* **wts** - an array with the weigths for each data (they sum to the number of data, 1 indicates nominal weight)\n",
    "* **cell_sizes** - an array with the considered cell sizes\n",
    "* **dmeans** - an array with the declustered mean for each of the **cell_sizes**\n",
    "\n",
    "The **wts** are the declustering weights for the selected (minimizing or maximizing cell size) and the **cell_sizes** and **dmeans** are plotted to build the diagnostic declustered mean vs. cell size plot (see below)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "There are 368 data with:\n",
      "   mean of      12.702581678766817 \n",
      "   min and max  4.11215184025049 and 21.02576261742328\n",
      "   standard dev 3.0600605579028985 \n",
      "\n",
      "Porosity Declustering Results:\n",
      "Stationary Mean: naive mean is 12.703, declustered mean is 12.133.\n",
      "Variance/Sill: naive variance is 9.364, declustered variance is 9.523.\n",
      "Declustering correction in mean of 4.48%.\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 125020 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 5 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "find_minimizing_cell_size = 1           # True - minimizing cell size, False - maximizing cell size\n",
    "number_offsets = 10\n",
    "number_cell_sizes = 100\n",
    "min_cell_size = 1\n",
    "max_cell_size = 5000\n",
    "\n",
    "wts, cell_sizes, dmeans = geostats.declus(df,'X','Y',feature,iminmax = int(find_minimizing_cell_size), \n",
    "                    noff= number_offsets,ncell=number_cell_sizes,cmin=min_cell_size,cmax=max_cell_size)\n",
    "df['Wts'] = wts                            # add weights to the sample data DataFrame\n",
    "df.head()                                  # preview to check the sample data DataFrame\n",
    "\n",
    "plt.subplot(221)\n",
    "GSLIB.locmap_st(df,'X','Y','Wts',xmin,xmax,ymin,ymax,0.0,2.0,'Declustering Weights','X (m)','Y (m)','Weights',cmap)\n",
    "\n",
    "plt.subplot(222)\n",
    "GSLIB.hist_st(df['Wts'],0.0,5.0,log=False,cumul=False,bins=40,weights=None,xlabel=\"Weights\",title=\"Declustering Weights\")\n",
    "plt.ylim(0.0,250)\n",
    "\n",
    "plt.subplot(223)\n",
    "GSLIB.hist_st(df[feature],vmin,vmax,log=False,cumul=False,bins=40,weights=None,xlabel=feature,title=\"Naive \" + feature)\n",
    "plt.ylim(0.0,250)\n",
    "\n",
    "plt.subplot(224)\n",
    "GSLIB.hist_st(df[feature],vmin,vmax,log=False,cumul=False,bins=40,weights=df['Wts'],xlabel=feature,title=\"Declustered \" + feature)\n",
    "plt.ylim(0.0,250)\n",
    "\n",
    "weighted_data = DescrStatsW(df['Porosity'].values, weights=df['Wts'], ddof=0)\n",
    "\n",
    "vmean = np.average(df[feature].values)\n",
    "vvar = np.var(df[feature].values)\n",
    "vdmean = weighted_data.mean\n",
    "vdvar = weighted_data.var\n",
    "\n",
    "print('\\n' + feature + ' Declustering Results:')\n",
    "print('Stationary Mean: naive mean is ' + str(round(vmean,3))+', declustered mean is ' + str(round(vdmean,3))+'.')\n",
    "print('Variance/Sill: naive variance is ' + str(round(vvar,3))+', declustered variance is ' + str(round(vdvar,3))+'.')\n",
    "\n",
    "cor = (vmean-vdmean)/vmean\n",
    "print('Declustering correction in mean of ' + str(round(cor*100,2)) +'%.')\n",
    "\n",
    "# print('\\nSummary statistics of the declustering weights:')\n",
    "# print(stats.describe(wts))\n",
    "\n",
    "plt.subplots_adjust(left=0.0, bottom=0.0, right=2.0, top=2.5, wspace=0.2, hspace=0.2)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Compare the Naive and Declustered Cumulative Distribution Functions\n",
    "\n",
    "Let's take a look at the naive and declustered feature cumulative distribution functions for a visual comparison."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 26956 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.subplot(111)                                        # plot original sand and shale porosity histograms\n",
    "plt.hist(df[feature], facecolor='none',bins=np.linspace(vmin,vmax,1000),histtype=\"step\",alpha=1.0,density=True,cumulative=True,edgecolor='black',label='Naive',linewidth=2)\n",
    "plt.hist(df[feature],weights=df['Wts'],facecolor='none',bins=np.linspace(vmin,vmax,1000),histtype=\"step\",alpha=1.0,density=True,cumulative=True,edgecolor='red',label='Declustered',linewidth=2)\n",
    "plt.xlim([vmin,vmax]); plt.ylim([0,1.0])\n",
    "plt.xlabel(feature + ' (' + feature_units + ')'); plt.ylabel('Cumulative Probability'); plt.title(feature)\n",
    "plt.grid(True); plt.legend(loc='upper left')\n",
    "\n",
    "plt.subplots_adjust(left=0.0, bottom=0.0, right=1.0, top=1.2, wspace=0.2, hspace=0.3)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we are ready to calculate our experimental variograms.\n",
    "\n",
    "#### Experimental Variograms\n",
    "\n",
    "We can use the location maps to help determine good variogram calculation parameters. For example:\n",
    "\n",
    "```p\n",
    "tmin = -1.0e21; tmax = 1.0e21; \n",
    "lag_dist = -999; lag_tol = -999; nlag = -999; bandh = -999; azi = -999; atol = -999, isill = 1\n",
    "```\n",
    "* **tmin**, **tmax** are trimming limits - set to have no impact, no need to filter the data\n",
    "* **lag_dist**, **lag_tol** are the lag distance, lag tolerance - set based on the common data spacing (100m) and tolerance as 100% of lag distance for additonal smoothing\n",
    "* **nlag** is number of lags - set to extend just past 50 of the data extent\n",
    "* **bandh** is the horizontal band width - set to have no effect\n",
    "* **azi** is the azimuth -  it has not effect since we set atol, the azimuth tolerance, to 90.0\n",
    "* **isill** is a boolean to standardize the distribution to a variance of 1 - it has no effect since the previous nscore transform sets the variance to 1.0\n",
    "\n",
    "#### Dashboard for Interactive Experimental Variogram Calculation\n",
    "\n",
    "Below we make a dashboard with the ipywidgets and matplotlib Python packages for calculating experimental variograms.\n",
    "\n",
    "* allowing you to calculate the experimental variogram interactively while changing (and exploring) the search template parameters.\n",
    "\n",
    "* first calculate the directional variogram(s)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "# interactive calculation of the experimental variogram\n",
    "l = widgets.Text(value='                              Variogram Calculation Interactive Demonstration, Michael Pyrcz, Associate Professor, The University of Texas at Austin',layout=Layout(width='950px', height='30px'))\n",
    "lag = widgets.FloatSlider(min = 10, max = 500, value = 10, step = 10, description = 'lag',orientation='vertical',layout=Layout(width='90px', height='200px'),continuous_update = False)\n",
    "lag.style.handle_color = 'gray'\n",
    "\n",
    "lag_tol = widgets.FloatSlider(min = 5, max = 500, value = 5, step = 10, description = 'lag tolerance',orientation='vertical',layout=Layout(width='90px', height='200px'),continuous_update = False)\n",
    "lag_tol.style.handle_color = 'gray'\n",
    "\n",
    "nlag = widgets.IntSlider(min = 1, max = 100, value = 100, step = 1, description = 'number of lags',orientation='vertical',layout=Layout(width='90px', height='200px'),continuous_update = False)\n",
    "nlag.style.handle_color = 'gray'\n",
    "\n",
    "azi = widgets.FloatSlider(min = 0, max = 360, value = 0, step = 5, description = 'azimuth',orientation='vertical',layout=Layout(width='90px', height='200px'),continuous_update = False)\n",
    "azi.style.handle_color = 'gray'\n",
    "\n",
    "azi_tol = widgets.FloatSlider(min = 10, max = 90, value = 10, step = 5, description = 'azimuth tolerance',orientation='vertical',layout=Layout(width='120px', height='200px'),continuous_update = False)\n",
    "azi_tol.style.handle_color = 'gray'\n",
    "\n",
    "bandwidth = widgets.FloatSlider(min = 100, max = 2000, value = 2000, step = 100, description = 'bandwidth',orientation='vertical',layout=Layout(width='90px', height='200px'),continuous_update = False)\n",
    "azi_tol.style.handle_color = 'gray'\n",
    "\n",
    "\n",
    "ui1 = widgets.HBox([lag,lag_tol,nlag,azi,azi_tol,bandwidth],) # basic widget formatting    \n",
    "ui = widgets.VBox([l,ui1],)\n",
    "\n",
    "def f_make(lag,lag_tol,nlag,azi,azi_tol,bandwidth):     # function to take parameters, calculate variogram and plot\n",
    "    global lags,gammas,npps,lags2,gammas2,npps2\n",
    "    tmin = -9999.9; tmax = 9999.9\n",
    "    lags, gammas, npps = geostats.gamv(df,\"X\",\"Y\",feature,tmin,tmax,lag,lag_tol,nlag,azi,azi_tol,bandwidth,isill=0.0)\n",
    "    lags2, gammas2, npps2 = geostats.gamv(df,\"X\",\"Y\",feature,tmin,tmax,lag,lag_tol,nlag,azi+90.0,azi_tol,bandwidth,isill=0.0)\n",
    "    \n",
    "    plt.subplot(111)                                    # plot experimental variogram\n",
    "    plt.scatter(lags,gammas,color = 'black',s = npps*0.03,label = 'Major Azimuth ' +str(azi), alpha = 0.8)\n",
    "    plt.scatter(lags2,gammas2,color = 'red',s = npps*0.03,label = 'Minor Azimuth ' +str(azi+90.0), alpha = 0.8)\n",
    "    plt.plot([0,2000],[vdvar,vdvar],color = 'black')\n",
    "    plt.xlabel(r'Lag Distance $\\bf(h)$, (m)')\n",
    "    plt.ylabel(r'$\\gamma \\bf(h)$')\n",
    "    if azi_tol < 90.0:\n",
    "        plt.title('Directional ' + feature + ' Variogram - Azi. ' + str(azi) + ', Azi. Tol.' + str(azi_tol))\n",
    "    else: \n",
    "        plt.title('Omni Directional ' + feature + ' Variogram ')\n",
    "    plt.xlim([0,1000]); plt.ylim([0,1.5*vdvar])\n",
    "    plt.legend(loc=\"lower right\")\n",
    "    plt.grid(True)\n",
    "    \n",
    "    plt.subplots_adjust(left=0.0, bottom=0.0, right=1.5, top=1.0, wspace=0.3, hspace=0.3)\n",
    "    plt.show()\n",
    "    \n",
    "    return\n",
    "    \n",
    "# connect the function to make the samples and plot to the widgets    \n",
    "interactive_plot = widgets.interactive_output(f_make, {'lag':lag,'lag_tol':lag_tol,'nlag':nlag,'azi':azi,'azi_tol':azi_tol,'bandwidth':bandwidth})\n",
    "interactive_plot.clear_output(wait = True)               # reduce flickering by delaying plot updating"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Interactive Variogram Calculation Demonstration\n",
    "\n",
    "* calculate omnidirectional and direction experimental variograms \n",
    "\n",
    "#### Michael Pyrcz, Associate Professor, University of Texas at Austin \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) | [GeostatsPy](https://github.com/GeostatsGuy/GeostatsPy)\n",
    "\n",
    "### The Problem\n",
    "\n",
    "Calculate interpretable experimental variograms for sparse, irregularly-space spatial data. Note, size of the experimental point is scaled by the number of pairs.\n",
    "\n",
    "* **azimuth** is the azimuth of the lag vector\n",
    "\n",
    "* **azimuth tolerance** is the maximum allowable departure from the azimuth\n",
    "\n",
    "* **unit lag distance** the size of the bins in lag distance\n",
    "\n",
    "* **lag distance tolerance** - the allowable tolerance in lage distance\n",
    "\n",
    "* **number of lags** - number of lags in the experimental variogram\n",
    "\n",
    "* **bandwidth** - maximum departure from the lag vector"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f95c0e9745f6480c94d743e66d2174bd",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "VBox(children=(Text(value='                              Variogram Calculation Interactive Demonstration, Mich…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "85b529d1ac384f368b2d75322c7230be",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Output(outputs=({'output_type': 'display_data', 'data': {'text/plain': '<Figure size 432x288 with 1 Axes>', 'i…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(ui, interactive_plot)                           # display the interactive plot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Dashboard for Interactive Variogram Modeling\n",
    "\n",
    "Below we make a dashboard with the ipywidgets and matplotlib Python packages for modeling experimental variograms.\n",
    "\n",
    "* fit the directional variogram(s)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "# interactive calculation of the sample set (control of source parametric distribution and number of samples)\n",
    "l = widgets.Text(value='                              Variogram Modeling, Michael Pyrcz, Associate Professor, The University of Texas at Austin',layout=Layout(width='950px', height='30px'))\n",
    "nug = widgets.FloatSlider(min = 0, max = 1.0, value = 0.0, step = 0.01, description = r'$c_{nug}$',\n",
    "                          orientation='vertical',layout=Layout(width='60px', height='200px'),\n",
    "                          readout_format='.0%',continuous_update = False)\n",
    "nug.style.handle_color = 'gray'\n",
    "it1 = widgets.Dropdown(options=['Spherical', 'Exponential', 'Gaussian'],value='Spherical',\n",
    "    description='$1^{st}$ structure:',disabled=False,layout=Layout(width='200px', height='30px'),continuous_update=False)\n",
    "c1 = widgets.FloatSlider(min=0.0, max = 1.0, value = 0.1,step=0.01,description = r'$c_{1}$',\n",
    "                          orientation='vertical',layout=Layout(width='60px', height='200px'),\n",
    "                          readout_format='.0%',continuous_update = False)\n",
    "c1.style.handle_color = 'gray'\n",
    "hmaj1 = widgets.FloatSlider(min=0.01, max = 10000.0, value = 0.01, step = 25.0, description = r'$a_{maj_1}$',orientation='vertical',layout=Layout(width='60px', height='200px'),continuous_update=False)\n",
    "hmaj1.style.handle_color = 'black'\n",
    "hmin1 = widgets.FloatSlider(min = 0.01, max = 10000.0, value = 0.01, step = 25.0, description = r'$a_{min_1}$',orientation='vertical',layout=Layout(width='60px', height='200px'),continuous_update=False)\n",
    "hmin1.style.handle_color = 'red'\n",
    "\n",
    "it2 = widgets.Dropdown(options=['Spherical', 'Exponential', 'Gaussian'],value='Spherical',\n",
    "    description='$2^{nd}$ structure:',disabled=False,layout=Layout(width='200px', height='30px'))\n",
    "c2 = widgets.FloatSlider(min=0.0, max = 1.0, value = 0.0,step=0.01,description = r'$c_{2}$',orientation='vertical',\n",
    "                          layout=Layout(width='60px', height='200px'),continuous_update=False,\n",
    "                          readout_format='.0%')\n",
    "c2.style.handle_color = 'gray'\n",
    "hmaj2 = widgets.FloatSlider(min=0.01, max = 10000.0, value = 0.01, step = 100.0, description = r'$a_{maj_2}$',\n",
    "                          orientation='vertical',layout=Layout(width='60px', height='200px'),continuous_update=False,)\n",
    "hmaj2.style.handle_color = 'black'\n",
    "hmin2 = widgets.FloatSlider(min = 0.01, max = 10000.0, value = 0.01, step = 100.0, description = r'$a_{min_2}$',orientation='vertical',layout=Layout(width='60px', height='200px'),continuous_update=False)\n",
    "hmin2.style.handle_color = 'red'\n",
    "\n",
    "ui1 = widgets.HBox([nug,it1,c1,hmaj1,hmin1,it2,c2,hmaj2,hmin2],)                   # basic widget formatting   \n",
    "#ui2 = widgets.HBox([it2,c2,hmaj2,hmin2],)                   # basic widget formatting   \n",
    "ui = widgets.VBox([l,ui1],)\n",
    "\n",
    "def convert_type(it):\n",
    "    if it == 'Spherical': \n",
    "        return 1\n",
    "    elif it == 'Exponential':\n",
    "        return 2\n",
    "    else: \n",
    "        return 3\n",
    "\n",
    "def f_make(nug,it1,c1, hmaj1, hmin1, it2, c2, hmaj2, hmin2):                       # function to take parameters, make sample and plot\n",
    "    nug = nug *  vdvar; c1 = c1 * vdvar; c2 = c2 * vdvar\n",
    "#     text_trap = io.StringIO()\n",
    "#     sys.stdout = text_trap\n",
    "    \n",
    "    it1 = convert_type(it1); it2 = convert_type(it2)\n",
    "    if c2 > 0.0:\n",
    "        nst = 2\n",
    "    else:\n",
    "        nst = 1\n",
    "    print('Ignore this warning, since we are kriging the original feature the sill is not one:')\n",
    "    vario = GSLIB.make_variogram(nug,nst,it1,c1,0.0,hmaj1,hmin1,it2,c2,0.0,hmaj2,hmin2) # make model object\n",
    "    nlag = 100; xlag = 10;                                     \n",
    "    index_maj,h_maj,gam_maj,cov_maj,ro_maj = geostats.vmodel(nlag,xlag,0.0,vario)   # project the model in the major azimuth                                                  # project the model in the 135 azimuth\n",
    "    index_min,h_min,gam_min,cov_min,ro_min = geostats.vmodel(nlag,xlag,90.0,vario)   \n",
    "\n",
    "    plt.subplot(111)                                    # plot experimental variogram\n",
    "    plt.scatter(lags,gammas,color = 'black',s = npps*0.03,label = 'Major Azimuth ' +str(azi.value), alpha = 0.8)\n",
    "    plt.plot(h_maj,gam_maj,color = 'black')\n",
    "    plt.scatter(lags2,gammas2,color = 'red',s = npps*0.03,label = 'Minor Azimuth ' +str(azi.value+90.0), alpha = 0.8)\n",
    "    plt.plot(h_min,gam_min,color = 'red')\n",
    "    plt.plot([0,2000],[vdvar,vdvar],color = 'black')\n",
    "    plt.xlabel(r'Lag Distance $\\bf(h)$, (m)')\n",
    "    plt.ylabel(r'$\\gamma \\bf(h)$')\n",
    "    if azi_tol.value < 90.0:\n",
    "        plt.title('Directional ' + feature + ' Variogram - Azi. ' + str(azi.value) + ', Azi. Tol.' + str(azi_tol.value))\n",
    "    else: \n",
    "        plt.title('Omni Directional ' + feature + ' Variogram ')\n",
    "    plt.xlim([0,1000]); plt.ylim([0,1.5*vdvar])\n",
    "    plt.legend(loc=\"lower right\")\n",
    "    plt.grid(True)\n",
    "    \n",
    "    plt.subplots_adjust(left=0.0, bottom=0.0, right=2.2, top=1.5, wspace=0.3, hspace=0.3)\n",
    "    plt.show()\n",
    "    \n",
    "# connect the function to make the samples and plot to the widgets    \n",
    "interactive_plot = widgets.interactive_output(f_make, {'nug':nug, 'it1':it1,'c1':c1, 'hmaj1':hmaj1, 'hmin1':hmin1, 'it2':it2, 'c2':c2, 'hmaj2':hmaj2, 'hmin2':hmin2})\n",
    "interactive_plot.clear_output(wait = True)               # reduce flickering by delaying plot updating"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Interactive Nested Variogram Modeling Demostration\n",
    "\n",
    "* select the nested structures and their types, contributions and major and minor ranges \n",
    "\n",
    "#### Michael Pyrcz, Associate Professor, University of Texas at Austin \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) | [GeostatsPy](https://github.com/GeostatsGuy/GeostatsPy)\n",
    "\n",
    "### The Problem\n",
    "\n",
    "Fit a positive definite variogram model based on the addition of multiple structures each describing spatial components of the feature variance \n",
    "\n",
    "* **nug**: nugget effect\n",
    "\n",
    "* **c1 / c2**: contributions of the sill, in percentage of the sill calculated from the declustered variance\n",
    "\n",
    "* **hmaj1 / hmaj2**: range in the major direction\n",
    "\n",
    "* **hmin1 / hmin2**: range in the minor direction"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "3aaf0877fb2b4a619b39606d4df1c5ad",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "VBox(children=(Text(value='                              Variogram Modeling, Michael Pyrcz, Associate Professo…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "043a7acba3514628ba9c0f7d7e9b6caf",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Output(outputs=({'output_type': 'stream', 'text': 'Ignore this warning, since we are kriging the original feat…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(ui, interactive_plot)                           # display the interactive plot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Kriging to Calculate Spatial Estimates\n",
    "\n",
    "Now let's build spaital maps with our data and modeled variogram.\n",
    "\n",
    "We require a grid for our map. \n",
    "\n",
    "* first we specify the extents of the grid with xmin, xmax, ymin and ymax.\n",
    "\n",
    "* then we specify the number of grid cells in x and y with nx and ny\n",
    "\n",
    "* then we calculate the required size of the cells in each direction, xsiz and ysiz, and the cell center of the grid origin, xmin and ymin"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "nx = 100; ny = 100                           # number of cells\n",
    "xsiz = (xmax-xmin)/nx; xmn = xmin + xsiz*0.5 # calculation for the size of each cell and the cell origin\n",
    "ysiz = (ymax-ymin)/ny; ymn = ymin + ysiz*0.5"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Perform kriging with specific kriging settings.\n",
    "\n",
    "* **skmean** - if performing simple kriging this is the stationary mean, use the declustered mean, vdmean\n",
    "* **ktype** - kriging type, 0 = simple kriging and 1 = ordinary kriging\n",
    "* **ndmin, ndmax** - the minimum and maximum number of data for each kriging estimate, reduce ndmax to speed up the calculation, increase ndmin to avoid making predictions with too few data\n",
    "* **tmin, tmax** - the trimming limits to remove data outliers\n",
    "\n",
    "We look at the kriging estimates and the kriging (estimation) variance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ignore this warning, since we are kriging the original feature the sill is not one:\n",
      "\u001b[0;30;41m make_variogram Warning: sill does not sum to 1.0, do not use in simulation \u001b[0m\n",
      "  Estimated   10000 blocks \n",
      "      average   12.000093435166745  variance  7.704696215375236\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 161352 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 4 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "skmean = vdmean                            # simple kriging mean (used if simple kriging is selected below)\n",
    "ktype = 0                               # kriging type, 0 - simple, 1 - ordinary\n",
    "radius = 500                              # search radius for neighbouring data\n",
    "ndmin = 0; ndmax = 50                 # minimum and maximum data for an estimate\n",
    "tmin =  -1.0e21; tmax = 1.0e21             # data trimming limits, set very small and large to not trim the data\n",
    "\n",
    "print('Ignore this warning, since we are kriging the original feature the sill is not one:')\n",
    "vario = GSLIB.make_variogram(nug=nug.value*vdvar,nst=2,\n",
    "         it1=convert_type(it1.value),cc1=c1.value*vdvar,azi1=azi.value,hmaj1=hmaj1.value,hmin1 = hmin1.value,\n",
    "         it2=convert_type(it2.value),cc2=c2.value*vdvar,azi2=azi.value,hmaj2=hmaj2.value,hmin2 = hmin2.value) # variogram\n",
    "\n",
    "kmap, vmap = geostats.kb2d(df,'X','Y',feature,tmin,tmax,nx,xmn,xsiz,ny,ymn,ysiz,1,1,\n",
    "         ndmin,ndmax,radius,ktype,skmean,vario)\n",
    "\n",
    "plt.subplot(121)\n",
    "GSLIB.pixelplt_st(kmap,xmin,xmax,ymin,ymax,xsiz,vmin,vmax,'Kriging Estimate','X(m)','Y(m)',feature + ' (' + feature_units + ')',cmap)\n",
    "plt.scatter(df['X'],df['Y'],marker='o',s=10,c=df[feature].values,edgecolor='black',cmap=cmap)\n",
    "\n",
    "plt.subplot(122)\n",
    "GSLIB.pixelplt_st(vmap,xmin,xmax,ymin,ymax,xsiz,vmin,7,'Kriging Variance','X(m)','Y(m)',feature + ' kriging variance (' + feature_units + '^2)',cmap)\n",
    "plt.scatter(df['X'],df['Y'],marker='o',s=10,color='white',edgecolor='black')\n",
    "\n",
    "\n",
    "plt.subplots_adjust(left=0.0, bottom=0.0, right=2.0, top=1.1, wspace=0.3, hspace=0.3)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Spatial Predictions with Kriging\n",
    "\n",
    "Select a location and observe the kriging estimate and estimation variance.\n",
    "\n",
    "* We assume Gaussian distribution for the uncertainty parameterized by a mean of the kriging estimate and variance of the kriging estimation variance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Kriging Estimate = 10.03, Kriging Estimation Variance = 0.32\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 98096 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 3 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "x = 500                                    # location to estimate\n",
    "y = 500\n",
    "\n",
    "ix = geostats.getindex(nx,xmn,xsiz,x); iy = ny-geostats.getindex(ny,ymn,ysiz,y)-1\n",
    "\n",
    "plt.subplot(121)\n",
    "GSLIB.pixelplt_st(kmap,xmin,xmax,ymin,ymax,xsiz,vmin,vmax,'Kriging Estimate','X(m)','Y(m)',feature + ' (' + feature_units + ')',cmap)\n",
    "plt.scatter(df['X'],df['Y'],marker='o',s=5,c=df[feature].values,edgecolor='black')\n",
    "plt.scatter(x,y,marker='o',s=50,color='white',edgecolor='black',linewidths=3)\n",
    "\n",
    "kest = kmap[iy,ix]; kerr = vmap[iy,ix]\n",
    "pvalues = np.linspace(0.01,0.99,100)\n",
    "print('Kriging Estimate = ' + str(round(kest,2)) + ', Kriging Estimation Variance = ' + str(round(kerr,2)))\n",
    "\n",
    "plt.subplot(122)\n",
    "plt.hist(norm.rvs(loc=kest,scale=math.sqrt(kerr),size = 1000),bins=np.linspace(vmin,vmax,40),color='darkorange',edgecolor='black',density=True)\n",
    "plt.xlabel(feature + ' Estimate and Uncertainty (' + feature_units + ') at Location (' + str(x) + ',' + str(y) + ')'); plt.ylabel('Probability')\n",
    "plt.title('Kriging Estimate and Kriging Variance-based Uncertainty')\n",
    "plt.xlim([vmin,vmax])\n",
    "\n",
    "plt.subplots_adjust(left=0.0, bottom=0.0, right=2.0, top=1.1, wspace=0.3, hspace=0.3)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Comments\n",
    "\n",
    "This was a basic demonstration / exercise of variogram calculation and modeling for spatial continuity analysis and spatial estimation with kriging. Much more could be done, I have other demonstrations on the 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, Associate Professor, 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. Associate Professor The Hildebrand Department of Petroleum and Geosystems Engineering, Bureau of Economic Geology, 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)  \n",
    "  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "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.9.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
