├── Demos ├── SubsurfaceDataAnalytics_Basic_Data_Loading_Display.ipynb ├── SubsurfaceDataAnalytics_Confidence_Hypothesis.ipynb ├── SubsurfaceDataAnalytics_DataFrame.ipynb ├── SubsurfaceDataAnalytics_Feature_Ranking.ipynb ├── SubsurfaceDataAnalytics_Gridded_Data.ipynb ├── SubsurfaceDataAnalytics_Multivariate.ipynb ├── SubsurfaceDataAnalytics_PCA.ipynb ├── SubsurfaceDataAnalytics_bootstrap.ipynb └── SubsurfaceDataAnalytics_spatial_data.ipynb ├── LICENSE ├── Lectures ├── 00_CourseOverview.pdf ├── 01_Introduction.pdf ├── 02_DataAnalytics.pdf ├── 03_Inference.pdf ├── 04_Prediction.pdf ├── 05_Advanced.pdf └── 06_Conclusion.pdf └── README.md /Demos/SubsurfaceDataAnalytics_Confidence_Hypothesis.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "collapsed": true 7 | }, 8 | "source": [ 9 | "

\n", 10 | " \n", 11 | "\n", 12 | "

\n", 13 | "\n", 14 | "## Subsurface Data Analytics \n", 15 | "\n", 16 | "### Confidence Intervals and Hypothesis Testing for Subsurface Data Analytics in Python \n", 17 | "\n", 18 | "\n", 19 | "#### Michael Pyrcz, Associate Professor, University of Texas at Austin \n", 20 | "\n", 21 | "##### [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" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "##### Reporting Uncertainty and Significance\n", 29 | "\n", 30 | "With confidence intervals and hypothesis testing we have the opportunity to report uncertainty and to report significance in our statistics. \n", 31 | "\n", 32 | "* report **uncertainty and significance** with our results\n", 33 | "\n", 34 | "This is a tutorial / demonstration of **Confidence Intervals and Hypothesis Testing in Python** for Subsurface Modeling. In Python, the SciPy package, specifically the Stats functions (https://docs.scipy.org/doc/scipy/reference/stats.html) provide excellent tools for efficient use of statistics. \n", 35 | "\n", 36 | "This tutorial includes basic, typical confidence interval and hypothesis testing methods that would commonly be required for Engineers and Geoscientists including:\n", 37 | "\n", 38 | "1. Student-t confidence interval for the mean\n", 39 | "2. Student-t hypothesis test for difference in means (pooled variance)\n", 40 | "3. Student-t hypothesis test for difference in means (difference variances), Welch's t Test\n", 41 | "3. F-distribution hypothesis test for difference in variances \n", 42 | "\n", 43 | "#### Additional Resources\n", 44 | "\n", 45 | "These workflows are based on standard methods with their associated limitations and assumptions. For more information see:\n", 46 | "\n", 47 | "* [Confidence Intervals Lecture](https://www.youtube.com/watch?v=oaXCcTWcU04)\n", 48 | "\n", 49 | "* [Hypothesis Testing_Lecture](https://www.youtube.com/watch?v=rvt9UM148tQ) \n", 50 | "\n", 51 | "* Also, see the lecture and workflow on Bootstrap https://git.io/fhgUW for a general, empirical approach to assess uncertianty in statistics.\n", 52 | "\n", 53 | "I have provided various workflows for subsurface data analytics, geostatistics and machine learning:\n", 54 | "\n", 55 | "* [Python](https://git.io/fh4eX)\n", 56 | "\n", 57 | "* [Excel](https://github.com/GeostatsGuy/LectureExercises/blob/master/Lecture7_CI_Hypoth_eg_R.xlsx) \n", 58 | "* [R](https://github.com/GeostatsGuy/LectureExercises/blob/master/Lecture7_CI_Hypoth_eg.R) \n", 59 | "\n", 60 | "and all of my University of Texas at Austin \n", 61 | "\n", 62 | "* [Lectures](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig/featured?view_as=subscriber)\n", 63 | "\n", 64 | "##### Caveats\n", 65 | "\n", 66 | "I have not included all the details, specifically the test assumptions in this document. These are included in the accompanying course notes, recorded lectures listed above.\n", 67 | "\n", 68 | "#### Workflow Goal\n", 69 | "\n", 70 | "0. Introduction to Python in Jupyter including setting a working directory, loading data into a Pandas DataFrame.\n", 71 | "1. Learn the basics for working with confidence intervals and hypothesis testing in Python. \n", 72 | "2. Demonstrate the efficiency of using Python and SciPy package for statistical analysis.\n", 73 | "3. Learn how to quantify uncertainty and significance in samples.\n", 74 | "\n", 75 | "#### Objective \n", 76 | "\n", 77 | "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", 78 | "\n", 79 | "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", 80 | "\n", 81 | "#### Getting Started\n", 82 | "\n", 83 | "Here's the steps to get setup in Python with the GeostatsPy package:\n", 84 | "\n", 85 | "1. Install Anaconda 3 on your machine (https://www.anaconda.com/download/). \n", 86 | "2. From Anaconda Navigator (within Anaconda3 group), go to the environment tab, click on base (root) green arrow and open a terminal. \n", 87 | "3. In the terminal type: pip install geostatspy. \n", 88 | "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", 89 | "\n", 90 | "You will need to copy the data file to your working directory. They are available here:\n", 91 | "\n", 92 | "* Tabular data - PorositySample2Units.csv at https://git.io/fhrM8\n", 93 | "\n", 94 | "#### Load the required libraries\n", 95 | "\n", 96 | "The following code loads the required libraries." 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": 29, 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [ 105 | "import geostatspy.GSLIB as GSLIB # GSLIB utilies, visualization and wrapper\n", 106 | "import geostatspy.geostats as geostats # GSLIB methods convert to Python " 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "We will also need some standard packages. These should have been installed with Anaconda 3." 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 30, 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "import os # to set current working directory \n", 123 | "import numpy as np # arrays and matrix math\n", 124 | "import scipy.stats as st # statistical methods\n", 125 | "import pandas as pd # DataFrames\n", 126 | "import matplotlib.pyplot as plt # plotting" 127 | ] 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "metadata": {}, 132 | "source": [ 133 | "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. \n", 134 | "\n" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": {}, 140 | "source": [ 141 | "#### Set the working directory\n", 142 | "\n", 143 | "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 below) data file in this directory. When we are done with this tutorial we will write our new dataset back to this directory. " 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": 31, 149 | "metadata": {}, 150 | "outputs": [], 151 | "source": [ 152 | "os.chdir(\"C:\\PGE337\") # set the working directory" 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "metadata": {}, 158 | "source": [ 159 | "#### Loading Data\n", 160 | "\n", 161 | "Let's load the provided dataset. 'PorositySamples2Units.csv' is available at https://github.com/GeostatsGuy/GeoDataSets. It is a comma delimited file with 20 porosity measures from 2 rock units from the subsurface:\n", 162 | "\n", 163 | "* porosity (fraction) \n", 164 | "\n", 165 | "We load it with the pandas 'read_csv' function into a data frame we called 'df' and then preview it by printing a slice and by utilizing the 'head' DataFrame member function (with a nice and clean format, see below).\n", 166 | "\n", 167 | "We load it with the pandas 'read_csv' function into a data frame we called 'df' and then preview it by printing a slice and by utilizing the 'head' DataFrame member function (with a nice and clean format, see below).\n", 168 | "\n", 169 | "**Python Tip: using functions from a package** just type the label for the package that we declared at the beginning:\n", 170 | "\n", 171 | "```python\n", 172 | "import pandas as pd\n", 173 | "```\n", 174 | "\n", 175 | "so we can access the pandas function 'read_csv' with the command: \n", 176 | "\n", 177 | "```python\n", 178 | "pd.read_csv()\n", 179 | "```\n", 180 | "\n", 181 | "but read csv has required input parameters. The essential one is the name of the file. For our circumstance all the other default parameters are fine. If you want to see all the possible parameters for this function, just go to the docs [here](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html). \n", 182 | "\n", 183 | "* The docs are always helpful\n", 184 | "* There is often a lot of flexibility for Python functions, possible through using various inputs parameters\n", 185 | "\n", 186 | "also, the program has an output, a pandas DataFrame loaded from the data. So we have to specficy the name / variable representing that new object.\n", 187 | "\n", 188 | "```python\n", 189 | "df = pd.read_csv(\"PorositySample2Units.csv\") \n", 190 | "```\n", 191 | "\n", 192 | "Let's run this command to load the data and then look at the resulting DataFrame to ensure that we loaded it." 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": 56, 198 | "metadata": { 199 | "scrolled": false 200 | }, 201 | "outputs": [ 202 | { 203 | "data": { 204 | "text/html": [ 205 | "
\n", 206 | "\n", 219 | "\n", 220 | " \n", 221 | " \n", 222 | " \n", 223 | " \n", 224 | " \n", 225 | " \n", 226 | " \n", 227 | " \n", 228 | " \n", 229 | " \n", 230 | " \n", 231 | " \n", 232 | " \n", 233 | " \n", 234 | " \n", 235 | " \n", 236 | " \n", 237 | " \n", 238 | " \n", 239 | " \n", 240 | " \n", 241 | " \n", 242 | " \n", 243 | " \n", 244 | " \n", 245 | " \n", 246 | " \n", 247 | " \n", 248 | " \n", 249 | " \n", 250 | " \n", 251 | " \n", 252 | " \n", 253 | " \n", 254 | "
Unit1_PorUnit2_Por
00.210.20
10.170.26
20.150.20
30.200.19
40.190.13
\n", 255 | "
" 256 | ], 257 | "text/plain": [ 258 | " Unit1_Por Unit2_Por\n", 259 | "0 0.21 0.20\n", 260 | "1 0.17 0.26\n", 261 | "2 0.15 0.20\n", 262 | "3 0.20 0.19\n", 263 | "4 0.19 0.13" 264 | ] 265 | }, 266 | "execution_count": 56, 267 | "metadata": {}, 268 | "output_type": "execute_result" 269 | } 270 | ], 271 | "source": [ 272 | "df = pd.read_csv(\"PorositySample2Units.csv\") # read a .csv file in as a DataFrame\n", 273 | "df = df.rename(columns={'X1': 'Unit1_Por','X2':'Unit2_Por'}) # rename variables for clarity\n", 274 | "#print(df.iloc[0:5,:]) # display first 4 samples in the table as a preview\n", 275 | "df.head() # we could also use this command for a table preview " 276 | ] 277 | }, 278 | { 279 | "cell_type": "markdown", 280 | "metadata": {}, 281 | "source": [ 282 | "#### Summary Statistics\n", 283 | "\n", 284 | "It is useful to review the summary statistics of our loaded DataFrame. That can be accomplished with the 'describe' DataFrame member function. We transpose to switch the axes for ease of visualization." 285 | ] 286 | }, 287 | { 288 | "cell_type": "code", 289 | "execution_count": 57, 290 | "metadata": {}, 291 | "outputs": [ 292 | { 293 | "data": { 294 | "text/html": [ 295 | "
\n", 296 | "\n", 309 | "\n", 310 | " \n", 311 | " \n", 312 | " \n", 313 | " \n", 314 | " \n", 315 | " \n", 316 | " \n", 317 | " \n", 318 | " \n", 319 | " \n", 320 | " \n", 321 | " \n", 322 | " \n", 323 | " \n", 324 | " \n", 325 | " \n", 326 | " \n", 327 | " \n", 328 | " \n", 329 | " \n", 330 | " \n", 331 | " \n", 332 | " \n", 333 | " \n", 334 | " \n", 335 | " \n", 336 | " \n", 337 | " \n", 338 | " \n", 339 | " \n", 340 | " \n", 341 | " \n", 342 | " \n", 343 | " \n", 344 | " \n", 345 | " \n", 346 | " \n", 347 | "
countmeanstdmin25%50%75%max
Unit1_Por20.00.16450.0278100.110.15000.170.190.21
Unit2_Por20.00.20000.0454220.110.16750.200.230.30
\n", 348 | "
" 349 | ], 350 | "text/plain": [ 351 | " count mean std min 25% 50% 75% max\n", 352 | "Unit1_Por 20.0 0.1645 0.027810 0.11 0.1500 0.17 0.19 0.21\n", 353 | "Unit2_Por 20.0 0.2000 0.045422 0.11 0.1675 0.20 0.23 0.30" 354 | ] 355 | }, 356 | "execution_count": 57, 357 | "metadata": {}, 358 | "output_type": "execute_result" 359 | } 360 | ], 361 | "source": [ 362 | "df.describe().transpose() # visualize summary statistics " 363 | ] 364 | }, 365 | { 366 | "cell_type": "markdown", 367 | "metadata": {}, 368 | "source": [ 369 | "##### Extract Porosity from Well1 and Well2\n", 370 | "\n", 371 | "Here we extract the X1 and X2 unit porosity samples from the DataFrame into separate arrays called 'Por1' and 'Por2' for convenience.\n", 372 | "\n", 373 | "**Python Tip: extracting data from DataFrames**\n", 374 | "\n", 375 | "We can extract the data for a single feature with this command:\n", 376 | "\n", 377 | "```python\n", 378 | "1D_series = df['feature_name']\n", 379 | "```\n", 380 | "\n", 381 | "The 'series' retains information about the feature that was included in the DataFrame including the name and the indexing. This is fine, but some methods don't work with series so we can also extract the data as a 1D ndarray. By adding the '.values' the series is converted to a 1D array.\n", 382 | "\n", 383 | "```python\n", 384 | "1D_ndarray = df['feature_name'].values\n", 385 | "```\n", 386 | "\n", 387 | "So " 388 | ] 389 | }, 390 | { 391 | "cell_type": "code", 392 | "execution_count": 58, 393 | "metadata": {}, 394 | "outputs": [], 395 | "source": [ 396 | "por1 = df['Unit1_Por'].values # extract well 1 porosity to a ndarray\n", 397 | "por2 = df['Unit2_Por'].values # extract well 2 porosity to a ndarray " 398 | ] 399 | }, 400 | { 401 | "cell_type": "markdown", 402 | "metadata": {}, 403 | "source": [ 404 | "We could also work with the DataFrame directly and just use this command everytime we use a function that requires a ndarray. For example:\n", 405 | "\n", 406 | "```python \n", 407 | "np.average(df['Unit1_Por'].values)\n", 408 | "```\n", 409 | "\n", 410 | "would return the average over unit 1. I like to extract the ndarray so we can use more compact code:\n", 411 | "\n", 412 | "```python\n", 413 | "np.average(por)\n", 414 | "```\n", 415 | "\n", 416 | "to get the same result.\n", 417 | "\n", 418 | "**Coding Tip** the ndarrays are **shallow copies**, that means that if you change the array the DataFrame will also be changed. In other words that DataFrame and ndarray remained linked together. \n", 419 | "\n", 420 | "* you can modify either the ndarray or DataFrame and they remain consistent\n", 421 | "\n", 422 | "#### Well Distributions\n", 423 | "\n", 424 | "We should first visualize the distributions. It is convenient to plot the distributions on top of each other for comparision.\n", 425 | "\n", 426 | "**Coding Tip** this is our first time to use MatplotLib. This exaple below makes the binned histogram and cumulative distribution function for the unit 1 and 2 porosity measures.\n", 427 | "\n", 428 | "* we use $plt$ to access the matplotlib.plotly functionality as imported above\n", 429 | "\n", 430 | "* after we declare a subplot all commands append and modify to the current plot\n", 431 | "\n", 432 | "* subplot(number of rows, number of columns, plot index) is the format\n", 433 | "\n", 434 | "```python \n", 435 | "plt.subplot(121) # the first plot for a 1 x 2 matrix of plots\n", 436 | "```\n", 437 | "\n", 438 | "* we superimpose two histograms on top of eachother with different colours and transparency \n", 439 | "\n", 440 | "```python \n", 441 | "plt.hist(por1, facecolor='red',bins=np.linspace(0.0,0.4,20),alpha=0.2,density=False,edgecolor='black',label='Unit 1')\n", 442 | "plt.hist(por2, facecolor='blue',bins=np.linspace(0.0,0.4,20),alpha=0.2,density=False,edgecolor='black',label = 'Unit 2')\n", 443 | "```\n", 444 | "\n", 445 | "* then we add labels, set the axes, and include a legend\n", 446 | "\n", 447 | "Later on will will use the histogram function from reimplimented in geostatspy from GSLIB for simple, easy histograms.\n" 448 | ] 449 | }, 450 | { 451 | "cell_type": "code", 452 | "execution_count": 73, 453 | "metadata": {}, 454 | "outputs": [ 455 | { 456 | "data": { 457 | "image/png": "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\n", 458 | "text/plain": [ 459 | "
" 460 | ] 461 | }, 462 | "metadata": {}, 463 | "output_type": "display_data" 464 | } 465 | ], 466 | "source": [ 467 | "plt.subplot(121)\n", 468 | "plt.hist(por1, facecolor='red',bins=np.linspace(0.0,0.4,20),alpha=0.2,density=False,edgecolor='black',label='Unit 1')\n", 469 | "plt.hist(por2, facecolor='blue',bins=np.linspace(0.0,0.4,20),alpha=0.2,density=False,edgecolor='black',label = 'Unit 2')\n", 470 | "plt.xlim([0.0,0.4]); plt.ylim([0,8.0])\n", 471 | "plt.xlabel('Porosity (fraction)'); plt.ylabel('Frequency'); plt.title('Porosity Unit 1 and 2')\n", 472 | "plt.legend(loc='upper left')\n", 473 | "plt.grid(True)\n", 474 | "\n", 475 | "plt.subplot(122)\n", 476 | "plt.hist(por1, facecolor='red',bins=np.linspace(0.0,0.4,50),histtype=\"stepfilled\",alpha=0.2,density=True,cumulative=True,edgecolor='black',label='Unit 1')\n", 477 | "plt.hist(por2, facecolor='blue',bins=np.linspace(0.0,0.4,50),histtype=\"stepfilled\",alpha=0.2,density=True,cumulative=True,edgecolor='black',label='Unit 2')\n", 478 | "plt.xlim([0.0,0.4]); plt.ylim([0,1.0])\n", 479 | "plt.xlabel('Porosity (fraction)'); plt.ylabel('Cumulative Probability'); plt.title('Porosity Unit 1 and 2')\n", 480 | "plt.legend(loc='upper left')\n", 481 | "plt.grid(True)\n", 482 | "\n", 483 | "plt.subplots_adjust(left=0.0, bottom=0.0, right=2.0, top=1.2, wspace=0.2, hspace=0.3)\n", 484 | "plt.show()" 485 | ] 486 | }, 487 | { 488 | "cell_type": "markdown", 489 | "metadata": {}, 490 | "source": [ 491 | "The histogram and cumulative distribution functions indicate the porosity distributions in both wells 1 and 2. Ocular inspection does indicate: \n", 492 | "\n", 493 | "1. not a lot of samples are available for each (n=20) \n", 494 | "2. the distributions look different\n", 495 | "\n", 496 | "Let's use statistics to learn more about the subsurface from these two wells." 497 | ] 498 | }, 499 | { 500 | "cell_type": "markdown", 501 | "metadata": {}, 502 | "source": [ 503 | "#### Confidence Intervals\n", 504 | "\n", 505 | "Let's first demonstrate the calculation of the confidence interval for the sample mean at a 95% confidence level. This could be interpreted as the interval over which there is a 95% confidence that it contains the true population mean. We use the student's t distribution as we assume we do not know the variance and the sample size is small. \n", 506 | "\n", 507 | "\\begin{equation}\n", 508 | "x̅ \\pm t_{\\frac{\\alpha}{2},n-1} \\times \\frac {s}{\\sqrt{n}} \n", 509 | "\\end{equation}\n", 510 | "\n", 511 | "**Coding Tip** it is easy to combine text, variables and equations in printed output.\n", 512 | "\n", 513 | "* we just need to use the '+' to concatenate (add) the values and the 'str()' command to convert the values to strings.\n", 514 | "\n", 515 | "For example:\n", 516 | "\n", 517 | "```python \n", 518 | "print('The average porosity is ' + str(np.average(por1)) + '.')\n", 519 | "```" 520 | ] 521 | }, 522 | { 523 | "cell_type": "code", 524 | "execution_count": 70, 525 | "metadata": {}, 526 | "outputs": [ 527 | { 528 | "name": "stdout", 529 | "output_type": "stream", 530 | "text": [ 531 | "The confidence interval for the Well 1 mean porosity is 0.164 +/- 0.011, with a range of 0.154, 0.175\n", 532 | "The confidence interval for the Well 2 mean porosity is 0.2 +/- 0.018, with a range of 0.182, 0.218\n" 533 | ] 534 | } 535 | ], 536 | "source": [ 537 | "ci_95_por1 = st.t.interval(alpha = 0.9, df = len(df)-1, loc=np.mean(por1), scale=st.sem(por1))\n", 538 | "print('The confidence interval for the Well 1 mean porosity is ' + str(round(np.mean(por1),3)) + ' +/- ' + str(round(ci_95_por1[1]-np.mean(por1),3))\n", 539 | " + ', with a range of ' + str(round(ci_95_por1[0],3)) + ', ' + str(round(ci_95_por1[1],3)))\n", 540 | "\n", 541 | "ci_95_por2 = st.t.interval(alpha = 0.9, df = len(df)-1, loc=np.mean(por2), scale=st.sem(por2))\n", 542 | "print('The confidence interval for the Well 2 mean porosity is ' + str(round(np.mean(por2),3)) + ' +/- ' + str(round(ci_95_por2[1]-np.mean(por2),3))\n", 543 | " + ', with a range of ' + str(round(ci_95_por2[0],3)) + ', ' + str(round(ci_95_por2[1],3)))" 544 | ] 545 | }, 546 | { 547 | "cell_type": "markdown", 548 | "metadata": {}, 549 | "source": [ 550 | "So with one line of code we got the confidence interval in the mean with the Student's distribution!\n", 551 | "\n", 552 | "```python\n", 553 | "ci_95_por1 = st.t.interval(alpha = 0.9, df = len(df)-1, loc=np.mean(por1), scale=st.sem(por1))\n", 554 | "```\n", 555 | "where:\n", 556 | "\n", 557 | "* alpha is the significance level (alpha should be 1 - significance)\n", 558 | "\n", 559 | "* df is the degrees of freedom\n", 560 | "\n", 561 | "* loc is the sample mean calculated with the numpy.mean of an array function\n", 562 | "\n", 563 | "* scale is the stand error of the mean calculated with the scipy.stats.sem function\n", 564 | "\n", 565 | "The output is an array with:\n", 566 | "\n", 567 | "```python\n", 568 | "ci_95_por2[0] # lower bound\n", 569 | "ci_95_por2[1] # upper bound\n", 570 | "```\n", 571 | "\n", 572 | "\n", 573 | "#### Using Confidence Intervals to Build an Uncertainty Model for by-Well Distributions\n", 574 | "\n", 575 | "The confidence interval for the mean could be applied directly as an uncertainty model! The uncertainty in mean porosity at each well could be applied to calculate the uncertainty in the mean porosity applied to model porosity around the two wells. This method would account for the limited number of porosity samples available. If more porosity samples we available then this uncertainty would decrease (note the $\\sqrt{n}$ in the denominator of the confidence interval in the mean calculation above.\n", 576 | "\n", 577 | "One possible work flow would be to apply the affine correction from the *GeostatsPy* package to formulate a the P5 and P95 porosity distributions for porosity in each well. Note, these distribution scenarios are based only on uncertainty in the mean porosity and assumes the distribution tails are not anchored (are free to shift)." 578 | ] 579 | }, 580 | { 581 | "cell_type": "code", 582 | "execution_count": 71, 583 | "metadata": {}, 584 | "outputs": [ 585 | { 586 | "data": { 587 | "image/png": "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qi2/TmXhgnn0rx7D7xp74X5c/73hbyM/adIk5s6du2X6pJNO4txzzx2zGIby+OOPc+WVV/Lud797u/p97GMfY+rUqfT09JQUmSSp3Zhbh2dulaThtWXh2b96JX1Ltu+7jobT/YH6iXaXXXZh2bJlY7bNRjz++OP84z/+43YnR0mStpe5VZI0Gp5qW6Lf/va3zJ49m7vvvhuAk08+mcsuuwyAqVOncs4553DkkUdy3HHH8cgjjwBw7733smDBAo466iiOOeYY7rrrLgAeeugh3vjGN3L44Ydz+OGH89Of/pRzzz2Xe++9lyOOOIIPfOADACxZsoSjjz6aww47jI9+9KNbYvnbv/1bZs+ezate9aot8UiS1GrMrZLUmiw8x8iGDRs44ogjtvx885vfpKuri4svvph3vOMdXHXVVTz22GOceuqpAPzud7/jyCOP5LbbbuPYY4/l4x//OACnnXYan//851m6dCmf/vSntxxxPeusszj22GP5xS9+wW233cYhhxzC3/3d3/EHf/AHLFu2jCVLlnDjjTdyzz33cMstt7Bs2TKWLl3KzTffzNKlS7nqqqv4+c9/zr/8y79w6623TtjzJElSo8ytktQ+2vJU24lQ63SgV7/61VxzzTWcccYZ/OIXv9gyf4cdduDNb34zAG9729v40z/9U9avX89Pf/pT3vSmN21p99RTTwFw00038dWvfhWoXPPS1dXFY489ts22brzxRm688UZe9KIXAbB+/Xruuece1q1bxxvf+EZ23XVXAE444YQxHLkkSeUwt0pS+7DwLNmmTZtYvnw5u+yyC2vXrmXmzJlDtosINm3axO677z7i61kykw996EP85V/+5TbzP/vZzxIRI1qnJEnNxtwqSa3HU21L9pnPfIY5c+bwjW98g1NOOYWBgQGgkjS/9a1vAXDllVfyile8gt12241Zs2ZxzTXXAJVkt/lI7nHHHccXvvAFAJ555hmeeOIJpk2bxrp167Zs6zWveQ1f/vKXWb9+PQCrV6/m4YcfZt68eXz7299mw4YNrFu3juuvv37cxi9J0lgzt0pS62nLTzxnvHC/hu6Wtz3rq2fzdSibLViwgFNOOYV/+qd/4pZbbmHatGnMmzePCy64gJ6eHp7znOdw5513ctRRR9HV1cU3v/lNAP75n/+Z008/nQsuuICBgQFOOukkDj/8cC666CJOO+00Lr/8ciZNmsQXvvAFXvayl/Hyl7+cQw89lNe+9rUsWbKE5cuX87KXvQyo3GTh61//OkceeSRvfvObOeKII9hvv/045phjxuy5kSR1BnOruVWSRqMtC8963wtWhmeeeWbI+cuXL9/y+H/+z/8JsOVI6vnnn8/555+/TftZs2bxve9971nr2Wuvvbj22mufNf/KK6/cZvrss8/m7LPPfla78847j/POO6/OKCRJGpq51dwqSaPhqbaSJEmSpFK15SeerWDztSJqLwsXzqO/f+Snoq1d8xDdc9fUXn7/AN2LBrZML/7wJnou3LBlekYm4M0uJHUmc6taVb33DzNm1Om/4C76Vw9sM2/x6RvoOfOOSv+uHL5/z5P0PzJ8m+H4/kONsPCUxlB//0r6+qaPuH/33DX0Lan9b9m9aIC+rq079t5JbDPtTl+SpNYz2vcP/asHnvX+offpGPY9xTb9H8lB7ye2l+8/VJ+n2kqSJEmSSmXhKUmSJEkqlYWnJEmSJKlUbXmN58J58+hfOYbfNbbfflx/8/C3kZ80aRJz585l48aNzJkzhyuuuIJdd92Viy66iMsuu4zM5NRTT+W9730vAB/72Me47LLLmD69cj7/hRdeyPHHHz9mMUuSNJbMrZKk0WjLwrN/5Ur6po/8Au3BuhtItLvssgvLli0D4K1vfSuXXnopf/zHf8xll13GLbfcwk477cSCBQt43etexwte8AIA3ve+99HT0zNmcUqSVBZzqyRpNDzVtgTHHHMMK1asYPny5bz0pS9l1113ZfLkyRx77LF8+9vfnujwJElqOeZWSWptFp5jbOPGjXz3u99l7ty5HHroodx88808+uij/P73v+eGG27ggQce2NL24osv5rDDDuOUU07hsccem8CoJUlqXuZWSWp9Fp5jZMOGDRxxxBF0d3ez7777snjxYubMmcNf//Vf8+pXv5oFCxZw+OGHM3ly5ezm008/nXvvvZdly5YxY8YMzjnnnAkegSRJzcXcKkntoy2v8ZwI1dehVFu8eDGLFy8G4MMf/jAzZ84EYK+99trS5tRTT+X1r3/9+AQqSVKLMLdKUvvwE8+SPfzwwwDcf//9/Mu//Asnn3wyAP39/VvafPvb3+bQQw+dkPgkSWo15lZJaj1t+YnnjP32a+hueduzvpH6sz/7Mx599FF23HFHLrnkEp773Oey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\n", 588 | "text/plain": [ 589 | "
" 590 | ] 591 | }, 592 | "metadata": {}, 593 | "output_type": "display_data" 594 | } 595 | ], 596 | "source": [ 597 | "stdev1 = np.std(por1); stdev2 = np.std(por2)\n", 598 | "por1r05 = GSLIB.affine(por1,tmean=ci_95_por1[0],tstdev=stdev1)\n", 599 | "por1r95 = GSLIB.affine(por1,tmean=ci_95_por1[1],tstdev=stdev1)\n", 600 | "por2r05 = GSLIB.affine(por2,tmean=ci_95_por2[0],tstdev=stdev2)\n", 601 | "por2r95 = GSLIB.affine(por2,tmean=ci_95_por2[1],tstdev=stdev2)\n", 602 | "\n", 603 | "plt.subplot(121)\n", 604 | "plt.hist(por1r05, facecolor='yellow',bins=np.linspace(0.0,0.4,50),histtype=\"stepfilled\",alpha=0.8,density=True,cumulative=True,edgecolor='black',label='P05')\n", 605 | "plt.hist(por1, facecolor='orange',bins=np.linspace(0.0,0.4,50),histtype=\"stepfilled\",alpha=0.8,density=True,cumulative=True,edgecolor='black',label='Expected')\n", 606 | "plt.hist(por1r95, facecolor='red',bins=np.linspace(0.0,0.4,50),histtype=\"stepfilled\",alpha=0.8,density=True,cumulative=True,edgecolor='black',label='P95')\n", 607 | "\n", 608 | "plt.xlim([0.0,0.4]); plt.ylim([0,1.0])\n", 609 | "plt.xlabel('Porosity (fraction)'); plt.ylabel('Cumulative Probability'); plt.title('Porosity Distribution Uncertainty - Well 1')\n", 610 | "plt.legend(loc='upper left')\n", 611 | "plt.grid(True)\n", 612 | "\n", 613 | "plt.subplot(122)\n", 614 | "plt.hist(por2r05, facecolor='yellow',bins=np.linspace(0.0,0.4,50),histtype=\"stepfilled\",alpha=0.8,density=True,cumulative=True,edgecolor='black',label='P05')\n", 615 | "plt.hist(por2, facecolor='orange',bins=np.linspace(0.0,0.4,50),histtype=\"stepfilled\",alpha=0.8,density=True,cumulative=True,edgecolor='black',label='Expected')\n", 616 | "plt.hist(por2r95, facecolor='red',bins=np.linspace(0.0,0.4,50),histtype=\"stepfilled\",alpha=0.8,density=True,cumulative=True,edgecolor='black',label='P95')\n", 617 | "\n", 618 | "plt.xlim([0.0,0.4]); plt.ylim([0,1.0])\n", 619 | "plt.xlabel('Porosity (fraction)'); plt.ylabel('Cumulative Probability'); plt.title('Porosity Distribution Uncertainty - Well 2')\n", 620 | "plt.legend(loc='upper left')\n", 621 | "plt.grid(True)\n", 622 | "\n", 623 | "plt.subplots_adjust(left=0.0, bottom=0.0, right=2.0, top=1.2, wspace=0.2, hspace=0.3)\n", 624 | "plt.show()" 625 | ] 626 | }, 627 | { 628 | "cell_type": "markdown", 629 | "metadata": {}, 630 | "source": [ 631 | "We just calculated a scenario-based uncertainty model for the porosity distribution around wells 1 and 2. Of course, we could actually sample porosity means continuously from our confidence calculation as we have access to the complete distribution for uncertainty in the mean porosity of both wells.\n", 632 | "\n", 633 | "Communicating uncertainty is powerful, but always remember to state the assumptions. For example, here we assumed:\n", 634 | "\n", 635 | "1. The population distribution of porosity for each well is Gaussian distributed\n", 636 | "2. That the samples are independent." 637 | ] 638 | }, 639 | { 640 | "cell_type": "markdown", 641 | "metadata": {}, 642 | "source": [ 643 | "One can check the Excel file linked above with the confidence interval calculated by hand and confirm that this result is correct.\n", 644 | "\n", 645 | "#### Are the Porosity Values from Unit 1 and Unit 2 Significantly Different?\n", 646 | "\n", 647 | "Let's first visually compare the porosity from the two units. We will use a box and wisker plot. \n", 648 | "\n", 649 | "* Box and Whiskers plot is a built in function with DataFrames\n", 650 | "\n", 651 | "```python\n", 652 | "df.boxplot()\n", 653 | "```\n", 654 | "\n", 655 | "* We return the 'plt' label so that we can further improve the plot\n", 656 | "\n" 657 | ] 658 | }, 659 | { 660 | "cell_type": "code", 661 | "execution_count": 94, 662 | "metadata": {}, 663 | "outputs": [ 664 | { 665 | "data": { 666 | "text/plain": [ 667 | "Text(0,0.5,'Porosity (fraction)')" 668 | ] 669 | }, 670 | "execution_count": 94, 671 | "metadata": {}, 672 | "output_type": "execute_result" 673 | }, 674 | { 675 | "data": { 676 | "image/png": "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\n", 677 | "text/plain": [ 678 | "
" 679 | ] 680 | }, 681 | "metadata": {}, 682 | "output_type": "display_data" 683 | } 684 | ], 685 | "source": [ 686 | "plt = df.boxplot(grid = False,fontsize = 10)\n", 687 | "plt.set_title('Unit 1 and 2 Porosity'); plt.set_xlabel(\"Unit\"); plt.set_ylabel('Porosity (fraction)')" 688 | ] 689 | }, 690 | { 691 | "cell_type": "markdown", 692 | "metadata": {}, 693 | "source": [ 694 | "So the porosity distributions seem quite different between the two units. \n", 695 | "\n", 696 | "* Let's check and see if they are **statistically significantly different**\n", 697 | "\n", 698 | "#### Hypothesis Testing of Difference Between Unit 1 and 2\n", 699 | "\n", 700 | "The confidence intervals help with uncertainty in the distributions of porosity. Now let's try to figure out if:\n", 701 | "\n", 702 | "1. could we combine units 1 and 2? \n", 703 | "2. did something change between the 2 units? \n", 704 | "\n", 705 | "Now, let's try the t test, hypothesis test for difference in means. This test assumes that the variances are similar along with the data being Gaussian distributed (see the course notes for more on this). This is our test:\n", 706 | "\n", 707 | "\\begin{equation}\n", 708 | "H_0: \\mu_{X1} = \\mu_{X2}\n", 709 | "\\end{equation}\n", 710 | "\n", 711 | "\\begin{equation}\n", 712 | "H_1: \\mu_{X1} \\ne \\mu_{X2}\n", 713 | "\\end{equation}\n", 714 | "\n", 715 | "For the resulting t-statistic and p-value we run this command." 716 | ] 717 | }, 718 | { 719 | "cell_type": "code", 720 | "execution_count": 15, 721 | "metadata": {}, 722 | "outputs": [ 723 | { 724 | "name": "stdout", 725 | "output_type": "stream", 726 | "text": [ 727 | "The t statistic is -2.981 and the p-value is 0.005\n" 728 | ] 729 | } 730 | ], 731 | "source": [ 732 | "t_pooled, p_pooled = st.ttest_ind(por1,por2)\n", 733 | "print('The t statistic is ' + str(round(t_pooled,3)) + ' and the p-value is ' + str(round(p_pooled,3)))" 734 | ] 735 | }, 736 | { 737 | "cell_type": "markdown", 738 | "metadata": {}, 739 | "source": [ 740 | "The p-value, $p$, is the symmetric interval probaiblity our outside. In other words the $p$ reported is 2 x cumulative probaiblity of the t statistic applied to the sampling t distribution. Another way to look at it, if one used the $\\pm t_{t_{statistic},.d.f}$ statistic as thresholds, $p$ is the probability being outside this symmetric interval. So we will reject the null hypothesis if $p \\lt \\alpha$. From the p-value alone it is clear that we would reject the null hypothesis and accept the alternative hypothesis that the means are not equal. \n", 741 | "\n", 742 | "In case you want to compare the t-statistic to t-critical, we can apply the inverse of the student's t distribution at $\\frac{\\alpha}{2}$ and $1-\\frac{\\alpha}{2}$ to get the upper and lower critcal values. " 743 | ] 744 | }, 745 | { 746 | "cell_type": "code", 747 | "execution_count": 16, 748 | "metadata": {}, 749 | "outputs": [ 750 | { 751 | "name": "stdout", 752 | "output_type": "stream", 753 | "text": [ 754 | "The t crical lower and upper values are [-2.02 2.02]\n" 755 | ] 756 | } 757 | ], 758 | "source": [ 759 | "t_critical = st.t.ppf([0.025,0.975], df=len(por1)+len(por2)-2)\n", 760 | "print('The t crical lower and upper values are ' + str(np.round(t_critical,2)))" 761 | ] 762 | }, 763 | { 764 | "cell_type": "markdown", 765 | "metadata": {}, 766 | "source": [ 767 | "We can observe that, as expected, the t-statistic is outside the t-critcal interval. These results are exactly what we got when we worked out the problem by hand in Excel, but so much more efficient! \n", 768 | "\n", 769 | "**The porosity from the 2 units are significantly different.**\n", 770 | "\n", 771 | "Now let's try the t-test, hypothesis test for difference in means allowing for unequal variances, this is also known as the Welch's t test. All we have to do is set the parameter 'equal_var' to false, note it defaults to true (e.g. the command above). " 772 | ] 773 | }, 774 | { 775 | "cell_type": "code", 776 | "execution_count": 17, 777 | "metadata": {}, 778 | "outputs": [ 779 | { 780 | "data": { 781 | "text/plain": [ 782 | "Ttest_indResult(statistic=-2.9808897468855644, pvalue=0.005502572350112333)" 783 | ] 784 | }, 785 | "execution_count": 17, 786 | "metadata": {}, 787 | "output_type": "execute_result" 788 | } 789 | ], 790 | "source": [ 791 | "st.ttest_ind(por1, por2, equal_var = False)" 792 | ] 793 | }, 794 | { 795 | "cell_type": "markdown", 796 | "metadata": {}, 797 | "source": [ 798 | "Once again we can see by $p$ that we will clearly reject the null hypothesis. \n", 799 | "\n", 800 | "Let's now compare the variances with the F-test for difference in variances. \n", 801 | "\n", 802 | "\\begin{equation}\n", 803 | "H_0: \\sigma^{2}_{X2} \\le \\sigma^{2}_{X1}\n", 804 | "\\end{equation}\n", 805 | "\n", 806 | "\\begin{equation}\n", 807 | "H_1: \\sigma^{2}_{X2} \\gt \\sigma^{2}_{X1}\n", 808 | "\\end{equation}\n", 809 | "\n", 810 | "Note, by ordering the variances we eliminate the case of $\\sigma^{2}_{X2} \\lt \\sigma^{2}_{X1}$.\n", 811 | "\n", 812 | "Details about the test are available in the course notes (along with assumptions such as Gaussian distributed) and this example is also worked out by hand in the linked Excel workbook. We can accomplish the F-test in with SciPy.Stats the function with one line of code if we calculate the ratio of the sample variances ensuring that the larger variance is in the numerator and get the degrees of freedom using the len() command, ensuring that we are consistent with the numerator degrees of freedom set as 'dfn' and the denominator degrees of freedom set as 'dfd'. We take a p-value of $1-p$ since the test is configured to be a single, right tailed test. " 813 | ] 814 | }, 815 | { 816 | "cell_type": "code", 817 | "execution_count": 18, 818 | "metadata": {}, 819 | "outputs": [ 820 | { 821 | "name": "stdout", 822 | "output_type": "stream", 823 | "text": [ 824 | "The p-value for the F-test for difference is variances is 0.019\n" 825 | ] 826 | } 827 | ], 828 | "source": [ 829 | "p_value = 1 - st.f.cdf(np.var(por2)/np.var(por1), dfn=len(por2)-1, dfd=len(por1)-1)\n", 830 | "print('The p-value for the F-test for difference is variances is ' + str(round(p_value,3)))" 831 | ] 832 | }, 833 | { 834 | "cell_type": "markdown", 835 | "metadata": {}, 836 | "source": [ 837 | "Once again we would clearly reject the null hypothesis since $p \\lt alpha$ and assume that the variances are not equal. Note this is a single tail test so our p-critical is 0.05 for a 95% confidence level.\n", 838 | "\n", 839 | "#### Reporting the Hypothesis Test Results\n", 840 | "\n", 841 | "The difference in the means and variances were tested for statistical significance for porosity measured in wells 1 and 2. In all cases we rejected the null hypotheses and adopted the alternative hypotheses that the porosity distributions' means and variances for well 1 and 2 are statistically significant in their differences. It is possible that Well 1 and 2 have drilled into different rock units or have encountered nonstationary behavoirs in a single rock unit. This result may be applied to update subsurface maps and consider a difference in the subsurface at each well for development decision making." 842 | ] 843 | }, 844 | { 845 | "cell_type": "markdown", 846 | "metadata": {}, 847 | "source": [ 848 | "#### Comments\n", 849 | "\n", 850 | "We are just scratching the surface for the application of confidence intervals and hypothesis tests to subsurface modeling. Once again, there are a lot of details left out of the problem formulation and assumptions, see the course notes for more coverage. By running the same confidence interval and hypothesis tests: 1) by hand in Excel and with 2) R and now here in 3) Python code, I hope this demonstration will enable and encourage more engineers and scientists to make these R and Python tools part of their common practice. I'm always happy to discuss,\n", 851 | "\n", 852 | "I hope this was helpful,\n", 853 | "\n", 854 | "*Michael*\n", 855 | "\n", 856 | "#### The Author:\n", 857 | "\n", 858 | "### Michael Pyrcz, Associate Professor, University of Texas at Austin \n", 859 | "*Novel Data Analytics, Geostatistics and Machine Learning Subsurface Solutions*\n", 860 | "\n", 861 | "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", 862 | "\n", 863 | "For more about Michael check out these links:\n", 864 | "\n", 865 | "#### [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", 866 | "\n", 867 | "#### Want to Work Together?\n", 868 | "\n", 869 | "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", 870 | "\n", 871 | "* 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", 872 | "\n", 873 | "* 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", 874 | "\n", 875 | "* I can be reached at mpyrcz@austin.utexas.edu.\n", 876 | "\n", 877 | "I'm always happy to discuss,\n", 878 | "\n", 879 | "*Michael*\n", 880 | "\n", 881 | "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" 882 | ] 883 | } 884 | ], 885 | "metadata": { 886 | "kernelspec": { 887 | "display_name": "Python 3", 888 | "language": "python", 889 | "name": "python3" 890 | }, 891 | "language_info": { 892 | "codemirror_mode": { 893 | "name": "ipython", 894 | "version": 3 895 | }, 896 | "file_extension": ".py", 897 | "mimetype": "text/x-python", 898 | "name": "python", 899 | "nbconvert_exporter": "python", 900 | "pygments_lexer": "ipython3", 901 | "version": "3.6.5" 902 | } 903 | }, 904 | "nbformat": 4, 905 | "nbformat_minor": 2 906 | } 907 | -------------------------------------------------------------------------------- /Demos/SubsurfaceDataAnalytics_DataFrame.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "collapsed": true 7 | }, 8 | "source": [ 9 | "\n", 10 | "

\n", 11 | " \n", 12 | "\n", 13 | "

\n", 14 | "\n", 15 | "## Subsurface Data Analytics \n", 16 | "\n", 17 | "### Tabular Data Structures / DataFrames in Python \n", 18 | "\n", 19 | "#### Michael Pyrcz, Associate Professor, University of Texas at Austin \n", 20 | "\n", 21 | "##### [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", 22 | "\n", 23 | "### Exercise: Tabular Data Structures / DataFrames in Python \n", 24 | "\n", 25 | "This is a tutorial for / demonstration of **Tabular Data Structures in Python**. In Python, the common tool for dealing with Tabular Data Structures is the DataFrame from the pandas Python package. \n", 26 | "\n", 27 | "* Tabular Data in subsurface data analytics includes any data set with a limited number of samples as oposed to gridded maps that provide exhaustively sampled data.\n", 28 | "\n", 29 | "This tutorial includes the methods and operations that would commonly be required for and Geoscientists, Engineers and Data Scientists working with Tabular Data Structures for the purpose of:\n", 30 | "\n", 31 | "1. Data Checking and Cleaning\n", 32 | "2. Data Mining / Inferential Data Analysis\n", 33 | "3. Data Analytics / Building Predictive Models with Geostatistics and Machine Learning\n", 34 | "\n", 35 | "Learning to work with Pandas DataFrames is essential for dealing with tabular data (e.g. well data) in subsurface modeling workflows and for subsurface machine learning.\n", 36 | "\n", 37 | "##### Tabular Data Structures\n", 38 | "\n", 39 | "In Python we will commonly store our data in two formats, tables and arrays. For sampled data with typically multiple features $1,\\ldots,m$ over $1,\\ldots,n$ samples we will work with tables. For exhaustive maps and models usually representing a single feature on a regular grid over $1,\\ldots,n_{i}$ for $i = 1,\\ldots,n_{dim}$ we will work with arrays.\n", 40 | "\n", 41 | "pandas package provides a convenient DataFrame object for working with data in a table and numpy package provides a convenient ndarray object for working with gridded data. In the following tutorial we will focus on DataFrames although we will utilize ndarrays a couple of times. There is another section on Gridded Data Structures that focuses on ndarrays.\n", 42 | "\n", 43 | "#### Additional Resources\n", 44 | "\n", 45 | "These workflows are based on standard methods with their associated limitations and assumptions. For more information see:\n", 46 | "\n", 47 | "* [pandas DataFrames Lecture](https://www.youtube.com/watch?v=cggieFcKdiM&list=PLG19vXLQHvSDUmEOmBoaxGbFAbvaLdfx4&index=8&t=0s)\n", 48 | "\n", 49 | "I have provided various workflows for subsurface data analytics, geostatistics and machine learning:\n", 50 | "\n", 51 | "* [Python](https://git.io/fh4eX)\n", 52 | "\n", 53 | "* [Excel](https://github.com/GeostatsGuy/LectureExercises/blob/master/Lecture7_CI_Hypoth_eg_R.xlsx) \n", 54 | "* [R](https://github.com/GeostatsGuy/LectureExercises/blob/master/Lecture7_CI_Hypoth_eg.R) \n", 55 | "\n", 56 | "and all of my University of Texas at Austin \n", 57 | "\n", 58 | "* [Lectures](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig/featured?view_as=subscriber)\n", 59 | "\n", 60 | "#### Workflow Goals\n", 61 | "\n", 62 | "Learn the basics for working with Tabular Data Structures in Python. This includes:\n", 63 | "\n", 64 | "* Loading tabular data\n", 65 | "* Visualizing tabular data\n", 66 | "* Data QC and Cleaning\n", 67 | "* Interacting with the tabular data\n", 68 | "\n", 69 | "#### Objective \n", 70 | "\n", 71 | "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", 72 | "\n", 73 | "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", 74 | "\n", 75 | "#### Getting Started\n", 76 | "\n", 77 | "Here's the steps to get setup in Python with the GeostatsPy package:\n", 78 | "\n", 79 | "1. Install Anaconda 3 on your machine (https://www.anaconda.com/download/). \n", 80 | "2. From Anaconda Navigator (within Anaconda3 group), go to the environment tab, click on base (root) green arrow and open a terminal. \n", 81 | "3. In the terminal type: pip install geostatspy. \n", 82 | "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", 83 | "\n", 84 | "You will need to copy the data file to your working directory. They are available here:\n", 85 | "\n", 86 | "* Tabular data - 2D_MV_200wells.csv at [here].(https://github.com/GeostatsGuy/GeoDataSets/blob/master/2D_MV_200wells.csv)\n", 87 | "\n", 88 | "I have put together various subsurface workflows for data analytics, geostatistics and machine learning. Go [here](https://git.io/fh4eX) for other example workflows and source code. \n", 89 | "\n", 90 | "#### Load the required libraries\n", 91 | "\n", 92 | "The following code loads the required libraries.\n" 93 | ] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "execution_count": 1, 98 | "metadata": {}, 99 | "outputs": [], 100 | "source": [ 101 | "import os # to set current working directory \n", 102 | "import numpy as np # arrays and matrix math\n", 103 | "import pandas as pd # DataFrames" 104 | ] 105 | }, 106 | { 107 | "cell_type": "markdown", 108 | "metadata": {}, 109 | "source": [ 110 | "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. \n", 111 | "\n" 112 | ] 113 | }, 114 | { 115 | "cell_type": "markdown", 116 | "metadata": {}, 117 | "source": [ 118 | "#### Set the working directory\n", 119 | "\n", 120 | "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 below) data file in this directory. When we are finished with this tutorial we will write our new dataset back to this directory. " 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 2, 126 | "metadata": {}, 127 | "outputs": [], 128 | "source": [ 129 | "os.chdir(\"c:/PGE383\") # set the working directory" 130 | ] 131 | }, 132 | { 133 | "cell_type": "markdown", 134 | "metadata": {}, 135 | "source": [ 136 | "#### Loading Data \n", 137 | "\n", 138 | "Let's load the provided multivariate, spatial dataset. '2D_MV_200wells.csv' is available [here](https://github.com/GeostatsGuy/GeoDataSets/blob/master/2D_MV_200wells.csv). It is a comma delimited file with: \n", 139 | "\n", 140 | "* X and Y coordinates ($m$)\n", 141 | "* facies 1 and 2 (1 is sandstone and 2 interbedded sand and mudstone)\n", 142 | "* porosity (fraction)\n", 143 | "* permeability ($mD$)\n", 144 | "* acoustic impedance ($\\frac{kg}{m^3} \\cdot \\frac{m}{s} \\cdot 10^6$). \n", 145 | "\n", 146 | "We load it with the pandas 'read_csv' function into a data frame we called 'df' and then preview it by printing a slice and by utilizing the 'head' DataFrame member function (with a nice and clean format, see below).\n", 147 | "\n", 148 | "**Python Tip: using functions from a package** just type the label for the package that we declared at the beginning:\n", 149 | "\n", 150 | "```python\n", 151 | "import pandas as pd\n", 152 | "```\n", 153 | "\n", 154 | "so we can access the pandas function 'read_csv' with the command: \n", 155 | "\n", 156 | "```python\n", 157 | "pd.read_csv()\n", 158 | "```\n", 159 | "\n", 160 | "but read csv has required input parameters. The essential one is the name of the file. For our circumstance all the other default parameters are fine. If you want to see all the possible parameters for this function, just go to the docs [here](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html). \n", 161 | "\n", 162 | "* The docs are always helpful\n", 163 | "* There is often a lot of flexibility for Python functions, possible through using various inputs parameters\n", 164 | "\n", 165 | "also, the program has an output, a pandas DataFrame loaded from the data. So we have to specficy the name / variable representing that new object.\n", 166 | "\n", 167 | "```python\n", 168 | "df = pd.read_csv(\"2D_MV_200wells.csv\") \n", 169 | "```\n", 170 | "\n", 171 | "Let's run this command to load the data and then look at the resulting DataFrame to ensure that we loaded it." 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": 3, 177 | "metadata": {}, 178 | "outputs": [ 179 | { 180 | "data": { 181 | "text/html": [ 182 | "
\n", 183 | "\n", 196 | "\n", 197 | " \n", 198 | " \n", 199 | " \n", 200 | " \n", 201 | " \n", 202 | " \n", 203 | " \n", 204 | " \n", 205 | " \n", 206 | " \n", 207 | " \n", 208 | " \n", 209 | " \n", 210 | " \n", 211 | " \n", 212 | " \n", 213 | " \n", 214 | " \n", 215 | " \n", 216 | " \n", 217 | " \n", 218 | " \n", 219 | " \n", 220 | " \n", 221 | " \n", 222 | " \n", 223 | " \n", 224 | " \n", 225 | " \n", 226 | " \n", 227 | " \n", 228 | " \n", 229 | " \n", 230 | " \n", 231 | " \n", 232 | " \n", 233 | " \n", 234 | " \n", 235 | " \n", 236 | " \n", 237 | " \n", 238 | " \n", 239 | " \n", 240 | " \n", 241 | " \n", 242 | " \n", 243 | " \n", 244 | " \n", 245 | " \n", 246 | " \n", 247 | " \n", 248 | " \n", 249 | " \n", 250 | " \n", 251 | " \n", 252 | " \n", 253 | " \n", 254 | " \n", 255 | "
XYfacies_threshold_0.3porositypermeabilityacoustic_impedance
0565148510.11846.1702.009
12585118510.15666.2752.864
22065286520.192092.2973.524
33575265510.16219.0482.157
418353510.17667.1233.979
\n", 256 | "
" 257 | ], 258 | "text/plain": [ 259 | " X Y facies_threshold_0.3 porosity permeability \\\n", 260 | "0 565 1485 1 0.1184 6.170 \n", 261 | "1 2585 1185 1 0.1566 6.275 \n", 262 | "2 2065 2865 2 0.1920 92.297 \n", 263 | "3 3575 2655 1 0.1621 9.048 \n", 264 | "4 1835 35 1 0.1766 7.123 \n", 265 | "\n", 266 | " acoustic_impedance \n", 267 | "0 2.009 \n", 268 | "1 2.864 \n", 269 | "2 3.524 \n", 270 | "3 2.157 \n", 271 | "4 3.979 " 272 | ] 273 | }, 274 | "execution_count": 3, 275 | "metadata": {}, 276 | "output_type": "execute_result" 277 | } 278 | ], 279 | "source": [ 280 | "df = pd.read_csv(\"2D_MV_200wells.csv\") # read a .csv file in as a DataFrame\n", 281 | "#print(df.iloc[0:5,:]) # display first 4 samples in the table as a preview\n", 282 | "df.head() # we could also use this command for a table preview " 283 | ] 284 | }, 285 | { 286 | "cell_type": "markdown", 287 | "metadata": {}, 288 | "source": [ 289 | "#### Summary Statistics\n", 290 | "\n", 291 | "It is useful to review the summary statistics of our loaded DataFrame. That can be accomplished with the 'describe' DataFrame member function. We transpose to switch the axes for ease of visualization." 292 | ] 293 | }, 294 | { 295 | "cell_type": "code", 296 | "execution_count": 4, 297 | "metadata": {}, 298 | "outputs": [ 299 | { 300 | "data": { 301 | "text/html": [ 302 | "
\n", 303 | "\n", 316 | "\n", 317 | " \n", 318 | " \n", 319 | " \n", 320 | " \n", 321 | " \n", 322 | " \n", 323 | " \n", 324 | " \n", 325 | " \n", 326 | " \n", 327 | " \n", 328 | " \n", 329 | " \n", 330 | " \n", 331 | " \n", 332 | " \n", 333 | " \n", 334 | " \n", 335 | " \n", 336 | " \n", 337 | " \n", 338 | " \n", 339 | " \n", 340 | " \n", 341 | " \n", 342 | " \n", 343 | " \n", 344 | " \n", 345 | " \n", 346 | " \n", 347 | " \n", 348 | " \n", 349 | " \n", 350 | " \n", 351 | " \n", 352 | " \n", 353 | " \n", 354 | " \n", 355 | " \n", 356 | " \n", 357 | " \n", 358 | " \n", 359 | " \n", 360 | " \n", 361 | " \n", 362 | " \n", 363 | " \n", 364 | " \n", 365 | " \n", 366 | " \n", 367 | " \n", 368 | " \n", 369 | " \n", 370 | " \n", 371 | " \n", 372 | " \n", 373 | " \n", 374 | " \n", 375 | " \n", 376 | " \n", 377 | " \n", 378 | " \n", 379 | " \n", 380 | " \n", 381 | " \n", 382 | " \n", 383 | " \n", 384 | " \n", 385 | " \n", 386 | " \n", 387 | " \n", 388 | " \n", 389 | " \n", 390 | " \n", 391 | " \n", 392 | " \n", 393 | " \n", 394 | " \n", 395 | " \n", 396 | " \n", 397 | " \n", 398 | " \n", 399 | " \n", 400 | " \n", 401 | " \n", 402 | "
XYfacies_threshold_0.3porositypermeabilityacoustic_impedance
count200.000000200.00000200.000000200.000000200.000000200.000000
mean2053.4000001876.150001.3300000.14930025.2874623.000435
std1113.5246411137.580160.4713930.03294864.4701350.592201
min25.00000035.000001.0000000.0500000.0158202.009000
25%1112.500000920.000001.0000000.1321751.3667502.483250
50%2160.0000001855.000001.0000000.1501504.8255002.964500
75%2915.0000002782.500002.0000000.17420014.5970003.527000
max3955.0000003995.000002.0000000.223200463.6410003.984000
\n", 403 | "
" 404 | ], 405 | "text/plain": [ 406 | " X Y facies_threshold_0.3 porosity \\\n", 407 | "count 200.000000 200.00000 200.000000 200.000000 \n", 408 | "mean 2053.400000 1876.15000 1.330000 0.149300 \n", 409 | "std 1113.524641 1137.58016 0.471393 0.032948 \n", 410 | "min 25.000000 35.00000 1.000000 0.050000 \n", 411 | "25% 1112.500000 920.00000 1.000000 0.132175 \n", 412 | "50% 2160.000000 1855.00000 1.000000 0.150150 \n", 413 | "75% 2915.000000 2782.50000 2.000000 0.174200 \n", 414 | "max 3955.000000 3995.00000 2.000000 0.223200 \n", 415 | "\n", 416 | " permeability acoustic_impedance \n", 417 | "count 200.000000 200.000000 \n", 418 | "mean 25.287462 3.000435 \n", 419 | "std 64.470135 0.592201 \n", 420 | "min 0.015820 2.009000 \n", 421 | "25% 1.366750 2.483250 \n", 422 | "50% 4.825500 2.964500 \n", 423 | "75% 14.597000 3.527000 \n", 424 | "max 463.641000 3.984000 " 425 | ] 426 | }, 427 | "execution_count": 4, 428 | "metadata": {}, 429 | "output_type": "execute_result" 430 | } 431 | ], 432 | "source": [ 433 | "df.describe()" 434 | ] 435 | }, 436 | { 437 | "cell_type": "markdown", 438 | "metadata": {}, 439 | "source": [ 440 | "#### Rename a Variable / Features\n", 441 | "\n", 442 | "Let's rename the facies, permeability and acoustic impedance for convenience." 443 | ] 444 | }, 445 | { 446 | "cell_type": "code", 447 | "execution_count": 5, 448 | "metadata": {}, 449 | "outputs": [ 450 | { 451 | "data": { 452 | "text/html": [ 453 | "
\n", 454 | "\n", 467 | "\n", 468 | " \n", 469 | " \n", 470 | " \n", 471 | " \n", 472 | " \n", 473 | " \n", 474 | " \n", 475 | " \n", 476 | " \n", 477 | " \n", 478 | " \n", 479 | " \n", 480 | " \n", 481 | " \n", 482 | " \n", 483 | " \n", 484 | " \n", 485 | " \n", 486 | " \n", 487 | " \n", 488 | " \n", 489 | " \n", 490 | " \n", 491 | " \n", 492 | " \n", 493 | " \n", 494 | " \n", 495 | " \n", 496 | " \n", 497 | " \n", 498 | " \n", 499 | " \n", 500 | " \n", 501 | " \n", 502 | " \n", 503 | " \n", 504 | " \n", 505 | " \n", 506 | " \n", 507 | " \n", 508 | " \n", 509 | " \n", 510 | " \n", 511 | " \n", 512 | " \n", 513 | " \n", 514 | " \n", 515 | " \n", 516 | " \n", 517 | " \n", 518 | " \n", 519 | " \n", 520 | " \n", 521 | " \n", 522 | " \n", 523 | " \n", 524 | " \n", 525 | " \n", 526 | "
XYfaciesporositypermai
0565148510.11846.1702.009
12585118510.15666.2752.864
22065286520.192092.2973.524
33575265510.16219.0482.157
418353510.17667.1233.979
\n", 527 | "
" 528 | ], 529 | "text/plain": [ 530 | " X Y facies porosity perm ai\n", 531 | "0 565 1485 1 0.1184 6.170 2.009\n", 532 | "1 2585 1185 1 0.1566 6.275 2.864\n", 533 | "2 2065 2865 2 0.1920 92.297 3.524\n", 534 | "3 3575 2655 1 0.1621 9.048 2.157\n", 535 | "4 1835 35 1 0.1766 7.123 3.979" 536 | ] 537 | }, 538 | "execution_count": 5, 539 | "metadata": {}, 540 | "output_type": "execute_result" 541 | } 542 | ], 543 | "source": [ 544 | "df = df.rename(columns={'facies_threshold_0.3': 'facies','permeability':'perm','acoustic_impedance':'ai'}) # rename columns of the \n", 545 | "df.head()" 546 | ] 547 | }, 548 | { 549 | "cell_type": "markdown", 550 | "metadata": {}, 551 | "source": [ 552 | "#### Slicing and Subsets\n", 553 | "\n", 554 | "It is straightforward to extract subsets from a DataFrame to make a new DataFrame. \n", 555 | "\n", 556 | "* This is useful for cleaning up data by removing features that are no longer of interest. \n", 557 | "\n", 558 | "If the samples are in random order then the first $n_{s}$ samples are a random sample of size $n_{s}$. Below we make a new DataFrame, 'df_subset', with the rows 0 to 4 and columns 2 to 6 and the **X and Y coordinates removed**." 559 | ] 560 | }, 561 | { 562 | "cell_type": "code", 563 | "execution_count": 44, 564 | "metadata": {}, 565 | "outputs": [ 566 | { 567 | "name": "stdout", 568 | "output_type": "stream", 569 | "text": [ 570 | " facies porosity perm ai\n", 571 | "0 1 0.1184 6.170 2.009\n", 572 | "1 1 0.1566 6.275 2.864\n", 573 | "2 2 0.1920 92.297 3.524\n", 574 | "3 1 0.1621 9.048 2.157\n", 575 | "4 1 0.1766 7.123 3.979\n" 576 | ] 577 | } 578 | ], 579 | "source": [ 580 | "df_subset = df.iloc[0:5,2:7] # make a new dataframe with just the first 4 samples and no X,Y\n", 581 | "print(df_subset)" 582 | ] 583 | }, 584 | { 585 | "cell_type": "markdown", 586 | "metadata": {}, 587 | "source": [ 588 | "Let's demonstrate some more complicated slicing options. We demonstrate two methods:\n", 589 | "\n", 590 | "* list the exact indexes that you want\n", 591 | "\n", 592 | "```python\n", 593 | "df_subset2 = df.iloc[[0,2,4,5,10,43],:] # extract rows 0,2,4,5...,43 for all columns\n", 594 | "```" 595 | ] 596 | }, 597 | { 598 | "cell_type": "code", 599 | "execution_count": 25, 600 | "metadata": { 601 | "scrolled": true 602 | }, 603 | "outputs": [ 604 | { 605 | "name": "stdout", 606 | "output_type": "stream", 607 | "text": [ 608 | " X Y facies porosity perm ai\n", 609 | "0 565 1485 1 0.1184 6.170 2.009\n", 610 | "2 2065 2865 2 0.1920 92.297 3.524\n", 611 | "4 1835 35 1 0.1766 7.123 3.979\n", 612 | "5 3375 2525 1 0.1239 1.468 2.337\n", 613 | "10 2125 1105 1 0.1369 3.693 3.627\n", 614 | "43 1785 1045 1 0.1517 2.766 3.562\n", 615 | " facies perm ai\n", 616 | "2 2 92.29700 3.524\n", 617 | "3 1 9.04800 2.157\n", 618 | "4 1 7.12300 3.979\n", 619 | "5 1 1.46800 2.337\n", 620 | "6 1 31.93300 3.491\n", 621 | "7 2 116.78100 2.187\n", 622 | "8 1 3.00300 2.048\n", 623 | "9 1 5.21300 2.251\n", 624 | "10 1 3.69300 3.627\n", 625 | "11 1 0.26270 2.860\n", 626 | "12 1 9.91400 2.742\n", 627 | "13 1 14.31100 3.045\n", 628 | "14 1 0.77310 2.323\n", 629 | "15 2 22.57800 2.711\n", 630 | "16 2 18.74300 2.583\n", 631 | "17 1 9.09200 3.801\n", 632 | "18 2 0.46420 3.771\n", 633 | "19 1 146.31900 2.341\n", 634 | "20 1 0.06073 3.872\n", 635 | "21 1 31.18100 2.316\n", 636 | "22 1 1.43200 3.081\n", 637 | "23 1 76.29800 2.175\n", 638 | "24 1 124.22200 2.451\n", 639 | "25 1 3.52300 3.173\n", 640 | "26 2 201.36300 2.877\n", 641 | "27 1 3.25400 2.484\n", 642 | "28 1 11.28300 3.697\n", 643 | "29 2 3.22700 3.801\n", 644 | "30 1 1.50400 2.221\n", 645 | "31 1 0.08758 2.533\n", 646 | ".. ... ... ...\n", 647 | "170 1 7.77100 3.807\n", 648 | "171 1 30.25800 2.100\n", 649 | "172 1 4.78400 3.410\n", 650 | "173 1 2.40300 3.121\n", 651 | "174 1 13.41600 3.530\n", 652 | "175 1 2.64800 2.954\n", 653 | "176 2 61.92900 3.170\n", 654 | "177 2 0.45250 2.162\n", 655 | "178 1 5.69200 3.913\n", 656 | "179 1 3.72200 3.501\n", 657 | "180 2 55.67800 3.881\n", 658 | "181 2 4.41500 3.065\n", 659 | "182 2 14.83700 3.394\n", 660 | "183 1 9.38200 2.434\n", 661 | "184 1 29.16500 2.267\n", 662 | "185 1 0.34410 3.086\n", 663 | "186 1 3.83000 2.112\n", 664 | "187 1 0.79830 3.135\n", 665 | "188 2 14.71400 2.192\n", 666 | "189 1 2.73000 3.692\n", 667 | "190 2 88.45900 2.416\n", 668 | "191 1 1.03800 3.574\n", 669 | "192 1 6.38600 2.072\n", 670 | "193 1 0.69850 3.933\n", 671 | "194 2 23.88300 2.625\n", 672 | "195 1 9.54200 2.945\n", 673 | "196 1 6.41200 2.298\n", 674 | "197 1 2.40400 2.087\n", 675 | "198 1 0.01582 3.907\n", 676 | "199 1 0.99000 2.992\n", 677 | "\n", 678 | "[198 rows x 3 columns]\n" 679 | ] 680 | } 681 | ], 682 | "source": [ 683 | "df_subset2 = df.iloc[[0,2,4,5,10,43],:] # new dataframe with samples 0, 2 ,...,43 and all features\n", 684 | "print(df_subset2)\n", 685 | "\n", 686 | "df_subset3 = df.iloc[2:,[2,4,5]] # new dataframe with all samples from 2 and features 2,4,5\n", 687 | "print(df_subset3)" 688 | ] 689 | }, 690 | { 691 | "cell_type": "markdown", 692 | "metadata": {}, 693 | "source": [ 694 | "#### Adding a Variable / Features\n", 695 | "\n", 696 | "It is also easy to add a column to our data frame. \n", 697 | "\n", 698 | "* Note, we assume that the array is in the same order as the DataFrame. This could be an issue if any rows were removed form either before adding etc. \n", 699 | "\n", 700 | "To demonstrate we make a 1D numpy array of zeros using the 'zeros' function and add it to our DataFrame with the feature name indicated as 'zero'." 701 | ] 702 | }, 703 | { 704 | "cell_type": "code", 705 | "execution_count": 45, 706 | "metadata": {}, 707 | "outputs": [ 708 | { 709 | "data": { 710 | "text/html": [ 711 | "
\n", 712 | "\n", 725 | "\n", 726 | " \n", 727 | " \n", 728 | " \n", 729 | " \n", 730 | " \n", 731 | " \n", 732 | " \n", 733 | " \n", 734 | " \n", 735 | " \n", 736 | " \n", 737 | " \n", 738 | " \n", 739 | " \n", 740 | " \n", 741 | " \n", 742 | " \n", 743 | " \n", 744 | " \n", 745 | " \n", 746 | " \n", 747 | " \n", 748 | " \n", 749 | " \n", 750 | " \n", 751 | " \n", 752 | " \n", 753 | " \n", 754 | " \n", 755 | " \n", 756 | " \n", 757 | " \n", 758 | " \n", 759 | " \n", 760 | " \n", 761 | " \n", 762 | " \n", 763 | " \n", 764 | " \n", 765 | " \n", 766 | " \n", 767 | " \n", 768 | " \n", 769 | " \n", 770 | " \n", 771 | " \n", 772 | " \n", 773 | " \n", 774 | " \n", 775 | " \n", 776 | " \n", 777 | " \n", 778 | " \n", 779 | " \n", 780 | " \n", 781 | " \n", 782 | " \n", 783 | " \n", 784 | " \n", 785 | " \n", 786 | " \n", 787 | " \n", 788 | " \n", 789 | " \n", 790 | "
XYfaciesporositypermaizero
0565148510.11846.1702.0090.0
12585118510.15666.2752.8640.0
22065286520.192092.2973.5240.0
33575265510.16219.0482.1570.0
418353510.17667.1233.9790.0
\n", 791 | "
" 792 | ], 793 | "text/plain": [ 794 | " X Y facies porosity perm ai zero\n", 795 | "0 565 1485 1 0.1184 6.170 2.009 0.0\n", 796 | "1 2585 1185 1 0.1566 6.275 2.864 0.0\n", 797 | "2 2065 2865 2 0.1920 92.297 3.524 0.0\n", 798 | "3 3575 2655 1 0.1621 9.048 2.157 0.0\n", 799 | "4 1835 35 1 0.1766 7.123 3.979 0.0" 800 | ] 801 | }, 802 | "execution_count": 45, 803 | "metadata": {}, 804 | "output_type": "execute_result" 805 | } 806 | ], 807 | "source": [ 808 | "zeros = np.zeros(200) # make a array of zeros\n", 809 | "df['zero'] = pd.Series(zeros) # add the array to our DataFrame\n", 810 | "df.head()" 811 | ] 812 | }, 813 | { 814 | "cell_type": "markdown", 815 | "metadata": {}, 816 | "source": [ 817 | "We can also remove unwanted columns without having to subset the DataFrame. \n", 818 | "\n", 819 | "* That's why we just added a column of zeros, I wanted to also demonstrated removing a column.\n", 820 | "\n", 821 | "We do this with the 'drop' member function of the DataFrame object. We just have the give the column name and by indicating axis=1 we specify to drop a column instead of a row." 822 | ] 823 | }, 824 | { 825 | "cell_type": "code", 826 | "execution_count": 46, 827 | "metadata": {}, 828 | "outputs": [ 829 | { 830 | "data": { 831 | "text/html": [ 832 | "
\n", 833 | "\n", 846 | "\n", 847 | " \n", 848 | " \n", 849 | " \n", 850 | " \n", 851 | " \n", 852 | " \n", 853 | " \n", 854 | " \n", 855 | " \n", 856 | " \n", 857 | " \n", 858 | " \n", 859 | " \n", 860 | " \n", 861 | " \n", 862 | " \n", 863 | " \n", 864 | " \n", 865 | " \n", 866 | " \n", 867 | " \n", 868 | " \n", 869 | " \n", 870 | " \n", 871 | " \n", 872 | " \n", 873 | " \n", 874 | " \n", 875 | " \n", 876 | " \n", 877 | " \n", 878 | " \n", 879 | " \n", 880 | " \n", 881 | " \n", 882 | " \n", 883 | " \n", 884 | " \n", 885 | " \n", 886 | " \n", 887 | " \n", 888 | " \n", 889 | " \n", 890 | " \n", 891 | " \n", 892 | " \n", 893 | " \n", 894 | " \n", 895 | " \n", 896 | " \n", 897 | " \n", 898 | " \n", 899 | " \n", 900 | " \n", 901 | " \n", 902 | " \n", 903 | " \n", 904 | " \n", 905 | "
XYfaciesporositypermai
0565148510.11846.1702.009
12585118510.15666.2752.864
22065286520.192092.2973.524
33575265510.16219.0482.157
418353510.17667.1233.979
\n", 906 | "
" 907 | ], 908 | "text/plain": [ 909 | " X Y facies porosity perm ai\n", 910 | "0 565 1485 1 0.1184 6.170 2.009\n", 911 | "1 2585 1185 1 0.1566 6.275 2.864\n", 912 | "2 2065 2865 2 0.1920 92.297 3.524\n", 913 | "3 3575 2655 1 0.1621 9.048 2.157\n", 914 | "4 1835 35 1 0.1766 7.123 3.979" 915 | ] 916 | }, 917 | "execution_count": 46, 918 | "metadata": {}, 919 | "output_type": "execute_result" 920 | } 921 | ], 922 | "source": [ 923 | "df = df.drop('zero',axis=1) # remove the zero column\n", 924 | "df.head()" 925 | ] 926 | }, 927 | { 928 | "cell_type": "markdown", 929 | "metadata": {}, 930 | "source": [ 931 | "#### Standardizing / Manipulating Variables / Features\n", 932 | "\n", 933 | "We may want to make new features by using mathematical operators applied to existing features.\n", 934 | "\n", 935 | "* We can use any combinations of features, constants, math and even other data tables\n", 936 | "\n", 937 | "For example, we can make a porosity feature that is in percentage instead of fraction (called 'porosity100') or a ratio of permeability divided by porosity (called 'permpor') may be useful for subsequent calculations such as the Lorenz Coefficient. " 938 | ] 939 | }, 940 | { 941 | "cell_type": "code", 942 | "execution_count": 47, 943 | "metadata": {}, 944 | "outputs": [ 945 | { 946 | "data": { 947 | "text/html": [ 948 | "
\n", 949 | "\n", 962 | "\n", 963 | " \n", 964 | " \n", 965 | " \n", 966 | " \n", 967 | " \n", 968 | " \n", 969 | " \n", 970 | " \n", 971 | " \n", 972 | " \n", 973 | " \n", 974 | " \n", 975 | " \n", 976 | " \n", 977 | " \n", 978 | " \n", 979 | " \n", 980 | " \n", 981 | " \n", 982 | " \n", 983 | " \n", 984 | " \n", 985 | " \n", 986 | " \n", 987 | " \n", 988 | " \n", 989 | " \n", 990 | " \n", 991 | " \n", 992 | " \n", 993 | " \n", 994 | " \n", 995 | " \n", 996 | " \n", 997 | " \n", 998 | " \n", 999 | " \n", 1000 | " \n", 1001 | " \n", 1002 | " \n", 1003 | " \n", 1004 | " \n", 1005 | " \n", 1006 | " \n", 1007 | " \n", 1008 | " \n", 1009 | " \n", 1010 | " \n", 1011 | " \n", 1012 | " \n", 1013 | " \n", 1014 | " \n", 1015 | " \n", 1016 | " \n", 1017 | " \n", 1018 | " \n", 1019 | " \n", 1020 | " \n", 1021 | " \n", 1022 | " \n", 1023 | " \n", 1024 | " \n", 1025 | " \n", 1026 | " \n", 1027 | " \n", 1028 | " \n", 1029 | " \n", 1030 | " \n", 1031 | " \n", 1032 | " \n", 1033 | "
XYfaciesporositypermaiporosity100permpor
0565148510.11846.1702.00911.8452.111486
12585118510.15666.2752.86415.6640.070243
22065286520.192092.2973.52419.20480.713542
33575265510.16219.0482.15716.2155.817397
418353510.17667.1233.97917.6640.334088
\n", 1034 | "
" 1035 | ], 1036 | "text/plain": [ 1037 | " X Y facies porosity perm ai porosity100 permpor\n", 1038 | "0 565 1485 1 0.1184 6.170 2.009 11.84 52.111486\n", 1039 | "1 2585 1185 1 0.1566 6.275 2.864 15.66 40.070243\n", 1040 | "2 2065 2865 2 0.1920 92.297 3.524 19.20 480.713542\n", 1041 | "3 3575 2655 1 0.1621 9.048 2.157 16.21 55.817397\n", 1042 | "4 1835 35 1 0.1766 7.123 3.979 17.66 40.334088" 1043 | ] 1044 | }, 1045 | "execution_count": 47, 1046 | "metadata": {}, 1047 | "output_type": "execute_result" 1048 | } 1049 | ], 1050 | "source": [ 1051 | "df['porosity100'] = df['porosity']*100 # add a new column with porosity in percentage\n", 1052 | "df['permpor'] = df['perm']/df['porosity'] # add a new feature with ratio of perm / por \n", 1053 | "df.head()" 1054 | ] 1055 | }, 1056 | { 1057 | "cell_type": "markdown", 1058 | "metadata": {}, 1059 | "source": [ 1060 | "#### Truncating and Categorizing Variables / Features\n", 1061 | "\n", 1062 | "We could also use conditional statements when assigning values to a new feature. \n", 1063 | "\n", 1064 | "* We could use any condition with any combination of features and variables from any sourc\n", 1065 | "\n", 1066 | "For example, we could have a categorical porosity measure for high and low porosity, called 'tporosity' for truncated porosity." 1067 | ] 1068 | }, 1069 | { 1070 | "cell_type": "code", 1071 | "execution_count": 48, 1072 | "metadata": {}, 1073 | "outputs": [ 1074 | { 1075 | "data": { 1076 | "text/html": [ 1077 | "
\n", 1078 | "\n", 1091 | "\n", 1092 | " \n", 1093 | " \n", 1094 | " \n", 1095 | " \n", 1096 | " \n", 1097 | " \n", 1098 | " \n", 1099 | " \n", 1100 | " \n", 1101 | " \n", 1102 | " \n", 1103 | " \n", 1104 | " \n", 1105 | " \n", 1106 | " \n", 1107 | " \n", 1108 | " \n", 1109 | " \n", 1110 | " \n", 1111 | " \n", 1112 | " \n", 1113 | " \n", 1114 | " \n", 1115 | " \n", 1116 | " \n", 1117 | " \n", 1118 | " \n", 1119 | " \n", 1120 | " \n", 1121 | " \n", 1122 | " \n", 1123 | " \n", 1124 | " \n", 1125 | " \n", 1126 | " \n", 1127 | " \n", 1128 | " \n", 1129 | " \n", 1130 | " \n", 1131 | " \n", 1132 | " \n", 1133 | " \n", 1134 | " \n", 1135 | " \n", 1136 | " \n", 1137 | " \n", 1138 | " \n", 1139 | " \n", 1140 | " \n", 1141 | " \n", 1142 | " \n", 1143 | " \n", 1144 | " \n", 1145 | " \n", 1146 | " \n", 1147 | " \n", 1148 | " \n", 1149 | " \n", 1150 | " \n", 1151 | " \n", 1152 | " \n", 1153 | " \n", 1154 | " \n", 1155 | " \n", 1156 | " \n", 1157 | " \n", 1158 | " \n", 1159 | " \n", 1160 | " \n", 1161 | " \n", 1162 | " \n", 1163 | " \n", 1164 | " \n", 1165 | " \n", 1166 | " \n", 1167 | " \n", 1168 | "
XYfaciesporositypermaiporosity100permportporosity
0565148510.11846.1702.00911.8452.111486low
12585118510.15666.2752.86415.6640.070243high
22065286520.192092.2973.52419.20480.713542high
33575265510.16219.0482.15716.2155.817397high
418353510.17667.1233.97917.6640.334088high
\n", 1169 | "
" 1170 | ], 1171 | "text/plain": [ 1172 | " X Y facies porosity perm ai porosity100 permpor \\\n", 1173 | "0 565 1485 1 0.1184 6.170 2.009 11.84 52.111486 \n", 1174 | "1 2585 1185 1 0.1566 6.275 2.864 15.66 40.070243 \n", 1175 | "2 2065 2865 2 0.1920 92.297 3.524 19.20 480.713542 \n", 1176 | "3 3575 2655 1 0.1621 9.048 2.157 16.21 55.817397 \n", 1177 | "4 1835 35 1 0.1766 7.123 3.979 17.66 40.334088 \n", 1178 | "\n", 1179 | " tporosity \n", 1180 | "0 low \n", 1181 | "1 high \n", 1182 | "2 high \n", 1183 | "3 high \n", 1184 | "4 high " 1185 | ] 1186 | }, 1187 | "execution_count": 48, 1188 | "metadata": {}, 1189 | "output_type": "execute_result" 1190 | } 1191 | ], 1192 | "source": [ 1193 | "df['tporosity'] = np.where(df['porosity']>=0.12, 'high', 'low') # conditional statement assign a new feature\n", 1194 | "df.head()" 1195 | ] 1196 | }, 1197 | { 1198 | "cell_type": "markdown", 1199 | "metadata": {}, 1200 | "source": [ 1201 | "#### Truncating to Remove Nonphysical Values\n", 1202 | "\n", 1203 | "Here's an example where we use a conditional statement to assign a very low permeability value (0.0001 mD) for all porosity values below a threshold. Of course, this is for demonstration, in practice a much lower porosity threshold would likely be applied. " 1204 | ] 1205 | }, 1206 | { 1207 | "cell_type": "code", 1208 | "execution_count": 49, 1209 | "metadata": {}, 1210 | "outputs": [ 1211 | { 1212 | "data": { 1213 | "text/html": [ 1214 | "
\n", 1215 | "\n", 1228 | "\n", 1229 | " \n", 1230 | " \n", 1231 | " \n", 1232 | " \n", 1233 | " \n", 1234 | " \n", 1235 | " \n", 1236 | " \n", 1237 | " \n", 1238 | " \n", 1239 | " \n", 1240 | " \n", 1241 | " \n", 1242 | " \n", 1243 | " \n", 1244 | " \n", 1245 | " \n", 1246 | " \n", 1247 | " \n", 1248 | " \n", 1249 | " \n", 1250 | " \n", 1251 | " \n", 1252 | " \n", 1253 | " \n", 1254 | " \n", 1255 | " \n", 1256 | " \n", 1257 | " \n", 1258 | " \n", 1259 | " \n", 1260 | " \n", 1261 | " \n", 1262 | " \n", 1263 | " \n", 1264 | " \n", 1265 | " \n", 1266 | " \n", 1267 | " \n", 1268 | " \n", 1269 | " \n", 1270 | " \n", 1271 | " \n", 1272 | " \n", 1273 | " \n", 1274 | " \n", 1275 | " \n", 1276 | " \n", 1277 | " \n", 1278 | " \n", 1279 | " \n", 1280 | " \n", 1281 | " \n", 1282 | " \n", 1283 | " \n", 1284 | " \n", 1285 | " \n", 1286 | " \n", 1287 | " \n", 1288 | " \n", 1289 | " \n", 1290 | " \n", 1291 | " \n", 1292 | " \n", 1293 | " \n", 1294 | " \n", 1295 | " \n", 1296 | " \n", 1297 | " \n", 1298 | " \n", 1299 | " \n", 1300 | " \n", 1301 | " \n", 1302 | " \n", 1303 | " \n", 1304 | " \n", 1305 | " \n", 1306 | " \n", 1307 | " \n", 1308 | " \n", 1309 | " \n", 1310 | " \n", 1311 | "
XYfaciesporositypermaiporosity100permportporosityperm_cutoff
0565148510.11846.1702.00911.8452.111486low0.0001
12585118510.15666.2752.86415.6640.070243high6.2750
22065286520.192092.2973.52419.20480.713542high92.2970
33575265510.16219.0482.15716.2155.817397high9.0480
418353510.17667.1233.97917.6640.334088high7.1230
\n", 1312 | "
" 1313 | ], 1314 | "text/plain": [ 1315 | " X Y facies porosity perm ai porosity100 permpor \\\n", 1316 | "0 565 1485 1 0.1184 6.170 2.009 11.84 52.111486 \n", 1317 | "1 2585 1185 1 0.1566 6.275 2.864 15.66 40.070243 \n", 1318 | "2 2065 2865 2 0.1920 92.297 3.524 19.20 480.713542 \n", 1319 | "3 3575 2655 1 0.1621 9.048 2.157 16.21 55.817397 \n", 1320 | "4 1835 35 1 0.1766 7.123 3.979 17.66 40.334088 \n", 1321 | "\n", 1322 | " tporosity perm_cutoff \n", 1323 | "0 low 0.0001 \n", 1324 | "1 high 6.2750 \n", 1325 | "2 high 92.2970 \n", 1326 | "3 high 9.0480 \n", 1327 | "4 high 7.1230 " 1328 | ] 1329 | }, 1330 | "execution_count": 49, 1331 | "metadata": {}, 1332 | "output_type": "execute_result" 1333 | } 1334 | ], 1335 | "source": [ 1336 | "df['perm_cutoff'] = np.where(df['porosity']>=0.12, df['perm'],0.0001) # conditional statement assign a new feature\n", 1337 | "df.head()" 1338 | ] 1339 | }, 1340 | { 1341 | "cell_type": "markdown", 1342 | "metadata": {}, 1343 | "source": [ 1344 | "#### Dealing with Missing Data\n", 1345 | "\n", 1346 | "What about missing or invalid values? Let's assign a single porosity value to NaN, 'not a number', indicating a missing or eroneous value. \n", 1347 | "\n", 1348 | "**Python Tip: manipulating DataFrames manually**\n", 1349 | "\n", 1350 | "We us this command to access anyone of the individual records in our data table.\n", 1351 | "\n", 1352 | "```p\n", 1353 | "df.at[irow,'feature name'] \n", 1354 | "```\n", 1355 | "\n", 1356 | "We can get the value or set the value to a new value:\n", 1357 | "\n", 1358 | "```p\n", 1359 | "df.at[irow,'feature name'] = new_value\n", 1360 | "```\n", 1361 | "\n", 1362 | "We will then check for the number of NaN values in our DataFrame. Then we can search for and display the sample with the NaN porosity value." 1363 | ] 1364 | }, 1365 | { 1366 | "cell_type": "code", 1367 | "execution_count": 50, 1368 | "metadata": {}, 1369 | "outputs": [ 1370 | { 1371 | "name": "stdout", 1372 | "output_type": "stream", 1373 | "text": [ 1374 | "Number of null values in our DataFrame = 1\n", 1375 | " X Y facies porosity perm ai porosity100 permpor \\\n", 1376 | "1 2585 1185 1 NaN 6.275 2.864 15.66 40.070243 \n", 1377 | "\n", 1378 | " tporosity perm_cutoff \n", 1379 | "1 high 6.275 \n" 1380 | ] 1381 | } 1382 | ], 1383 | "source": [ 1384 | "df.at[1,'porosity'] = np.NaN # let's give ourselves a NaN / missing value in our table\n", 1385 | "\n", 1386 | "# Count the number of samples with missing values\n", 1387 | "print('Number of null values in our DataFrame = ', str(df.isnull().sum().sum())) \n", 1388 | "# Find the row(s) with missing values and look at them\n", 1389 | "nan_rows = df[df['porosity'].isnull()] # find the row with missing values\n", 1390 | "print(nan_rows)" 1391 | ] 1392 | }, 1393 | { 1394 | "cell_type": "markdown", 1395 | "metadata": {}, 1396 | "source": [ 1397 | "We can see that sample 1 (see the '1' on the left hand side, that is the sample index in the table) has a NaN porosity value. \n", 1398 | "\n", 1399 | "#### Drop Samples with Missing Values\n", 1400 | "\n", 1401 | "Now we may choose to remove the sample with the NaN. The 'dropna' DataFrame member function will remove all samples with NaN entries from the entire DataFrame. By visualizing the index at the left of the DataFrame preview you can confirm that sample 1 is removed (it jumps from 0 to 2)." 1402 | ] 1403 | }, 1404 | { 1405 | "cell_type": "code", 1406 | "execution_count": 51, 1407 | "metadata": {}, 1408 | "outputs": [ 1409 | { 1410 | "data": { 1411 | "text/html": [ 1412 | "
\n", 1413 | "\n", 1426 | "\n", 1427 | " \n", 1428 | " \n", 1429 | " \n", 1430 | " \n", 1431 | " \n", 1432 | " \n", 1433 | " \n", 1434 | " \n", 1435 | " \n", 1436 | " \n", 1437 | " \n", 1438 | " \n", 1439 | " \n", 1440 | " \n", 1441 | " \n", 1442 | " \n", 1443 | " \n", 1444 | " \n", 1445 | " \n", 1446 | " \n", 1447 | " \n", 1448 | " \n", 1449 | " \n", 1450 | " \n", 1451 | " \n", 1452 | " \n", 1453 | " \n", 1454 | " \n", 1455 | " \n", 1456 | " \n", 1457 | " \n", 1458 | " \n", 1459 | " \n", 1460 | " \n", 1461 | " \n", 1462 | " \n", 1463 | " \n", 1464 | " \n", 1465 | " \n", 1466 | " \n", 1467 | " \n", 1468 | " \n", 1469 | " \n", 1470 | " \n", 1471 | " \n", 1472 | " \n", 1473 | " \n", 1474 | " \n", 1475 | " \n", 1476 | " \n", 1477 | " \n", 1478 | " \n", 1479 | " \n", 1480 | " \n", 1481 | " \n", 1482 | " \n", 1483 | " \n", 1484 | " \n", 1485 | " \n", 1486 | " \n", 1487 | " \n", 1488 | " \n", 1489 | " \n", 1490 | " \n", 1491 | " \n", 1492 | " \n", 1493 | " \n", 1494 | " \n", 1495 | " \n", 1496 | " \n", 1497 | " \n", 1498 | " \n", 1499 | " \n", 1500 | " \n", 1501 | " \n", 1502 | " \n", 1503 | " \n", 1504 | " \n", 1505 | " \n", 1506 | " \n", 1507 | " \n", 1508 | " \n", 1509 | "
XYfaciesporositypermaiporosity100permportporosityperm_cutoff
0565148510.11846.1702.00911.8452.111486low0.0001
22065286520.192092.2973.52419.20480.713542high92.2970
33575265510.16219.0482.15716.2155.817397high9.0480
418353510.17667.1233.97917.6640.334088high7.1230
53375252510.12391.4682.33712.3911.848265high1.4680
\n", 1510 | "
" 1511 | ], 1512 | "text/plain": [ 1513 | " X Y facies porosity perm ai porosity100 permpor \\\n", 1514 | "0 565 1485 1 0.1184 6.170 2.009 11.84 52.111486 \n", 1515 | "2 2065 2865 2 0.1920 92.297 3.524 19.20 480.713542 \n", 1516 | "3 3575 2655 1 0.1621 9.048 2.157 16.21 55.817397 \n", 1517 | "4 1835 35 1 0.1766 7.123 3.979 17.66 40.334088 \n", 1518 | "5 3375 2525 1 0.1239 1.468 2.337 12.39 11.848265 \n", 1519 | "\n", 1520 | " tporosity perm_cutoff \n", 1521 | "0 low 0.0001 \n", 1522 | "2 high 92.2970 \n", 1523 | "3 high 9.0480 \n", 1524 | "4 high 7.1230 \n", 1525 | "5 high 1.4680 " 1526 | ] 1527 | }, 1528 | "execution_count": 51, 1529 | "metadata": {}, 1530 | "output_type": "execute_result" 1531 | } 1532 | ], 1533 | "source": [ 1534 | "df = df.dropna() # drop any rows (samples) with atleast one missing value \n", 1535 | "df.head()" 1536 | ] 1537 | }, 1538 | { 1539 | "cell_type": "markdown", 1540 | "metadata": {}, 1541 | "source": [ 1542 | "#### Searching with Tabular Data\n", 1543 | "\n", 1544 | "One could extract samples into a new DataFrame with multiple criteria. This is shown below." 1545 | ] 1546 | }, 1547 | { 1548 | "cell_type": "code", 1549 | "execution_count": 52, 1550 | "metadata": {}, 1551 | "outputs": [ 1552 | { 1553 | "data": { 1554 | "text/html": [ 1555 | "
\n", 1556 | "\n", 1569 | "\n", 1570 | " \n", 1571 | " \n", 1572 | " \n", 1573 | " \n", 1574 | " \n", 1575 | " \n", 1576 | " \n", 1577 | " \n", 1578 | " \n", 1579 | " \n", 1580 | " \n", 1581 | " \n", 1582 | " \n", 1583 | " \n", 1584 | " \n", 1585 | " \n", 1586 | " \n", 1587 | " \n", 1588 | " \n", 1589 | " \n", 1590 | " \n", 1591 | " \n", 1592 | " \n", 1593 | " \n", 1594 | " \n", 1595 | " \n", 1596 | " \n", 1597 | " \n", 1598 | " \n", 1599 | " \n", 1600 | " \n", 1601 | " \n", 1602 | " \n", 1603 | " \n", 1604 | " \n", 1605 | " \n", 1606 | " \n", 1607 | " \n", 1608 | " \n", 1609 | " \n", 1610 | " \n", 1611 | " \n", 1612 | " \n", 1613 | " \n", 1614 | " \n", 1615 | " \n", 1616 | " \n", 1617 | " \n", 1618 | " \n", 1619 | " \n", 1620 | " \n", 1621 | " \n", 1622 | " \n", 1623 | " \n", 1624 | " \n", 1625 | " \n", 1626 | " \n", 1627 | " \n", 1628 | " \n", 1629 | " \n", 1630 | " \n", 1631 | " \n", 1632 | " \n", 1633 | " \n", 1634 | " \n", 1635 | " \n", 1636 | " \n", 1637 | " \n", 1638 | " \n", 1639 | " \n", 1640 | " \n", 1641 | " \n", 1642 | " \n", 1643 | " \n", 1644 | " \n", 1645 | " \n", 1646 | " \n", 1647 | " \n", 1648 | " \n", 1649 | " \n", 1650 | " \n", 1651 | " \n", 1652 | "
XYfaciesporositypermaiporosity100permportporosityperm_cutoff
22065286520.192092.2973.52419.20480.713542high92.297
62295132510.179031.9333.49117.90178.396648high31.933
73715304520.1914116.7812.18719.14610.141066high116.781
13545376510.181714.3113.04518.1778.761695high14.311
151385241520.177422.5782.71117.74127.271702high22.578
\n", 1653 | "
" 1654 | ], 1655 | "text/plain": [ 1656 | " X Y facies porosity perm ai porosity100 permpor \\\n", 1657 | "2 2065 2865 2 0.1920 92.297 3.524 19.20 480.713542 \n", 1658 | "6 2295 1325 1 0.1790 31.933 3.491 17.90 178.396648 \n", 1659 | "7 3715 3045 2 0.1914 116.781 2.187 19.14 610.141066 \n", 1660 | "13 545 3765 1 0.1817 14.311 3.045 18.17 78.761695 \n", 1661 | "15 1385 2415 2 0.1774 22.578 2.711 17.74 127.271702 \n", 1662 | "\n", 1663 | " tporosity perm_cutoff \n", 1664 | "2 high 92.297 \n", 1665 | "6 high 31.933 \n", 1666 | "7 high 116.781 \n", 1667 | "13 high 14.311 \n", 1668 | "15 high 22.578 " 1669 | ] 1670 | }, 1671 | "execution_count": 52, 1672 | "metadata": {}, 1673 | "output_type": "execute_result" 1674 | } 1675 | ], 1676 | "source": [ 1677 | "df_extract = df.loc[(df['porosity'] > 0.12) & (df['perm'] > 10.0)] # extract with multiple conditions to a new table\n", 1678 | "df_extract.head()" 1679 | ] 1680 | }, 1681 | { 1682 | "cell_type": "markdown", 1683 | "metadata": {}, 1684 | "source": [ 1685 | "#### Making Data Frames\n", 1686 | "\n", 1687 | "We already covered the idea of making a DataFrame by loading data from a file. \n", 1688 | "\n", 1689 | "It is also possible to build a brandnew DataFrame from a set of 1D arrays. \n", 1690 | "\n", 1691 | "* Note, they must have the same size and be sorted consistently. \n", 1692 | "\n", 1693 | "We will extract 'porosity' and 'perm' features as arrays.\n", 1694 | "\n", 1695 | "**Python Tip: extracting data from DataFrames**\n", 1696 | "\n", 1697 | "We can extract the data for a single feature with this command:\n", 1698 | "\n", 1699 | "```python\n", 1700 | "1D_series = df['feature_name']\n", 1701 | "```\n", 1702 | "\n", 1703 | "The 'series' retains information about the feature that was included in the DataFrame including the name and the indexing. This is fine, but some methods don't work with series so we can also extract the data as a 1D ndarray. By adding the '.values' the series is converted to a 1D array.\n", 1704 | "\n", 1705 | "```python\n", 1706 | "1D_ndarray = df['feature_name'].values\n", 1707 | "```\n", 1708 | "\n", 1709 | "We then use the pandas DataFrame command to make a new DataFrame with each 1D array and the column names specified as 'porosity' and 'permeabilty'." 1710 | ] 1711 | }, 1712 | { 1713 | "cell_type": "code", 1714 | "execution_count": 53, 1715 | "metadata": {}, 1716 | "outputs": [ 1717 | { 1718 | "data": { 1719 | "text/html": [ 1720 | "
\n", 1721 | "\n", 1734 | "\n", 1735 | " \n", 1736 | " \n", 1737 | " \n", 1738 | " \n", 1739 | " \n", 1740 | " \n", 1741 | " \n", 1742 | " \n", 1743 | " \n", 1744 | " \n", 1745 | " \n", 1746 | " \n", 1747 | " \n", 1748 | " \n", 1749 | " \n", 1750 | " \n", 1751 | " \n", 1752 | " \n", 1753 | " \n", 1754 | " \n", 1755 | " \n", 1756 | " \n", 1757 | " \n", 1758 | " \n", 1759 | " \n", 1760 | " \n", 1761 | " \n", 1762 | " \n", 1763 | " \n", 1764 | " \n", 1765 | " \n", 1766 | " \n", 1767 | " \n", 1768 | " \n", 1769 | "
porositypermeability
00.11846.170
10.192092.297
20.16219.048
30.17667.123
40.12391.468
\n", 1770 | "
" 1771 | ], 1772 | "text/plain": [ 1773 | " porosity permeability\n", 1774 | "0 0.1184 6.170\n", 1775 | "1 0.1920 92.297\n", 1776 | "2 0.1621 9.048\n", 1777 | "3 0.1766 7.123\n", 1778 | "4 0.1239 1.468" 1779 | ] 1780 | }, 1781 | "execution_count": 53, 1782 | "metadata": {}, 1783 | "output_type": "execute_result" 1784 | } 1785 | ], 1786 | "source": [ 1787 | "por = df['porosity'].values # extract porosity column as vector\n", 1788 | "perm = df['perm'].values # extract permeability column as vector\n", 1789 | "df_new = pd.DataFrame({'porosity': por, 'permeability': perm}) # make a new DataFrame from the vectors\n", 1790 | "df_new.head()" 1791 | ] 1792 | }, 1793 | { 1794 | "cell_type": "markdown", 1795 | "metadata": {}, 1796 | "source": [ 1797 | "#### Information About Our Tabular Data\n", 1798 | "\n", 1799 | "We can reach in and retrieve the actual raw information in the DataFrame including the column names and actual values as an numpy array. \n", 1800 | "\n", 1801 | "* We can't edit them like this, but we can access and use this information. \n", 1802 | "\n", 1803 | "This includs:\n", 1804 | "\n", 1805 | "1. 'index' with information about the index (i.e. index from start to stop with step)\n", 1806 | "2. 'columns' with the names of the features \n", 1807 | "3. 'values' with the data table entries as a 2D array. \n", 1808 | "\n", 1809 | "Let's look at these components of our DataFrame:" 1810 | ] 1811 | }, 1812 | { 1813 | "cell_type": "code", 1814 | "execution_count": 54, 1815 | "metadata": {}, 1816 | "outputs": [ 1817 | { 1818 | "name": "stdout", 1819 | "output_type": "stream", 1820 | "text": [ 1821 | "Int64Index([ 0, 2, 3, 4, 5, 6, 7, 8, 9, 10,\n", 1822 | " ...\n", 1823 | " 190, 191, 192, 193, 194, 195, 196, 197, 198, 199],\n", 1824 | " dtype='int64', length=199)\n", 1825 | "Index(['X', 'Y', 'facies', 'porosity', 'perm', 'ai', 'porosity100', 'permpor',\n", 1826 | " 'tporosity', 'perm_cutoff'],\n", 1827 | " dtype='object')\n", 1828 | "[[565 1485 1 ... 52.111486486486484 'low' 0.0001]\n", 1829 | " [2065 2865 2 ... 480.71354166666674 'high' 92.29700000000001]\n", 1830 | " [3575 2655 1 ... 55.81739666872301 'high' 9.048]\n", 1831 | " ...\n", 1832 | " [375 1705 1 ... 18.198334595003786 'high' 2.404]\n", 1833 | " [3795 535 1 ... 0.25968483256730135 'low' 0.0001]\n", 1834 | " [3455 1645 1 ... 6.578073089700997 'high' 0.99]]\n" 1835 | ] 1836 | } 1837 | ], 1838 | "source": [ 1839 | "print(df.index) # get information about the index\n", 1840 | "print(df.columns) # get the list of feature names\n", 1841 | "print(df.values) # get the 2D array with all the table data" 1842 | ] 1843 | }, 1844 | { 1845 | "cell_type": "markdown", 1846 | "metadata": {}, 1847 | "source": [ 1848 | "Here's a method for getting a list of the DataFrame feature names:" 1849 | ] 1850 | }, 1851 | { 1852 | "cell_type": "code", 1853 | "execution_count": 55, 1854 | "metadata": {}, 1855 | "outputs": [ 1856 | { 1857 | "data": { 1858 | "text/plain": [ 1859 | "['X',\n", 1860 | " 'Y',\n", 1861 | " 'facies',\n", 1862 | " 'porosity',\n", 1863 | " 'perm',\n", 1864 | " 'ai',\n", 1865 | " 'porosity100',\n", 1866 | " 'permpor',\n", 1867 | " 'tporosity',\n", 1868 | " 'perm_cutoff']" 1869 | ] 1870 | }, 1871 | "execution_count": 55, 1872 | "metadata": {}, 1873 | "output_type": "execute_result" 1874 | } 1875 | ], 1876 | "source": [ 1877 | "list(df) # get a list with the feature names" 1878 | ] 1879 | }, 1880 | { 1881 | "cell_type": "markdown", 1882 | "metadata": {}, 1883 | "source": [ 1884 | "#### More Precise Information from Tabular Data\n", 1885 | "\n", 1886 | "Let's interact with the DataFrame more surgically, one feature and sample at a time. Here we retrieve the 4th column feature name and the porosity value for sample \\#1. " 1887 | ] 1888 | }, 1889 | { 1890 | "cell_type": "code", 1891 | "execution_count": 56, 1892 | "metadata": {}, 1893 | "outputs": [ 1894 | { 1895 | "name": "stdout", 1896 | "output_type": "stream", 1897 | "text": [ 1898 | "porosity\n", 1899 | "Porosity value for sample number 1 is 0.192.\n" 1900 | ] 1901 | } 1902 | ], 1903 | "source": [ 1904 | "col2_name = df.columns[3] # get the name of the 4th feature (porosity)\n", 1905 | "print(col2_name) \n", 1906 | "por1 = df.values[1,3] # get the value for sample 1 of the 4th feature (porosity)\n", 1907 | "print('Porosity value for sample number 1 is ' + str(por1) + '.') " 1908 | ] 1909 | }, 1910 | { 1911 | "cell_type": "markdown", 1912 | "metadata": {}, 1913 | "source": [ 1914 | "We can also manually change values. \n", 1915 | "\n", 1916 | "* We can use the 'at' pandas DataFrame member function to get and set manually individual records. \n", 1917 | "\n", 1918 | "We look up the porosity value for sample 1 and then we use the 'at' again DataFrame member function to change the value to 0.1000. " 1919 | ] 1920 | }, 1921 | { 1922 | "cell_type": "code", 1923 | "execution_count": 59, 1924 | "metadata": {}, 1925 | "outputs": [ 1926 | { 1927 | "name": "stdout", 1928 | "output_type": "stream", 1929 | "text": [ 1930 | "The value of porosity for sample 2 is 0.1.\n", 1931 | "The value of porosity for sample 2 is now 0.1000.\n" 1932 | ] 1933 | }, 1934 | { 1935 | "data": { 1936 | "text/html": [ 1937 | "
\n", 1938 | "\n", 1951 | "\n", 1952 | " \n", 1953 | " \n", 1954 | " \n", 1955 | " \n", 1956 | " \n", 1957 | " \n", 1958 | " \n", 1959 | " \n", 1960 | " \n", 1961 | " \n", 1962 | " \n", 1963 | " \n", 1964 | " \n", 1965 | " \n", 1966 | " \n", 1967 | " \n", 1968 | " \n", 1969 | " \n", 1970 | " \n", 1971 | " \n", 1972 | " \n", 1973 | " \n", 1974 | " \n", 1975 | " \n", 1976 | " \n", 1977 | " \n", 1978 | " \n", 1979 | " \n", 1980 | " \n", 1981 | " \n", 1982 | " \n", 1983 | " \n", 1984 | " \n", 1985 | " \n", 1986 | " \n", 1987 | " \n", 1988 | " \n", 1989 | " \n", 1990 | " \n", 1991 | " \n", 1992 | " \n", 1993 | " \n", 1994 | " \n", 1995 | " \n", 1996 | " \n", 1997 | " \n", 1998 | " \n", 1999 | " \n", 2000 | " \n", 2001 | " \n", 2002 | " \n", 2003 | " \n", 2004 | " \n", 2005 | " \n", 2006 | " \n", 2007 | " \n", 2008 | " \n", 2009 | " \n", 2010 | " \n", 2011 | " \n", 2012 | " \n", 2013 | " \n", 2014 | " \n", 2015 | " \n", 2016 | " \n", 2017 | " \n", 2018 | " \n", 2019 | " \n", 2020 | " \n", 2021 | " \n", 2022 | " \n", 2023 | " \n", 2024 | " \n", 2025 | " \n", 2026 | " \n", 2027 | " \n", 2028 | " \n", 2029 | " \n", 2030 | " \n", 2031 | " \n", 2032 | " \n", 2033 | " \n", 2034 | "
XYfaciesporositypermaiporosity100permportporosityperm_cutoff
0565148510.11846.1702.00911.8452.111486low0.0001
22065286520.100092.2973.52419.20480.713542high92.2970
33575265510.16219.0482.15716.2155.817397high9.0480
418353510.17667.1233.97917.6640.334088high7.1230
53375252510.12391.4682.33712.3911.848265high1.4680
\n", 2035 | "
" 2036 | ], 2037 | "text/plain": [ 2038 | " X Y facies porosity perm ai porosity100 permpor \\\n", 2039 | "0 565 1485 1 0.1184 6.170 2.009 11.84 52.111486 \n", 2040 | "2 2065 2865 2 0.1000 92.297 3.524 19.20 480.713542 \n", 2041 | "3 3575 2655 1 0.1621 9.048 2.157 16.21 55.817397 \n", 2042 | "4 1835 35 1 0.1766 7.123 3.979 17.66 40.334088 \n", 2043 | "5 3375 2525 1 0.1239 1.468 2.337 12.39 11.848265 \n", 2044 | "\n", 2045 | " tporosity perm_cutoff \n", 2046 | "0 low 0.0001 \n", 2047 | "2 high 92.2970 \n", 2048 | "3 high 9.0480 \n", 2049 | "4 high 7.1230 \n", 2050 | "5 high 1.4680 " 2051 | ] 2052 | }, 2053 | "execution_count": 59, 2054 | "metadata": {}, 2055 | "output_type": "execute_result" 2056 | } 2057 | ], 2058 | "source": [ 2059 | "por = df.at[2,'porosity'] # get the value for sample 1 of the porosity feature\n", 2060 | "print('The value of porosity for sample 2 is ' + str(por) + '.')\n", 2061 | "df.at[2,'porosity'] = 0.10 # set the value for sample 1 of the porosity feature\n", 2062 | "print('The value of porosity for sample 2 is now 0.1000.')\n", 2063 | "df.head()" 2064 | ] 2065 | }, 2066 | { 2067 | "cell_type": "markdown", 2068 | "metadata": {}, 2069 | "source": [ 2070 | "#### Writing the Tabular Data to a File\n", 2071 | "\n", 2072 | "It may be useful to write the DataFrame out for storage or curation and / or to be utilize with another platform (even R or Excel!). It is easy to write the DataFrame back to a comma delimited file. We have the 'to_csv' DataFrame member function to accomplish this. The file will write to the working directory (another reason we set that at the beginning). Go to that folder and open this new file with TextPad, Excel or any other program that opens .txt files to check it out." 2073 | ] 2074 | }, 2075 | { 2076 | "cell_type": "code", 2077 | "execution_count": null, 2078 | "metadata": {}, 2079 | "outputs": [], 2080 | "source": [ 2081 | "df.to_csv(\"2D_MV_200wells_out.csv\") # write out the df DataFrame to a comma delimited file " 2082 | ] 2083 | }, 2084 | { 2085 | "cell_type": "markdown", 2086 | "metadata": {}, 2087 | "source": [ 2088 | "#### More Exercises\n", 2089 | "\n", 2090 | "There are so many more exercises and tests that one could attempt to gain experience with the pandas package, DataFrames objects in Python. I'll end here for brevity, but I invite you to continue. Check out the docs at https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.html. I'm always happy to discuss,\n", 2091 | "\n", 2092 | "*Michael*\n", 2093 | "\n", 2094 | "#### The Author:\n", 2095 | "\n", 2096 | "### Michael Pyrcz, Associate Professor, University of Texas at Austin \n", 2097 | "*Novel Data Analytics, Geostatistics and Machine Learning Subsurface Solutions*\n", 2098 | "\n", 2099 | "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", 2100 | "\n", 2101 | "For more about Michael check out these links:\n", 2102 | "\n", 2103 | "#### [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", 2104 | "\n", 2105 | "#### Want to Work Together?\n", 2106 | "\n", 2107 | "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", 2108 | "\n", 2109 | "* 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", 2110 | "\n", 2111 | "* 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", 2112 | "\n", 2113 | "* I can be reached at mpyrcz@austin.utexas.edu.\n", 2114 | "\n", 2115 | "I'm always happy to discuss,\n", 2116 | "\n", 2117 | "*Michael*\n", 2118 | "\n", 2119 | "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" 2120 | ] 2121 | }, 2122 | { 2123 | "cell_type": "code", 2124 | "execution_count": null, 2125 | "metadata": {}, 2126 | "outputs": [], 2127 | "source": [] 2128 | } 2129 | ], 2130 | "metadata": { 2131 | "kernelspec": { 2132 | "display_name": "Python 3", 2133 | "language": "python", 2134 | "name": "python3" 2135 | }, 2136 | "language_info": { 2137 | "codemirror_mode": { 2138 | "name": "ipython", 2139 | "version": 3 2140 | }, 2141 | "file_extension": ".py", 2142 | "mimetype": "text/x-python", 2143 | "name": "python", 2144 | "nbconvert_exporter": "python", 2145 | "pygments_lexer": "ipython3", 2146 | "version": "3.6.5" 2147 | } 2148 | }, 2149 | "nbformat": 4, 2150 | "nbformat_minor": 2 2151 | } 2152 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Michael J Pyrcz 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of these lectures and workflows and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /Lectures/00_CourseOverview.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeostatsGuy/SubsurfaceMachineLearning/c22a95c42a814e76087d3af13d59cc81ef53cf91/Lectures/00_CourseOverview.pdf -------------------------------------------------------------------------------- /Lectures/01_Introduction.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeostatsGuy/SubsurfaceMachineLearning/c22a95c42a814e76087d3af13d59cc81ef53cf91/Lectures/01_Introduction.pdf -------------------------------------------------------------------------------- /Lectures/02_DataAnalytics.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeostatsGuy/SubsurfaceMachineLearning/c22a95c42a814e76087d3af13d59cc81ef53cf91/Lectures/02_DataAnalytics.pdf -------------------------------------------------------------------------------- /Lectures/03_Inference.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeostatsGuy/SubsurfaceMachineLearning/c22a95c42a814e76087d3af13d59cc81ef53cf91/Lectures/03_Inference.pdf -------------------------------------------------------------------------------- /Lectures/04_Prediction.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeostatsGuy/SubsurfaceMachineLearning/c22a95c42a814e76087d3af13d59cc81ef53cf91/Lectures/04_Prediction.pdf -------------------------------------------------------------------------------- /Lectures/05_Advanced.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeostatsGuy/SubsurfaceMachineLearning/c22a95c42a814e76087d3af13d59cc81ef53cf91/Lectures/05_Advanced.pdf -------------------------------------------------------------------------------- /Lectures/06_Conclusion.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeostatsGuy/SubsurfaceMachineLearning/c22a95c42a814e76087d3af13d59cc81ef53cf91/Lectures/06_Conclusion.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # Subsurface Data Analytics and Machine Learning 6 | 7 | Short course on data analytics, geostatistics and machine learning for spatial modeling. 8 | 9 | #### Course Objectives: 10 | 11 | You will gain: 12 | * knowledge concerning data analytics and machine learning for subsurface modeling. 13 | 14 | #### Course Agenda 15 | * Introduction: objectives, plan 16 | * Data analytics - definitions, bootstrap, confidence intervals and hypothesis testing 17 | * Inference – multivariate analysis, feature selection and principal component analysis 18 | * Prediction - simple model demonstration of prediction concepts, decision tree 19 | * Advanced Prediction – support vector machines, neural nets (artifical, convolutional and recurrent) 20 | * Conclusions 21 | 22 | #### The Instructor: 23 | 24 | ### Michael Pyrcz, Associate Professor, University of Texas at Austin 25 | *Novel Data Analytics, Geostatistics and Machine Learning Subsurface Solutions* 26 | 27 | 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. 28 | 29 | For more about Michael check out these links: 30 | 31 | #### [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) 32 | 33 | #### Want to Work Together? 34 | 35 | I hope that this 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. 36 | 37 | * Want to invite me to visit your company for training, mentoring, project review, workflow design and consulting, I'd be happy to drop by and work with you! 38 | 39 | * 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! 40 | 41 | * I can be reached at mpyrcz@austin.utexas.edu. 42 | 43 | I'm always happy to discuss, 44 | 45 | *Michael* 46 | 47 | 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 48 | 49 | #### 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) 50 | --------------------------------------------------------------------------------