diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..c3cdbf8 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,10 @@ +[run] +source = pykrige +omit = *docs*, *examples*, *tests* + +[report] +exclude_lines = + pragma: no cover + if __name__ == '__main__': + def __repr__ + def __str__ diff --git a/.gitignore b/.gitignore index b27793d..353c781 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,123 @@ -*.pyc -*.egg-info/ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ build/ -doc/_build/ -*.swp -.idea/* -dist/* +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +docs/output.txt +docs/source/examples/ +docs/source/generated/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# dotenv +.env + +# virtualenv +.venv +venv/ +ENV/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +tags +/test_* + +# own stuff +info/ +pykrige/_version.py + +# Cython generated C code *.c -*.so -.cache/ -doc/examples/ -doc/generated/ -doc/modules/ +*.cpp +# special +*.DS_Store +*.zip +*.vtu +*.vtr examples/meuse_example_data/* *.ipynb_checkpoints/* diff --git a/.travis.yml b/.travis.yml index 38cf95f..64f379f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,49 +1,107 @@ language: python -sudo: required -services: - - xvfb -matrix: - include: - - python: "3.6" - env: DEPS="numpy scipy cython nose matplotlib scikit-learn gstools" - - python: "3.5" - env: DEPS="numpy scipy=0.18 cython nose matplotlib scikit-learn=0.18.1" - - python: "2.7" - env: DEPS="numpy=1.10.4 scipy=0.17 cython nose matplotlib scikit-learn=0.17.1" - -# setup adapted from https://github.com/soft-matter/trackpy/blob/master/.travis.yml +python: 3.8 + +# setuptools-scm needs all tags in order to obtain a proper version +git: + depth: false + +env: + global: + - TWINE_USERNAME=geostatframework + - CIBW_BEFORE_BUILD="pip install numpy==1.17.3 scipy==1.3.2 cython==0.29.14 setuptools" + - CIBW_TEST_REQUIRES="pytest scikit-learn gstools" + - CIBW_TEST_COMMAND="pytest -v {project}/tests" + before_install: - - wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh - - chmod +x miniconda.sh - - ./miniconda.sh -b - - export PATH=/home/travis/miniconda3/bin:$PATH - - conda update --yes conda - - conda info -a - - conda config --add channels conda-forge - -install: - - conda create -q -n krige-env -y $DEPS pip pytest-cov pytest python=${TRAVIS_PYTHON_VERSION} - - source activate krige-env - - which pip - - pip install coveralls - - pip install -e . - -before_script: - - "export DISPLAY=:99.0" - -# command to run tests -script: - - source activate krige-env - - pytest -sv ./pykrige/ --cov=pykrige - - | - # run examples - # scikit-learn needed by the regression kriging example - # conda install -y scikit-learn gstools - set -x - for f in examples/*.py; do - python $f 2>&1 | tee -a ~/log.txt - done - if grep -q "Traceback (most recent call last):" ~/log.txt; then false; else true; fi - -#after_success: -# coveralls + - | + if [[ "$TRAVIS_OS_NAME" = windows ]]; then + choco install python --version 3.8.0 + export PATH="/c/Python38:/c/Python38/Scripts:$PATH" + # make sure it's on PATH as 'python3' + ln -s /c/Python38/python.exe /c/Python38/python3.exe + fi + +install: python3 -m pip install cibuildwheel==1.3.0 + +script: python3 -m cibuildwheel --output-dir dist + +after_success: + - | + if [[ $TRAVIS_PULL_REQUEST == 'false' ]]; then + python3 -m pip install twine + python3 -m twine upload --verbose --skip-existing --repository-url https://test.pypi.org/legacy/ dist/* + if [[ $TRAVIS_TAG ]]; then python3 -m twine upload --verbose --skip-existing dist/*; fi + fi + +notifications: + email: + recipients: + - info@geostat-framework.org + +jobs: + include: + - name: "black check" + services: docker + install: python3 -m pip install black + script: python3 -m black --check pykrige/ examples/ tests/ + after_success: echo "all files formatted correctly" + after_failure: echo "some files not formatted correctly" + + - name: "sdist and coverage" + services: docker + install: + - python3 -m pip install -r requirements_setup.txt + - python3 -m pip install -r requirements_test.txt + - python3 -m pip install -r requirements.txt + script: + - python3 setup.py sdist -d dist + - python3 setup.py build_ext --inplace + - python3 -m pytest --cov pykrige --cov-report term-missing -v tests/ + - python3 -m coveralls + + - name: "Linux py35" + services: docker + env: CIBW_BUILD="cp35-*" + - name: "Linux py36" + services: docker + env: CIBW_BUILD="cp36-*" + - name: "Linux py37" + services: docker + env: CIBW_BUILD="cp37-*" + - name: "Linux py38" + services: docker + env: CIBW_BUILD="cp38-*" + + - name: "MacOS py35" + os: osx + language: shell + env: CIBW_BUILD="cp35-*" + - name: "MacOS py36" + os: osx + language: shell + env: CIBW_BUILD="cp36-*" + - name: "MacOS py37" + os: osx + language: shell + env: CIBW_BUILD="cp37-*" + - name: "MacOS py38" + os: osx + language: shell + env: CIBW_BUILD="cp38-*" + + - name: "Win py35" + os: windows + language: shell + env: CIBW_BUILD="cp35-*" + - name: "Win py36" + os: windows + language: shell + env: CIBW_BUILD="cp36-*" + - name: "Win py37" + os: windows + language: shell + env: CIBW_BUILD="cp37-*" + - name: "Win py38" + os: windows + language: shell + env: CIBW_BUILD="cp38-*" diff --git a/.zenodo.json b/.zenodo.json new file mode 100644 index 0000000..4e384e2 --- /dev/null +++ b/.zenodo.json @@ -0,0 +1,62 @@ +{ + "license": "BSD-3-Clause", + "contributors": [ + { + "type": "Other", + "name": "Malte Ziebarth" + }, + { + "type": "Other", + "name": "Sudipta Basak" + }, + { + "type": "Other", + "name": "Matthew Peveler" + }, + { + "type": "Other", + "name": "kvanlombeek" + }, + { + "type": "Other", + "name": "Will Chang" + }, + { + "type": "Other", + "name": "Harry Matchette-Downes" + }, + { + "type": "Other", + "name": "Daniel Mej\u00eda Raigosa" + } + ], + "language": "eng", + "keywords": [ + "kriging", + "ordinary kriging", + "universal kriging", + "external drift kriging", + "regression kriging", + "variogram", + "geostatistics", + "Python", + "GeoStat-Framework" + ], + "creators": [ + { + "orcid": "0000-0001-7636-3711", + "affiliation": "United State Geological Survey: Golden, CO, US", + "name": "Benjamin Murphy" + }, + { + "orcid": "0000-0002-2565-4444", + "affiliation": "Symerio", + "name": "Roman Yurchak" + }, + { + "orcid": "0000-0001-9060-4008", + "affiliation": "Helmholtz Centre for Environmental Research - UFZ", + "name": "Sebastian M\u00fcller" + } + ] +} diff --git a/doc/release_notes.md b/CHANGELOG.md similarity index 76% rename from doc/release_notes.md rename to CHANGELOG.md index f8b2712..a9930b3 100755 --- a/doc/release_notes.md +++ b/CHANGELOG.md @@ -1,55 +1,85 @@ -# Release notes +Changelog +========= -### Version 1.4.1 + +Version 1.5.0 +------------- +*April 04, 2020* + +**New features** + +* support for GSTools covariance models (#125) +* pre-build wheels for py35-py38 on Linux, Windows and MacOS (#142) +* GridSerachCV from the compat module sets iid=False by default (if present in sklearn) + to be future prove (iid will be deprecated) (#144) + +**Changes** + +* dropped py2* and py<3.5 support (#142) +* installation now requires cython (#142) +* codebase was formatted with black (#144) +* internally use of scipys lapack/blas bindings (#142) +* PyKrige is now part of the GeoStat-Framework + + +Version 1.4.1 +------------- *January 13, 2019* -#### New features -* Added method to obtain variogram model points. PR[#94](https://github.com/bsmurphy/PyKrige/pull/94) by [Daniel Mejía Raigosa](https://github.com/Daniel-M) +**New features** -#### Bug fixes -* Fixed OrdinaryKriging readme example. PR[#107](https://github.com/bsmurphy/PyKrige/pull/107) by [Harry Matchette-Downes](https://github.com/harrymd) -* Fixed kriging matrix not being calculated correctly for geographic coordinates. PR[99](https://github.com/bsmurphy/PyKrige/pull/99) by [Mike Rilee](https://github.com/michaelleerilee) +* Added method to obtain variogram model points. PR[#94](https://github.com/GeoStat-Framework/PyKrige/pull/94) by [Daniel Mejía Raigosa](https://github.com/Daniel-M) -### Version 1.4.0 -*April 24, 2018* +**Bug fixes** + +* Fixed OrdinaryKriging readme example. PR[#107](https://github.com/GeoStat-Framework/PyKrige/pull/107) by [Harry Matchette-Downes](https://github.com/harrymd) +* Fixed kriging matrix not being calculated correctly for geographic coordinates. PR[99](https://github.com/GeoStat-Framework/PyKrige/pull/99) by [Mike Rilee](https://github.com/michaelleerilee) -#### New features -* Regression kriging algotithm. PR [#27](https://github.com/bsmurphy/PyKrige/pull/27) by [Sudipta Basaks](https://github.com/basaks). -* Support for spherical coordinates. PR [#23](https://github.com/bsmurphy/PyKrige/pull/23) by [Malte Ziebarth](https://github.com/mjziebarth) -* Kriging parameter tuning with scikit-learn. PR [#24](https://github.com/bsmurphy/PyKrige/pull/24) by [Sudipta Basaks](https://github.com/basaks). -* Variogram model parameters can be specified using a list or a dict. Allows for directly feeding in the partial sill rather than the full sill. PR [#47](https://github.com/bsmurphy/PyKrige/pull/47) by [Benjamin Murphy](https://github.com/bsmurphy). +Version 1.4.0 +------------- +*April 24, 2018* + +**New features** -#### Enhancements +* Regression kriging algotithm. PR [#27](https://github.com/GeoStat-Framework/PyKrige/pull/27) by [Sudipta Basaks](https://github.com/basaks). +* Support for spherical coordinates. PR [#23](https://github.com/GeoStat-Framework/PyKrige/pull/23) by [Malte Ziebarth](https://github.com/mjziebarth) +* Kriging parameter tuning with scikit-learn. PR [#24](https://github.com/GeoStat-Framework/PyKrige/pull/24) by [Sudipta Basaks](https://github.com/basaks). +* Variogram model parameters can be specified using a list or a dict. Allows for directly feeding in the partial sill rather than the full sill. PR [#47](https://github.com/GeoStat-Framework/PyKrige/pull/47) by [Benjamin Murphy](https://github.com/bsmurphy). -* Improved memory usage in variogram calculations. PR [#42](https://github.com/bsmurphy/PyKrige/pull/42) by [Sudipta Basaks](https://github.com/basaks). -* Added benchmark scripts. PR [#36](https://github.com/bsmurphy/PyKrige/pull/36) by [Roman Yurchak](https://github.com/rth) -* Added an extensive example using the meusegrids dataset. PR [#28](https://github.com/bsmurphy/PyKrige/pull/28) by [kvanlombeek](https://github.com/kvanlombeek). +**Enhancements** -#### Bug fixes +* Improved memory usage in variogram calculations. PR [#42](https://github.com/GeoStat-Framework/PyKrige/pull/42) by [Sudipta Basaks](https://github.com/basaks). +* Added benchmark scripts. PR [#36](https://github.com/GeoStat-Framework/PyKrige/pull/36) by [Roman Yurchak](https://github.com/rth) +* Added an extensive example using the meusegrids dataset. PR [#28](https://github.com/GeoStat-Framework/PyKrige/pull/28) by [kvanlombeek](https://github.com/kvanlombeek). -* Statistics calculations in 3D kriging. PR [#45](https://github.com/bsmurphy/PyKrige/pull/45) by [Will Chang](https://github.com/whdc). -* Automatic variogram estimation robustified. PR [#47](https://github.com/bsmurphy/PyKrige/pull/47) by [Benjamin Murphy](https://github.com/bsmurphy). +**Bug fixes** +* Statistics calculations in 3D kriging. PR [#45](https://github.com/GeoStat-Framework/PyKrige/pull/45) by [Will Chang](https://github.com/whdc). +* Automatic variogram estimation robustified. PR [#47](https://github.com/GeoStat-Framework/PyKrige/pull/47) by [Benjamin Murphy](https://github.com/bsmurphy). -### Version 1.3.1 +Version 1.3.1 +------------- *December 10, 2016* * More robust setup for building Cython extensions -### Version 1.3.0 +Version 1.3.0 +------------- *October 23, 2015* * Added support for Python 3. * Updated the setup script to handle problems with trying to build the Cython extensions. If the appropriate compiler hasn't been installed on Windows, then the extensions won't work (see [this discussion of using Cython extensions on Windows] for how to deal with this problem). The setup script now attempts to build the Cython extensions and automatically falls back to pure Python if the build fails. **NOTE that the Cython extensions currently are not set up to work in Python 3** (see [discussion in issue #10]), so they are not built when installing with Python 3. This will be changed in the future. -[closed issue #2]: https://github.com/bsmurphy/PyKrige/issues/2 -[this discussion of using Cython extensions on Windows]: https://github.com/cython/cython/wiki/CythonExtensionsOnWindows -[discussion in issue #10]: https://github.com/bsmurphy/PyKrige/issues/10 +* [closed issue #2]: https://github.com/GeoStat-Framework/PyKrige/issues/2 +* [this discussion of using Cython extensions on Windows]: https://github.com/cython/cython/wiki/CythonExtensionsOnWindows +* [discussion in issue #10]: https://github.com/GeoStat-Framework/PyKrige/issues/10 -### Version 1.2.0 + +Version 1.2.0 +------------- *August 1, 2015* * Updated the execution portion of each class to streamline processing and reduce redundancy in the code. @@ -60,7 +90,8 @@ * Added support for three-dimensional universal kriging. The previous three-dimensional kriging class has been renamed OrdinaryKriging3D within module ok3d, and the new class is called UniversalKriging3D within module uk3d. See `UniversalKriging3D.__doc__` for usage information. A regional linear drift ('regional_linear') is the only code-internal drift that is currently supported, but the 'specified' and 'functional' generic drift capabilities are also implemented here (see above). The regional linear drift is applied in all three spatial dimensions. -### Version 1.1.0 +Version 1.1.0 +------------- *May 25, 2015* * Added support for two different approaches to solving the entire kriging problem. One approach solves for the specified grid or set of points in a single vectorized operation; this method is default. The other approach loops through the specified points and solves the kriging system at each point. In both of these techniques, the kriging matrix is set up and inverted only once. In the vectorized approach, the rest of the kriging system (i.e., the RHS matrix) is set up as a single large array, and the whole system is solved with a single call to `numpy.dot()`. This approach is faster, but it can consume a lot of RAM for large datasets and/or large grids. In the looping approach, the rest of the kriging system (the RHS matrix) is set up at each point, and the kriging system at that point is solved with a call to `numpy.dot()`. This approach is slower, but it does not take as much memory. The approach can be specified by using the `backend` kwarg in the `execute()` method: `'vectorized'` (default) for the vectorized approach, `'loop'` for the looping approach. Thanks to Roman Yurchak for these changes and optimizations. @@ -69,13 +100,16 @@ * Added support for 3D kriging. This is now available as class `Krige3D` in `pykrige.k3d`. The usage is essentially the same as with the two-dimensional kriging classes, except for a few extra arguments that must be passed during instantiation and when calling `Krige3D.execute()`. See `Krige3D.__doc__` for more information. -### Version 1.0.3 +Version 1.0.3 +------------- *February 15, 2015* * Fixed a problem with the tests that are performed to see if the kriging system is to be solved at a data point. (Tests are completed in order to determine whether to force the kriging solution to converge to the true data value.) * Changed setup script. -### Version 1.0 + +Version 1.0 +----------- *January 25, 2015* * Changed license to New BSD. @@ -87,14 +121,17 @@ * Fixed slight problem with `read_asc_grid()` function in `kriging_tools`. Also made some code improvements to both the `write_asc_grid()` and `read_asc_grid()` functions in `kriging_tools`. -### Version 0.2.0 +Version 0.2.0 +------------- *November 23, 2014* * Consolidated backbone functions into a single module in order to reduce redundancy in the code. `OrdinaryKriging` and `UniversalKriging` classes now import and call the `core` module for the standard functions. * Fixed a few glaring mistakes in the code. * Added more documentation. -### Version 0.1.2 + +Version 0.1.2 +------------- *October 27, 2014* * First complete release. diff --git a/LICENSE.txt b/LICENSE similarity index 95% rename from LICENSE.txt rename to LICENSE index 3ad825c..57b77f7 100755 --- a/LICENSE.txt +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2015-2018, PyKrige Developers +Copyright (c) 2015-2020, PyKrige Developers All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/MANIFEST.in b/MANIFEST.in index e7c8b51..96c8456 100755 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,18 +1,7 @@ -include LICENSE.txt include README.md -include CHANGELOG.md -include setup.cfg +include MANIFEST.in include setup.py -recursive-include pykrige *.py -recursive-include pykrige *.pyx -recursive-include pykrige *.pxd -include pykrige/tests/test_data/test_data.txt -include pykrige/tests/test_data/test1_answer.asc -include pykrige/tests/test_data/test1_settings.txt -include pykrige/tests/test_data/test2_answer.asc -include pykrige/tests/test_data/test2_settings.txt -include pykrige/tests/test_data/test3_answer.asc -include pykrige/tests/test_data/test3_dem.asc -include pykrige/tests/test_data/test3_settings.txt -include pykrige/tests/test_data/test3d_answer.txt -include pykrige/tests/test_data/test3d_data.txt +include setup.cfg +recursive-include pykrige *.py *.pyx *.pxd +recursive-include tests *.py *.txt *.asc +include LICENSE diff --git a/README.rst b/README.rst index c58c031..8673739 100644 --- a/README.rst +++ b/README.rst @@ -1,25 +1,33 @@ PyKrige ======= -Kriging Toolkit for Python - -.. image:: https://img.shields.io/pypi/v/pykrige.svg - :target: https://pypi.python.org/pypi/pykrige - -.. image:: https://anaconda.org/conda-forge/pykrige/badges/version.svg - :target: https://github.com/conda-forge/pykrige-feedstock - -.. image:: https://readthedocs.org/projects/pykrige/badge/?version=latest - :target: http://pykrige.readthedocs.io/en/latest/?badge=latest - :alt: Documentation Status - -.. image:: https://travis-ci.org/bsmurphy/PyKrige.svg?branch=master - :target: https://travis-ci.org/bsmurphy/PyKrige +.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.3738604.svg + :target: https://doi.org/10.5281/zenodo.3738604 +.. image:: https://badge.fury.io/py/PyKrige.svg + :target: https://badge.fury.io/py/PyKrige +.. image:: https://img.shields.io/conda/vn/conda-forge/pykrige.svg + :target: https://anaconda.org/conda-forge/pykrige +.. image:: https://travis-ci.com/GeoStat-Framework/PyKrige.svg?branch=master + :target: https://travis-ci.com/GeoStat-Framework/PyKrige +.. image:: https://coveralls.io/repos/github/GeoStat-Framework/PyKrige/badge.svg?branch=master + :target: https://coveralls.io/github/GeoStat-Framework/PyKrige?branch=master +.. image:: https://readthedocs.org/projects/pykrige/badge/?version=stable + :target: http://pykrige.readthedocs.io/en/latest/?badge=stable + :alt: Documentation Status +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + + +.. figure:: https://github.com/GeoStat-Framework/GeoStat-Framework.github.io/raw/master/docs/source/pics/PyKrige_250.png + :align: center + :alt: PyKrige + :figclass: align-center -.. image:: https://ci.appveyor.com/api/projects/status/github/bsmurphy/PyKrige?branch=master&svg=true - :target: https://ci.appveyor.com/project/bsmurphy/pykrige +Kriging Toolkit for Python. +Purpose +^^^^^^^ The code supports 2D and 3D ordinary and universal kriging. Standard variogram models (linear, power, spherical, gaussian, exponential) are built in, but custom variogram models can also be used. @@ -36,14 +44,14 @@ See the documentation at `http://pykrige.readthedocs.io/ &1 | Tee-Object -Append -FilePath "C:\\log.txt" - } - type "C:\\log.txt" - if (sls "Traceback \(most recent call last\):" "C:\\log.txt" -ca -quiet) { - exit 1 - } diff --git a/benchmarks/kriging_benchmarks.py b/benchmarks/kriging_benchmarks.py index 1053c45..9f03688 100644 --- a/benchmarks/kriging_benchmarks.py +++ b/benchmarks/kriging_benchmarks.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import print_function +"""Benchmarks.""" from time import time import numpy as np from pykrige.ok import OrdinaryKriging @@ -12,7 +12,7 @@ def make_benchark(n_train, n_test, n_dim=2): - """ Compute the benchmarks for Ordianry Kriging + """Compute the benchmarks for Ordianry Kriging. Parameters ---------- @@ -58,7 +58,7 @@ def make_benchark(n_train, n_test, n_dim=2): def print_benchmark(n_train, n_test, n_dim, res): - """ Print the benchmarks + """Print the benchmarks. Parameters ---------- diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index 2a34390..0000000 --- a/doc/Makefile +++ /dev/null @@ -1,195 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# User-friendly check for sphinx-build -ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) -endif - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " applehelp to make an Apple Help Book" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - @echo " coverage to run coverage check of the documentation (if enabled)" - -clean: - rm -rf $(BUILDDIR)/* - rm -rf generated/ - rm -rf auto_examples/ - rm -rf examples/ - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PyKrige.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PyKrige.qhc" - -applehelp: - $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp - @echo - @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." - @echo "N.B. You won't be able to view it unless you put it in" \ - "~/Library/Documentation/Help or install it in your application" \ - "bundle." - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/PyKrige" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PyKrige" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -latexpdfja: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through platex and dvipdfmx..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." - -coverage: - $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage - @echo "Testing of coverage in the sources finished, look at the " \ - "results in $(BUILDDIR)/coverage/python.txt." - -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml - @echo - @echo "Build finished. The XML files are in $(BUILDDIR)/xml." - -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/doc/overview.rst b/doc/overview.rst deleted file mode 100644 index 04abf49..0000000 --- a/doc/overview.rst +++ /dev/null @@ -1,2 +0,0 @@ - -.. include:: ../README.rst diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..5cac5a0 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = python3 -msphinx +SPHINXPROJ = PyKrige +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..c5a6a23 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,3 @@ +-r requirements_doc.txt +-r ../requirements_setup.txt +-r ../requirements.txt diff --git a/doc/requirements.txt b/docs/requirements_doc.txt similarity index 70% rename from doc/requirements.txt rename to docs/requirements_doc.txt index d6e4e53..34ee608 100644 --- a/doc/requirements.txt +++ b/docs/requirements_doc.txt @@ -1,4 +1,5 @@ -scikit-learn>=0.18 +scikit-learn>=0.19 +gstools>=1.1.1 sphinx pillow recommonmark diff --git a/docs/source/_templates/layout.html b/docs/source/_templates/layout.html new file mode 100644 index 0000000..eccaa77 --- /dev/null +++ b/docs/source/_templates/layout.html @@ -0,0 +1,20 @@ +{% extends "!layout.html" %} + {% block menu %} + + {{ super() }} +
+ + PyKrige GitHub + PyKrige Zenodo DOI + PyKrige PyPI +
+ + GeoStat Website + GeoStat Github + GeoStat ReadTheDocs + GeoStat PyPI +
+
+ Index + Sitemap + {% endblock %} diff --git a/doc/api.rst b/docs/source/api.rst similarity index 99% rename from doc/api.rst rename to docs/source/api.rst index 0c6ea82..57b24b7 100644 --- a/doc/api.rst +++ b/docs/source/api.rst @@ -1,4 +1,3 @@ - API Reference ============= diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst new file mode 100755 index 0000000..4bb9e62 --- /dev/null +++ b/docs/source/changelog.rst @@ -0,0 +1,2 @@ + +.. include:: ../../CHANGELOG.md diff --git a/doc/conf.py b/docs/source/conf.py similarity index 71% rename from doc/conf.py rename to docs/source/conf.py index 808e986..b36d737 100644 --- a/doc/conf.py +++ b/docs/source/conf.py @@ -12,82 +12,81 @@ # # All configuration values have a default; values that are commented out # serve to show the default. - +import datetime import sys import os import shlex - -import matplotlib -matplotlib.use("Agg") - import sphinx_rtd_theme -import pykrige +import matplotlib -from recommonmark.parser import CommonMarkParser +matplotlib.use("Agg") # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('../')) -sys.path.insert(0, os.path.abspath('sphinxext')) - +# sys.path.insert(0, os.path.abspath("../../")) +sys.path.insert(0, os.path.abspath("sphinxext")) +import pykrige from github_link import make_linkcode_resolve -source_parsers = { - '.md': CommonMarkParser, -} # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.mathjax', - 'sphinx.ext.autodoc', - 'sphinx.ext.autosummary', - 'sphinxcontrib.napoleon', - 'sphinx_gallery.gen_gallery', - 'sphinx.ext.linkcode' + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.mathjax", + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinxcontrib.napoleon", + "sphinx_gallery.gen_gallery", + "sphinx.ext.linkcode", + "recommonmark", ] autosummary_generate = True -autodoc_default_flags = ['members', 'inherited-members'] +autodoc_default_flags = ["members", "inherited-members"] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: -source_suffix = ['.rst', '.md'] +source_suffix = { + ".rst": "restructuredtext", + ".txt": "restructuredtext", + ".md": "markdown", +} # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "contents" + - sphinx_gallery_conf = { # path to your examples scripts - 'examples_dirs' : '../examples', + "examples_dirs": "../../examples", # path where to save gallery generated examples - 'gallery_dirs' : 'examples', - 'filename_pattern' : '/.*.py' + "gallery_dirs": "examples", + "filename_pattern": "/.*.py", } # General information about the project. -project = 'PyKrige' -copyright = '2017, PyKrige developers' -author = 'PyKrige developers' +curr_year = datetime.datetime.now().year +project = "PyKrige" +copyright = "2017 - {}, PyKrige developers".format(curr_year) +author = "PyKrige developers" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -107,37 +106,37 @@ # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False @@ -147,156 +146,151 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +# html_static_path = ["_static"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -#html_extra_path = [] +# html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' -#html_search_language = 'en' +# html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value -#html_search_options = {'type': 'default'} +# html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. -#html_search_scorer = 'scorer.js' +# html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'PyKrigedoc' +htmlhelp_basename = "PyKrigedoc" +html_logo = "pics/PyKrige_150.png" +html_favicon = "pics/PyKrige.ico" # -- Options for LaTeX output --------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', - -# Latex figure (float) alignment -#'figure_align': 'htbp', + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + #'preamble': '', + # Latex figure (float) alignment + #'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'PyKrige.tex', 'PyKrige Documentation', - 'PyKrige developers', 'manual'), + (master_doc, "PyKrige.tex", "PyKrige Documentation", "PyKrige developers", "manual") ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'pykrige', 'PyKrige Documentation', - [author], 1) -] +man_pages = [(master_doc, "pykrige", "PyKrige Documentation", [author], 1)] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -305,24 +299,32 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'PyKrige', 'PyKrige Documentation', - author, 'PyKrige', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "PyKrige", + "PyKrige Documentation", + author, + "PyKrige", + "One line description of project.", + "Miscellaneous", + ) ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False # The following is used by sphinx.ext.linkcode to provide links to github -linkcode_resolve = make_linkcode_resolve('pykrige', - u'https://github.com/bsmurphy/' - 'PyKrige/blob/{revision}/' - '{package}/{path}#L{lineno}') +linkcode_resolve = make_linkcode_resolve( + "pykrige", + u"https://github.com/GeoStat-Framework/" + "PyKrige/blob/{revision}/" + "{package}/{path}#L{lineno}", +) diff --git a/doc/index.rst b/docs/source/contents.rst similarity index 86% rename from doc/index.rst rename to docs/source/contents.rst index c3f3b19..b2fd607 100644 --- a/doc/index.rst +++ b/docs/source/contents.rst @@ -8,10 +8,10 @@ Contents ^^^^^^^^ .. toctree:: - :maxdepth: 2 + :maxdepth: 3 - overview + index variogram_models api examples/index - release_notes + changelog diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..4d2c74b --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,2 @@ + +.. include:: ../../README.rst diff --git a/docs/source/pics/PyKrige.ico b/docs/source/pics/PyKrige.ico new file mode 100644 index 0000000..160e80a Binary files /dev/null and b/docs/source/pics/PyKrige.ico differ diff --git a/docs/source/pics/PyKrige.png b/docs/source/pics/PyKrige.png new file mode 100644 index 0000000..5e35e71 Binary files /dev/null and b/docs/source/pics/PyKrige.png differ diff --git a/docs/source/pics/PyKrige_150.png b/docs/source/pics/PyKrige_150.png new file mode 100644 index 0000000..8cf6ff1 Binary files /dev/null and b/docs/source/pics/PyKrige_150.png differ diff --git a/doc/sphinxext/github_link.py b/docs/source/sphinxext/github_link.py similarity index 70% rename from doc/sphinxext/github_link.py rename to docs/source/sphinxext/github_link.py index 66b58c8..9ce0e9c 100644 --- a/doc/sphinxext/github_link.py +++ b/docs/source/sphinxext/github_link.py @@ -7,16 +7,16 @@ import sys from functools import partial -REVISION_CMD = 'git rev-parse --short HEAD' +REVISION_CMD = "git rev-parse --short HEAD" def _get_git_revision(): try: revision = subprocess.check_output(REVISION_CMD.split()).strip() except (subprocess.CalledProcessError, OSError): - print('Failed to execute git to get revision') + print("Failed to execute git to get revision") return None - return revision.decode('utf-8') + return revision.decode("utf-8") def _linkcode_resolve(domain, info, package, url_fmt, revision): @@ -36,17 +36,17 @@ def _linkcode_resolve(domain, info, package, url_fmt, revision): if revision is None: return - if domain not in ('py', 'pyx'): + if domain not in ("py", "pyx"): return - if not info.get('module') or not info.get('fullname'): + if not info.get("module") or not info.get("fullname"): return - class_name = info['fullname'].split('.')[0] + class_name = info["fullname"].split(".")[0] if type(class_name) != str: # Python 2 only - class_name = class_name.encode('utf-8') - module = __import__(info['module'], fromlist=[class_name]) - obj = attrgetter(info['fullname'])(module) + class_name = class_name.encode("utf-8") + module = __import__(info["module"], fromlist=[class_name]) + obj = attrgetter(info["fullname"])(module) try: fn = inspect.getsourcefile(obj) @@ -60,14 +60,12 @@ def _linkcode_resolve(domain, info, package, url_fmt, revision): if not fn: return - fn = os.path.relpath(fn, - start=os.path.dirname(__import__(package).__file__)) + fn = os.path.relpath(fn, start=os.path.dirname(__import__(package).__file__)) try: lineno = inspect.getsourcelines(obj)[1] except Exception: - lineno = '' - return url_fmt.format(revision=revision, package=package, - path=fn, lineno=lineno) + lineno = "" + return url_fmt.format(revision=revision, package=package, path=fn, lineno=lineno) def make_linkcode_resolve(package, url_fmt): @@ -82,5 +80,6 @@ def make_linkcode_resolve(package, url_fmt): '{path}#L{lineno}') """ revision = _get_git_revision() - return partial(_linkcode_resolve, revision=revision, package=package, - url_fmt=url_fmt) + return partial( + _linkcode_resolve, revision=revision, package=package, url_fmt=url_fmt + ) diff --git a/doc/variogram_models.rst b/docs/source/variogram_models.rst similarity index 96% rename from doc/variogram_models.rst rename to docs/source/variogram_models.rst index 0e0a6f3..b811fab 100644 --- a/doc/variogram_models.rst +++ b/docs/source/variogram_models.rst @@ -73,7 +73,7 @@ anomalously richer than the surrounding area.) For nonstationary models (linear and power models, with unbounded spatial variances), the nugget has the same meaning. The exponent for the power-law -model should be between 0 and 2 [1]. +model should be between 0 and 2 [1]_. **A few important notes:** @@ -87,7 +87,7 @@ The exact definitions of the variogram models here may differ from those used elsewhere. Keep that in mind when switching from another kriging code over to PyKrige. -According to [1], the hole-effect variogram model is only correct for the +According to [1]_, the hole-effect variogram model is only correct for the 1D case. It's implemented here for completeness and should be used cautiously. References diff --git a/examples/gstools_covmodel.py b/examples/gstools_covmodel.py index ca47cab..6d1508b 100644 --- a/examples/gstools_covmodel.py +++ b/examples/gstools_covmodel.py @@ -5,37 +5,28 @@ Example how to use the PyKrige routines with a GSTools CovModel. """ -import os - import numpy as np from pykrige.ok import OrdinaryKriging from matplotlib import pyplot as plt -try: - from gstools import Gaussian - GS_IMP = True -except ImportError: - GS_IMP = False +import gstools as gs # conditioning data -data = np.array([[0.3, 1.2, 0.47], - [1.9, 0.6, 0.56], - [1.1, 3.2, 0.74], - [3.3, 4.4, 1.47], - [4.7, 3.8, 1.74]]) +data = np.array( + [ + [0.3, 1.2, 0.47], + [1.9, 0.6, 0.56], + [1.1, 3.2, 0.74], + [3.3, 4.4, 1.47], + [4.7, 3.8, 1.74], + ] +) # grid definition for output field gridx = np.arange(0.0, 5.5, 0.1) gridy = np.arange(0.0, 6.5, 0.1) # a GSTools based covariance model -if GS_IMP: - cov_model = Gaussian( - dim=2, len_scale=4, anis=.2, angles=-.5, var=.5, nugget=.1 - ) -else: - cov_model = "gaussian" +cov_model = gs.Gaussian(dim=2, len_scale=4, anis=0.2, angles=-0.5, var=0.5, nugget=0.1) # ordinary kriging with pykrige OK1 = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2], cov_model) -z1, ss1 = OK1.execute('grid', gridx, gridy) +z1, ss1 = OK1.execute("grid", gridx, gridy) plt.imshow(z1, origin="lower") -if 'CI' not in os.environ: - # skip in continous integration - plt.show() +plt.show() diff --git a/examples/krige_cv.py b/examples/krige_cv.py index c1cdde2..b39f02d 100644 --- a/examples/krige_cv.py +++ b/examples/krige_cv.py @@ -13,11 +13,12 @@ # 2D Kring param opt -param_dict = {"method": ["ordinary", "universal"], - "variogram_model": ["linear", "power", "gaussian", "spherical"], - # "nlags": [4, 6, 8], - # "weight": [True, False] - } +param_dict = { + "method": ["ordinary", "universal"], + "variogram_model": ["linear", "power", "gaussian", "spherical"], + # "nlags": [4, 6, 8], + # "weight": [True, False] +} estimator = GridSearchCV(Krige(), param_dict, verbose=True) @@ -29,23 +30,28 @@ estimator.fit(X=X, y=y) -if hasattr(estimator, 'best_score_'): - print('best_score R² = {:.3f}'.format(estimator.best_score_)) - print('best_params = ', estimator.best_params_) +if hasattr(estimator, "best_score_"): + print("best_score R² = {:.3f}".format(estimator.best_score_)) + print("best_params = ", estimator.best_params_) -print('\nCV results::') -if hasattr(estimator, 'cv_results_'): - for key in ['mean_test_score', 'mean_train_score', - 'param_method', 'param_variogram_model']: - print(' - {} : {}'.format(key, estimator.cv_results_[key])) +print("\nCV results::") +if hasattr(estimator, "cv_results_"): + for key in [ + "mean_test_score", + "mean_train_score", + "param_method", + "param_variogram_model", + ]: + print(" - {} : {}".format(key, estimator.cv_results_[key])) # 3D Kring param opt -param_dict3d = {"method": ["ordinary3d", "universal3d"], - "variogram_model": ["linear", "power", "gaussian", "spherical"], - # "nlags": [4, 6, 8], - # "weight": [True, False] - } +param_dict3d = { + "method": ["ordinary3d", "universal3d"], + "variogram_model": ["linear", "power", "gaussian", "spherical"], + # "nlags": [4, 6, 8], + # "weight": [True, False] +} estimator = GridSearchCV(Krige(), param_dict3d, verbose=True) @@ -57,12 +63,16 @@ estimator.fit(X=X3, y=y) -if hasattr(estimator, 'best_score_'): - print('best_score R² = {:.3f}'.format(estimator.best_score_)) - print('best_params = ', estimator.best_params_) +if hasattr(estimator, "best_score_"): + print("best_score R² = {:.3f}".format(estimator.best_score_)) + print("best_params = ", estimator.best_params_) -print('\nCV results::') -if hasattr(estimator, 'cv_results_'): - for key in ['mean_test_score', 'mean_train_score', - 'param_method', 'param_variogram_model']: - print(' - {} : {}'.format(key, estimator.cv_results_[key])) +print("\nCV results::") +if hasattr(estimator, "cv_results_"): + for key in [ + "mean_test_score", + "mean_train_score", + "param_method", + "param_variogram_model", + ]: + print(" - {} : {}".format(key, estimator.cv_results_[key])) diff --git a/examples/krige_geometric.py b/examples/krige_geometric.py index 847c6dc..41732b1 100644 --- a/examples/krige_geometric.py +++ b/examples/krige_geometric.py @@ -17,42 +17,50 @@ # of nodes and a uniform distribution of values in the interval # [2.0, 5.5]: N = 7 -lon = 360.0*np.random.random(N) -lat = 180.0/np.pi*np.arcsin(2*np.random.random(N)-1) -z = 3.5*np.random.rand(N) + 2.0 +lon = 360.0 * np.random.random(N) +lat = 180.0 / np.pi * np.arcsin(2 * np.random.random(N) - 1) +z = 3.5 * np.random.rand(N) + 2.0 # Generate a regular grid with 60° longitude and 30° latitude steps: grid_lon = np.linspace(0.0, 360.0, 7) grid_lat = np.linspace(-90.0, 90.0, 7) # Create ordinary kriging object: -OK = OrdinaryKriging(lon, lat, z, variogram_model='linear', verbose=False, - enable_plotting=False, coordinates_type='geographic') +OK = OrdinaryKriging( + lon, + lat, + z, + variogram_model="linear", + verbose=False, + enable_plotting=False, + coordinates_type="geographic", +) # Execute on grid: -z1, ss1 = OK.execute('grid', grid_lon, grid_lat) +z1, ss1 = OK.execute("grid", grid_lon, grid_lat) # Create ordinary kriging object ignoring curvature: -OK = OrdinaryKriging(lon, lat, z, variogram_model='linear', verbose=False, - enable_plotting=False) +OK = OrdinaryKriging( + lon, lat, z, variogram_model="linear", verbose=False, enable_plotting=False +) # Execute on grid: -z2, ss2 = OK.execute('grid', grid_lon, grid_lat) +z2, ss2 = OK.execute("grid", grid_lon, grid_lat) # Print data at equator (last longitude index will show periodicity): print("Original data:") -print("Longitude:",lon.astype(int)) -print("Latitude: ",lat.astype(int)) -print("z: ",np.array_str(z, precision=2)) +print("Longitude:", lon.astype(int)) +print("Latitude: ", lat.astype(int)) +print("z: ", np.array_str(z, precision=2)) print("\nKrige at 60° latitude:\n======================") -print("Longitude:",grid_lon) -print("Value: ",np.array_str(z1[5,:], precision=2)) -print("Sigma²: ",np.array_str(ss1[5,:], precision=2)) +print("Longitude:", grid_lon) +print("Value: ", np.array_str(z1[5, :], precision=2)) +print("Sigma²: ", np.array_str(ss1[5, :], precision=2)) print("\nIgnoring curvature:\n=====================") -print("Value: ",np.array_str(z2[5,:], precision=2)) -print("Sigma²: ",np.array_str(ss2[5,:], precision=2)) +print("Value: ", np.array_str(z2[5, :], precision=2)) +print("Sigma²: ", np.array_str(ss2[5, :], precision=2)) -##====================================OUTPUT================================== +# ====================================OUTPUT================================== # >>> Original data: # >>> Longitude: [122 166 92 138 86 122 136] # >>> Latitude: [-46 -36 -25 -73 -25 50 -29] diff --git a/examples/kriging_1D.py b/examples/kriging_1D.py index 06c2a9c..dc21d1a 100644 --- a/examples/kriging_1D.py +++ b/examples/kriging_1D.py @@ -4,16 +4,17 @@ An example of 1D kriging with PyKrige """ -import os - import numpy as np import matplotlib.pyplot as plt +from pykrige import OrdinaryKriging -plt.style.use('ggplot') - -# Data taken from https://blog.dominodatalab.com/fitting-gaussian-process-models-python/ +plt.style.use("ggplot") -X, y = np.array([[-5.01, 1.06], [-4.90, 0.92], [-4.82, 0.35], [-4.69, 0.49], [-4.56, 0.52], +# fmt: off +# Data taken from +# https://blog.dominodatalab.com/fitting-gaussian-process-models-python/ +X, y = np.array([ + [-5.01, 1.06], [-4.90, 0.92], [-4.82, 0.35], [-4.69, 0.49], [-4.56, 0.52], [-4.52, 0.12], [-4.39, 0.47], [-4.32,-0.19], [-4.19, 0.08], [-4.11,-0.19], [-4.00,-0.03], [-3.89,-0.03], [-3.78,-0.05], [-3.67, 0.10], [-3.59, 0.44], [-3.50, 0.66], [-3.39,-0.12], [-3.28, 0.45], [-3.20, 0.14], [-3.07,-0.28], @@ -33,33 +34,36 @@ [3.52 ,-0.55], [3.63 ,-0.92], [3.72 ,-0.76], [3.80 ,-0.41], [3.91 , 0.12], [4.04 , 0.25], [4.13 , 0.16], [4.24 , 0.26], [4.32 , 0.62], [4.44 , 1.69], [4.52 , 1.11], [4.65 , 0.36], [4.74 , 0.79], [4.84 , 0.87], [4.93 , 1.01], - [5.02 , 0.55]]).T - - -from pykrige import OrdinaryKriging + [5.02 , 0.55] +]).T +# fmt: on X_pred = np.linspace(-6, 6, 200) # pykrige doesn't support 1D data for now, only 2D or 3D # adapting the 1D input to 2D -uk = OrdinaryKriging(X, np.zeros(X.shape), y, variogram_model='gaussian',) +uk = OrdinaryKriging(X, np.zeros(X.shape), y, variogram_model="gaussian") -y_pred, y_std = uk.execute('grid', X_pred, np.array([0.])) +y_pred, y_std = uk.execute("grid", X_pred, np.array([0.0])) y_pred = np.squeeze(y_pred) y_std = np.squeeze(y_std) fig, ax = plt.subplots(1, 1, figsize=(10, 4)) -ax.scatter(X, y, s=40, label='Input data') +ax.scatter(X, y, s=40, label="Input data") -ax.plot(X_pred, y_pred, label='Predicted values') -ax.fill_between(X_pred, y_pred - 3*y_std, y_pred + 3*y_std, alpha=0.3, label='Confidence interval') +ax.plot(X_pred, y_pred, label="Predicted values") +ax.fill_between( + X_pred, + y_pred - 3 * y_std, + y_pred + 3 * y_std, + alpha=0.3, + label="Confidence interval", +) ax.legend(loc=9) -ax.set_xlabel('x') -ax.set_ylabel('y') +ax.set_xlabel("x") +ax.set_ylabel("y") ax.set_xlim(-6, 6) ax.set_ylim(-2.8, 3.5) -if 'CI' not in os.environ: - # skip in continous integration - plt.show() +plt.show() diff --git a/examples/regression_kriging2d.py b/examples/regression_kriging2d.py index 5f57f4d..46d25fd 100644 --- a/examples/regression_kriging2d.py +++ b/examples/regression_kriging2d.py @@ -27,22 +27,23 @@ sys.exit(0) # take the first 5000 as Kriging is memory intensive -p = housing['data'][:5000, :-2] -x = housing['data'][:5000, -2:] -target = housing['target'][:5000] +p = housing["data"][:5000, :-2] +x = housing["data"][:5000, -2:] +target = housing["target"][:5000] -p_train, p_test, x_train, x_test, target_train, target_test \ - = train_test_split(p, x, target, test_size=0.3, random_state=42) +p_train, p_test, x_train, x_test, target_train, target_test = train_test_split( + p, x, target, test_size=0.3, random_state=42 +) for m in models: - print('=' * 40) - print('regression model:', m.__class__.__name__) + print("=" * 40) + print("regression model:", m.__class__.__name__) m_rk = RegressionKriging(regression_model=m, n_closest_points=10) m_rk.fit(p_train, x_train, target_train) - print('Regression Score: ', m_rk.regression_model.score(p_test, target_test)) - print('RK score: ', m_rk.score(p_test, x_test, target_test)) + print("Regression Score: ", m_rk.regression_model.score(p_test, target_test)) + print("RK score: ", m_rk.score(p_test, x_test, target_test)) -##====================================OUTPUT================================== +# ====================================OUTPUT================================== # ======================================== # regression model: diff --git a/pykrige/__init__.py b/pykrige/__init__.py index 31c3265..6a5be84 100755 --- a/pykrige/__init__.py +++ b/pykrige/__init__.py @@ -1,6 +1,4 @@ -__author__ = 'Benjamin S. Murphy' -__version__ = '1.4.1' -__doc__ = """ +""" PyKrige ======= @@ -40,7 +38,7 @@ .. [1] P.K. Kitanidis, Introduction to Geostatistics: Applications in Hydrogeology, (Cambridge University Press, 1997) 272 p. -Copyright (c) 2015-2018, PyKrige Developers +Copyright (c) 2015-2020, PyKrige Developers """ from . import kriging_tools as kt # noqa @@ -49,4 +47,19 @@ from .ok3d import OrdinaryKriging3D # noqa from .uk3d import UniversalKriging3D # noqa -__all__ = ['ok', 'uk', 'ok3d', 'uk3d', 'kriging_tools'] +try: + from pykrige._version import __version__ +except ImportError: # pragma: nocover + # package is not installed + __version__ = "0.0.0.dev0" + + +__author__ = "Benjamin S. Murphy" + + +__all__ = ["__version__"] +__all__ += ["kt", "ok", "uk", "ok3d", "uk3d", "kriging_tools"] +__all__ += ["OrdinaryKriging"] +__all__ += ["UniversalKriging"] +__all__ += ["OrdinaryKriging3D"] +__all__ += ["UniversalKriging3D"] diff --git a/pykrige/compat.py b/pykrige/compat.py index 9a98aa8..4fbe3a8 100644 --- a/pykrige/compat.py +++ b/pykrige/compat.py @@ -1,31 +1,22 @@ # coding: utf-8 # pylint: disable= invalid-name, unused-import """For compatibility""" - -from __future__ import absolute_import -import sys import inspect from functools import partial -PY3 = (sys.version_info[0] == 3) - - # sklearn try: - try: # scikit-learn 1.18.+ - from sklearn.model_selection import GridSearchCV - from sklearn.model_selection import train_test_split - except ImportError: # older scikit-learn versions - from sklearn.grid_search import GridSearchCV - from sklearn.cross_validation import train_test_split + from sklearn.model_selection import GridSearchCV + from sklearn.model_selection import train_test_split SKLEARN_INSTALLED = True - if PY3: - arg_spec = inspect.getfullargspec(GridSearchCV)[0] - # https://stackoverflow.com/a/56618067/6696397 - if "return_train_score" in arg_spec: - GridSearchCV = partial(GridSearchCV, return_train_score=True) + arg_spec = inspect.getfullargspec(GridSearchCV)[0] + # https://stackoverflow.com/a/56618067/6696397 + if "return_train_score" in arg_spec: + GridSearchCV = partial(GridSearchCV, return_train_score=True) + if "iid" in arg_spec: + GridSearchCV = partial(GridSearchCV, iid=False) except ImportError: SKLEARN_INSTALLED = False @@ -37,5 +28,6 @@ class SklearnException(Exception): def validate_sklearn(): if not SKLEARN_INSTALLED: - raise SklearnException('sklearn needs to be installed in order ' - 'to use this module') + raise SklearnException( + "sklearn needs to be installed in order to use this module" + ) diff --git a/pykrige/core.py b/pykrige/core.py index cc4a661..781c55c 100755 --- a/pykrige/core.py +++ b/pykrige/core.py @@ -1,9 +1,5 @@ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -__doc__ = """ +# coding: utf-8 +""" PyKrige ======= @@ -23,14 +19,14 @@ with Application of Nested Equations, Survey Review 23 (176), (Directorate of Overseas Survey, Kingston Road, Tolworth, Surrey 1975) -Copyright (c) 2015-2018, PyKrige Developers +Copyright (c) 2015-2020, PyKrige Developers """ import numpy as np from scipy.spatial.distance import pdist, squareform, cdist from scipy.optimize import least_squares -eps = 1.e-10 # Cutoff for comparison to zero +eps = 1.0e-10 # Cutoff for comparison to zero def great_circle_distance(lon1, lat1, lon2, lat2): @@ -69,9 +65,9 @@ def great_circle_distance(lon1, lat1, lon2, lat2): """ # Convert to radians: - lat1 = np.array(lat1)*np.pi/180.0 - lat2 = np.array(lat2)*np.pi/180.0 - dlon = (lon1-lon2)*np.pi/180.0 + lat1 = np.array(lat1) * np.pi / 180.0 + lat2 = np.array(lat2) * np.pi / 180.0 + dlon = (lon1 - lon2) * np.pi / 180.0 # Evaluate trigonometric functions that need to be evaluated more # than once: @@ -87,7 +83,14 @@ def great_circle_distance(lon1, lat1, lon2, lat2): # Formula can be obtained from [2] combining eqns. (14)-(16) # for spherical geometry (f=0). - return 180.0 / np.pi * np.arctan2(np.sqrt((c2*np.sin(dlon))**2 + (c1*s2-s1*c2*cd)**2), s1*s2+c1*c2*cd) + return ( + 180.0 + / np.pi + * np.arctan2( + np.sqrt((c2 * np.sin(dlon)) ** 2 + (c1 * s2 - s1 * c2 * cd) ** 2), + s1 * s2 + c1 * c2 * cd, + ) + ) def euclid3_to_great_circle(euclid3_distance): @@ -106,8 +109,8 @@ def euclid3_to_great_circle(euclid3_distance): The corresponding great circle distance(s) between the points. """ # Eliminate some possible numerical errors: - euclid3_distance[euclid3_distance>2.0] = 2.0 - return 180.0 - 360.0/np.pi*np.arccos(0.5*euclid3_distance) + euclid3_distance[euclid3_distance > 2.0] = 2.0 + return 180.0 - 360.0 / np.pi * np.arccos(0.5 * euclid3_distance) def _adjust_for_anisotropy(X, center, scaling, angle): @@ -133,33 +136,52 @@ def _adjust_for_anisotropy(X, center, scaling, angle): """ center = np.asarray(center)[None, :] - angle = np.asarray(angle)*np.pi/180 + angle = np.asarray(angle) * np.pi / 180 X -= center Ndim = X.shape[1] if Ndim == 1: - raise NotImplementedError('Not implemnented yet?') + raise NotImplementedError("Not implemnented yet?") elif Ndim == 2: stretch = np.array([[1, 0], [0, scaling[0]]]) - rot_tot = np.array([[np.cos(-angle[0]), -np.sin(-angle[0])], - [np.sin(-angle[0]), np.cos(-angle[0])]]) + rot_tot = np.array( + [ + [np.cos(-angle[0]), -np.sin(-angle[0])], + [np.sin(-angle[0]), np.cos(-angle[0])], + ] + ) elif Ndim == 3: - stretch = np.array([[1., 0., 0.], [0., scaling[0], 0.], [0., 0., scaling[1]]]) - rotate_x = np.array([[1., 0., 0.], - [0., np.cos(-angle[0]), -np.sin(-angle[0])], - [0., np.sin(-angle[0]), np.cos(-angle[0])]]) - rotate_y = np.array([[np.cos(-angle[1]), 0., np.sin(-angle[1])], - [0., 1., 0.], - [-np.sin(-angle[1]), 0., np.cos(-angle[1])]]) - rotate_z = np.array([[np.cos(-angle[2]), -np.sin(-angle[2]), 0.], - [np.sin(-angle[2]), np.cos(-angle[2]), 0.], - [0., 0., 1.]]) + stretch = np.array( + [[1.0, 0.0, 0.0], [0.0, scaling[0], 0.0], [0.0, 0.0, scaling[1]]] + ) + rotate_x = np.array( + [ + [1.0, 0.0, 0.0], + [0.0, np.cos(-angle[0]), -np.sin(-angle[0])], + [0.0, np.sin(-angle[0]), np.cos(-angle[0])], + ] + ) + rotate_y = np.array( + [ + [np.cos(-angle[1]), 0.0, np.sin(-angle[1])], + [0.0, 1.0, 0.0], + [-np.sin(-angle[1]), 0.0, np.cos(-angle[1])], + ] + ) + rotate_z = np.array( + [ + [np.cos(-angle[2]), -np.sin(-angle[2]), 0.0], + [np.sin(-angle[2]), np.cos(-angle[2]), 0.0], + [0.0, 0.0, 1.0], + ] + ) rot_tot = np.dot(rotate_z, np.dot(rotate_y, rotate_x)) else: - raise ValueError("Adjust for anisotropy function doesn't " - "support ND spaces where N>3") + raise ValueError( + "Adjust for anisotropy function doesn't support ND spaces where N>3" + ) X_adj = np.dot(stretch, np.dot(rot_tot, X.T)).T X_adj += center @@ -210,140 +232,182 @@ def _make_variogram_parameter_list(variogram_model, variogram_model_parameters): elif type(variogram_model_parameters) is dict: - if variogram_model in ['linear']: + if variogram_model in ["linear"]: - if 'slope' not in variogram_model_parameters.keys() \ - or 'nugget' not in variogram_model_parameters.keys(): + if ( + "slope" not in variogram_model_parameters.keys() + or "nugget" not in variogram_model_parameters.keys() + ): - raise KeyError("'linear' variogram model requires 'slope' " - "and 'nugget' specified in variogram model " - "parameter dictionary.") + raise KeyError( + "'linear' variogram model requires 'slope' " + "and 'nugget' specified in variogram model " + "parameter dictionary." + ) else: - parameter_list = [variogram_model_parameters['slope'], - variogram_model_parameters['nugget']] + parameter_list = [ + variogram_model_parameters["slope"], + variogram_model_parameters["nugget"], + ] - elif variogram_model in ['power']: + elif variogram_model in ["power"]: - if 'scale' not in variogram_model_parameters.keys() \ - or 'exponent' not in variogram_model_parameters.keys() \ - or 'nugget' not in variogram_model_parameters.keys(): + if ( + "scale" not in variogram_model_parameters.keys() + or "exponent" not in variogram_model_parameters.keys() + or "nugget" not in variogram_model_parameters.keys() + ): - raise KeyError("'power' variogram model requires 'scale', " - "'exponent', and 'nugget' specified in " - "variogram model parameter dictionary.") + raise KeyError( + "'power' variogram model requires 'scale', " + "'exponent', and 'nugget' specified in " + "variogram model parameter dictionary." + ) else: - parameter_list = [variogram_model_parameters['scale'], - variogram_model_parameters['exponent'], - variogram_model_parameters['nugget']] + parameter_list = [ + variogram_model_parameters["scale"], + variogram_model_parameters["exponent"], + variogram_model_parameters["nugget"], + ] - elif variogram_model in ['gaussian', 'spherical', 'exponential', - 'hole-effect']: + elif variogram_model in ["gaussian", "spherical", "exponential", "hole-effect"]: - if 'range' not in variogram_model_parameters.keys() \ - or 'nugget' not in variogram_model_parameters.keys(): + if ( + "range" not in variogram_model_parameters.keys() + or "nugget" not in variogram_model_parameters.keys() + ): - raise KeyError("'%s' variogram model requires 'range', " - "'nugget', and either 'sill' or 'psill' " - "specified in variogram model parameter " - "dictionary." % variogram_model) + raise KeyError( + "'%s' variogram model requires 'range', " + "'nugget', and either 'sill' or 'psill' " + "specified in variogram model parameter " + "dictionary." % variogram_model + ) else: - if 'sill' in variogram_model_parameters.keys(): + if "sill" in variogram_model_parameters.keys(): - parameter_list = [variogram_model_parameters['sill'] - - variogram_model_parameters['nugget'], - variogram_model_parameters['range'], - variogram_model_parameters['nugget']] + parameter_list = [ + variogram_model_parameters["sill"] + - variogram_model_parameters["nugget"], + variogram_model_parameters["range"], + variogram_model_parameters["nugget"], + ] - elif 'psill' in variogram_model_parameters.keys(): + elif "psill" in variogram_model_parameters.keys(): - parameter_list = [variogram_model_parameters['psill'], - variogram_model_parameters['range'], - variogram_model_parameters['nugget']] + parameter_list = [ + variogram_model_parameters["psill"], + variogram_model_parameters["range"], + variogram_model_parameters["nugget"], + ] else: - raise KeyError("'%s' variogram model requires either " - "'sill' or 'psill' specified in " - "variogram model parameter " - "dictionary." % variogram_model) + raise KeyError( + "'%s' variogram model requires either " + "'sill' or 'psill' specified in " + "variogram model parameter " + "dictionary." % variogram_model + ) - elif variogram_model in ['custom']: + elif variogram_model in ["custom"]: - raise TypeError("For user-specified custom variogram model, " - "parameters must be specified in a list, " - "not a dict.") + raise TypeError( + "For user-specified custom variogram model, " + "parameters must be specified in a list, " + "not a dict." + ) else: - raise ValueError("Specified variogram model must be one of the " - "following: 'linear', 'power', 'gaussian', " - "'spherical', 'exponential', 'hole-effect', " - "'custom'.") + raise ValueError( + "Specified variogram model must be one of the " + "following: 'linear', 'power', 'gaussian', " + "'spherical', 'exponential', 'hole-effect', " + "'custom'." + ) elif type(variogram_model_parameters) is list: - if variogram_model in ['linear']: + if variogram_model in ["linear"]: if len(variogram_model_parameters) != 2: - raise ValueError("Variogram model parameter list must have " - "exactly two entries when variogram model " - "set to 'linear'.") + raise ValueError( + "Variogram model parameter list must have " + "exactly two entries when variogram model " + "set to 'linear'." + ) parameter_list = variogram_model_parameters - elif variogram_model in ['power']: + elif variogram_model in ["power"]: if len(variogram_model_parameters) != 3: - raise ValueError("Variogram model parameter list must have " - "exactly three entries when variogram model " - "set to 'power'.") + raise ValueError( + "Variogram model parameter list must have " + "exactly three entries when variogram model " + "set to 'power'." + ) parameter_list = variogram_model_parameters - elif variogram_model in ['gaussian', 'spherical', 'exponential', - 'hole-effect']: + elif variogram_model in ["gaussian", "spherical", "exponential", "hole-effect"]: if len(variogram_model_parameters) != 3: - raise ValueError("Variogram model parameter list must have " - "exactly three entries when variogram model " - "set to '%s'." % variogram_model) + raise ValueError( + "Variogram model parameter list must have " + "exactly three entries when variogram model " + "set to '%s'." % variogram_model + ) - parameter_list = [variogram_model_parameters[0] - - variogram_model_parameters[2], - variogram_model_parameters[1], - variogram_model_parameters[2]] + parameter_list = [ + variogram_model_parameters[0] - variogram_model_parameters[2], + variogram_model_parameters[1], + variogram_model_parameters[2], + ] - elif variogram_model in ['custom']: + elif variogram_model in ["custom"]: parameter_list = variogram_model_parameters else: - raise ValueError("Specified variogram model must be one of the " - "following: 'linear', 'power', 'gaussian', " - "'spherical', 'exponential', 'hole-effect', " - "'custom'.") + raise ValueError( + "Specified variogram model must be one of the " + "following: 'linear', 'power', 'gaussian', " + "'spherical', 'exponential', 'hole-effect', " + "'custom'." + ) else: - raise TypeError("Variogram model parameters must be provided in either " - "a list or a dict when they are explicitly specified.") + raise TypeError( + "Variogram model parameters must be provided in either " + "a list or a dict when they are explicitly specified." + ) return parameter_list -def _initialize_variogram_model(X, y, variogram_model, - variogram_model_parameters, variogram_function, - nlags, weight, coordinates_type): +def _initialize_variogram_model( + X, + y, + variogram_model, + variogram_model_parameters, + variogram_function, + nlags, + weight, + coordinates_type, +): """Initializes the variogram model for kriging. If user does not specify parameters, calls automatic variogram estimation routine. Returns lags, semivariance, and variogram model parameters. @@ -387,30 +451,32 @@ def _initialize_variogram_model(X, y, variogram_model, # scipy.spatial.distance's pdist function, which gives pairwise distances # in a condensed distance vector (distance matrix flattened to a vector) # to calculate semivariances... - if coordinates_type == 'euclidean': - d = pdist(X, metric='euclidean') - g = 0.5 * pdist(y[:, None], metric='sqeuclidean') + if coordinates_type == "euclidean": + d = pdist(X, metric="euclidean") + g = 0.5 * pdist(y[:, None], metric="sqeuclidean") # geographic coordinates only accepted if the problem is 2D # assume X[:, 0] ('x') => lon, X[:, 1] ('y') => lat # old method of distance calculation is retained here... # could be improved in the future - elif coordinates_type == 'geographic': + elif coordinates_type == "geographic": if X.shape[1] != 2: - raise ValueError('Geographic coordinate type only ' - 'supported for 2D datasets.') + raise ValueError( + "Geographic coordinate type only supported for 2D datasets." + ) x1, x2 = np.meshgrid(X[:, 0], X[:, 0], sparse=True) y1, y2 = np.meshgrid(X[:, 1], X[:, 1], sparse=True) z1, z2 = np.meshgrid(y, y, sparse=True) d = great_circle_distance(x1, y1, x2, y2) - g = 0.5 * (z1 - z2)**2. + g = 0.5 * (z1 - z2) ** 2.0 indices = np.indices(d.shape) d = d[(indices[0, :, :] > indices[1, :, :])] g = g[(indices[0, :, :] > indices[1, :, :])] else: - raise ValueError("Specified coordinate type '%s' " - "is not supported." % coordinates_type) + raise ValueError( + "Specified coordinate type '%s' is not supported." % coordinates_type + ) # Equal-sized bins are now implemented. The upper limit on the bins # is appended to the list (instead of calculated as part of the @@ -464,22 +530,29 @@ def _initialize_variogram_model(X, y, variogram_model, # are supplied, they have been supplied as expected... # if variogram_model_parameters was not defined, then estimate the variogram if variogram_model_parameters is not None: - if variogram_model == 'linear' and len(variogram_model_parameters) != 2: - raise ValueError("Exactly two parameters required " - "for linear variogram model.") - elif variogram_model in ['power', 'spherical', 'exponential', - 'gaussian', 'hole-effect'] \ - and len(variogram_model_parameters) != 3: - raise ValueError("Exactly three parameters required for " - "%s variogram model" % variogram_model) + if variogram_model == "linear" and len(variogram_model_parameters) != 2: + raise ValueError( + "Exactly two parameters required for linear variogram model." + ) + elif ( + variogram_model + in ["power", "spherical", "exponential", "gaussian", "hole-effect"] + and len(variogram_model_parameters) != 3 + ): + raise ValueError( + "Exactly three parameters required for " + "%s variogram model" % variogram_model + ) else: - if variogram_model == 'custom': - raise ValueError("Variogram parameters must be specified when " - "implementing custom variogram model.") + if variogram_model == "custom": + raise ValueError( + "Variogram parameters must be specified when " + "implementing custom variogram model." + ) else: - variogram_model_parameters = \ - _calculate_variogram_model(lags, semivariance, variogram_model, - variogram_function, weight) + variogram_model_parameters = _calculate_variogram_model( + lags, semivariance, variogram_model, variogram_function, weight + ) return lags, semivariance, variogram_model_parameters @@ -519,7 +592,7 @@ def _variogram_residuals(params, x, y, variogram_function, weight): drange = np.amax(x) - np.amin(x) k = 2.1972 / (0.1 * drange) x0 = 0.7 * drange + np.amin(x) - weights = 1. / (1. + np.exp(-k * (x0 - x))) + weights = 1.0 / (1.0 + np.exp(-k * (x0 - x))) weights /= np.sum(weights) resid = (variogram_function(params, x) - y) * weights else: @@ -528,8 +601,9 @@ def _variogram_residuals(params, x, y, variogram_function, weight): return resid -def _calculate_variogram_model(lags, semivariance, variogram_model, - variogram_function, weight): +def _calculate_variogram_model( + lags, semivariance, variogram_model, variogram_function, weight +): """Function that fits a variogram model when parameters are not specified. Returns variogram model parameters that minimize the RMSE between the specified variogram function and the actual calculated variogram points. @@ -560,30 +634,48 @@ def _calculate_variogram_model(lags, semivariance, variogram_model, the sill will always be greater than the nugget... """ - if variogram_model == 'linear': - x0 = [(np.amax(semivariance) - np.amin(semivariance)) / - (np.amax(lags) - np.amin(lags)), np.amin(semivariance)] - bnds = ([0., 0.], [np.inf, np.amax(semivariance)]) - elif variogram_model == 'power': - x0 = [(np.amax(semivariance) - np.amin(semivariance)) / - (np.amax(lags) - np.amin(lags)), 1.1, np.amin(semivariance)] - bnds = ([0., 0.001, 0.], [np.inf, 1.999, np.amax(semivariance)]) + if variogram_model == "linear": + x0 = [ + (np.amax(semivariance) - np.amin(semivariance)) + / (np.amax(lags) - np.amin(lags)), + np.amin(semivariance), + ] + bnds = ([0.0, 0.0], [np.inf, np.amax(semivariance)]) + elif variogram_model == "power": + x0 = [ + (np.amax(semivariance) - np.amin(semivariance)) + / (np.amax(lags) - np.amin(lags)), + 1.1, + np.amin(semivariance), + ] + bnds = ([0.0, 0.001, 0.0], [np.inf, 1.999, np.amax(semivariance)]) else: - x0 = [np.amax(semivariance) - np.amin(semivariance), - 0.25*np.amax(lags), np.amin(semivariance)] - bnds = ([0., 0., 0.], [10.*np.amax(semivariance), np.amax(lags), - np.amax(semivariance)]) + x0 = [ + np.amax(semivariance) - np.amin(semivariance), + 0.25 * np.amax(lags), + np.amin(semivariance), + ] + bnds = ( + [0.0, 0.0, 0.0], + [10.0 * np.amax(semivariance), np.amax(lags), np.amax(semivariance)], + ) # use 'soft' L1-norm minimization in order to buffer against # potential outliers (weird/skewed points) - res = least_squares(_variogram_residuals, x0, bounds=bnds, loss='soft_l1', - args=(lags, semivariance, variogram_function, weight)) + res = least_squares( + _variogram_residuals, + x0, + bounds=bnds, + loss="soft_l1", + args=(lags, semivariance, variogram_function, weight), + ) return res.x -def _krige(X, y, coords, variogram_function, - variogram_model_parameters, coordinates_type): +def _krige( + X, y, coords, variogram_function, variogram_model_parameters, coordinates_type +): """Sets up and solves the ordinary kriging system for the given coordinate pair. This function is only used for the statistics calculations. @@ -617,25 +709,29 @@ def _krige(X, y, coords, variogram_function, # calculate distance between points... need a square distance matrix # of inter-measurement-point distances and a vector of distances between # measurement points (X) and the kriging point (coords) - if coordinates_type == 'euclidean': - d = squareform(pdist(X, metric='euclidean')) - bd = np.squeeze(cdist(X, coords[None, :], metric='euclidean')) + if coordinates_type == "euclidean": + d = squareform(pdist(X, metric="euclidean")) + bd = np.squeeze(cdist(X, coords[None, :], metric="euclidean")) # geographic coordinate distances still calculated in the old way... # assume X[:, 0] ('x') => lon, X[:, 1] ('y') => lat # also assume problem is 2D; check done earlier in initializing variogram - elif coordinates_type == 'geographic': + elif coordinates_type == "geographic": x1, x2 = np.meshgrid(X[:, 0], X[:, 0], sparse=True) y1, y2 = np.meshgrid(X[:, 1], X[:, 1], sparse=True) d = great_circle_distance(x1, y1, x2, y2) - bd = great_circle_distance(X[:, 0], X[:, 1], - coords[0] * np.ones(X.shape[0]), - coords[1] * np.ones(X.shape[0])) + bd = great_circle_distance( + X[:, 0], + X[:, 1], + coords[0] * np.ones(X.shape[0]), + coords[1] * np.ones(X.shape[0]), + ) # this check is done when initializing variogram, but kept here anyways... else: - raise ValueError("Specified coordinate type '%s' " - "is not supported." % coordinates_type) + raise ValueError( + "Specified coordinate type '%s' is not supported." % coordinates_type + ) # check if kriging point overlaps with measurement point if np.any(np.absolute(bd) <= 1e-10): @@ -644,16 +740,16 @@ def _krige(X, y, coords, variogram_function, # set up kriging matrix n = X.shape[0] - a = np.zeros((n+1, n+1)) - a[:n, :n] = - variogram_function(variogram_model_parameters, d) + a = np.zeros((n + 1, n + 1)) + a[:n, :n] = -variogram_function(variogram_model_parameters, d) np.fill_diagonal(a, 0.0) a[n, :] = 1.0 a[:, n] = 1.0 a[n, n] = 0.0 # set up RHS - b = np.zeros((n+1, 1)) - b[:n, 0] = - variogram_function(variogram_model_parameters, bd) + b = np.zeros((n + 1, 1)) + b[:n, 0] = -variogram_function(variogram_model_parameters, bd) if zero_value: b[zero_index, 0] = 0.0 b[n, 0] = 1.0 @@ -666,8 +762,9 @@ def _krige(X, y, coords, variogram_function, return zinterp, sigmasq -def _find_statistics(X, y, variogram_function, - variogram_model_parameters, coordinates_type): +def _find_statistics( + X, y, variogram_function, variogram_model_parameters, coordinates_type +): """Calculates variogram fit statistics. Returns the delta, sigma, and epsilon values for the variogram fit. These arrays are used for statistics calculations. @@ -706,8 +803,14 @@ def _find_statistics(X, y, variogram_function, continue else: - k, ss = _krige(X[:i, :], y[:i], X[i, :], variogram_function, - variogram_model_parameters, coordinates_type) + k, ss = _krige( + X[:i, :], + y[:i], + X[i, :], + variogram_function, + variogram_model_parameters, + coordinates_type, + ) # if the estimation error is zero, it's probably because # the evaluation point X[i, :] is really close to one of the @@ -724,21 +827,21 @@ def _find_statistics(X, y, variogram_function, # whereas delta can be either positive or negative delta = delta[sigma > eps] sigma = sigma[sigma > eps] - epsilon = delta/sigma + epsilon = delta / sigma return delta, sigma, epsilon def calcQ1(epsilon): """Returns the Q1 statistic for the variogram fit (see [1]).""" - return abs(np.sum(epsilon)/(epsilon.shape[0] - 1)) + return abs(np.sum(epsilon) / (epsilon.shape[0] - 1)) def calcQ2(epsilon): """Returns the Q2 statistic for the variogram fit (see [1]).""" - return np.sum(epsilon**2)/(epsilon.shape[0] - 1) + return np.sum(epsilon ** 2) / (epsilon.shape[0] - 1) def calc_cR(Q2, sigma): """Returns the cR statistic for the variogram fit (see [1]).""" - return Q2 * np.exp(np.sum(np.log(sigma**2))/sigma.shape[0]) + return Q2 * np.exp(np.sum(np.log(sigma ** 2)) / sigma.shape[0]) diff --git a/pykrige/kriging_tools.py b/pykrige/kriging_tools.py index a34ea31..8dd4a13 100755 --- a/pykrige/kriging_tools.py +++ b/pykrige/kriging_tools.py @@ -1,9 +1,5 @@ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -__doc__ = """ +# coding: utf-8 +""" PyKrige ======= @@ -14,17 +10,17 @@ ------- Methods for reading/writing ASCII grid files. -Copyright (c) 2015-2018, PyKrige Developers +Copyright (c) 2015-2020, PyKrige Developers """ - import numpy as np import warnings import io -def write_asc_grid(x, y, z, filename='output.asc', style=1): - """Writes gridded data to ASCII grid file (*.asc). This is useful for - exporting data to a GIS program. +def write_asc_grid(x, y, z, filename="output.asc", style=1): + r"""Writes gridded data to ASCII grid file (\*.asc). + + This is useful for exporting data to a GIS program. Parameters ---------- @@ -35,16 +31,16 @@ def write_asc_grid(x, y, z, filename='output.asc', style=1): z : array_like, shape (M, N) Gridded data values. May be a masked array. filename : string, optional - Name of output *.asc file. Default name is 'output.asc'. + Name of output \*.asc file. Default name is 'output.asc'. style : int, optional - Determines how to write the *.asc file header. + Determines how to write the \*.asc file header. Specifying 1 writes out DX, DY, XLLCENTER, YLLCENTER. Specifying 2 writes out CELLSIZE (note DX must be the same as DY), XLLCORNER, YLLCORNER. Default is 1. """ if np.ma.is_masked(z): - z = np.array(z.tolist(-999.)) + z = np.array(z.tolist(-999.0)) x = np.squeeze(np.array(x)) y = np.squeeze(np.array(y)) @@ -53,31 +49,41 @@ def write_asc_grid(x, y, z, filename='output.asc', style=1): ncols = z.shape[1] if z.ndim != 2: - raise ValueError("Two-dimensional grid is required to " - "write *.asc grid.") + raise ValueError("Two-dimensional grid is required to write *.asc grid.") if x.ndim > 1 or y.ndim > 1: - raise ValueError("Dimensions of X and/or Y coordinate arrays are not " - "as expected. Could not write *.asc grid.") + raise ValueError( + "Dimensions of X and/or Y coordinate arrays are not " + "as expected. Could not write *.asc grid." + ) if z.shape != (y.size, x.size): - warnings.warn("Grid dimensions are not as expected. " - "Incorrect *.asc file generation may result.", - RuntimeWarning) + warnings.warn( + "Grid dimensions are not as expected. " + "Incorrect *.asc file generation may result.", + RuntimeWarning, + ) if np.amin(x) != x[0] or np.amin(y) != y[0]: - warnings.warn("Order of X or Y coordinates is not as expected. " - "Incorrect *.asc file generation may result.", - RuntimeWarning) + warnings.warn( + "Order of X or Y coordinates is not as expected. " + "Incorrect *.asc file generation may result.", + RuntimeWarning, + ) dx = abs(x[1] - x[0]) dy = abs(y[1] - y[0]) - if abs((x[-1] - x[0])/(x.shape[0] - 1)) != dx or \ - abs((y[-1] - y[0])/(y.shape[0] - 1)) != dy: - raise ValueError("X or Y spacing is not constant; *.asc " - "grid cannot be written.") + if ( + abs((x[-1] - x[0]) / (x.shape[0] - 1)) != dx + or abs((y[-1] - y[0]) / (y.shape[0] - 1)) != dy + ): + raise ValueError( + "X or Y spacing is not constant; *.asc grid cannot be written." + ) cellsize = -1 if style == 2: if dx != dy: - raise ValueError("X and Y spacing is not the same. " - "Cannot write *.asc file in the specified format.") + raise ValueError( + "X and Y spacing is not the same. " + "Cannot write *.asc file in the specified format." + ) cellsize = dx xllcenter = x[0] @@ -88,46 +94,46 @@ def write_asc_grid(x, y, z, filename='output.asc', style=1): xllcorner = -1 yllcorner = -1 if style == 2: - xllcorner = xllcenter - dx/2.0 - yllcorner = yllcenter - dy/2.0 + xllcorner = xllcenter - dx / 2.0 + yllcorner = yllcenter - dy / 2.0 - no_data = -999. + no_data = -999.0 - with io.open(filename, 'w') as f: + with io.open(filename, "w") as f: if style == 1: - f.write("NCOLS " + '{:<10n}'.format(ncols) + '\n') - f.write("NROWS " + '{:<10n}'.format(nrows) + '\n') - f.write("XLLCENTER " + '{:<10.2f}'.format(xllcenter) + '\n') - f.write("YLLCENTER " + '{:<10.2f}'.format(yllcenter) + '\n') - f.write("DX " + '{:<10.2f}'.format(dx) + '\n') - f.write("DY " + '{:<10.2f}'.format(dy) + '\n') - f.write("NODATA_VALUE " + '{:<10.2f}'.format(no_data) + '\n') + f.write("NCOLS " + "{:<10n}".format(ncols) + "\n") + f.write("NROWS " + "{:<10n}".format(nrows) + "\n") + f.write("XLLCENTER " + "{:<10.2f}".format(xllcenter) + "\n") + f.write("YLLCENTER " + "{:<10.2f}".format(yllcenter) + "\n") + f.write("DX " + "{:<10.2f}".format(dx) + "\n") + f.write("DY " + "{:<10.2f}".format(dy) + "\n") + f.write("NODATA_VALUE " + "{:<10.2f}".format(no_data) + "\n") elif style == 2: - f.write("NCOLS " + '{:<10n}'.format(ncols) + '\n') - f.write("NROWS " + '{:<10n}'.format(nrows) + '\n') - f.write("XLLCORNER " + '{:<10.2f}'.format(xllcorner) + '\n') - f.write("YLLCORNER " + '{:<10.2f}'.format(yllcorner) + '\n') - f.write("CELLSIZE " + '{:<10.2f}'.format(cellsize) + '\n') - f.write("NODATA_VALUE " + '{:<10.2f}'.format(no_data) + '\n') + f.write("NCOLS " + "{:<10n}".format(ncols) + "\n") + f.write("NROWS " + "{:<10n}".format(nrows) + "\n") + f.write("XLLCORNER " + "{:<10.2f}".format(xllcorner) + "\n") + f.write("YLLCORNER " + "{:<10.2f}".format(yllcorner) + "\n") + f.write("CELLSIZE " + "{:<10.2f}".format(cellsize) + "\n") + f.write("NODATA_VALUE " + "{:<10.2f}".format(no_data) + "\n") else: raise ValueError("style kwarg must be either 1 or 2.") for m in range(z.shape[0] - 1, -1, -1): for n in range(z.shape[1]): - f.write('{:<16.2f}'.format(z[m, n])) + f.write("{:<16.2f}".format(z[m, n])) if m != 0: - f.write('\n') + f.write("\n") def read_asc_grid(filename, footer=0): - """Reads ASCII grid file (*.asc). + r"""Reads ASCII grid file (\*.asc). Parameters ---------- filename : str - Name of *.asc file. + Name of \*.asc file. footer : int, optional - Number of lines at bottom of *.asc file to skip. + Number of lines at bottom of \*.asc file to skip. Returns ------- @@ -158,68 +164,75 @@ def read_asc_grid(filename, footer=0): dy = None no_data = None header_lines = 0 - with io.open(filename, 'r') as f: + with io.open(filename, "r") as f: while True: string, value = f.readline().split() header_lines += 1 - if string.lower() == 'ncols': + if string.lower() == "ncols": ncols = int(value) - elif string.lower() == 'nrows': + elif string.lower() == "nrows": nrows = int(value) - elif string.lower() == 'xllcorner': + elif string.lower() == "xllcorner": xllcorner = float(value) - elif string.lower() == 'xllcenter': + elif string.lower() == "xllcenter": xllcenter = float(value) - elif string.lower() == 'yllcorner': + elif string.lower() == "yllcorner": yllcorner = float(value) - elif string.lower() == 'yllcenter': + elif string.lower() == "yllcenter": yllcenter = float(value) - elif string.lower() == 'cellsize': + elif string.lower() == "cellsize": cellsize = float(value) - elif string.lower() == 'cell_size': + elif string.lower() == "cell_size": cellsize = float(value) - elif string.lower() == 'dx': + elif string.lower() == "dx": dx = float(value) - elif string.lower() == 'dy': + elif string.lower() == "dy": dy = float(value) - elif string.lower() == 'nodata_value': + elif string.lower() == "nodata_value": no_data = float(value) - elif string.lower() == 'nodatavalue': + elif string.lower() == "nodatavalue": no_data = float(value) else: raise IOError("could not read *.asc file. Error in header.") - if (ncols is not None) and \ - (nrows is not None) and \ - (((xllcorner is not None) and (yllcorner is not None)) or - ((xllcenter is not None) and (yllcenter is not None))) and \ - ((cellsize is not None) or ((dx is not None) and (dy is not None))) and \ - (no_data is not None): + if ( + (ncols is not None) + and (nrows is not None) + and ( + ((xllcorner is not None) and (yllcorner is not None)) + or ((xllcenter is not None) and (yllcenter is not None)) + ) + and ((cellsize is not None) or ((dx is not None) and (dy is not None))) + and (no_data is not None) + ): break - raw_grid_array = np.genfromtxt(filename, skip_header=header_lines, - skip_footer=footer) + raw_grid_array = np.genfromtxt( + filename, skip_header=header_lines, skip_footer=footer + ) grid_array = np.flipud(raw_grid_array) if nrows != grid_array.shape[0] or ncols != grid_array.shape[1]: - raise IOError("Error reading *.asc file. Encountered problem " - "with header: NCOLS and/or NROWS does not match " - "number of columns/rows in data file body.") + raise IOError( + "Error reading *.asc file. Encountered problem " + "with header: NCOLS and/or NROWS does not match " + "number of columns/rows in data file body." + ) if xllcorner is not None and yllcorner is not None: if dx is not None and dy is not None: - xllcenter = xllcorner + dx/2.0 - yllcenter = yllcorner + dy/2.0 + xllcenter = xllcorner + dx / 2.0 + yllcenter = yllcorner + dy / 2.0 else: - xllcenter = xllcorner + cellsize/2.0 - yllcenter = yllcorner + cellsize/2.0 + xllcenter = xllcorner + cellsize / 2.0 + yllcenter = yllcorner + cellsize / 2.0 if dx is not None and dy is not None: - x = np.arange(xllcenter, xllcenter + ncols*dx, dx) - y = np.arange(yllcenter, yllcenter + nrows*dy, dy) + x = np.arange(xllcenter, xllcenter + ncols * dx, dx) + y = np.arange(yllcenter, yllcenter + nrows * dy, dy) else: - x = np.arange(xllcenter, xllcenter + ncols*cellsize, cellsize) - y = np.arange(yllcenter, yllcenter + nrows*cellsize, cellsize) + x = np.arange(xllcenter, xllcenter + ncols * cellsize, cellsize) + y = np.arange(yllcenter, yllcenter + nrows * cellsize, cellsize) # Sometimes x and y and can be an entry too long due to imprecision # in calculating the upper cutoff for np.arange(); this bit takes care of diff --git a/pykrige/lib/__init__.py b/pykrige/lib/__init__.py index 34610af..3da76c4 100644 --- a/pykrige/lib/__init__.py +++ b/pykrige/lib/__init__.py @@ -1 +1 @@ -__all__ = ['cok', 'lapack', 'variogram_models'] +__all__ = ["cok", "lapack", "variogram_models"] diff --git a/pykrige/lib/cok.pyx b/pykrige/lib/cok.pyx index c936773..be15dbe 100644 --- a/pykrige/lib/cok.pyx +++ b/pykrige/lib/cok.pyx @@ -1,13 +1,11 @@ -# cython: profile=False -# cython: boundscheck=False -# cython: wraparound=False -# cython: cdivision=True +#cython: language_level=3, boundscheck=False, wraparound=False, cdivision=True +# -*- coding: utf-8 -*- import numpy as np cimport numpy as np -import scipy.linalg.blas from libc.math cimport sqrt import scipy.linalg -from .lapack cimport dgemv, dgesv +from scipy.linalg.cython_blas cimport dgemv +from scipy.linalg.cython_lapack cimport dgesv from .variogram_models cimport get_variogram_model diff --git a/pykrige/lib/lapack.pxd b/pykrige/lib/lapack.pxd deleted file mode 100644 index 2d048a0..0000000 --- a/pykrige/lib/lapack.pxd +++ /dev/null @@ -1,32 +0,0 @@ -cimport numpy as np - -ctypedef int dgemv_t( - # Compute y := alpha*A*x + beta*y - char *trans, # {'T','C'}: o(A)=A'; {'N'}: o(A)=A - int *m, # Rows of A (prior to transpose from *trans) - int *n, # Columns of A / min(len(x)) - np.float64_t *alpha, # Scalar multiple - np.float64_t *a, # Matrix A: mxn - int *lda, # The size of the first dimension of A (in memory) - np.float64_t *x, # Vector x, min(len(x)) = n - int *incx, # The increment between elements of x (usually 1) - np.float64_t *beta, # Scalar multiple - np.float64_t *y, # Vector y, min(len(y)) = m - int *incy # The increment between elements of y (usually 1) - ) - -ctypedef int dgesv_t( - # Solve A*x=b - int *n, # The number of linear equations, - int *nrhs, # The number of right hand sides, i.e., the number of columns of the matrix B. NRHS >= 0. - np.float64_t *a, # Matrix A: mxn - int *lda, # The size of the first dimension of A (in memory) - int *ipiv, # Integer array - np.float64_t *b, # Vector b - int *ldb, # The size of the first dimension of A (in memory) - int *info, # The size of the first dimension of A (in memory) - ) - -cdef dgemv_t *dgemv - -cdef dgesv_t *dgesv diff --git a/pykrige/lib/lapack.pyx b/pykrige/lib/lapack.pyx deleted file mode 100644 index 453aa6b..0000000 --- a/pykrige/lib/lapack.pyx +++ /dev/null @@ -1,20 +0,0 @@ -# cython: profile=False -# cython: boundscheck=False -# cython: wraparound=False -# cython: cdivision=True -import numpy as np -cimport numpy as np -import scipy.linalg.blas -import scipy.linalg.lapack -from cpython.cobject cimport PyCObject_AsVoidPtr # only works with python 2, use capsules for python 3 - -np.import_array() - - -# interface to BLAS matrix-vector multipilcation though scipy.linalg -# adapted from -# https://github.com/statsmodels/statsmodels/blob/master/statsmodels/tsa/kalmanf/kalman_loglike.pyx - - -cdef dgemv_t *dgemv = PyCObject_AsVoidPtr(scipy.linalg.blas.get_blas_funcs('gemv', dtype='float64')._cpointer) -cdef dgesv_t *dgesv = PyCObject_AsVoidPtr(scipy.linalg.lapack.get_lapack_funcs('gesv', dtype='float64')._cpointer) diff --git a/pykrige/lib/lapack_py3.pxd b/pykrige/lib/lapack_py3.pxd deleted file mode 100644 index 2d048a0..0000000 --- a/pykrige/lib/lapack_py3.pxd +++ /dev/null @@ -1,32 +0,0 @@ -cimport numpy as np - -ctypedef int dgemv_t( - # Compute y := alpha*A*x + beta*y - char *trans, # {'T','C'}: o(A)=A'; {'N'}: o(A)=A - int *m, # Rows of A (prior to transpose from *trans) - int *n, # Columns of A / min(len(x)) - np.float64_t *alpha, # Scalar multiple - np.float64_t *a, # Matrix A: mxn - int *lda, # The size of the first dimension of A (in memory) - np.float64_t *x, # Vector x, min(len(x)) = n - int *incx, # The increment between elements of x (usually 1) - np.float64_t *beta, # Scalar multiple - np.float64_t *y, # Vector y, min(len(y)) = m - int *incy # The increment between elements of y (usually 1) - ) - -ctypedef int dgesv_t( - # Solve A*x=b - int *n, # The number of linear equations, - int *nrhs, # The number of right hand sides, i.e., the number of columns of the matrix B. NRHS >= 0. - np.float64_t *a, # Matrix A: mxn - int *lda, # The size of the first dimension of A (in memory) - int *ipiv, # Integer array - np.float64_t *b, # Vector b - int *ldb, # The size of the first dimension of A (in memory) - int *info, # The size of the first dimension of A (in memory) - ) - -cdef dgemv_t *dgemv - -cdef dgesv_t *dgesv diff --git a/pykrige/lib/lapack_py3.pyx b/pykrige/lib/lapack_py3.pyx deleted file mode 100644 index f9a863f..0000000 --- a/pykrige/lib/lapack_py3.pyx +++ /dev/null @@ -1,33 +0,0 @@ -# cython: profile=False -# cython: boundscheck=False -# cython: wraparound=False -# cython: cdivision=True -import numpy as np -cimport numpy as np -import scipy.linalg.blas -import scipy.linalg.lapack -#from cpython.cobject cimport PyCObject_AsVoidPtr # only works with python 2, use capsules for python 3 -from cpython.pycapsule cimport PyCapsule_GetPointer - -cdef extern from "Python.h": - void PyErr_Clear() - - -np.import_array() - - -# Snippet adapted from -# https://github.com/statsmodels/statsmodels/blob/master/statsmodels/src/capsule.h -cdef void* Capsule_AsVoidPtr(object obj): - cdef void* ret = PyCapsule_GetPointer(obj, NULL); - if (ret == NULL): - PyErr_Clear() - return ret - - -# interface to BLAS matrix-vector multipilcation though scipy.linalg -# adapted from -# https://github.com/statsmodels/statsmodels/blob/master/statsmodels/tsa/kalmanf/kalman_loglike.pyx - -cdef dgemv_t *dgemv = Capsule_AsVoidPtr(scipy.linalg.blas.get_blas_funcs('gemv', dtype='float64')._cpointer) -cdef dgesv_t *dgesv = Capsule_AsVoidPtr(scipy.linalg.lapack.get_lapack_funcs('gesv', dtype='float64')._cpointer) diff --git a/pykrige/lib/variogram_models.pyx b/pykrige/lib/variogram_models.pyx index 44f5000..e45a469 100644 --- a/pykrige/lib/variogram_models.pyx +++ b/pykrige/lib/variogram_models.pyx @@ -1,7 +1,5 @@ -# cython: profile=False -# cython: boundscheck=False -# cython: wraparound=False -# cython: cdivision=True +#cython: language_level=3, boundscheck=False, wraparound=False, cdivision=True +# -*- coding: utf-8 -*- import numpy as np cimport numpy as np from libc.math cimport exp diff --git a/pykrige/ok.py b/pykrige/ok.py index b4a7f69..6935ac3 100644 --- a/pykrige/ok.py +++ b/pykrige/ok.py @@ -1,9 +1,5 @@ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -__doc__ = """ +# coding: utf-8 +""" PyKrige ======= @@ -20,22 +16,25 @@ .. [1] P.K. Kitanidis, Introduction to Geostatistcs: Applications in Hydrogeology, (Cambridge University Press, 1997) 272 p. -Copyright (c) 2015-2018, PyKrige Developers +Copyright (c) 2015-2020, PyKrige Developers """ import numpy as np import scipy.linalg from scipy.spatial.distance import cdist -import matplotlib.pyplot as plt from . import variogram_models from . import core -from .core import _adjust_for_anisotropy, _initialize_variogram_model, \ - _make_variogram_parameter_list, _find_statistics +from .core import ( + _adjust_for_anisotropy, + _initialize_variogram_model, + _make_variogram_parameter_list, + _find_statistics, +) import warnings class OrdinaryKriging: - """Convenience class for easy access to 2D Ordinary Kriging. + r"""Convenience class for easy access to 2D Ordinary Kriging. Parameters ---------- @@ -60,30 +59,28 @@ class OrdinaryKriging: minimization scheme. For variogram model parameters provided in a dict, the required dict keys vary according to the specified variogram model: :: - linear - {'slope': slope, 'nugget': nugget} - power - {'scale': scale, 'exponent': exponent, 'nugget': nugget} - gaussian - {'sill': s, 'range': r, 'nugget': n} - OR - {'psill': p, 'range': r, 'nugget':n} - spherical - {'sill': s, 'range': r, 'nugget': n} - OR - {'psill': p, 'range': r, 'nugget':n} - exponential - {'sill': s, 'range': r, 'nugget': n} - OR - {'psill': p, 'range': r, 'nugget':n} - hole-effect - {'sill': s, 'range': r, 'nugget': n} - OR - {'psill': p, 'range': r, 'nugget':n} + + # linear + {'slope': slope, 'nugget': nugget} + # power + {'scale': scale, 'exponent': exponent, 'nugget': nugget} + # gaussian, spherical, exponential and hole-effect: + {'sill': s, 'range': r, 'nugget': n} + # OR + {'psill': p, 'range': r, 'nugget': n} + Note that either the full sill or the partial sill (psill = sill - nugget) can be specified in the dict. For variogram model parameters provided in a list, the entries must be as follows: :: - linear - [slope, nugget] - power - [scale, exponent, nugget] - gaussian - [sill, range, nugget] - spherical - [sill, range, nugget] - exponential - [sill, range, nugget] - hole-effect - [sill, range, nugget] + + # linear + [slope, nugget] + # power + [scale, exponent, nugget] + # gaussian, spherical, exponential and hole-effect: + [sill, range, nugget] + Note that the full sill (NOT the partial sill) must be specified in the list format. For a custom variogram model, the parameters are required, as custom @@ -147,22 +144,36 @@ class OrdinaryKriging: References ---------- .. [1] P.K. Kitanidis, Introduction to Geostatistcs: Applications in - Hydrogeology, (Cambridge University Press, 1997) 272 p. + Hydrogeology, (Cambridge University Press, 1997) 272 p. """ - eps = 1.e-10 # Cutoff for comparison to zero - variogram_dict = {'linear': variogram_models.linear_variogram_model, - 'power': variogram_models.power_variogram_model, - 'gaussian': variogram_models.gaussian_variogram_model, - 'spherical': variogram_models.spherical_variogram_model, - 'exponential': variogram_models.exponential_variogram_model, - 'hole-effect': variogram_models.hole_effect_variogram_model} - - def __init__(self, x, y, z, variogram_model='linear', - variogram_parameters=None, variogram_function=None, nlags=6, - weight=False, anisotropy_scaling=1.0, anisotropy_angle=0.0, - verbose=False, enable_plotting=False, enable_statistics=False, - coordinates_type='euclidean'): + eps = 1.0e-10 # Cutoff for comparison to zero + variogram_dict = { + "linear": variogram_models.linear_variogram_model, + "power": variogram_models.power_variogram_model, + "gaussian": variogram_models.gaussian_variogram_model, + "spherical": variogram_models.spherical_variogram_model, + "exponential": variogram_models.exponential_variogram_model, + "hole-effect": variogram_models.hole_effect_variogram_model, + } + + def __init__( + self, + x, + y, + z, + variogram_model="linear", + variogram_parameters=None, + variogram_function=None, + nlags=6, + weight=False, + anisotropy_scaling=1.0, + anisotropy_angle=0.0, + verbose=False, + enable_plotting=False, + enable_statistics=False, + coordinates_type="euclidean", + ): # set up variogram model and parameters... self.variogram_model = variogram_model @@ -178,14 +189,18 @@ def __init__(self, x, y, z, variogram_model='linear', variogram_parameters = [] anisotropy_scaling = self.model.pykrige_anis anisotropy_angle = self.model.pykrige_angle - if self.variogram_model not in self.variogram_dict.keys() and \ - self.variogram_model != 'custom': - raise ValueError("Specified variogram model '%s' " - "is not supported." % variogram_model) - elif self.variogram_model == 'custom': + if ( + self.variogram_model not in self.variogram_dict.keys() + and self.variogram_model != "custom" + ): + raise ValueError( + "Specified variogram model '%s' is not supported." % variogram_model + ) + elif self.variogram_model == "custom": if variogram_function is None or not callable(variogram_function): - raise ValueError("Must specify callable function for " - "custom variogram model.") + raise ValueError( + "Must specify callable function for custom variogram model." + ) else: self.variogram_function = variogram_function else: @@ -196,12 +211,13 @@ def __init__(self, x, y, z, variogram_model='linear', # problems with referencing the original passed arguments. # Also, values are forced to be float... in the future, might be worth # developing complex-number kriging (useful for vector field kriging) - self.X_ORIG = \ - np.atleast_1d(np.squeeze(np.array(x, copy=True, dtype=np.float64))) - self.Y_ORIG = \ - np.atleast_1d(np.squeeze(np.array(y, copy=True, dtype=np.float64))) - self.Z = \ - np.atleast_1d(np.squeeze(np.array(z, copy=True, dtype=np.float64))) + self.X_ORIG = np.atleast_1d( + np.squeeze(np.array(x, copy=True, dtype=np.float64)) + ) + self.Y_ORIG = np.atleast_1d( + np.squeeze(np.array(y, copy=True, dtype=np.float64)) + ) + self.Z = np.atleast_1d(np.squeeze(np.array(z, copy=True, dtype=np.float64))) self.verbose = verbose self.enable_plotting = enable_plotting @@ -210,93 +226,118 @@ def __init__(self, x, y, z, variogram_model='linear', # adjust for anisotropy... only implemented for euclidean (rectangular) # coordinates, as anisotropy is ambiguous for geographic coordinates... - if coordinates_type == 'euclidean': - self.XCENTER = (np.amax(self.X_ORIG) + np.amin(self.X_ORIG))/2.0 - self.YCENTER = (np.amax(self.Y_ORIG) + np.amin(self.Y_ORIG))/2.0 + if coordinates_type == "euclidean": + self.XCENTER = (np.amax(self.X_ORIG) + np.amin(self.X_ORIG)) / 2.0 + self.YCENTER = (np.amax(self.Y_ORIG) + np.amin(self.Y_ORIG)) / 2.0 self.anisotropy_scaling = anisotropy_scaling self.anisotropy_angle = anisotropy_angle if self.verbose: print("Adjusting data for anisotropy...") - self.X_ADJUSTED, self.Y_ADJUSTED = \ - _adjust_for_anisotropy(np.vstack((self.X_ORIG, self.Y_ORIG)).T, - [self.XCENTER, self.YCENTER], - [self.anisotropy_scaling], - [self.anisotropy_angle]).T - elif coordinates_type == 'geographic': + self.X_ADJUSTED, self.Y_ADJUSTED = _adjust_for_anisotropy( + np.vstack((self.X_ORIG, self.Y_ORIG)).T, + [self.XCENTER, self.YCENTER], + [self.anisotropy_scaling], + [self.anisotropy_angle], + ).T + elif coordinates_type == "geographic": # Leave everything as is in geographic case. # May be open to discussion? if anisotropy_scaling != 1.0: - warnings.warn("Anisotropy is not compatible with geographic " - "coordinates. Ignoring user set anisotropy.", - UserWarning) - self.XCENTER= 0.0 - self.YCENTER= 0.0 + warnings.warn( + "Anisotropy is not compatible with geographic " + "coordinates. Ignoring user set anisotropy.", + UserWarning, + ) + self.XCENTER = 0.0 + self.YCENTER = 0.0 self.anisotropy_scaling = 1.0 self.anisotropy_angle = 0.0 self.X_ADJUSTED = self.X_ORIG self.Y_ADJUSTED = self.Y_ORIG else: - raise ValueError("Only 'euclidean' and 'geographic' are valid " - "values for coordinates-keyword.") + raise ValueError( + "Only 'euclidean' and 'geographic' are valid " + "values for coordinates-keyword." + ) self.coordinates_type = coordinates_type if self.verbose: print("Initializing variogram model...") - vp_temp = _make_variogram_parameter_list(self.variogram_model, - variogram_parameters) - self.lags, self.semivariance, self.variogram_model_parameters = \ - _initialize_variogram_model(np.vstack((self.X_ADJUSTED, - self.Y_ADJUSTED)).T, - self.Z, self.variogram_model, vp_temp, - self.variogram_function, nlags, - weight, self.coordinates_type) + vp_temp = _make_variogram_parameter_list( + self.variogram_model, variogram_parameters + ) + ( + self.lags, + self.semivariance, + self.variogram_model_parameters, + ) = _initialize_variogram_model( + np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED)).T, + self.Z, + self.variogram_model, + vp_temp, + self.variogram_function, + nlags, + weight, + self.coordinates_type, + ) if self.verbose: - print("Coordinates type: '%s'" % self.coordinates_type, '\n') - if self.variogram_model == 'linear': - print("Using '%s' Variogram Model" % 'linear') + print("Coordinates type: '%s'" % self.coordinates_type, "\n") + if self.variogram_model == "linear": + print("Using '%s' Variogram Model" % "linear") print("Slope:", self.variogram_model_parameters[0]) - print("Nugget:", self.variogram_model_parameters[1], '\n') - elif self.variogram_model == 'power': - print("Using '%s' Variogram Model" % 'power') + print("Nugget:", self.variogram_model_parameters[1], "\n") + elif self.variogram_model == "power": + print("Using '%s' Variogram Model" % "power") print("Scale:", self.variogram_model_parameters[0]) print("Exponent:", self.variogram_model_parameters[1]) - print("Nugget:", self.variogram_model_parameters[2], '\n') - elif self.variogram_model == 'custom': + print("Nugget:", self.variogram_model_parameters[2], "\n") + elif self.variogram_model == "custom": print("Using Custom Variogram Model") else: print("Using '%s' Variogram Model" % self.variogram_model) print("Partial Sill:", self.variogram_model_parameters[0]) - print("Full Sill:", self.variogram_model_parameters[0] + - self.variogram_model_parameters[2]) + print( + "Full Sill:", + self.variogram_model_parameters[0] + + self.variogram_model_parameters[2], + ) print("Range:", self.variogram_model_parameters[1]) - print("Nugget:", self.variogram_model_parameters[2], '\n') + print("Nugget:", self.variogram_model_parameters[2], "\n") if self.enable_plotting: self.display_variogram_model() if self.verbose: print("Calculating statistics on variogram model fit...") if enable_statistics: - self.delta, self.sigma, self.epsilon = \ - _find_statistics(np.vstack((self.X_ADJUSTED, - self.Y_ADJUSTED)).T, - self.Z, self.variogram_function, - self.variogram_model_parameters, - self.coordinates_type) + self.delta, self.sigma, self.epsilon = _find_statistics( + np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED)).T, + self.Z, + self.variogram_function, + self.variogram_model_parameters, + self.coordinates_type, + ) self.Q1 = core.calcQ1(self.epsilon) self.Q2 = core.calcQ2(self.epsilon) self.cR = core.calc_cR(self.Q2, self.sigma) if self.verbose: print("Q1 =", self.Q1) print("Q2 =", self.Q2) - print("cR =", self.cR, '\n') + print("cR =", self.cR, "\n") else: - self.delta, self.sigma, self.epsilon, self.Q1, self.Q2, self.cR = [None]*6 - - def update_variogram_model(self, variogram_model, variogram_parameters=None, - variogram_function=None, nlags=6, weight=False, - anisotropy_scaling=1., anisotropy_angle=0.): + self.delta, self.sigma, self.epsilon, self.Q1, self.Q2, self.cR = [None] * 6 + + def update_variogram_model( + self, + variogram_model, + variogram_parameters=None, + variogram_function=None, + nlags=6, + weight=False, + anisotropy_scaling=1.0, + anisotropy_angle=0.0, + ): """Allows user to update variogram type and/or variogram model parameters. @@ -348,36 +389,45 @@ def update_variogram_model(self, variogram_model, variogram_parameters=None, variogram_parameters = [] anisotropy_scaling = self.model.pykrige_anis anisotropy_angle = self.model.pykrige_angle - if self.variogram_model not in self.variogram_dict.keys() and \ - self.variogram_model != 'custom': - raise ValueError("Specified variogram model '%s' " - "is not supported." % variogram_model) - elif self.variogram_model == 'custom': + if ( + self.variogram_model not in self.variogram_dict.keys() + and self.variogram_model != "custom" + ): + raise ValueError( + "Specified variogram model '%s' is not supported." % variogram_model + ) + elif self.variogram_model == "custom": if variogram_function is None or not callable(variogram_function): - raise ValueError("Must specify callable function for " - "custom variogram model.") + raise ValueError( + "Must specify callable function for custom variogram model." + ) else: self.variogram_function = variogram_function else: self.variogram_function = self.variogram_dict[self.variogram_model] - if anisotropy_scaling != self.anisotropy_scaling or \ - anisotropy_angle != self.anisotropy_angle: - if self.coordinates_type == 'euclidean': + if ( + anisotropy_scaling != self.anisotropy_scaling + or anisotropy_angle != self.anisotropy_angle + ): + if self.coordinates_type == "euclidean": if self.verbose: print("Adjusting data for anisotropy...") self.anisotropy_scaling = anisotropy_scaling self.anisotropy_angle = anisotropy_angle - self.X_ADJUSTED, self.Y_ADJUSTED = \ - _adjust_for_anisotropy(np.vstack((self.X_ORIG, self.Y_ORIG)).T, - [self.XCENTER, self.YCENTER], - [self.anisotropy_scaling], - [self.anisotropy_angle]).T - elif self.coordinates_type == 'geographic': + self.X_ADJUSTED, self.Y_ADJUSTED = _adjust_for_anisotropy( + np.vstack((self.X_ORIG, self.Y_ORIG)).T, + [self.XCENTER, self.YCENTER], + [self.anisotropy_scaling], + [self.anisotropy_angle], + ).T + elif self.coordinates_type == "geographic": if anisotropy_scaling != 1.0: - warnings.warn("Anisotropy is not compatible with geographic" - " coordinates. Ignoring user set anisotropy.", - UserWarning) + warnings.warn( + "Anisotropy is not compatible with geographic" + " coordinates. Ignoring user set anisotropy.", + UserWarning, + ) self.anisotropy_scaling = 1.0 self.anisotropy_angle = 0.0 self.X_ADJUSTED = self.X_ORIG @@ -386,61 +436,79 @@ def update_variogram_model(self, variogram_model, variogram_parameters=None, print("Updating variogram mode...") # See note above about the 'use_psill' kwarg... - vp_temp = _make_variogram_parameter_list(self.variogram_model, - variogram_parameters) - self.lags, self.semivariance, self.variogram_model_parameters = \ - _initialize_variogram_model(np.vstack((self.X_ADJUSTED, - self.Y_ADJUSTED)).T, - self.Z, self.variogram_model, vp_temp, - self.variogram_function, nlags, - weight, self.coordinates_type) + vp_temp = _make_variogram_parameter_list( + self.variogram_model, variogram_parameters + ) + ( + self.lags, + self.semivariance, + self.variogram_model_parameters, + ) = _initialize_variogram_model( + np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED)).T, + self.Z, + self.variogram_model, + vp_temp, + self.variogram_function, + nlags, + weight, + self.coordinates_type, + ) if self.verbose: - print("Coordinates type: '%s'" % self.coordinates_type, '\n') - if self.variogram_model == 'linear': - print("Using '%s' Variogram Model" % 'linear') + print("Coordinates type: '%s'" % self.coordinates_type, "\n") + if self.variogram_model == "linear": + print("Using '%s' Variogram Model" % "linear") print("Slope:", self.variogram_model_parameters[0]) - print("Nugget:", self.variogram_model_parameters[1], '\n') - elif self.variogram_model == 'power': - print("Using '%s' Variogram Model" % 'power') + print("Nugget:", self.variogram_model_parameters[1], "\n") + elif self.variogram_model == "power": + print("Using '%s' Variogram Model" % "power") print("Scale:", self.variogram_model_parameters[0]) print("Exponent:", self.variogram_model_parameters[1]) - print("Nugget:", self.variogram_model_parameters[2], '\n') - elif self.variogram_model == 'custom': + print("Nugget:", self.variogram_model_parameters[2], "\n") + elif self.variogram_model == "custom": print("Using Custom Variogram Model") else: print("Using '%s' Variogram Model" % self.variogram_model) print("Partial Sill:", self.variogram_model_parameters[0]) - print("Full Sill:", self.variogram_model_parameters[0] + - self.variogram_model_parameters[2]) + print( + "Full Sill:", + self.variogram_model_parameters[0] + + self.variogram_model_parameters[2], + ) print("Range:", self.variogram_model_parameters[1]) - print("Nugget:", self.variogram_model_parameters[2], '\n') + print("Nugget:", self.variogram_model_parameters[2], "\n") if self.enable_plotting: self.display_variogram_model() if self.verbose: print("Calculating statistics on variogram model fit...") - self.delta, self.sigma, self.epsilon = \ - _find_statistics(np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED)).T, - self.Z, self.variogram_function, - self.variogram_model_parameters, - self.coordinates_type) + self.delta, self.sigma, self.epsilon = _find_statistics( + np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED)).T, + self.Z, + self.variogram_function, + self.variogram_model_parameters, + self.coordinates_type, + ) self.Q1 = core.calcQ1(self.epsilon) self.Q2 = core.calcQ2(self.epsilon) self.cR = core.calc_cR(self.Q2, self.sigma) if self.verbose: print("Q1 =", self.Q1) print("Q2 =", self.Q2) - print("cR =", self.cR, '\n') + print("cR =", self.cR, "\n") def display_variogram_model(self): """Displays variogram model with the actual binned data.""" + import matplotlib.pyplot as plt + fig = plt.figure() ax = fig.add_subplot(111) - ax.plot(self.lags, self.semivariance, 'r*') - ax.plot(self.lags, - self.variogram_function(self.variogram_model_parameters, - self.lags), 'k-') + ax.plot(self.lags, self.semivariance, "r*") + ax.plot( + self.lags, + self.variogram_function(self.variogram_model_parameters, self.lags), + "k-", + ) plt.show() def get_variogram_points(self): @@ -458,7 +526,10 @@ def get_variogram_points(self): lags (array) - the lags at which the variogram was evaluated variogram (array) - the variogram function evaluated at the lags """ - return self.lags, self.variogram_function(self.variogram_model_parameters, self.lags) + return ( + self.lags, + self.variogram_function(self.variogram_model_parameters, self.lags), + ) def switch_verbose(self): """Allows user to switch code talk-back on/off. Takes no arguments.""" @@ -474,9 +545,11 @@ def get_epsilon_residuals(self): def plot_epsilon_residuals(self): """Plots the epsilon residuals for the variogram fit.""" + import matplotlib.pyplot as plt + fig = plt.figure() ax = fig.add_subplot(111) - ax.scatter(range(self.epsilon.size), self.epsilon, c='k', marker='*') + ax.scatter(range(self.epsilon.size), self.epsilon, c="k", marker="*") ax.axhline(y=0.0) plt.show() @@ -498,18 +571,21 @@ def print_statistics(self): def _get_kriging_matrix(self, n): """Assembles the kriging matrix.""" - if self.coordinates_type == 'euclidean': - xy = np.concatenate((self.X_ADJUSTED[:, np.newaxis], - self.Y_ADJUSTED[:, np.newaxis]), axis=1) - d = cdist(xy, xy, 'euclidean') - elif self.coordinates_type == 'geographic': - d = core.great_circle_distance(self.X_ADJUSTED[:,np.newaxis], - self.Y_ADJUSTED[:,np.newaxis], - self.X_ADJUSTED, self.Y_ADJUSTED) - a = np.zeros((n+1, n+1)) - a[:n, :n] = - self.variogram_function(self.variogram_model_parameters, - d) - np.fill_diagonal(a, 0.) + if self.coordinates_type == "euclidean": + xy = np.concatenate( + (self.X_ADJUSTED[:, np.newaxis], self.Y_ADJUSTED[:, np.newaxis]), axis=1 + ) + d = cdist(xy, xy, "euclidean") + elif self.coordinates_type == "geographic": + d = core.great_circle_distance( + self.X_ADJUSTED[:, np.newaxis], + self.Y_ADJUSTED[:, np.newaxis], + self.X_ADJUSTED, + self.Y_ADJUSTED, + ) + a = np.zeros((n + 1, n + 1)) + a[:n, :n] = -self.variogram_function(self.variogram_model_parameters, d) + np.fill_diagonal(a, 0.0) a[n, :] = 1.0 a[:, n] = 1.0 a[n, n] = 0.0 @@ -531,17 +607,17 @@ def _exec_vector(self, a, bd, mask): zero_value = True zero_index = np.where(np.absolute(bd) <= self.eps) - b = np.zeros((npt, n+1, 1)) - b[:, :n, 0] = - self.variogram_function(self.variogram_model_parameters, bd) + b = np.zeros((npt, n + 1, 1)) + b[:, :n, 0] = -self.variogram_function(self.variogram_model_parameters, bd) if zero_value: b[zero_index[0], zero_index[1], 0] = 0.0 b[:, n, 0] = 1.0 if (~mask).any(): - mask_b = np.repeat(mask[:, np.newaxis, np.newaxis], n+1, axis=1) + mask_b = np.repeat(mask[:, np.newaxis, np.newaxis], n + 1, axis=1) b = np.ma.array(b, mask=mask_b) - x = np.dot(a_inv, b.reshape((npt, n+1)).T).reshape((1, n+1, npt)).T + x = np.dot(a_inv, b.reshape((npt, n + 1)).T).reshape((1, n + 1, npt)).T zvalues = np.sum(x[:, :n, 0] * self.Z, axis=1) sigmasq = np.sum(x[:, :, 0] * -b[:, :, 0], axis=1) @@ -558,8 +634,10 @@ def _exec_loop(self, a, bd_all, mask): a_inv = scipy.linalg.inv(a) - for j in np.nonzero(~mask)[0]: # Note that this is the same thing as range(npt) if mask is not defined, - bd = bd_all[j] # otherwise it takes the non-masked elements. + for j in np.nonzero(~mask)[ + 0 + ]: # Note that this is the same thing as range(npt) if mask is not defined, + bd = bd_all[j] # otherwise it takes the non-masked elements. if np.any(np.absolute(bd) <= self.eps): zero_value = True zero_index = np.where(np.absolute(bd) <= self.eps) @@ -567,8 +645,8 @@ def _exec_loop(self, a, bd_all, mask): zero_index = None zero_value = False - b = np.zeros((n+1, 1)) - b[:n, 0] = - self.variogram_function(self.variogram_model_parameters, bd) + b = np.zeros((n + 1, 1)) + b[:n, 0] = -self.variogram_function(self.variogram_model_parameters, bd) if zero_value: b[zero_index[0], 0] = 0.0 b[n, 0] = 1.0 @@ -588,8 +666,10 @@ def _exec_loop_moving_window(self, a_all, bd_all, mask, bd_idx): zvalues = np.zeros(npt) sigmasq = np.zeros(npt) - for i in np.nonzero(~mask)[0]: # Note that this is the same thing as range(npt) if mask is not defined, - b_selector = bd_idx[i] # otherwise it takes the non-masked elements. + for i in np.nonzero(~mask)[ + 0 + ]: # Note that this is the same thing as range(npt) if mask is not defined, + b_selector = bd_idx[i] # otherwise it takes the non-masked elements. bd = bd_all[i] a_selector = np.concatenate((b_selector, np.array([a_all.shape[0] - 1]))) @@ -601,8 +681,8 @@ def _exec_loop_moving_window(self, a_all, bd_all, mask, bd_idx): else: zero_index = None zero_value = False - b = np.zeros((n+1, 1)) - b[:n, 0] = - self.variogram_function(self.variogram_model_parameters, bd) + b = np.zeros((n + 1, 1)) + b[:n, 0] = -self.variogram_function(self.variogram_model_parameters, bd) if zero_value: b[zero_index[0], 0] = 0.0 b[n, 0] = 1.0 @@ -610,12 +690,19 @@ def _exec_loop_moving_window(self, a_all, bd_all, mask, bd_idx): x = scipy.linalg.solve(a, b) zvalues[i] = x[:n, 0].dot(self.Z[b_selector]) - sigmasq[i] = - x[:, 0].dot(b[:, 0]) + sigmasq[i] = -x[:, 0].dot(b[:, 0]) return zvalues, sigmasq - def execute(self, style, xpoints, ypoints, mask=None, backend='vectorized', - n_closest_points=None): + def execute( + self, + style, + xpoints, + ypoints, + mask=None, + backend="vectorized", + n_closest_points=None, + ): """Calculates a kriged grid and the associated variance. This is now the method that performs the main kriging calculation. @@ -700,9 +787,8 @@ def execute(self, style, xpoints, ypoints, mask=None, backend='vectorized', if self.verbose: print("Executing Ordinary Kriging...\n") - if style != 'grid' and style != 'masked' and style != 'points': - raise ValueError("style argument must be 'grid', " - "'points', or 'masked'") + if style != "grid" and style != "masked" and style != "points": + raise ValueError("style argument must be 'grid', 'points', or 'masked'") if n_closest_points is not None and n_closest_points <= 1: # If this is not checked, nondescriptive errors emerge @@ -716,69 +802,85 @@ def execute(self, style, xpoints, ypoints, mask=None, backend='vectorized', ny = ypts.size a = self._get_kriging_matrix(n) - if style in ['grid', 'masked']: - if style == 'masked': + if style in ["grid", "masked"]: + if style == "masked": if mask is None: - raise IOError("Must specify boolean masking array " - "when style is 'masked'.") + raise IOError( + "Must specify boolean masking array when style is 'masked'." + ) if mask.shape[0] != ny or mask.shape[1] != nx: if mask.shape[0] == nx and mask.shape[1] == ny: mask = mask.T else: - raise ValueError("Mask dimensions do not match " - "specified grid dimensions.") + raise ValueError( + "Mask dimensions do not match specified grid dimensions." + ) mask = mask.flatten() - npt = ny*nx + npt = ny * nx grid_x, grid_y = np.meshgrid(xpts, ypts) xpts = grid_x.flatten() ypts = grid_y.flatten() - elif style == 'points': + elif style == "points": if xpts.size != ypts.size: - raise ValueError("xpoints and ypoints must have " - "same dimensions when treated as " - "listing discrete points.") + raise ValueError( + "xpoints and ypoints must have " + "same dimensions when treated as " + "listing discrete points." + ) npt = nx else: - raise ValueError("style argument must be 'grid', " - "'points', or 'masked'") - - if self.coordinates_type == 'euclidean': - xpts, ypts = _adjust_for_anisotropy(np.vstack((xpts, ypts)).T, - [self.XCENTER, self.YCENTER], - [self.anisotropy_scaling], - [self.anisotropy_angle]).T + raise ValueError("style argument must be 'grid', 'points', or 'masked'") + + if self.coordinates_type == "euclidean": + xpts, ypts = _adjust_for_anisotropy( + np.vstack((xpts, ypts)).T, + [self.XCENTER, self.YCENTER], + [self.anisotropy_scaling], + [self.anisotropy_angle], + ).T # Prepare for cdist: - xy_data = np.concatenate((self.X_ADJUSTED[:, np.newaxis], - self.Y_ADJUSTED[:, np.newaxis]), axis=1) - xy_points = np.concatenate((xpts[:, np.newaxis], - ypts[:, np.newaxis]), axis=1) - elif self.coordinates_type == 'geographic': + xy_data = np.concatenate( + (self.X_ADJUSTED[:, np.newaxis], self.Y_ADJUSTED[:, np.newaxis]), axis=1 + ) + xy_points = np.concatenate( + (xpts[:, np.newaxis], ypts[:, np.newaxis]), axis=1 + ) + elif self.coordinates_type == "geographic": # In spherical coordinates, we do not correct for anisotropy. # Also, we don't use scipy.spatial.cdist, so we do not have to # format the input data accordingly. pass - if style != 'masked': - mask = np.zeros(npt, dtype='bool') + if style != "masked": + mask = np.zeros(npt, dtype="bool") c_pars = None - if backend == 'C': + if backend == "C": try: from .lib.cok import _c_exec_loop, _c_exec_loop_moving_window except ImportError: - print('Warning: failed to load Cython extensions.\n' - ' See https://github.com/bsmurphy/PyKrige/issues/8 \n' - ' Falling back to a pure python backend...') - backend = 'loop' + print( + "Warning: failed to load Cython extensions.\n" + " See https://github.com/GeoStat-Framework/PyKrige/issues/8 \n" + " Falling back to a pure python backend..." + ) + backend = "loop" except: - raise RuntimeError("Unknown error in trying to " - "load Cython extension.") - - c_pars = {key: getattr(self, key) for key in ['Z', 'eps', 'variogram_model_parameters', 'variogram_function']} + raise RuntimeError("Unknown error in trying to load Cython extension.") + + c_pars = { + key: getattr(self, key) + for key in [ + "Z", + "eps", + "variogram_model_parameters", + "variogram_function", + ] + } if n_closest_points is not None: - if self.coordinates_type == 'geographic': + if self.coordinates_type == "geographic": # To make use of the KDTree, we have to convert the # spherical coordinates into three dimensional Euclidean # coordinates, since the standard KDTree cannot handle @@ -786,64 +888,80 @@ def execute(self, style, xpoints, ypoints, mask=None, backend='vectorized', # Do the conversion just for the step involving the KDTree: lon_d = self.X_ADJUSTED[:, np.newaxis] * np.pi / 180.0 lat_d = self.Y_ADJUSTED[:, np.newaxis] * np.pi / 180.0 - xy_data = np.concatenate((np.cos(lon_d) * np.cos(lat_d), - np.sin(lon_d) * np.cos(lat_d), - np.sin(lat_d)), axis=1) + xy_data = np.concatenate( + ( + np.cos(lon_d) * np.cos(lat_d), + np.sin(lon_d) * np.cos(lat_d), + np.sin(lat_d), + ), + axis=1, + ) lon_p = xpts[:, np.newaxis] * np.pi / 180.0 lat_p = ypts[:, np.newaxis] * np.pi / 180.0 - xy_points = np.concatenate((np.cos(lon_p) * np.cos(lat_p), - np.sin(lon_p) * np.cos(lat_p), - np.sin(lat_p)), axis=1) + xy_points = np.concatenate( + ( + np.cos(lon_p) * np.cos(lat_p), + np.sin(lon_p) * np.cos(lat_p), + np.sin(lat_p), + ), + axis=1, + ) from scipy.spatial import cKDTree + tree = cKDTree(xy_data) bd, bd_idx = tree.query(xy_points, k=n_closest_points, eps=0.0) - if self.coordinates_type == 'geographic': + if self.coordinates_type == "geographic": # Between the nearest neighbours from Euclidean search, # calculate the great circle distance using the standard method: - x_points = np.tile(xpts[:,np.newaxis],(1,n_closest_points)) - y_points = np.tile(ypts[:,np.newaxis],(1,n_closest_points)) - bd = core.great_circle_distance(x_points, y_points, - self.X_ADJUSTED[bd_idx], - self.Y_ADJUSTED[bd_idx]) - - if backend == 'loop': - zvalues, sigmasq = \ - self._exec_loop_moving_window(a, bd, mask, bd_idx) - elif backend == 'C': - zvalues, sigmasq = \ - _c_exec_loop_moving_window(a, bd, mask.astype('int8'), - bd_idx, self.X_ADJUSTED.shape[0], - c_pars) + x_points = np.tile(xpts[:, np.newaxis], (1, n_closest_points)) + y_points = np.tile(ypts[:, np.newaxis], (1, n_closest_points)) + bd = core.great_circle_distance( + x_points, y_points, self.X_ADJUSTED[bd_idx], self.Y_ADJUSTED[bd_idx] + ) + + if backend == "loop": + zvalues, sigmasq = self._exec_loop_moving_window(a, bd, mask, bd_idx) + elif backend == "C": + zvalues, sigmasq = _c_exec_loop_moving_window( + a, bd, mask.astype("int8"), bd_idx, self.X_ADJUSTED.shape[0], c_pars + ) else: - raise ValueError('Specified backend {} for a moving window ' - 'is not supported.'.format(backend)) + raise ValueError( + "Specified backend {} for a moving window " + "is not supported.".format(backend) + ) else: - if self.coordinates_type == 'euclidean': - bd = cdist(xy_points, xy_data, 'euclidean') - elif self.coordinates_type == 'geographic': - bd = core.great_circle_distance(xpts[:,np.newaxis], - ypts[:,np.newaxis], - self.X_ADJUSTED, self.Y_ADJUSTED) - - if backend == 'vectorized': + if self.coordinates_type == "euclidean": + bd = cdist(xy_points, xy_data, "euclidean") + elif self.coordinates_type == "geographic": + bd = core.great_circle_distance( + xpts[:, np.newaxis], + ypts[:, np.newaxis], + self.X_ADJUSTED, + self.Y_ADJUSTED, + ) + + if backend == "vectorized": zvalues, sigmasq = self._exec_vector(a, bd, mask) - elif backend == 'loop': + elif backend == "loop": zvalues, sigmasq = self._exec_loop(a, bd, mask) - elif backend == 'C': - zvalues, sigmasq = _c_exec_loop(a, bd, mask.astype('int8'), - self.X_ADJUSTED.shape[0], - c_pars) + elif backend == "C": + zvalues, sigmasq = _c_exec_loop( + a, bd, mask.astype("int8"), self.X_ADJUSTED.shape[0], c_pars + ) else: - raise ValueError('Specified backend {} is not supported for ' - '2D ordinary kriging.'.format(backend)) + raise ValueError( + "Specified backend {} is not supported for " + "2D ordinary kriging.".format(backend) + ) - if style == 'masked': + if style == "masked": zvalues = np.ma.array(zvalues, mask=mask) sigmasq = np.ma.array(sigmasq, mask=mask) - if style in ['masked', 'grid']: + if style in ["masked", "grid"]: zvalues = zvalues.reshape((ny, nx)) sigmasq = sigmasq.reshape((ny, nx)) diff --git a/pykrige/ok3d.py b/pykrige/ok3d.py index 0384dd4..679d2c9 100644 --- a/pykrige/ok3d.py +++ b/pykrige/ok3d.py @@ -1,9 +1,5 @@ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -__doc__ = """ +# coding: utf-8 +""" PyKrige ======= @@ -19,22 +15,25 @@ .. [1] P.K. Kitanidis, Introduction to Geostatistcs: Applications in Hydrogeology, (Cambridge University Press, 1997) 272 p. -Copyright (c) 2015-2018, PyKrige Developers +Copyright (c) 2015-2020, PyKrige Developers """ import numpy as np import scipy.linalg from scipy.spatial.distance import cdist -import matplotlib.pyplot as plt from . import variogram_models from . import core -from .core import _adjust_for_anisotropy, _initialize_variogram_model, \ - _make_variogram_parameter_list, _find_statistics +from .core import ( + _adjust_for_anisotropy, + _initialize_variogram_model, + _make_variogram_parameter_list, + _find_statistics, +) import warnings class OrdinaryKriging3D: - """Three-dimensional ordinary kriging + """Three-dimensional ordinary kriging. Parameters ---------- @@ -61,30 +60,28 @@ class OrdinaryKriging3D: minimization scheme. For variogram model parameters provided in a dict, the required dict keys vary according to the specified variogram model: :: - linear - {'slope': slope, 'nugget': nugget} - power - {'scale': scale, 'exponent': exponent, 'nugget': nugget} - gaussian - {'sill': s, 'range': r, 'nugget': n} - OR - {'psill': p, 'range': r, 'nugget':n} - spherical - {'sill': s, 'range': r, 'nugget': n} - OR - {'psill': p, 'range': r, 'nugget':n} - exponential - {'sill': s, 'range': r, 'nugget': n} - OR - {'psill': p, 'range': r, 'nugget':n} - hole-effect - {'sill': s, 'range': r, 'nugget': n} - OR - {'psill': p, 'range': r, 'nugget':n} + + # linear + {'slope': slope, 'nugget': nugget} + # power + {'scale': scale, 'exponent': exponent, 'nugget': nugget} + # gaussian, spherical, exponential and hole-effect: + {'sill': s, 'range': r, 'nugget': n} + # OR + {'psill': p, 'range': r, 'nugget': n} + Note that either the full sill or the partial sill (psill = sill - nugget) can be specified in the dict. For variogram model parameters provided in a list, the entries must be as follows: :: - linear - [slope, nugget] - power - [scale, exponent, nugget] - gaussian - [sill, range, nugget] - spherical - [sill, range, nugget] - exponential - [sill, range, nugget] - hole-effect - [sill, range, nugget] + + # linear + [slope, nugget] + # power + [scale, exponent, nugget] + # gaussian, spherical, exponential and hole-effect: + [sill, range, nugget] + Note that the full sill (NOT the partial sill) must be specified in the list format. For a custom variogram model, the parameters are required, as custom @@ -158,22 +155,38 @@ class OrdinaryKriging3D: References ---------- .. [1] P.K. Kitanidis, Introduction to Geostatistcs: Applications in - Hydrogeology, (Cambridge University Press, 1997) 272 p. + Hydrogeology, (Cambridge University Press, 1997) 272 p. """ - eps = 1.e-10 # Cutoff for comparison to zero - variogram_dict = {'linear': variogram_models.linear_variogram_model, - 'power': variogram_models.power_variogram_model, - 'gaussian': variogram_models.gaussian_variogram_model, - 'spherical': variogram_models.spherical_variogram_model, - 'exponential': variogram_models.exponential_variogram_model, - 'hole-effect': variogram_models.hole_effect_variogram_model} - - def __init__(self, x, y, z, val, variogram_model='linear', - variogram_parameters=None, variogram_function=None, nlags=6, - weight=False, anisotropy_scaling_y=1., anisotropy_scaling_z=1., - anisotropy_angle_x=0., anisotropy_angle_y=0., - anisotropy_angle_z=0., verbose=False, enable_plotting=False): + eps = 1.0e-10 # Cutoff for comparison to zero + variogram_dict = { + "linear": variogram_models.linear_variogram_model, + "power": variogram_models.power_variogram_model, + "gaussian": variogram_models.gaussian_variogram_model, + "spherical": variogram_models.spherical_variogram_model, + "exponential": variogram_models.exponential_variogram_model, + "hole-effect": variogram_models.hole_effect_variogram_model, + } + + def __init__( + self, + x, + y, + z, + val, + variogram_model="linear", + variogram_parameters=None, + variogram_function=None, + nlags=6, + weight=False, + anisotropy_scaling_y=1.0, + anisotropy_scaling_z=1.0, + anisotropy_angle_x=0.0, + anisotropy_angle_y=0.0, + anisotropy_angle_z=0.0, + verbose=False, + enable_plotting=False, + ): # set up variogram model and parameters... self.variogram_model = variogram_model @@ -192,14 +205,18 @@ def __init__(self, x, y, z, val, variogram_model='linear', anisotropy_angle_x = self.model.pykrige_angle_x anisotropy_angle_y = self.model.pykrige_angle_y anisotropy_angle_z = self.model.pykrige_angle_z - if self.variogram_model not in self.variogram_dict.keys() and \ - self.variogram_model != 'custom': - raise ValueError("Specified variogram model '%s' " - "is not supported." % variogram_model) - elif self.variogram_model == 'custom': + if ( + self.variogram_model not in self.variogram_dict.keys() + and self.variogram_model != "custom" + ): + raise ValueError( + "Specified variogram model '%s' is not supported." % variogram_model + ) + elif self.variogram_model == "custom": if variogram_function is None or not callable(variogram_function): - raise ValueError("Must specify callable function for " - "custom variogram model.") + raise ValueError( + "Must specify callable function for custom variogram model." + ) else: self.variogram_function = variogram_function else: @@ -208,23 +225,27 @@ def __init__(self, x, y, z, val, variogram_model='linear', # Code assumes 1D input arrays. Ensures that any extraneous dimensions # don't get in the way. Copies are created to avoid any problems with # referencing the original passed arguments. - self.X_ORIG = \ - np.atleast_1d(np.squeeze(np.array(x, copy=True, dtype=np.float64))) - self.Y_ORIG = \ - np.atleast_1d(np.squeeze(np.array(y, copy=True, dtype=np.float64))) - self.Z_ORIG = \ - np.atleast_1d(np.squeeze(np.array(z, copy=True, dtype=np.float64))) - self.VALUES = \ - np.atleast_1d(np.squeeze(np.array(val, copy=True, dtype=np.float64))) + self.X_ORIG = np.atleast_1d( + np.squeeze(np.array(x, copy=True, dtype=np.float64)) + ) + self.Y_ORIG = np.atleast_1d( + np.squeeze(np.array(y, copy=True, dtype=np.float64)) + ) + self.Z_ORIG = np.atleast_1d( + np.squeeze(np.array(z, copy=True, dtype=np.float64)) + ) + self.VALUES = np.atleast_1d( + np.squeeze(np.array(val, copy=True, dtype=np.float64)) + ) self.verbose = verbose self.enable_plotting = enable_plotting if self.enable_plotting and self.verbose: print("Plotting Enabled\n") - self.XCENTER = (np.amax(self.X_ORIG) + np.amin(self.X_ORIG))/2.0 - self.YCENTER = (np.amax(self.Y_ORIG) + np.amin(self.Y_ORIG))/2.0 - self.ZCENTER = (np.amax(self.Z_ORIG) + np.amin(self.Z_ORIG))/2.0 + self.XCENTER = (np.amax(self.X_ORIG) + np.amin(self.X_ORIG)) / 2.0 + self.YCENTER = (np.amax(self.Y_ORIG) + np.amin(self.Y_ORIG)) / 2.0 + self.ZCENTER = (np.amax(self.Z_ORIG) + np.amin(self.Z_ORIG)) / 2.0 self.anisotropy_scaling_y = anisotropy_scaling_y self.anisotropy_scaling_z = anisotropy_scaling_z self.anisotropy_angle_x = anisotropy_angle_x @@ -232,68 +253,89 @@ def __init__(self, x, y, z, val, variogram_model='linear', self.anisotropy_angle_z = anisotropy_angle_z if self.verbose: print("Adjusting data for anisotropy...") - self.X_ADJUSTED, self.Y_ADJUSTED, self.Z_ADJUSTED = \ - _adjust_for_anisotropy(np.vstack((self.X_ORIG, self.Y_ORIG, self.Z_ORIG)).T, - [self.XCENTER, self.YCENTER, self.ZCENTER], - [self.anisotropy_scaling_y, self.anisotropy_scaling_z], - [self.anisotropy_angle_x, self.anisotropy_angle_y, self.anisotropy_angle_z]).T + self.X_ADJUSTED, self.Y_ADJUSTED, self.Z_ADJUSTED = _adjust_for_anisotropy( + np.vstack((self.X_ORIG, self.Y_ORIG, self.Z_ORIG)).T, + [self.XCENTER, self.YCENTER, self.ZCENTER], + [self.anisotropy_scaling_y, self.anisotropy_scaling_z], + [self.anisotropy_angle_x, self.anisotropy_angle_y, self.anisotropy_angle_z], + ).T if self.verbose: print("Initializing variogram model...") - vp_temp = _make_variogram_parameter_list(self.variogram_model, - variogram_parameters) - self.lags, self.semivariance, self.variogram_model_parameters = \ - _initialize_variogram_model(np.vstack((self.X_ADJUSTED, - self.Y_ADJUSTED, - self.Z_ADJUSTED)).T, - self.VALUES, self.variogram_model, - vp_temp, self.variogram_function, - nlags, weight, 'euclidean') + vp_temp = _make_variogram_parameter_list( + self.variogram_model, variogram_parameters + ) + ( + self.lags, + self.semivariance, + self.variogram_model_parameters, + ) = _initialize_variogram_model( + np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED, self.Z_ADJUSTED)).T, + self.VALUES, + self.variogram_model, + vp_temp, + self.variogram_function, + nlags, + weight, + "euclidean", + ) if self.verbose: - if self.variogram_model == 'linear': - print("Using '%s' Variogram Model" % 'linear') + if self.variogram_model == "linear": + print("Using '%s' Variogram Model" % "linear") print("Slope:", self.variogram_model_parameters[0]) - print("Nugget:", self.variogram_model_parameters[1], '\n') - elif self.variogram_model == 'power': - print("Using '%s' Variogram Model" % 'power') + print("Nugget:", self.variogram_model_parameters[1], "\n") + elif self.variogram_model == "power": + print("Using '%s' Variogram Model" % "power") print("Scale:", self.variogram_model_parameters[0]) print("Exponent:", self.variogram_model_parameters[1]) - print("Nugget:", self.variogram_model_parameters[2], '\n') - elif self.variogram_model == 'custom': + print("Nugget:", self.variogram_model_parameters[2], "\n") + elif self.variogram_model == "custom": print("Using Custom Variogram Model") else: print("Using '%s' Variogram Model" % self.variogram_model) print("Partial Sill:", self.variogram_model_parameters[0]) - print("Full Sill:", self.variogram_model_parameters[0] + - self.variogram_model_parameters[2]) + print( + "Full Sill:", + self.variogram_model_parameters[0] + + self.variogram_model_parameters[2], + ) print("Range:", self.variogram_model_parameters[1]) - print("Nugget:", self.variogram_model_parameters[2], '\n') + print("Nugget:", self.variogram_model_parameters[2], "\n") if self.enable_plotting: self.display_variogram_model() if self.verbose: print("Calculating statistics on variogram model fit...") - self.delta, self.sigma, self.epsilon = \ - _find_statistics(np.vstack((self.X_ADJUSTED, - self.Y_ADJUSTED, - self.Z_ADJUSTED)).T, - self.VALUES, self.variogram_function, - self.variogram_model_parameters, 'euclidean') + self.delta, self.sigma, self.epsilon = _find_statistics( + np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED, self.Z_ADJUSTED)).T, + self.VALUES, + self.variogram_function, + self.variogram_model_parameters, + "euclidean", + ) self.Q1 = core.calcQ1(self.epsilon) self.Q2 = core.calcQ2(self.epsilon) self.cR = core.calc_cR(self.Q2, self.sigma) if self.verbose: print("Q1 =", self.Q1) print("Q2 =", self.Q2) - print("cR =", self.cR, '\n') - - def update_variogram_model(self, variogram_model, variogram_parameters=None, - variogram_function=None, nlags=6, weight=False, - anisotropy_scaling_y=1., anisotropy_scaling_z=1., - anisotropy_angle_x=0., anisotropy_angle_y=0., - anisotropy_angle_z=0.): + print("cR =", self.cR, "\n") + + def update_variogram_model( + self, + variogram_model, + variogram_parameters=None, + variogram_function=None, + nlags=6, + weight=False, + anisotropy_scaling_y=1.0, + anisotropy_scaling_z=1.0, + anisotropy_angle_x=0.0, + anisotropy_angle_y=0.0, + anisotropy_angle_z=0.0, + ): """Changes the variogram model and variogram parameters for the kriging system. @@ -358,22 +400,30 @@ def update_variogram_model(self, variogram_model, variogram_parameters=None, anisotropy_angle_x = self.model.pykrige_angle_x anisotropy_angle_y = self.model.pykrige_angle_y anisotropy_angle_z = self.model.pykrige_angle_z - if self.variogram_model not in self.variogram_dict.keys() and \ - self.variogram_model != 'custom': - raise ValueError("Specified variogram model '%s' " - "is not supported." % variogram_model) - elif self.variogram_model == 'custom': + if ( + self.variogram_model not in self.variogram_dict.keys() + and self.variogram_model != "custom" + ): + raise ValueError( + "Specified variogram model '%s' is not supported." % variogram_model + ) + elif self.variogram_model == "custom": if variogram_function is None or not callable(variogram_function): - raise ValueError("Must specify callable function for " - "custom variogram model.") + raise ValueError( + "Must specify callable function for custom variogram model." + ) else: self.variogram_function = variogram_function else: self.variogram_function = self.variogram_dict[self.variogram_model] - if anisotropy_scaling_y != self.anisotropy_scaling_y or anisotropy_scaling_z != self.anisotropy_scaling_z or \ - anisotropy_angle_x != self.anisotropy_angle_x or anisotropy_angle_y != self.anisotropy_angle_y or \ - anisotropy_angle_z != self.anisotropy_angle_z: + if ( + anisotropy_scaling_y != self.anisotropy_scaling_y + or anisotropy_scaling_z != self.anisotropy_scaling_z + or anisotropy_angle_x != self.anisotropy_angle_x + or anisotropy_angle_y != self.anisotropy_angle_y + or anisotropy_angle_z != self.anisotropy_angle_z + ): if self.verbose: print("Adjusting data for anisotropy...") self.anisotropy_scaling_y = anisotropy_scaling_y @@ -381,71 +431,92 @@ def update_variogram_model(self, variogram_model, variogram_parameters=None, self.anisotropy_angle_x = anisotropy_angle_x self.anisotropy_angle_y = anisotropy_angle_y self.anisotropy_angle_z = anisotropy_angle_z - self.X_ADJUSTED, self.Y_ADJUSTED, self.Z_ADJUSTED = \ - _adjust_for_anisotropy(np.vstack((self.X_ORIG, self.Y_ORIG, self.Z_ORIG)).T, - [self.XCENTER, self.YCENTER, self.ZCENTER], - [self.anisotropy_scaling_y, self.anisotropy_scaling_z], - [self.anisotropy_angle_x, self.anisotropy_angle_y, self.anisotropy_angle_z]).T + self.X_ADJUSTED, self.Y_ADJUSTED, self.Z_ADJUSTED = _adjust_for_anisotropy( + np.vstack((self.X_ORIG, self.Y_ORIG, self.Z_ORIG)).T, + [self.XCENTER, self.YCENTER, self.ZCENTER], + [self.anisotropy_scaling_y, self.anisotropy_scaling_z], + [ + self.anisotropy_angle_x, + self.anisotropy_angle_y, + self.anisotropy_angle_z, + ], + ).T if self.verbose: print("Updating variogram mode...") - vp_temp = _make_variogram_parameter_list(self.variogram_model, - variogram_parameters) - self.lags, self.semivariance, self.variogram_model_parameters = \ - _initialize_variogram_model(np.vstack((self.X_ADJUSTED, - self.Y_ADJUSTED, - self.Z_ADJUSTED)).T, - self.VALUES, self.variogram_model, - vp_temp, self.variogram_function, - nlags, weight, 'euclidean') + vp_temp = _make_variogram_parameter_list( + self.variogram_model, variogram_parameters + ) + ( + self.lags, + self.semivariance, + self.variogram_model_parameters, + ) = _initialize_variogram_model( + np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED, self.Z_ADJUSTED)).T, + self.VALUES, + self.variogram_model, + vp_temp, + self.variogram_function, + nlags, + weight, + "euclidean", + ) if self.verbose: - if self.variogram_model == 'linear': - print("Using '%s' Variogram Model" % 'linear') + if self.variogram_model == "linear": + print("Using '%s' Variogram Model" % "linear") print("Slope:", self.variogram_model_parameters[0]) - print("Nugget:", self.variogram_model_parameters[1], '\n') - elif self.variogram_model == 'power': - print("Using '%s' Variogram Model" % 'power') + print("Nugget:", self.variogram_model_parameters[1], "\n") + elif self.variogram_model == "power": + print("Using '%s' Variogram Model" % "power") print("Scale:", self.variogram_model_parameters[0]) print("Exponent:", self.variogram_model_parameters[1]) - print("Nugget:", self.variogram_model_parameters[2], '\n') - elif self.variogram_model == 'custom': + print("Nugget:", self.variogram_model_parameters[2], "\n") + elif self.variogram_model == "custom": print("Using Custom Variogram Model") else: print("Using '%s' Variogram Model" % self.variogram_model) print("Partial Sill:", self.variogram_model_parameters[0]) - print("Full Sill:", self.variogram_model_parameters[0] + - self.variogram_model_parameters[2]) + print( + "Full Sill:", + self.variogram_model_parameters[0] + + self.variogram_model_parameters[2], + ) print("Range:", self.variogram_model_parameters[1]) - print("Nugget:", self.variogram_model_parameters[2], '\n') + print("Nugget:", self.variogram_model_parameters[2], "\n") if self.enable_plotting: self.display_variogram_model() if self.verbose: print("Calculating statistics on variogram model fit...") - self.delta, self.sigma, self.epsilon = \ - _find_statistics(np.vstack((self.X_ADJUSTED, - self.Y_ADJUSTED, - self.Z_ADJUSTED)).T, - self.VALUES, self.variogram_function, - self.variogram_model_parameters, 'euclidean') + self.delta, self.sigma, self.epsilon = _find_statistics( + np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED, self.Z_ADJUSTED)).T, + self.VALUES, + self.variogram_function, + self.variogram_model_parameters, + "euclidean", + ) self.Q1 = core.calcQ1(self.epsilon) self.Q2 = core.calcQ2(self.epsilon) self.cR = core.calc_cR(self.Q2, self.sigma) if self.verbose: print("Q1 =", self.Q1) print("Q2 =", self.Q2) - print("cR =", self.cR, '\n') + print("cR =", self.cR, "\n") def display_variogram_model(self): """Displays variogram model with the actual binned data.""" + import matplotlib.pyplot as plt + fig = plt.figure() ax = fig.add_subplot(111) - ax.plot(self.lags, self.semivariance, 'r*') - ax.plot(self.lags, - self.variogram_function(self.variogram_model_parameters, - self.lags), 'k-') + ax.plot(self.lags, self.semivariance, "r*") + ax.plot( + self.lags, + self.variogram_function(self.variogram_model_parameters, self.lags), + "k-", + ) plt.show() def switch_verbose(self): @@ -462,9 +533,11 @@ def get_epsilon_residuals(self): def plot_epsilon_residuals(self): """Plots the epsilon residuals for the variogram fit.""" + import matplotlib.pyplot as plt + fig = plt.figure() ax = fig.add_subplot(111) - ax.scatter(range(self.epsilon.size), self.epsilon, c='k', marker='*') + ax.scatter(range(self.epsilon.size), self.epsilon, c="k", marker="*") ax.axhline(y=0.0) plt.show() @@ -486,13 +559,18 @@ def print_statistics(self): def _get_kriging_matrix(self, n): """Assembles the kriging matrix.""" - xyz = np.concatenate((self.X_ADJUSTED[:, np.newaxis], - self.Y_ADJUSTED[:, np.newaxis], - self.Z_ADJUSTED[:, np.newaxis]), axis=1) - d = cdist(xyz, xyz, 'euclidean') - a = np.zeros((n+1, n+1)) - a[:n, :n] = - self.variogram_function(self.variogram_model_parameters, d) - np.fill_diagonal(a, 0.) + xyz = np.concatenate( + ( + self.X_ADJUSTED[:, np.newaxis], + self.Y_ADJUSTED[:, np.newaxis], + self.Z_ADJUSTED[:, np.newaxis], + ), + axis=1, + ) + d = cdist(xyz, xyz, "euclidean") + a = np.zeros((n + 1, n + 1)) + a[:n, :n] = -self.variogram_function(self.variogram_model_parameters, d) + np.fill_diagonal(a, 0.0) a[n, :] = 1.0 a[:, n] = 1.0 a[n, n] = 0.0 @@ -514,17 +592,17 @@ def _exec_vector(self, a, bd, mask): zero_value = True zero_index = np.where(np.absolute(bd) <= self.eps) - b = np.zeros((npt, n+1, 1)) - b[:, :n, 0] = - self.variogram_function(self.variogram_model_parameters, bd) + b = np.zeros((npt, n + 1, 1)) + b[:, :n, 0] = -self.variogram_function(self.variogram_model_parameters, bd) if zero_value: b[zero_index[0], zero_index[1], 0] = 0.0 b[:, n, 0] = 1.0 if (~mask).any(): - mask_b = np.repeat(mask[:, np.newaxis, np.newaxis], n+1, axis=1) + mask_b = np.repeat(mask[:, np.newaxis, np.newaxis], n + 1, axis=1) b = np.ma.array(b, mask=mask_b) - x = np.dot(a_inv, b.reshape((npt, n+1)).T).reshape((1, n+1, npt)).T + x = np.dot(a_inv, b.reshape((npt, n + 1)).T).reshape((1, n + 1, npt)).T kvalues = np.sum(x[:, :n, 0] * self.VALUES, axis=1) sigmasq = np.sum(x[:, :, 0] * -b[:, :, 0], axis=1) @@ -541,8 +619,10 @@ def _exec_loop(self, a, bd_all, mask): a_inv = scipy.linalg.inv(a) - for j in np.nonzero(~mask)[0]: # Note that this is the same thing as range(npt) if mask is not defined, - bd = bd_all[j] # otherwise it takes the non-masked elements. + for j in np.nonzero(~mask)[ + 0 + ]: # Note that this is the same thing as range(npt) if mask is not defined, + bd = bd_all[j] # otherwise it takes the non-masked elements. if np.any(np.absolute(bd) <= self.eps): zero_value = True zero_index = np.where(np.absolute(bd) <= self.eps) @@ -550,8 +630,8 @@ def _exec_loop(self, a, bd_all, mask): zero_value = False zero_index = None - b = np.zeros((n+1, 1)) - b[:n, 0] = - self.variogram_function(self.variogram_model_parameters, bd) + b = np.zeros((n + 1, 1)) + b[:n, 0] = -self.variogram_function(self.variogram_model_parameters, bd) if zero_value: b[zero_index[0], 0] = 0.0 b[n, 0] = 1.0 @@ -587,8 +667,8 @@ def _exec_loop_moving_window(self, a_all, bd_all, mask, bd_idx): else: zero_value = False zero_index = None - b = np.zeros((n+1, 1)) - b[:n, 0] = - self.variogram_function(self.variogram_model_parameters, bd) + b = np.zeros((n + 1, 1)) + b[:n, 0] = -self.variogram_function(self.variogram_model_parameters, bd) if zero_value: b[zero_index[0], 0] = 0.0 b[n, 0] = 1.0 @@ -596,12 +676,20 @@ def _exec_loop_moving_window(self, a_all, bd_all, mask, bd_idx): x = scipy.linalg.solve(a, b) kvalues[i] = x[:n, 0].dot(self.VALUES[b_selector]) - sigmasq[i] = - x[:, 0].dot(b[:, 0]) + sigmasq[i] = -x[:, 0].dot(b[:, 0]) return kvalues, sigmasq - def execute(self, style, xpoints, ypoints, zpoints, mask=None, - backend='vectorized', n_closest_points=None): + def execute( + self, + style, + xpoints, + ypoints, + zpoints, + mask=None, + backend="vectorized", + n_closest_points=None, + ): """Calculates a kriged grid and the associated variance. This is now the method that performs the main kriging calculation. @@ -687,9 +775,8 @@ def execute(self, style, xpoints, ypoints, zpoints, mask=None, if self.verbose: print("Executing Ordinary Kriging...\n") - if style != 'grid' and style != 'masked' and style != 'points': - raise ValueError("style argument must be 'grid', 'points', " - "or 'masked'") + if style != "grid" and style != "masked" and style != "points": + raise ValueError("style argument must be 'grid', 'points', or 'masked'") xpts = np.atleast_1d(np.squeeze(np.array(xpoints, copy=True))) ypts = np.atleast_1d(np.squeeze(np.array(ypoints, copy=True))) @@ -700,75 +787,93 @@ def execute(self, style, xpoints, ypoints, zpoints, mask=None, nz = zpts.size a = self._get_kriging_matrix(n) - if style in ['grid', 'masked']: - if style == 'masked': + if style in ["grid", "masked"]: + if style == "masked": if mask is None: - raise IOError("Must specify boolean masking array when " - "style is 'masked'.") + raise IOError( + "Must specify boolean masking array when style is 'masked'." + ) if mask.ndim != 3: raise ValueError("Mask is not three-dimensional.") if mask.shape[0] != nz or mask.shape[1] != ny or mask.shape[2] != nx: - if mask.shape[0] == nx and mask.shape[2] == nz and mask.shape[1] == ny: + if ( + mask.shape[0] == nx + and mask.shape[2] == nz + and mask.shape[1] == ny + ): mask = mask.swapaxes(0, 2) else: - raise ValueError("Mask dimensions do not match " - "specified grid dimensions.") + raise ValueError( + "Mask dimensions do not match specified grid dimensions." + ) mask = mask.flatten() npt = nz * ny * nx - grid_z, grid_y, grid_x = np.meshgrid(zpts, ypts, xpts, indexing='ij') + grid_z, grid_y, grid_x = np.meshgrid(zpts, ypts, xpts, indexing="ij") xpts = grid_x.flatten() ypts = grid_y.flatten() zpts = grid_z.flatten() - elif style == 'points': + elif style == "points": if xpts.size != ypts.size and ypts.size != zpts.size: - raise ValueError("xpoints, ypoints, and zpoints must have " - "same dimensions when treated as listing " - "discrete points.") + raise ValueError( + "xpoints, ypoints, and zpoints must have " + "same dimensions when treated as listing " + "discrete points." + ) npt = nx else: - raise ValueError("style argument must be 'grid', " - "'points', or 'masked'") - - xpts, ypts, zpts = \ - _adjust_for_anisotropy(np.vstack((xpts, ypts, zpts)).T, - [self.XCENTER, self.YCENTER, self.ZCENTER], - [self.anisotropy_scaling_y, self.anisotropy_scaling_z], - [self.anisotropy_angle_x, self.anisotropy_angle_y, self.anisotropy_angle_z]).T - - if style != 'masked': - mask = np.zeros(npt, dtype='bool') - - xyz_points = np.concatenate((zpts[:, np.newaxis], ypts[:, np.newaxis], - xpts[:, np.newaxis]), axis=1) - xyz_data = np.concatenate((self.Z_ADJUSTED[:, np.newaxis], - self.Y_ADJUSTED[:, np.newaxis], - self.X_ADJUSTED[:, np.newaxis]), axis=1) - bd = cdist(xyz_points, xyz_data, 'euclidean') + raise ValueError("style argument must be 'grid', 'points', or 'masked'") + + xpts, ypts, zpts = _adjust_for_anisotropy( + np.vstack((xpts, ypts, zpts)).T, + [self.XCENTER, self.YCENTER, self.ZCENTER], + [self.anisotropy_scaling_y, self.anisotropy_scaling_z], + [self.anisotropy_angle_x, self.anisotropy_angle_y, self.anisotropy_angle_z], + ).T + + if style != "masked": + mask = np.zeros(npt, dtype="bool") + + xyz_points = np.concatenate( + (zpts[:, np.newaxis], ypts[:, np.newaxis], xpts[:, np.newaxis]), axis=1 + ) + xyz_data = np.concatenate( + ( + self.Z_ADJUSTED[:, np.newaxis], + self.Y_ADJUSTED[:, np.newaxis], + self.X_ADJUSTED[:, np.newaxis], + ), + axis=1, + ) + bd = cdist(xyz_points, xyz_data, "euclidean") if n_closest_points is not None: from scipy.spatial import cKDTree + tree = cKDTree(xyz_data) bd, bd_idx = tree.query(xyz_points, k=n_closest_points, eps=0.0) - if backend == 'loop': - kvalues, sigmasq = \ - self._exec_loop_moving_window(a, bd, mask, bd_idx) + if backend == "loop": + kvalues, sigmasq = self._exec_loop_moving_window(a, bd, mask, bd_idx) else: - raise ValueError("Specified backend '{}' not supported " - "for moving window.".format(backend)) + raise ValueError( + "Specified backend '{}' not supported " + "for moving window.".format(backend) + ) else: - if backend == 'vectorized': + if backend == "vectorized": kvalues, sigmasq = self._exec_vector(a, bd, mask) - elif backend == 'loop': + elif backend == "loop": kvalues, sigmasq = self._exec_loop(a, bd, mask) else: - raise ValueError('Specified backend {} is not supported for ' - '3D ordinary kriging.'.format(backend)) + raise ValueError( + "Specified backend {} is not supported for " + "3D ordinary kriging.".format(backend) + ) - if style == 'masked': + if style == "masked": kvalues = np.ma.array(kvalues, mask=mask) sigmasq = np.ma.array(sigmasq, mask=mask) - if style in ['masked', 'grid']: + if style in ["masked", "grid"]: kvalues = kvalues.reshape((nz, ny, nx)) sigmasq = sigmasq.reshape((nz, ny, nx)) diff --git a/pykrige/rk.py b/pykrige/rk.py index b87f3c0..1f08ff2 100644 --- a/pykrige/rk.py +++ b/pykrige/rk.py @@ -1,5 +1,6 @@ # coding: utf-8 from pykrige.compat import validate_sklearn + validate_sklearn() from pykrige.ok import OrdinaryKriging from pykrige.uk import UniversalKriging @@ -9,19 +10,21 @@ from sklearn.svm import SVR from sklearn.metrics import r2_score -krige_methods = {'ordinary': OrdinaryKriging, - 'universal': UniversalKriging, - 'ordinary3d': OrdinaryKriging3D, - 'universal3d': UniversalKriging3D - } +krige_methods = { + "ordinary": OrdinaryKriging, + "universal": UniversalKriging, + "ordinary3d": OrdinaryKriging3D, + "universal3d": UniversalKriging3D, +} -threed_krige = ('ordinary3d', 'universal3d') +threed_krige = ("ordinary3d", "universal3d") def validate_method(method): if method not in krige_methods.keys(): - raise ValueError('Kriging method must be ' - 'one of {}'.format(krige_methods.keys())) + raise ValueError( + "Kriging method must be one of {}".format(krige_methods.keys()) + ) class Krige(RegressorMixin, BaseEstimator): @@ -32,13 +35,15 @@ class Krige(RegressorMixin, BaseEstimator): """ - def __init__(self, - method='ordinary', - variogram_model='linear', - nlags=6, - weight=False, - n_closest_points=10, - verbose=False): + def __init__( + self, + method="ordinary", + variogram_model="linear", + nlags=6, + weight=False, + n_closest_points=10, + verbose=False, + ): validate_method(method) self.variogram_model = variogram_model @@ -82,19 +87,17 @@ def fit(self, x, y, *args, **kwargs): **points ) - def _dimensionality_check(self, x, ext=''): - if self.method in ('ordinary', 'universal'): + def _dimensionality_check(self, x, ext=""): + if self.method in ("ordinary", "universal"): if x.shape[1] != 2: - raise ValueError('2d krige can use only 2d points') + raise ValueError("2d krige can use only 2d points") else: - return {'x' + ext: x[:, 0], 'y' + ext: x[:, 1]} - if self.method in ('ordinary3d', 'universal3d'): + return {"x" + ext: x[:, 0], "y" + ext: x[:, 1]} + if self.method in ("ordinary3d", "universal3d"): if x.shape[1] != 3: - raise ValueError('3d krige can use only 3d points') + raise ValueError("3d krige can use only 3d points") else: - return {'x' + ext: x[:, 0], - 'y' + ext: x[:, 1], - 'z' + ext: x[:, 2]} + return {"x" + ext: x[:, 0], "y" + ext: x[:, 1], "z" + ext: x[:, 2]} def predict(self, x, *args, **kwargs): """ @@ -109,9 +112,9 @@ def predict(self, x, *args, **kwargs): Prediction array """ if not self.model: - raise Exception('Not trained. Train first') + raise Exception("Not trained. Train first") - points = self._dimensionality_check(x, ext='points') + points = self._dimensionality_check(x, ext="points") return self.execute(points, *args, **kwargs)[0] @@ -127,26 +130,29 @@ def execute(self, points, *args, **kwargs): Prediction array Variance array """ - if isinstance(self.model, OrdinaryKriging) or \ - isinstance(self.model, OrdinaryKriging3D): - prediction, variance = \ - self.model.execute('points', - n_closest_points=self.n_closest_points, - backend='loop', - **points) + if isinstance(self.model, OrdinaryKriging) or isinstance( + self.model, OrdinaryKriging3D + ): + prediction, variance = self.model.execute( + "points", + n_closest_points=self.n_closest_points, + backend="loop", + **points + ) else: - print('n_closest_points will be ignored for UniversalKriging') - prediction, variance = \ - self.model.execute('points', backend='loop', **points) + print("n_closest_points will be ignored for UniversalKriging") + prediction, variance = self.model.execute( + "points", backend="loop", **points + ) return prediction, variance def check_sklearn_model(model): - if not (isinstance(model, BaseEstimator) and - isinstance(model, RegressorMixin)): - raise RuntimeError('Needs to supply an instance of a scikit-learn ' - 'regression class.') + if not (isinstance(model, BaseEstimator) and isinstance(model, RegressorMixin)): + raise RuntimeError( + "Needs to supply an instance of a scikit-learn regression class." + ) class RegressionKriging: @@ -155,14 +161,16 @@ class RegressionKriging: https://en.wikipedia.org/wiki/Regression-Kriging """ - def __init__(self, - regression_model=SVR(), - method='ordinary', - variogram_model='linear', - n_closest_points=10, - nlags=6, - weight=False, - verbose=False): + def __init__( + self, + regression_model=SVR(), + method="ordinary", + variogram_model="linear", + n_closest_points=10, + nlags=6, + weight=False, + verbose=False, + ): """ Parameters ---------- @@ -190,7 +198,7 @@ def __init__(self, weight=weight, n_closest_points=n_closest_points, verbose=verbose, - ) + ) def fit(self, p, x, y): """ @@ -210,10 +218,10 @@ def fit(self, p, x, y): """ self.regression_model.fit(p, y) ml_pred = self.regression_model.predict(p) - print('Finished learning regression model') + print("Finished learning regression model") # residual=y-ml_pred self.krige.fit(x=x, y=y - ml_pred) - print('Finished kriging residuals') + print("Finished kriging residuals") def predict(self, p, x): """ @@ -268,8 +276,6 @@ def score(self, p, x, y, sample_weight=None): array of targets (Ns, ) """ - return r2_score(y_pred=self.predict(p, x), - y_true=y, - sample_weight=sample_weight) - - + return r2_score( + y_pred=self.predict(p, x), y_true=y, sample_weight=sample_weight + ) diff --git a/pykrige/tests/__init__.py b/pykrige/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/pykrige/tests/test_api.py b/pykrige/tests/test_api.py deleted file mode 100644 index 9390228..0000000 --- a/pykrige/tests/test_api.py +++ /dev/null @@ -1,48 +0,0 @@ -from __future__ import absolute_import -from __future__ import print_function - -from itertools import product - -import numpy as np - -from pykrige.rk import Krige -from pykrige.rk import threed_krige -from pykrige.compat import GridSearchCV - - -def _method_and_vergiogram(): - method = ['ordinary', 'universal', 'ordinary3d', 'universal3d'] - variogram_model = ['linear', 'power', 'gaussian', 'spherical', - 'exponential'] - return product(method, variogram_model) - - -def test_krige(): - # dummy data - np.random.seed(1) - X = np.random.randint(0, 400, size=(20, 3)).astype(float) - y = 5 * np.random.rand(20) - - for m, v in _method_and_vergiogram(): - param_dict = {'method': [m], 'variogram_model': [v]} - - estimator = GridSearchCV(Krige(), - param_dict, - n_jobs=-1, - iid=False, - pre_dispatch='2*n_jobs', - verbose=False, - cv=5, - ) - # run the gridsearch - if m in ['ordinary', 'universal']: - estimator.fit(X=X[:, :2], y=y) - else: - estimator.fit(X=X, y=y) - if hasattr(estimator, 'best_score_'): - if m in threed_krige: - assert estimator.best_score_ > -10.0 - else: - assert estimator.best_score_ > -3.0 - if hasattr(estimator, 'cv_results_'): - assert estimator.cv_results_['mean_train_score'] > 0 diff --git a/pykrige/tests/test_core.py b/pykrige/tests/test_core.py deleted file mode 100755 index 5ff53be..0000000 --- a/pykrige/tests/test_core.py +++ /dev/null @@ -1,2122 +0,0 @@ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -""" -Testing code. -Updated BSM February 2017 -""" - -import os - -import numpy as np -import pytest -from pytest import approx -from numpy.testing import assert_allclose -from scipy.spatial.distance import cdist - -from pykrige import kriging_tools as kt -from pykrige import core -from pykrige import variogram_models -from pykrige.ok import OrdinaryKriging -from pykrige.uk import UniversalKriging -from pykrige.ok3d import OrdinaryKriging3D -from pykrige.uk3d import UniversalKriging3D - -BASE_DIR = os.path.abspath(os.path.dirname(__file__)) - -allclose_pars = {'rtol': 1e-05, 'atol': 1e-08} - - -@pytest.fixture -def validation_ref(): - - data = np.genfromtxt(os.path.join(BASE_DIR, 'test_data/test_data.txt')) - ok_test_answer, ok_test_gridx, ok_test_gridy, \ - cellsize, no_data = kt.read_asc_grid( - os.path.join(BASE_DIR, 'test_data/test1_answer.asc'), - footer=2) - uk_test_answer, uk_test_gridx, uk_test_gridy, \ - cellsize, no_data = kt.read_asc_grid( - os.path.join(BASE_DIR, 'test_data/test2_answer.asc'), - footer=2) - - return (data, (ok_test_answer, ok_test_gridx, ok_test_gridy), - (uk_test_answer, uk_test_gridx, uk_test_gridy)) - - -@pytest.fixture -def sample_data_2d(): - - data = np.array([[0.3, 1.2, 0.47], - [1.9, 0.6, 0.56], - [1.1, 3.2, 0.74], - [3.3, 4.4, 1.47], - [4.7, 3.8, 1.74]]) - gridx = np.arange(0.0, 6.0, 1.0) - gridx_2 = np.arange(0.0, 5.5, 0.5) - gridy = np.arange(0.0, 5.5, 0.5) - xi, yi = np.meshgrid(gridx, gridy) - mask = np.array(xi == yi) - return data, (gridx, gridy, gridx_2), mask - - -@pytest.fixture -def sample_data_3d(): - data = np.array([[0.1, 0.1, 0.3, 0.9], - [0.2, 0.1, 0.4, 0.8], - [0.1, 0.3, 0.1, 0.9], - [0.5, 0.4, 0.4, 0.5], - [0.3, 0.3, 0.2, 0.7]]) - gridx = np.arange(0.0, 0.6, 0.05) - gridy = np.arange(0.0, 0.6, 0.01) - gridz = np.arange(0.0, 0.6, 0.1) - zi, yi, xi = np.meshgrid(gridz, gridy, gridx, indexing='ij') - mask = np.array((xi == yi) & (yi == zi)) - return data, (gridx, gridy, gridz), mask - - -def test_core_adjust_for_anisotropy(): - - X = np.array([[1.0, 0.0, -1.0, 0.0], - [0.0, 1.0, 0.0, -1.0]]).T - X_adj = core._adjust_for_anisotropy(X, [0.0, 0.0], [2.0], [90.0]) - assert_allclose(X_adj[:, 0], - np.array([0.0, 1.0, 0.0, -1.0]), **allclose_pars) - assert_allclose(X_adj[:, 1], - np.array([-2.0, 0.0, 2.0, 0.0]), **allclose_pars) - - -def test_core_adjust_for_anisotropy_3d(): - - # this is a bad examples, as the X matrix is symmetric - # and insensitive to transpositions - X = np.array([[1.0, 0.0, 0.0], - [0.0, 1.0, 0.0], - [0.0, 0.0, 1.0]]).T - X_adj = core._adjust_for_anisotropy(X, [0., 0., 0.], [2., 2.], - [90., 0., 0.]) - assert_allclose(X_adj[:, 0], np.array([1., 0., 0.]), **allclose_pars) - assert_allclose(X_adj[:, 1], np.array([0., 0., 2.]), **allclose_pars) - assert_allclose(X_adj[:, 2], np.array([0., -2., 0.]), **allclose_pars) - X_adj = core._adjust_for_anisotropy(X, [0., 0., 0.], [2., 2.], - [0., 90., 0.]) - assert_allclose(X_adj[:, 0], np.array([0., 0., -1.]), **allclose_pars) - assert_allclose(X_adj[:, 1], np.array([0., 2., 0.]), **allclose_pars) - assert_allclose(X_adj[:, 2], np.array([2., 0., 0.]), **allclose_pars) - X_adj = core._adjust_for_anisotropy(X, [0., 0., 0.], [2., 2.], - [0., 0., 90.]) - assert_allclose(X_adj[:, 0], np.array([0., 1., 0.]), **allclose_pars) - assert_allclose(X_adj[:, 1], np.array([-2., 0., 0.]), **allclose_pars) - assert_allclose(X_adj[:, 2], np.array([0., 0., 2.]), **allclose_pars) - - -def test_core_make_variogram_parameter_list(): - - # test of first case - variogram_model_parameters is None - # function should return None unaffected - result = core._make_variogram_parameter_list('linear', None) - assert result is None - - # tests for second case - variogram_model_parameters is dict - with pytest.raises(KeyError): - core._make_variogram_parameter_list( - 'linear', {'tacos': 1., 'burritos': 2.}) - result = core._make_variogram_parameter_list('linear', {'slope': 1., - 'nugget': 0.}) - assert result == [1., 0.] - - with pytest.raises(KeyError): - core._make_variogram_parameter_list( - 'power', {'frijoles': 1.}) - result = core._make_variogram_parameter_list('power', {'scale': 2., - 'exponent': 1., - 'nugget': 0.}) - assert result == [2., 1., 0.] - - with pytest.raises(KeyError): - core._make_variogram_parameter_list( - 'exponential', {'tacos': 1.}) - with pytest.raises(KeyError): - core._make_variogram_parameter_list( - 'exponential', {'range': 1., 'nugget': 1.}) - result = core._make_variogram_parameter_list('exponential', - {'sill': 5., 'range': 2., - 'nugget': 1.}) - assert result == [4., 2., 1.] - result = core._make_variogram_parameter_list('exponential', - {'psill': 4., 'range': 2., - 'nugget': 1.}) - assert result == [4., 2., 1.] - - with pytest.raises(TypeError): - core._make_variogram_parameter_list('custom', {'junk': 1.}) - with pytest.raises(ValueError): - core._make_variogram_parameter_list('blarg', {'junk': 1.}) - - # tests for third case - variogram_model_parameters is list - with pytest.raises(ValueError): - core._make_variogram_parameter_list( - 'linear', [1., 2., 3.]) - result = core._make_variogram_parameter_list('linear', [1., 2.]) - assert result == [1., 2.] - - with pytest.raises(ValueError): - core._make_variogram_parameter_list('power', [1., 2.]) - - result = core._make_variogram_parameter_list('power', [1., 2., 3.]) - assert result == [1., 2., 3.] - - with pytest.raises(ValueError): - core._make_variogram_parameter_list( - 'exponential', [1., 2., 3., 4.]) - result = core._make_variogram_parameter_list('exponential', - [5., 2., 1.]) - assert result == [4., 2., 1.] - - result = core._make_variogram_parameter_list('custom', [1., 2., 3.]) - assert result == [1., 2., 3] - - with pytest.raises(ValueError): - core._make_variogram_parameter_list('junk', [1., 1., 1.]) - - # test for last case - make sure function handles incorrect - # variogram_model_parameters type appropriately - with pytest.raises(TypeError): - core._make_variogram_parameter_list('linear', 'tacos') - - -def test_core_initialize_variogram_model(validation_ref): - - data, _, _ = validation_ref - - # Note the variogram_function argument is not a string in real life... - # core._initialize_variogram_model also checks the length of input - # lists, which is redundant now because the same tests are done in - # core._make_variogram_parameter_list - with pytest.raises(ValueError): - core._initialize_variogram_model( - np.vstack((data[:, 0], data[:, 1])).T, - data[:, 2], 'linear', [0.0], 'linear', - 6, False, 'euclidean') - with pytest.raises(ValueError): - core._initialize_variogram_model( - np.vstack((data[:, 0], data[:, 1])).T, - data[:, 2], 'spherical', [0.0], - 'spherical', 6, False, 'euclidean') - - # core._initialize_variogram_model does also check coordinate type, - # this is NOT redundant - with pytest.raises(ValueError): - core._initialize_variogram_model( - np.vstack((data[:, 0], data[:, 1])).T, - data[:, 2], 'spherical', [0.0, 0.0, 0.0], - 'spherical', 6, False, 'tacos') - - x = np.array([1.0 + n/np.sqrt(2) for n in range(4)]) - y = np.array([1.0 + n/np.sqrt(2) for n in range(4)]) - z = np.arange(1.0, 5.0, 1.0) - lags, semivariance, variogram_model_parameters = \ - core._initialize_variogram_model(np.vstack((x, y)).T, z, - 'linear', [0.0, 0.0], - 'linear', 6, False, 'euclidean') - - assert_allclose(lags, np.array([1.0, 2.0, 3.0])) - assert_allclose(semivariance, np.array([0.5, 2.0, 4.5])) - - -def test_core_initialize_variogram_model_3d(sample_data_3d): - - data, _, _ = sample_data_3d - - # Note the variogram_function argument is not a string in real life... - # again, these checks in core._initialize_variogram_model are redundant - # now because the same tests are done in - # core._make_variogram_parameter_list - with pytest.raises(ValueError): - core._initialize_variogram_model( - np.vstack((data[:, 0], data[:, 1], data[:, 2])).T, - data[:, 3], 'linear', [0.0], 'linear', - 6, False, 'euclidean') - - with pytest.raises(ValueError): - core._initialize_variogram_model( - np.vstack((data[:, 0], data[:, 1], data[:, 2])).T, - data[:, 3], 'spherical', [0.0], - 'spherical', 6, False, 'euclidean') - - with pytest.raises(ValueError): - core._initialize_variogram_model( - np.vstack((data[:, 0], data[:, 1], data[:, 2])).T, - data[:, 3], 'linear', [0.0, 0.0], - 'linear', 6, False, 'geographic') - - lags, semivariance, variogram_model_parameters = \ - core._initialize_variogram_model( - np.vstack((np.array([1., 2., 3., 4.]), - np.array([1., 2., 3., 4.]), - np.array([1., 2., 3., 4.]))).T, - np.array([1., 2., 3., 4.]), 'linear', [0.0, 0.0], 'linear', 3, - False, 'euclidean') - assert_allclose(lags, np.array([np.sqrt(3.), 2.*np.sqrt(3.), - 3.*np.sqrt(3.)])) - assert_allclose(semivariance, np.array([0.5, 2.0, 4.5])) - - -def test_core_calculate_variogram_model(): - - res = core._calculate_variogram_model( - np.array([1.0, 2.0, 3.0, 4.0]), np.array([2.05, 2.95, 4.05, 4.95]), - 'linear', variogram_models.linear_variogram_model, False) - assert_allclose(res, np.array([0.98, 1.05]), 0.01, 0.01) - - res = core._calculate_variogram_model( - np.array([1.0, 2.0, 3.0, 4.0]), np.array([2.05, 2.95, 4.05, 4.95]), - 'linear', variogram_models.linear_variogram_model, True) - assert_allclose(res, np.array([0.98, 1.05]), 0.01, 0.01) - - res = core._calculate_variogram_model( - np.array([1., 2., 3., 4.]), np.array([1., 2.8284271, - 5.1961524, 8.]), - 'power', variogram_models.power_variogram_model, False) - assert_allclose(res, np.array([1., 1.5, 0.]), 0.001, 0.001) - - res = core._calculate_variogram_model( - np.array([1., 2., 3., 4.]), np.array([1.0, 1.4142, 1.7321, 2.0]), - 'power', variogram_models.power_variogram_model, False) - assert_allclose(res, np.array([1., 0.5, 0.]), 0.001, 0.001) - - res = core._calculate_variogram_model( - np.array([1., 2., 3., 4.]), np.array([1.2642, 1.7293, - 1.9004, 1.9634]), - 'exponential', variogram_models.exponential_variogram_model, False) - assert_allclose(res, np.array([2., 3., 0.]), 0.001, 0.001) - - res = core._calculate_variogram_model( - np.array([1., 2., 3., 4.]), np.array([0.5769, 1.4872, - 1.9065, 1.9914]), - 'gaussian', variogram_models.gaussian_variogram_model, False) - assert_allclose(res, np.array([2., 3., 0.]), 0.001, 0.001) - - res = core._calculate_variogram_model( - np.array([1., 2., 3., 4.]), np.array([3.33060952, 3.85063879, - 3.96667301, 3.99256374]), - 'exponential', variogram_models.exponential_variogram_model, False) - assert_allclose(res, np.array([3., 2., 1.]), 0.001, 0.001) - - res = core._calculate_variogram_model( - np.array([1., 2., 3., 4.]), np.array([2.60487044, 3.85968813, - 3.99694817, 3.99998564]), - 'gaussian', variogram_models.gaussian_variogram_model, False) - assert_allclose(res, np.array([3., 2., 1.]), 0.001, 0.001) - - -def test_core_krige(): - - # Example 3.2 from Kitanidis - data = np.array([[9.7, 47.6, 1.22], - [43.8, 24.6, 2.822]]) - z, ss = core._krige(np.vstack((data[:, 0], data[:, 1])).T, data[:, 2], - np.array([18.8, 67.9]), - variogram_models.linear_variogram_model, - [0.006, 0.1], 'euclidean') - assert z == approx(1.6364, rel=1e-4) - assert ss == approx(0.4201, rel=1e-4) - - z, ss = core._krige(np.vstack((data[:, 0], data[:, 1])).T, data[:, 2], - np.array([43.8, 24.6]), - variogram_models.linear_variogram_model, - [0.006, 0.1], 'euclidean') - assert z == approx(2.822, rel=1e-3) - assert ss == approx(0.0, rel=1e-3) - - -def test_core_krige_3d(): - - # Adapted from example 3.2 from Kitanidis - data = np.array([[9.7, 47.6, 1.0, 1.22], - [43.8, 24.6, 1.0, 2.822]]) - z, ss = core._krige(np.vstack((data[:, 0], data[:, 1], data[:, 2])).T, - data[:, 3], np.array([18.8, 67.9, 1.0]), - variogram_models.linear_variogram_model, - [0.006, 0.1], 'euclidean') - assert z == approx(1.6364, rel=1e-4) - assert ss == approx(0.4201, rel=1e-4) - - z, ss = core._krige(np.vstack((data[:, 0], data[:, 1], data[:, 2])).T, - data[:, 3], np.array([43.8, 24.6, 1.0]), - variogram_models.linear_variogram_model, - [0.006, 0.1], 'euclidean') - assert z == approx(2.822, rel=1e-3) - assert ss == approx(0.0, rel=1e-3) - - -def test_ok(validation_ref): - - # Test to compare OK results to those obtained using KT3D_H2O. - # (M. Karanovic, M. Tonkin, and D. Wilson, 2009, Groundwater, - # vol. 47, no. 4, 580-586.) - - data, (ok_test_answer, gridx, gridy), _ = validation_ref - - ok = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='exponential', - variogram_parameters=[500.0, 3000.0, 0.0]) - z, ss = ok.execute('grid', gridx, gridy, backend='vectorized') - assert_allclose(z, ok_test_answer) - z, ss = ok.execute('grid', gridx, gridy, backend='loop') - assert_allclose(z, ok_test_answer) - - -def test_ok_update_variogram_model(validation_ref): - - data, (ok_test_answer, gridx, gridy), _ = validation_ref - - with pytest.raises(ValueError): - OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='blurg') - - ok = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2]) - variogram_model = ok.variogram_model - variogram_parameters = ok.variogram_model_parameters - anisotropy_scaling = ok.anisotropy_scaling - anisotropy_angle = ok.anisotropy_angle - - with pytest.raises(ValueError): - ok.update_variogram_model('blurg') - - ok.update_variogram_model('power', anisotropy_scaling=3.0, - anisotropy_angle=45.0) - - # TODO: check that new parameters equal to the set parameters - assert variogram_model != ok.variogram_model - assert variogram_parameters != ok.variogram_model_parameters - assert anisotropy_scaling != ok.anisotropy_scaling - assert anisotropy_angle != ok.anisotropy_angle - - -def test_ok_get_variogram_points(validation_ref): - # Test to compare the variogram of OK results to those obtained using - # KT3D_H2O. - # (M. Karanovic, M. Tonkin, and D. Wilson, 2009, Groundwater, - # vol. 47, no. 4, 580-586.) - - # Variogram parameters - _variogram_parameters = [500.0, 3000.0, 0.0] - - data, _, (ok_test_answer, gridx, gridy) = validation_ref - - ok = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='exponential', - variogram_parameters=_variogram_parameters) - - # Get the variogram points from the UniversalKriging instance - lags, calculated_variogram = ok.get_variogram_points() - - # Generate the expected variogram points according to the - # exponential variogram model - expected_variogram = variogram_models.exponential_variogram_model( - _variogram_parameters, lags) - - assert_allclose(calculated_variogram, expected_variogram) - - -def test_ok_execute(sample_data_2d): - - data, (gridx, gridy, _), mask_ref = sample_data_2d - - ok = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2]) - - with pytest.raises(ValueError): - ok.execute('blurg', gridx, gridy) - - z, ss = ok.execute('grid', gridx, gridy, backend='vectorized') - shape = (gridy.size, gridx.size) - assert z.shape == shape - assert ss.shape == shape - assert np.amax(z) != np.amin(z) - assert np.amax(ss) != np.amin(ss) - assert not np.ma.is_masked(z) - - z, ss = ok.execute('grid', gridx, gridy, backend='loop') - shape = (gridy.size, gridx.size) - assert z.shape == shape - assert ss.shape == shape - assert np.amax(z) != np.amin(z) - assert np.amax(ss) != np.amin(ss) - assert not np.ma.is_masked(z) - - with pytest.raises(IOError): - ok.execute('masked', gridx, gridy, backend='vectorized') - mask = np.array([True, False]) - with pytest.raises(ValueError): - ok.execute('masked', gridx, gridy, mask=mask, - backend='vectorized') - z, ss = ok.execute('masked', gridx, gridy, mask=mask_ref, - backend='vectorized') - assert (np.ma.is_masked(z)) - assert (np.ma.is_masked(ss)) - assert z[0, 0] is np.ma.masked - assert ss[0, 0] is np.ma.masked - z, ss = ok.execute('masked', gridx, gridy, mask=mask_ref.T, - backend='vectorized') - assert (np.ma.is_masked(z)) - assert (np.ma.is_masked(ss)) - assert z[0, 0] is np.ma.masked - assert ss[0, 0] is np.ma.masked - - with pytest.raises(IOError): - ok.execute('masked', gridx, gridy, backend='loop') - mask = np.array([True, False]) - with pytest.raises(ValueError): - ok.execute('masked', gridx, gridy, mask=mask, - backend='loop') - z, ss = ok.execute('masked', gridx, gridy, mask=mask_ref, - backend='loop') - assert (np.ma.is_masked(z)) - assert (np.ma.is_masked(ss)) - assert z[0, 0] is np.ma.masked - assert ss[0, 0] is np.ma.masked - z, ss = ok.execute('masked', gridx, gridy, mask=mask_ref.T, backend='loop') - assert (np.ma.is_masked(z)) - assert (np.ma.is_masked(ss)) - assert z[0, 0] is np.ma.masked - assert ss[0, 0] is np.ma.masked - - with pytest.raises(ValueError): - ok.execute('points', np.array([0.0, 1.0, 2.0]), np.array([0.0, 1.0]), - backend='vectorized') - z, ss = ok.execute('points', gridx[0], gridy[0], backend='vectorized') - assert z.shape == (1,) - assert ss.shape == (1,) - - with pytest.raises(ValueError): - ok.execute('points', np.array([0.0, 1.0, 2.0]), np.array([0.0, 1.0]), - backend='loop') - z, ss = ok.execute('points', gridx[0], gridy[0], backend='loop') - assert z.shape == (1,) - assert ss.shape == (1,) - - -def test_cython_ok(sample_data_2d): - data, (gridx, gridy, _), mask_ref = sample_data_2d - - ok = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2]) - z1, ss1 = ok.execute('grid', gridx, gridy, backend='loop') - z2, ss2 = ok.execute('grid', gridx, gridy, backend='C') - assert_allclose(z1, z2) - assert_allclose(ss1, ss2) - - closest_points = 4 - - z1, ss1 = ok.execute('grid', gridx, gridy, backend='loop', - n_closest_points=closest_points) - z2, ss2 = ok.execute('grid', gridx, gridy, backend='C', - n_closest_points=closest_points) - assert_allclose(z1, z2) - assert_allclose(ss1, ss2) - - -def test_uk(validation_ref): - - # Test to compare UK with linear drift to results from KT3D_H2O. - # (M. Karanovic, M. Tonkin, and D. Wilson, 2009, Groundwater, - # vol. 47, no. 4, 580-586.) - - data, _, (uk_test_answer, gridx, gridy) = validation_ref - - uk = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='exponential', - variogram_parameters=[500.0, 3000.0, 0.0], - drift_terms=['regional_linear']) - z, ss = uk.execute('grid', gridx, gridy, backend='vectorized') - assert_allclose(z, uk_test_answer) - z, ss = uk.execute('grid', gridx, gridy, backend='loop') - assert_allclose(z, uk_test_answer) - - -def test_uk_update_variogram_model(sample_data_2d): - - data, (gridx, gridy, _), mask_ref = sample_data_2d - - with pytest.raises(ValueError): - UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='blurg') - with pytest.raises(ValueError): - UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - drift_terms=['external_Z']) - with pytest.raises(ValueError): - UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - drift_terms=['external_Z'], - external_drift=np.array([0])) - with pytest.raises(ValueError): - UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - drift_terms=['point_log']) - - uk = UniversalKriging(data[:, 0], data[:, 1], data[:, 2]) - variogram_model = uk.variogram_model - variogram_parameters = uk.variogram_model_parameters - anisotropy_scaling = uk.anisotropy_scaling - anisotropy_angle = uk.anisotropy_angle - - with pytest.raises(ValueError): - uk.update_variogram_model('blurg') - uk.update_variogram_model('power', anisotropy_scaling=3.0, - anisotropy_angle=45.0) - # TODO: check that the new parameters are equal to the expected ones - assert variogram_model != uk.variogram_model - assert variogram_parameters != uk.variogram_model_parameters - assert anisotropy_scaling != uk.anisotropy_scaling - assert anisotropy_angle != uk.anisotropy_angle - - -def test_uk_get_variogram_points(validation_ref): - # Test to compare the variogram of UK with linear drift to results from - # KT3D_H2O. - # (M. Karanovic, M. Tonkin, and D. Wilson, 2009, Groundwater, - # vol. 47, no. 4, 580-586.) - - # Variogram parameters - _variogram_parameters = [500.0, 3000.0, 0.0] - - data, _, (uk_test_answer, gridx, gridy) = validation_ref - - uk = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='exponential', - variogram_parameters=_variogram_parameters, - drift_terms=['regional_linear']) - - # Get the variogram points from the UniversalKriging instance - lags, calculated_variogram = uk.get_variogram_points() - - # Generate the expected variogram points according to the - # exponential variogram model - expected_variogram = variogram_models.exponential_variogram_model( - _variogram_parameters, lags) - - assert_allclose(calculated_variogram, expected_variogram) - - -def test_uk_calculate_data_point_zscalars(sample_data_2d): - - data, (gridx, gridy, _), mask_ref = sample_data_2d - - dem = np.arange(0.0, 5.1, 0.1) - dem = np.repeat(dem[np.newaxis, :], 6, axis=0) - dem_x = np.arange(0.0, 5.1, 0.1) - dem_y = np.arange(0.0, 6.0, 1.0) - - with pytest.raises(ValueError): - UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - variogram_parameters=[1.0, 0.0], - drift_terms=['external_Z']) - with pytest.raises(ValueError): - UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - variogram_parameters=[1.0, 0.0], - drift_terms=['external_Z'], external_drift=dem) - with pytest.raises(ValueError): - UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - variogram_parameters=[1.0, 0.0], - drift_terms=['external_Z'], - external_drift=dem, external_drift_x=dem_x, - external_drift_y=np.arange(0.0, 5.0, 1.0)) - - uk = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - variogram_parameters=[1.0, 0.0], - drift_terms=['external_Z'], - external_drift=dem, external_drift_x=dem_x, - external_drift_y=dem_y) - assert_allclose(uk.z_scalars, data[:, 0]) - - xi, yi = np.meshgrid(np.arange(0.0, 5.3, 0.1), gridy) - with pytest.raises(ValueError): - uk._calculate_data_point_zscalars(xi, yi) - - xi, yi = np.meshgrid(np.arange(0.0, 5.0, 0.1), gridy) - z_scalars = uk._calculate_data_point_zscalars(xi, yi) - assert_allclose(z_scalars[0, :], np.arange(0.0, 5.0, 0.1)) - - -def test_uk_execute_single_point(): - - # Test data and answer from lecture notes by Nicolas Christou, UCLA Stats - data = np.array([[61.0, 139.0, 477.0], - [63.0, 140.0, 696.0], - [64.0, 129.0, 227.0], - [68.0, 128.0, 646.0], - [71.0, 140.0, 606.0], - [73.0, 141.0, 791.0], - [75.0, 128.0, 783.0]]) - point = (65.0, 137.0) - z_answer = 567.54 - ss_answer = 9.044 - - uk = UniversalKriging( - data[:, 0], data[:, 1], data[:, 2], variogram_model='exponential', - variogram_parameters=[10.0, 9.99, 0.0], - drift_terms=['regional_linear']) - z, ss = uk.execute('points', np.array([point[0]]), np.array([point[1]]), - backend='vectorized') - assert z_answer == approx(z[0], rel=0.1) - assert ss_answer == approx(ss[0], rel=0.1) - - z, ss = uk.execute('points', np.array([61.0]), np.array([139.0]), - backend='vectorized') - assert z[0] == approx(477.0, rel=1e-3) - assert ss[0] == approx(0.0, rel=1e-3) - - z, ss = uk.execute('points', np.array([61.0]), np.array([139.0]), - backend='loop') - assert z[0] == approx(477.0, rel=1e-3) - assert ss[0] == approx(0.0, rel=1e-3) - - -def test_uk_execute(sample_data_2d): - - data, (gridx, gridy, _), mask_ref = sample_data_2d - - uk = UniversalKriging( - data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', drift_terms=['regional_linear']) - - with pytest.raises(ValueError): - uk.execute('blurg', gridx, gridy) - with pytest.raises(ValueError): - uk.execute('grid', gridx, gridy, backend='mrow') - - z, ss = uk.execute('grid', gridx, gridy, backend='vectorized') - shape = (gridy.size, gridx.size) - assert z.shape == shape - assert ss.shape == shape - assert np.amax(z) != np.amin(z) - assert np.amax(ss) != np.amin(ss) - assert not np.ma.is_masked(z) - - z, ss = uk.execute('grid', gridx, gridy, backend='loop') - shape = (gridy.size, gridx.size) - assert z.shape == shape - assert ss.shape == shape - assert np.amax(z) != np.amin(z) - assert np.amax(ss) != np.amin(ss) - assert not np.ma.is_masked(z) - - with pytest.raises(IOError): - uk.execute('masked', gridx, gridy, backend='vectorized') - mask = np.array([True, False]) - with pytest.raises(ValueError): - uk.execute('masked', gridx, gridy, mask=mask, - backend='vectorized') - z, ss = uk.execute('masked', gridx, gridy, mask=mask_ref, - backend='vectorized') - assert (np.ma.is_masked(z)) - assert (np.ma.is_masked(ss)) - assert z[0, 0] is np.ma.masked - assert ss[0, 0] is np.ma.masked - z, ss = uk.execute('masked', gridx, gridy, mask=mask_ref.T, - backend='vectorized') - assert (np.ma.is_masked(z)) - assert (np.ma.is_masked(ss)) - assert z[0, 0] is np.ma.masked - assert ss[0, 0] is np.ma.masked - - with pytest.raises(IOError): - uk.execute('masked', gridx, gridy, backend='loop') - mask = np.array([True, False]) - with pytest.raises(ValueError): - uk.execute('masked', gridx, gridy, mask=mask, - backend='loop') - z, ss = uk.execute('masked', gridx, gridy, mask=mask_ref, backend='loop') - assert (np.ma.is_masked(z)) - assert (np.ma.is_masked(ss)) - assert z[0, 0] is np.ma.masked - assert ss[0, 0] is np.ma.masked - z, ss = uk.execute('masked', gridx, gridy, mask=mask_ref.T, backend='loop') - assert (np.ma.is_masked(z)) - assert (np.ma.is_masked(ss)) - assert z[0, 0] is np.ma.masked - assert ss[0, 0] is np.ma.masked - - with pytest.raises(ValueError): - uk.execute('points', np.array([0.0, 1.0, 2.0]), np.array([0.0, 1.0]), - backend='vectorized') - z, ss = uk.execute('points', gridx[0], gridy[0], backend='vectorized') - assert z.shape == (1,) - assert ss.shape == (1,) - - with pytest.raises(ValueError): - uk.execute('points', np.array([0.0, 1.0, 2.0]), np.array([0.0, 1.0]), - backend='loop') - z, ss = uk.execute('points', gridx[0], gridy[0], backend='loop') - assert z.shape == (1,) - assert ss.shape == (1,) - - -def test_ok_uk_produce_same_result(validation_ref): - - data, _, (uk_test_answer, gridx_ref, gridy_ref) = validation_ref - - gridx = np.linspace(1067000.0, 1072000.0, 100) - gridy = np.linspace(241500.0, 244000.0, 100) - ok = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', verbose=False, - enable_plotting=False) - z_ok, ss_ok = ok.execute('grid', gridx, gridy, backend='vectorized') - uk = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', verbose=False, - enable_plotting=False) - z_uk, ss_uk = uk.execute('grid', gridx, gridy, backend='vectorized') - assert_allclose(z_ok, z_uk) - assert_allclose(ss_ok, ss_uk) - - z_ok, ss_ok = ok.execute('grid', gridx, gridy, backend='loop') - z_uk, ss_uk = uk.execute('grid', gridx, gridy, backend='loop') - assert_allclose(z_ok, z_uk) - assert_allclose(ss_ok, ss_uk) - - -def test_ok_backends_produce_same_result(validation_ref): - - data, _, (uk_test_answer, gridx_ref, gridy_ref) = validation_ref - - gridx = np.linspace(1067000.0, 1072000.0, 100) - gridy = np.linspace(241500.0, 244000.0, 100) - ok = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', verbose=False, - enable_plotting=False) - z_ok_v, ss_ok_v = ok.execute('grid', gridx, gridy, backend='vectorized') - z_ok_l, ss_ok_l = ok.execute('grid', gridx, gridy, backend='loop') - assert_allclose(z_ok_v, z_ok_l) - assert_allclose(ss_ok_v, ss_ok_l) - - -def test_uk_backends_produce_same_result(validation_ref): - - data, _, (uk_test_answer, gridx_ref, gridy_ref) = validation_ref - - gridx = np.linspace(1067000.0, 1072000.0, 100) - gridy = np.linspace(241500.0, 244000.0, 100) - uk = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', verbose=False, - enable_plotting=False) - z_uk_v, ss_uk_v = uk.execute('grid', gridx, gridy, backend='vectorized') - z_uk_l, ss_uk_l = uk.execute('grid', gridx, gridy, backend='loop') - assert_allclose(z_uk_v, z_uk_l) - assert_allclose(ss_uk_v, ss_uk_l) - - -def test_kriging_tools(sample_data_2d): - - data, (gridx, gridy, gridx_2), mask_ref = sample_data_2d - - ok = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2]) - z_write, ss_write = ok.execute('grid', gridx, gridy) - - kt.write_asc_grid(gridx, gridy, z_write, - filename=os.path.join(BASE_DIR, 'test_data/temp.asc'), - style=1) - z_read, x_read, y_read, cellsize, no_data = kt.read_asc_grid( - os.path.join(BASE_DIR, 'test_data/temp.asc')) - assert_allclose(z_write, z_read, 0.01, 0.01) - assert_allclose(gridx, x_read) - assert_allclose(gridy, y_read) - - z_write, ss_write = ok.execute('masked', gridx, gridy, mask=mask_ref) - kt.write_asc_grid(gridx, gridy, z_write, - filename=os.path.join(BASE_DIR, 'test_data/temp.asc'), - style=1) - z_read, x_read, y_read, cellsize, no_data = kt.read_asc_grid( - os.path.join(BASE_DIR, 'test_data/temp.asc')) - assert (np.ma.allclose(z_write, - np.ma.masked_where(z_read == no_data, z_read), - masked_equal=True, rtol=0.01, atol=0.01)) - assert_allclose(gridx, x_read) - assert_allclose(gridy, y_read) - - ok = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2]) - z_write, ss_write = ok.execute('grid', gridx_2, gridy) - - kt.write_asc_grid(gridx_2, gridy, z_write, - filename=os.path.join(BASE_DIR, 'test_data/temp.asc'), - style=2) - z_read, x_read, y_read, cellsize, no_data = kt.read_asc_grid( - os.path.join(BASE_DIR, 'test_data/temp.asc')) - assert_allclose(z_write, z_read, 0.01, 0.01) - assert_allclose(gridx_2, x_read) - assert_allclose(gridy, y_read) - - os.remove(os.path.join(BASE_DIR, 'test_data/temp.asc')) - - -def test_uk_three_primary_drifts(sample_data_2d): - - data, (gridx, gridy, gridx_2), mask_ref = sample_data_2d - - well = np.array([[1.1, 1.1, -1.0]]) - dem = np.arange(0.0, 5.1, 0.1) - dem = np.repeat(dem[np.newaxis, :], 6, axis=0) - dem_x = np.arange(0.0, 5.1, 0.1) - dem_y = np.arange(0.0, 6.0, 1.0) - - uk = UniversalKriging( - data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - drift_terms=['regional_linear', 'external_Z', 'point_log'], - point_drift=well, external_drift=dem, - external_drift_x=dem_x, external_drift_y=dem_y) - - z, ss = uk.execute('grid', gridx, gridy, backend='vectorized') - assert z.shape == (gridy.shape[0], gridx.shape[0]) - assert ss.shape == (gridy.shape[0], gridx.shape[0]) - assert np.all(np.isfinite(z)) - assert not np.all(np.isnan(z)) - assert np.all(np.isfinite(ss)) - assert not np.all(np.isnan(ss)) - - z, ss = uk.execute('grid', gridx, gridy, backend='loop') - assert z.shape == (gridy.shape[0], gridx.shape[0]) - assert ss.shape == (gridy.shape[0], gridx.shape[0]) - assert np.all(np.isfinite(z)) - assert not np.all(np.isnan(z)) - assert np.all(np.isfinite(ss)) - assert not np.all(np.isnan(ss)) - - -def test_uk_specified_drift(sample_data_2d): - - data, (gridx, gridy, gridx_2), mask_ref = sample_data_2d - - xg, yg = np.meshgrid(gridx, gridy) - well = np.array([[1.1, 1.1, -1.0]]) - point_log = well[0, 2] * np.log(np.sqrt((xg - well[0, 0])**2. - + (yg - well[0, 1])**2.)) * -1. - if np.any(np.isinf(point_log)): - point_log[np.isinf(point_log)] = -100. * well[0, 2] * -1. - point_log_data = well[0, 2] * np.log( - np.sqrt((data[:, 0] - well[0, 0])**2. + - (data[:, 1] - well[0, 1])**2.)) * -1. - if np.any(np.isinf(point_log_data)): - point_log_data[np.isinf(point_log_data)] = -100. * well[0, 2] * -1. - - with pytest.raises(ValueError): - UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', drift_terms=['specified']) - with pytest.raises(TypeError): - UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', drift_terms=['specified'], - specified_drift=data[:, 0]) - with pytest.raises(ValueError): - UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', drift_terms=['specified'], - specified_drift=[data[:2, 0]]) - - uk_spec = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - drift_terms=['specified'], - specified_drift=[data[:, 0], data[:, 1]]) - with pytest.raises(ValueError): - uk_spec.execute('grid', gridx, gridy, - specified_drift_arrays=[gridx, gridy]) - with pytest.raises(TypeError): - uk_spec.execute('grid', gridx, gridy, - specified_drift_arrays=gridx) - with pytest.raises(ValueError): - uk_spec.execute('grid', gridx, gridy, - specified_drift_arrays=[xg]) - z_spec, ss_spec = uk_spec.execute('grid', gridx, gridy, - specified_drift_arrays=[xg, yg]) - - uk_lin = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - drift_terms=['regional_linear']) - z_lin, ss_lin = uk_lin.execute('grid', gridx, gridy) - - assert_allclose(z_spec, z_lin) - assert_allclose(ss_spec, ss_lin) - - uk_spec = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - drift_terms=['specified'], - specified_drift=[point_log_data]) - z_spec, ss_spec = uk_spec.execute('grid', gridx, gridy, - specified_drift_arrays=[point_log]) - - uk_lin = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - drift_terms=['point_log'], point_drift=well) - z_lin, ss_lin = uk_lin.execute('grid', gridx, gridy) - - assert_allclose(z_spec, z_lin) - assert_allclose(ss_spec, ss_lin) - - uk_spec = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - drift_terms=['specified'], - specified_drift=[data[:, 0], data[:, 1], - point_log_data]) - z_spec, ss_spec = uk_spec.execute( - 'grid', gridx, gridy, - specified_drift_arrays=[xg, yg, point_log]) - uk_lin = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - drift_terms=['regional_linear', 'point_log'], - point_drift=well) - z_lin, ss_lin = uk_lin.execute('grid', gridx, gridy) - - assert_allclose(z_spec, z_lin) - assert_allclose(ss_spec, ss_lin) - - -def test_uk_functional_drift(sample_data_2d): - - data, (gridx, gridy, gridx_2), mask_ref = sample_data_2d - - well = np.array([[1.1, 1.1, -1.0]]) - func_x = lambda x, y: x # noqa - func_y = lambda x, y: y # noqa - - def func_well(x, y): - return - well[0, 2] * np.log( - np.sqrt((x - well[0, 0])**2. + (y - well[0, 1])**2.)) - - with pytest.raises(ValueError): - UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', drift_terms=['functional']) - with pytest.raises(TypeError): - UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', drift_terms=['functional'], - functional_drift=func_x) - - uk_func = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - drift_terms=['functional'], - functional_drift=[func_x, func_y]) - z_func, ss_func = uk_func.execute('grid', gridx, gridy) - uk_lin = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - drift_terms=['regional_linear']) - z_lin, ss_lin = uk_lin.execute('grid', gridx, gridy) - assert_allclose(z_func, z_lin) - assert_allclose(ss_func, ss_lin) - - uk_func = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - drift_terms=['functional'], - functional_drift=[func_well]) - z_func, ss_func = uk_func.execute('grid', gridx, gridy) - uk_lin = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - drift_terms=['point_log'], - point_drift=well) - z_lin, ss_lin = uk_lin.execute('grid', gridx, gridy) - assert_allclose(z_func, z_lin) - assert_allclose(ss_func, ss_lin) - - uk_func = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - drift_terms=['functional'], - functional_drift=[func_x, func_y, func_well]) - z_func, ss_func = uk_func.execute('grid', gridx, gridy) - uk_lin = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - drift_terms=['regional_linear', 'point_log'], - point_drift=well) - z_lin, ss_lin = uk_lin.execute('grid', gridx, gridy) - assert_allclose(z_func, z_lin) - assert_allclose(ss_func, ss_lin) - - -def test_uk_with_external_drift(validation_ref): - - data, _, (uk_test_answer, gridx_ref, gridy_ref) = validation_ref - - dem, demx, demy, cellsize, no_data = \ - kt.read_asc_grid(os.path.join(BASE_DIR, 'test_data/test3_dem.asc')) - uk = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='spherical', - variogram_parameters=[500.0, 3000.0, 0.0], - anisotropy_scaling=1.0, anisotropy_angle=0.0, - drift_terms=['external_Z'], external_drift=dem, - external_drift_x=demx, external_drift_y=demy, - verbose=False) - answer, gridx, gridy, cellsize, no_data = \ - kt.read_asc_grid(os.path.join(BASE_DIR, 'test_data/test3_answer.asc')) - - z, ss = uk.execute('grid', gridx, gridy, backend='vectorized') - assert_allclose(z, answer, **allclose_pars) - - z, ss = uk.execute('grid', gridx, gridy, backend='loop') - assert_allclose(z, answer, **allclose_pars) - - -def test_force_exact(): - data = np.array([[1., 1., 2.], - [2., 2., 1.5], - [3., 3., 1.]]) - ok = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - variogram_parameters=[1.0, 1.0]) - z, ss = ok.execute('grid', [1., 2., 3.], [1., 2., 3.], - backend='vectorized') - assert z[0, 0] == approx(2.0) - assert ss[0, 0] == approx(0.0) - assert z[1, 1] == approx(1.5) - assert ss[1, 1] == approx(0.0) - assert z[2, 2] == approx(1.0) - assert ss[2, 2] == approx(0.0) - assert ss[0, 2] != approx(0.0) - assert ss[2, 0] != approx(0.0) - z, ss = ok.execute('points', [1., 2., 3., 3.], [2., 1., 1., 3.], - backend='vectorized') - assert ss[0] != approx(0.0) - assert ss[1] != approx(0.0) - assert ss[2] != approx(0.0) - assert z[3] == approx(1.0) - assert ss[3] == approx(0.0) - z, ss = ok.execute('grid', np.arange(0., 4., 0.1), - np.arange(0., 4., 0.1), backend='vectorized') - assert z[10, 10] == approx(2.) - assert ss[10, 10] == approx(0.) - assert z[20, 20] == approx(1.5) - assert ss[20, 20] == approx(0.) - assert z[30, 30] == approx(1.0) - assert ss[30, 30] == approx(0.) - assert ss[0, 0] != approx(0.0) - assert ss[15, 15] != approx(0.0) - assert ss[10, 0] != approx(0.0) - assert ss[0, 10] != approx(0.0) - assert ss[20, 10] != approx(0.0) - assert ss[10, 20] != approx(0.0) - assert ss[30, 20] != approx(0.0) - assert ss[20, 30] != approx(0.0) - z, ss = ok.execute('grid', np.arange(0., 3.1, 0.1), - np.arange(2.1, 3.1, 0.1), backend='vectorized') - assert (np.any(ss <= 1e-15)) - assert not np.any(ss[:9, :30] <= 1e-15) - assert not np.allclose(z[:9, :30], 0.) - z, ss = ok.execute('grid', np.arange(0., 1.9, 0.1), - np.arange(2.1, 3.1, 0.1), backend='vectorized') - assert not np.any(ss <= 1e-15) - z, ss = ok.execute('masked', np.arange(2.5, 3.5, 0.1), - np.arange(2.5, 3.5, 0.25), backend='vectorized', - mask=np.asarray(np.meshgrid(np.arange(2.5, 3.5, 0.1), - np.arange(2.5, 3.5, 0.25))[0] == 0.)) - assert (ss[2, 5] <= 1e-15) - assert not np.allclose(ss, 0.) - - z, ss = ok.execute('grid', [1., 2., 3.], [1., 2., 3.], backend='loop') - assert z[0, 0] == approx(2.0) - assert ss[0, 0] == approx(0.0) - assert z[1, 1] == approx(1.5) - assert ss[1, 1] == approx(0.0) - assert z[2, 2] == approx(1.0) - assert ss[2, 2] == approx(0.0) - assert ss[0, 2] != approx(0.0) - assert ss[2, 0] != approx(0.0) - z, ss = ok.execute('points', [1., 2., 3., 3.], [2., 1., 1., 3.], - backend='loop') - assert ss[0] != approx(0.0) - assert ss[1] != approx(0.0) - assert ss[2] != approx(0.0) - assert z[3] == approx(1.0) - assert ss[3] == approx(0.0) - z, ss = ok.execute('grid', np.arange(0., 4., 0.1), - np.arange(0., 4., 0.1), backend='loop') - assert z[10, 10] == approx(2.) - assert ss[10, 10] == approx(0.) - assert z[20, 20] == approx(1.5) - assert ss[20, 20] == approx(0.) - assert z[30, 30] == approx(1.0) - assert ss[30, 30] == approx(0.) - assert ss[0, 0] != approx(0.0) - assert ss[15, 15] != approx(0.0) - assert ss[10, 0] != approx(0.0) - assert ss[0, 10] != approx(0.0) - assert ss[20, 10] != approx(0.0) - assert ss[10, 20] != approx(0.0) - assert ss[30, 20] != approx(0.0) - assert ss[20, 30] != approx(0.0) - z, ss = ok.execute('grid', np.arange(0., 3.1, 0.1), - np.arange(2.1, 3.1, 0.1), backend='loop') - assert (np.any(ss <= 1e-15)) - assert not np.any(ss[:9, :30] <= 1e-15) - assert not np.allclose(z[:9, :30], 0.) - z, ss = ok.execute('grid', np.arange(0., 1.9, 0.1), - np.arange(2.1, 3.1, 0.1), backend='loop') - assert not np.any(ss <= 1e-15) - z, ss = ok.execute( - 'masked', np.arange(2.5, 3.5, 0.1), - np.arange(2.5, 3.5, 0.25), backend='loop', - mask=np.asarray(np.meshgrid(np.arange(2.5, 3.5, 0.1), - np.arange(2.5, 3.5, 0.25))[0] == 0.)) - assert ss[2, 5] <= 1e-15 - assert not np.allclose(ss, 0.) - - uk = UniversalKriging(data[:, 0], data[:, 1], data[:, 2]) - z, ss = uk.execute('grid', [1., 2., 3.], [1., 2., 3.], - backend='vectorized') - assert z[0, 0] == approx(2.0) - assert ss[0, 0] == approx(0.0) - assert z[1, 1] == approx(1.5) - assert ss[1, 1] == approx(0.0) - assert z[2, 2] == approx(1.0) - assert ss[2, 2] == approx(0.0) - assert ss[0, 2] != approx(0.0) - assert ss[2, 0] != approx(0.0) - z, ss = uk.execute('points', [1., 2., 3., 3.], [2., 1., 1., 3.], - backend='vectorized') - assert ss[0] != approx(0.0) - assert ss[1] != approx(0.0) - assert ss[2] != approx(0.0) - assert z[3] == approx(1.0) - assert ss[3] == approx(0.0) - z, ss = uk.execute('grid', np.arange(0., 4., 0.1), - np.arange(0., 4., 0.1), backend='vectorized') - assert z[10, 10] == approx(2.) - assert ss[10, 10] == approx(0.) - assert z[20, 20] == approx(1.5) - assert ss[20, 20] == approx(0.) - assert z[30, 30] == approx(1.0) - assert ss[30, 30] == approx(0.) - assert ss[0, 0] != approx(0.0) - assert ss[15, 15] != approx(0.0) - assert ss[10, 0] != approx(0.0) - assert ss[0, 10] != approx(0.0) - assert ss[20, 10] != approx(0.0) - assert ss[10, 20] != approx(0.0) - assert ss[30, 20] != approx(0.0) - assert ss[20, 30] != approx(0.0) - z, ss = uk.execute('grid', np.arange(0., 3.1, 0.1), - np.arange(2.1, 3.1, 0.1), backend='vectorized') - assert np.any(ss <= 1e-15) - assert not np.any(ss[:9, :30] <= 1e-15) - assert not np.allclose(z[:9, :30], 0.) - z, ss = uk.execute('grid', np.arange(0., 1.9, 0.1), - np.arange(2.1, 3.1, 0.1), backend='vectorized') - assert not(np.any(ss <= 1e-15)) - z, ss = uk.execute( - 'masked', np.arange(2.5, 3.5, 0.1), - np.arange(2.5, 3.5, 0.25), backend='vectorized', - mask=np.asarray(np.meshgrid(np.arange(2.5, 3.5, 0.1), - np.arange(2.5, 3.5, 0.25))[0] == 0.)) - assert (ss[2, 5] <= 1e-15) - assert not np.allclose(ss, 0.) - z, ss = uk.execute('grid', [1., 2., 3.], [1., 2., 3.], backend='loop') - assert z[0, 0] == approx(2.0) - assert ss[0, 0] == approx(0.0) - assert z[1, 1] == approx(1.5) - assert ss[1, 1] == approx(0.0) - assert z[2, 2] == approx(1.0) - assert ss[2, 2] == approx(0.0) - assert ss[0, 2] != approx(0.0) - assert ss[2, 0] != approx(0.0) - z, ss = uk.execute('points', [1., 2., 3., 3.], [2., 1., 1., 3.], - backend='loop') - assert ss[0] != approx(0.0) - assert ss[1] != approx(0.0) - assert ss[2] != approx(0.0) - assert z[3] == approx(1.0) - assert ss[3] == approx(0.0) - z, ss = uk.execute('grid', np.arange(0., 4., 0.1), - np.arange(0., 4., 0.1), backend='loop') - assert z[10, 10] == approx(2.) - assert ss[10, 10] == approx(0.) - assert z[20, 20] == approx(1.5) - assert ss[20, 20] == approx(0.) - assert z[30, 30] == approx(1.0) - assert ss[30, 30] == approx(0.) - assert ss[0, 0] != approx(0.0) - assert ss[15, 15] != approx(0.0) - assert ss[10, 0] != approx(0.0) - assert ss[0, 10] != approx(0.0) - assert ss[20, 10] != approx(0.0) - assert ss[10, 20] != approx(0.0) - assert ss[30, 20] != approx(0.0) - assert ss[20, 30] != approx(0.0) - z, ss = uk.execute('grid', np.arange(0., 3.1, 0.1), - np.arange(2.1, 3.1, 0.1), backend='loop') - assert np.any(ss <= 1e-15) - assert not np.any(ss[:9, :30] <= 1e-15) - assert not np.allclose(z[:9, :30], 0.) - z, ss = uk.execute('grid', np.arange(0., 1.9, 0.1), - np.arange(2.1, 3.1, 0.1), backend='loop') - assert not np.any(ss <= 1e-15) - z, ss = uk.execute('masked', np.arange(2.5, 3.5, 0.1), - np.arange(2.5, 3.5, 0.25), backend='loop', - mask=np.asarray(np.meshgrid(np.arange(2.5, 3.5, 0.1), - np.arange(2.5, 3.5, 0.25))[0] == 0.)) - assert ss[2, 5] <= 1e-15 - assert not np.allclose(ss, 0.) - - z, ss = core._krige(np.vstack((data[:, 0], data[:, 1])).T, - data[:, 2], np.array([1., 1.]), - variogram_models.linear_variogram_model, [1.0, 1.0], - 'euclidean') - assert z == approx(2.) - assert ss == approx(0.) - z, ss = core._krige(np.vstack((data[:, 0], data[:, 1])).T, - data[:, 2], np.array([1., 2.]), - variogram_models.linear_variogram_model, [1.0, 1.0], - 'euclidean') - assert ss != approx(0.) - - data = np.zeros((50, 3)) - x, y = np.meshgrid(np.arange(0., 10., 1.), np.arange(0., 10., 2.)) - data[:, 0] = np.ravel(x) - data[:, 1] = np.ravel(y) - data[:, 2] = np.ravel(x) * np.ravel(y) - ok = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - variogram_parameters=[100.0, 1.0]) - z, ss = ok.execute('grid', np.arange(0., 10., 1.), - np.arange(0., 10., 2.), backend='vectorized') - assert_allclose(np.ravel(z), data[:, 2], **allclose_pars) - assert_allclose(ss, 0., **allclose_pars) - z, ss = ok.execute('grid', np.arange(0.5, 10., 1.), - np.arange(0.5, 10., 2.), backend='vectorized') - assert not np.allclose(np.ravel(z), data[:, 2]) - assert not np.allclose(ss, 0.) - z, ss = ok.execute('grid', np.arange(0., 10., 1.), - np.arange(0., 10., 2.), backend='loop') - assert_allclose(np.ravel(z), data[:, 2], **allclose_pars) - assert_allclose(ss, 0., **allclose_pars) - z, ss = ok.execute('grid', np.arange(0.5, 10., 1.), - np.arange(0.5, 10., 2.), backend='loop') - assert not np.allclose(np.ravel(z), data[:, 2]) - assert not np.allclose(ss, 0.) - - uk = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear', - variogram_parameters=[100.0, 1.0]) - z, ss = uk.execute('grid', np.arange(0., 10., 1.), - np.arange(0., 10., 2.), backend='vectorized') - assert_allclose(np.ravel(z), data[:, 2], **allclose_pars) - assert_allclose(ss, 0., **allclose_pars) - z, ss = uk.execute('grid', np.arange(0.5, 10., 1.), - np.arange(0.5, 10., 2.), backend='vectorized') - assert not np.allclose(np.ravel(z), data[:, 2]) - assert not np.allclose(ss, 0.) - z, ss = uk.execute('grid', np.arange(0., 10., 1.), - np.arange(0., 10., 2.), backend='loop') - assert_allclose(np.ravel(z), data[:, 2], **allclose_pars) - assert_allclose(ss, 0., **allclose_pars) - z, ss = uk.execute('grid', np.arange(0.5, 10., 1.), - np.arange(0.5, 10., 2.), backend='loop') - assert not np.allclose(np.ravel(z), data[:, 2]) - assert not np.allclose(ss, 0.) - - -def test_custom_variogram(sample_data_2d): - data, (gridx, gridy, gridx_2), mask_ref = sample_data_2d - - def func(params, dist): - return params[0] * np.log10(dist + params[1]) + params[2] - - with pytest.raises(ValueError): - UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='mrow') - with pytest.raises(ValueError): - UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='custom') - with pytest.raises(ValueError): - UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='custom', variogram_function=0) - with pytest.raises(ValueError): - UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='custom', variogram_function=func) - uk = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='custom', - variogram_parameters=[1., 1., 1.], - variogram_function=func) - assert uk.variogram_function([1., 1., 1.], 1.) == approx(1.3010, rel=1e-4) - uk = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear') - uk.update_variogram_model('custom', variogram_parameters=[1., 1., 1.], - variogram_function=func) - assert uk.variogram_function([1., 1., 1.], 1.) == approx(1.3010, rel=1e-4) - - with pytest.raises(ValueError): - OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='mrow') - with pytest.raises(ValueError): - OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='custom') - with pytest.raises(ValueError): - OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='custom', variogram_function=0) - with pytest.raises(ValueError): - OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='custom', variogram_function=func) - ok = OrdinaryKriging( - data[:, 0], data[:, 1], data[:, 2], - variogram_model='custom', variogram_parameters=[1., 1., 1.], - variogram_function=func) - assert ok.variogram_function([1., 1., 1.], 1.) == approx(1.3010, rel=1e-4) - ok = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2], - variogram_model='linear') - ok.update_variogram_model( - 'custom', variogram_parameters=[1., 1., 1.], - variogram_function=func) - assert ok.variogram_function([1., 1., 1.], 1.) == approx(1.3010, rel=1e-4) - - -def test_ok3d(validation_ref): - - data, (ok_test_answer, gridx_ref, gridy_ref), _ = validation_ref - - # Test to compare K3D results to those obtained using KT3D_H2O. - # (M. Karanovic, M. Tonkin, and D. Wilson, 2009, Groundwater, vol. 47, - # no. 4, 580-586.) - k3d = OrdinaryKriging3D(data[:, 0], data[:, 1], np.zeros(data[:, 1].shape), - data[:, 2], variogram_model='exponential', - variogram_parameters=[500.0, 3000.0, 0.0]) - k, ss = k3d.execute('grid', gridx_ref, gridy_ref, np.array([0.]), - backend='vectorized') - assert_allclose(np.squeeze(k), ok_test_answer) - k, ss = k3d.execute('grid', gridx_ref, gridy_ref, np.array([0.]), - backend='loop') - assert_allclose(np.squeeze(k), ok_test_answer) - - # Test to compare K3D results to those obtained using KT3D. - data = np.genfromtxt( - os.path.join(BASE_DIR, 'test_data', 'test3d_data.txt'), - skip_header=1) - ans = np.genfromtxt( - os.path.join(BASE_DIR, 'test_data', 'test3d_answer.txt')) - ans_z = ans[:, 0].reshape((10, 10, 10)) - ans_ss = ans[:, 1].reshape((10, 10, 10)) - k3d = OrdinaryKriging3D( - data[:, 0], data[:, 1], data[:, 2], data[:, 3], - variogram_model='linear', variogram_parameters=[1., 0.1]) - k, ss = k3d.execute('grid', np.arange(10.), np.arange(10.), np.arange(10.), - backend='vectorized') - assert_allclose(k, ans_z, rtol=1e-3, atol=1e-8) - assert_allclose(ss, ans_ss, rtol=1e-3, atol=1e-8) - k3d = OrdinaryKriging3D( - data[:, 0], data[:, 1], data[:, 2], data[:, 3], - variogram_model='linear', variogram_parameters=[1., 0.1]) - k, ss = k3d.execute( - 'grid', np.arange(10.), np.arange(10.), np.arange(10.), - backend='loop') - assert_allclose(k, ans_z, rtol=1e-3, atol=1e-8) - assert_allclose(ss, ans_ss, rtol=1e-3, atol=1e-8) - - -def test_ok3d_moving_window(): - - # Test to compare K3D results to those obtained using KT3D. - data = np.genfromtxt( - os.path.join(BASE_DIR, 'test_data', 'test3d_data.txt'), - skip_header=1) - ans = np.genfromtxt( - os.path.join(BASE_DIR, './test_data/test3d_answer.txt')) - ans_z = ans[:, 0].reshape((10, 10, 10)) - ans_ss = ans[:, 1].reshape((10, 10, 10)) - k3d = OrdinaryKriging3D(data[:, 0], data[:, 1], data[:, 2], data[:, 3], - variogram_model='linear', - variogram_parameters=[1., 0.1]) - k, ss = k3d.execute( - 'grid', np.arange(10.), np.arange(10.), np.arange(10.), - backend='loop', n_closest_points=10) - assert_allclose(k, ans_z, rtol=1e-3) - assert_allclose(ss, ans_ss, rtol=1e-3) - - -def test_ok3d_uk3d_and_backends_produce_same_results(validation_ref): - - data, _, (uk_test_answer, gridx_ref, gridy_ref) = validation_ref - - ok3d = OrdinaryKriging3D( - data[:, 0], data[:, 1], np.zeros(data[:, 1].shape), - data[:, 2], variogram_model='exponential', - variogram_parameters=[500.0, 3000.0, 0.0]) - ok_v, oss_v = ok3d.execute( - 'grid', gridx_ref, gridy_ref, np.array([0.]), backend='vectorized') - ok_l, oss_l = ok3d.execute( - 'grid', gridx_ref, gridy_ref, np.array([0.]), backend='loop') - - uk3d = UniversalKriging3D( - data[:, 0], data[:, 1], np.zeros(data[:, 1].shape), - data[:, 2], variogram_model='exponential', - variogram_parameters=[500., 3000., 0.]) - uk_v, uss_v = uk3d.execute( - 'grid', gridx_ref, gridy_ref, np.array([0.]), backend='vectorized') - assert_allclose(uk_v, ok_v) - uk_l, uss_l = uk3d.execute( - 'grid', gridx_ref, gridy_ref, np.array([0.]), backend='loop') - assert_allclose(uk_l, ok_l) - assert_allclose(uk_l, uk_v) - assert_allclose(uss_l, uss_v) - - data = np.genfromtxt( - os.path.join(BASE_DIR, 'test_data', 'test3d_data.txt'), - skip_header=1) - ok3d = OrdinaryKriging3D( - data[:, 0], data[:, 1], data[:, 2], data[:, 3], - variogram_model='linear', variogram_parameters=[1., 0.1]) - ok_v, oss_v = ok3d.execute( - 'grid', np.arange(10.), np.arange(10.), np.arange(10.), - backend='vectorized') - ok_l, oss_l = ok3d.execute( - 'grid', np.arange(10.), np.arange(10.), np.arange(10.), - backend='loop') - - uk3d = UniversalKriging3D( - data[:, 0], data[:, 1], data[:, 2], data[:, 3], - variogram_model='linear', variogram_parameters=[1., 0.1]) - uk_v, uss_v = uk3d.execute( - 'grid', np.arange(10.), np.arange(10.), np.arange(10.), - backend='vectorized') - assert_allclose(uk_v, ok_v) - assert_allclose(uss_v, oss_v) - uk_l, uss_l = uk3d.execute( - 'grid', np.arange(10.), np.arange(10.), np.arange(10.), - backend='loop') - assert_allclose(uk_l, ok_l) - assert_allclose(uss_l, oss_l) - assert_allclose(uk_l, uk_v) - assert_allclose(uss_l, uss_v) - - -def test_ok3d_update_variogram_model(sample_data_3d): - - data, (gridx_ref, gridy_ref, gridz_ref), mask_ref = sample_data_3d - - with pytest.raises(ValueError): - OrdinaryKriging3D(data[:, 0], data[:, 1], - data[:, 2], data[:, 3], variogram_model='blurg') - - k3d = OrdinaryKriging3D(data[:, 0], data[:, 1], - data[:, 2], data[:, 3]) - variogram_model = k3d.variogram_model - variogram_parameters = k3d.variogram_model_parameters - anisotropy_scaling_y = k3d.anisotropy_scaling_y - anisotropy_scaling_z = k3d.anisotropy_scaling_z - anisotropy_angle_x = k3d.anisotropy_angle_x - anisotropy_angle_y = k3d.anisotropy_angle_y - anisotropy_angle_z = k3d.anisotropy_angle_z - - with pytest.raises(ValueError): - k3d.update_variogram_model('blurg') - k3d.update_variogram_model( - 'power', anisotropy_scaling_y=3.0, anisotropy_scaling_z=3.0, - anisotropy_angle_x=45.0, anisotropy_angle_y=45.0, - anisotropy_angle_z=45.0) - assert variogram_model != k3d.variogram_model - assert variogram_parameters != k3d.variogram_model_parameters - assert anisotropy_scaling_y != k3d.anisotropy_scaling_y - assert anisotropy_scaling_z != k3d.anisotropy_scaling_z - assert anisotropy_angle_x != k3d.anisotropy_angle_x - assert anisotropy_angle_y != k3d.anisotropy_angle_y - assert anisotropy_angle_z != k3d.anisotropy_angle_z - - -def test_uk3d_update_variogram_model(sample_data_3d): - - data, (gridx_ref, gridy_ref, gridz_ref), mask_ref = sample_data_3d - - with pytest.raises(ValueError): - UniversalKriging3D(data[:, 0], data[:, 1], - data[:, 2], data[:, 3], variogram_model='blurg') - - uk3d = UniversalKriging3D(data[:, 0], data[:, 1], - data[:, 2], data[:, 3]) - variogram_model = uk3d.variogram_model - variogram_parameters = uk3d.variogram_model_parameters - anisotropy_scaling_y = uk3d.anisotropy_scaling_y - anisotropy_scaling_z = uk3d.anisotropy_scaling_z - anisotropy_angle_x = uk3d.anisotropy_angle_x - anisotropy_angle_y = uk3d.anisotropy_angle_y - anisotropy_angle_z = uk3d.anisotropy_angle_z - - with pytest.raises(ValueError): - uk3d.update_variogram_model('blurg') - uk3d.update_variogram_model( - 'power', anisotropy_scaling_y=3.0, - anisotropy_scaling_z=3.0, anisotropy_angle_x=45.0, - anisotropy_angle_y=45.0, anisotropy_angle_z=45.0) - assert not variogram_model == uk3d.variogram_model - assert not variogram_parameters == uk3d.variogram_model_parameters - assert not anisotropy_scaling_y == uk3d.anisotropy_scaling_y - assert not anisotropy_scaling_z == uk3d.anisotropy_scaling_z - assert not anisotropy_angle_x == uk3d.anisotropy_angle_x - assert not anisotropy_angle_y == uk3d.anisotropy_angle_y - assert not anisotropy_angle_z == uk3d.anisotropy_angle_z - - -def test_ok3d_backends_produce_same_result(sample_data_3d): - - data, (gridx_ref, gridy_ref, gridz_ref), mask_ref = sample_data_3d - - k3d = OrdinaryKriging3D(data[:, 0], data[:, 1], data[:, 2], - data[:, 3], variogram_model='linear') - k_k3d_v, ss_k3d_v = k3d.execute('grid', gridx_ref, gridy_ref, gridz_ref, - backend='vectorized') - k_k3d_l, ss_k3d_l = k3d.execute('grid', gridx_ref, gridy_ref, gridz_ref, - backend='loop') - assert_allclose(k_k3d_v, k_k3d_l, rtol=1e-05, atol=1e-8) - assert_allclose(ss_k3d_v, ss_k3d_l, rtol=1e-05, atol=1e-8) - - -def test_ok3d_execute(sample_data_3d): - - data, (gridx_ref, gridy_ref, gridz_ref), mask_ref = sample_data_3d - - k3d = OrdinaryKriging3D(data[:, 0], data[:, 1], - data[:, 2], data[:, 3]) - - with pytest.raises(ValueError): - k3d.execute('blurg', gridx_ref, - gridy_ref, gridz_ref) - - k, ss = k3d.execute('grid', gridx_ref, gridy_ref, - gridz_ref, backend='vectorized') - shape = (gridz_ref.size, gridy_ref.size, gridx_ref.size) - assert k.shape == shape - assert ss.shape == shape - assert np.amax(k) != np.amin(k) - assert np.amax(ss) != np.amin(ss) - assert not np.ma.is_masked(k) - - k, ss = k3d.execute('grid', gridx_ref, gridy_ref, - gridz_ref, backend='loop') - shape = (gridz_ref.size, gridy_ref.size, gridx_ref.size) - assert k.shape == shape - assert ss.shape == shape - assert np.amax(k) != np.amin(k) - assert np.amax(ss) != np.amin(ss) - assert not np.ma.is_masked(k) - - with pytest.raises(IOError): - k3d.execute('masked', gridx_ref, gridy_ref, - gridz_ref, backend='vectorized') - mask = np.array([True, False]) - with pytest.raises(ValueError): - k3d.execute('masked', gridx_ref, gridy_ref, - gridz_ref, mask=mask, backend='vectorized') - k, ss = k3d.execute('masked', gridx_ref, gridy_ref, gridz_ref, - mask=mask_ref, backend='vectorized') - assert (np.ma.is_masked(k)) - assert (np.ma.is_masked(ss)) - assert k[0, 0, 0] is np.ma.masked - assert ss[0, 0, 0] is np.ma.masked - z, ss = k3d.execute('masked', gridx_ref, gridy_ref, gridz_ref, - mask=mask_ref.T, backend='vectorized') - assert (np.ma.is_masked(z)) - assert (np.ma.is_masked(ss)) - assert z[0, 0, 0] is np.ma.masked - assert ss[0, 0, 0] is np.ma.masked - - with pytest.raises(IOError): - k3d.execute('masked', gridx_ref, gridy_ref, - gridz_ref, backend='loop') - mask = np.array([True, False]) - with pytest.raises(ValueError): - k3d.execute('masked', gridx_ref, gridy_ref, - gridz_ref, mask=mask, backend='loop') - k, ss = k3d.execute('masked', gridx_ref, gridy_ref, gridz_ref, - mask=mask_ref, backend='loop') - assert (np.ma.is_masked(k)) - assert (np.ma.is_masked(ss)) - assert k[0, 0, 0] is np.ma.masked - assert ss[0, 0, 0] is np.ma.masked - z, ss = k3d.execute('masked', gridx_ref, gridy_ref, gridz_ref, - mask=mask_ref.T, backend='loop') - assert (np.ma.is_masked(z)) - assert (np.ma.is_masked(ss)) - assert z[0, 0, 0] is np.ma.masked - assert ss[0, 0, 0] is np.ma.masked - - with pytest.raises(ValueError): - k3d.execute('points', np.array([0.0, 1.0, 2.0]), np.array([0.0, 1.0]), - np.array([1.0]), backend='vectorized') - k, ss = k3d.execute('points', gridx_ref[0], gridy_ref[0], - gridz_ref[0], backend='vectorized') - assert k.shape == (1,) - assert ss.shape == (1,) - - with pytest.raises(ValueError): - k3d.execute('points', np.array([0.0, 1.0, 2.0]), np.array([0.0, 1.0]), - np.array([1.0]), backend='loop') - k, ss = k3d.execute('points', gridx_ref[0], gridy_ref[0], - gridz_ref[0], backend='loop') - assert k.shape == (1,) - assert ss.shape == (1,) - - data = np.zeros((125, 4)) - z, y, x = np.meshgrid(np.arange(0., 5., 1.), - np.arange(0., 5., 1.), np.arange(0., 5., 1.)) - data[:, 0] = np.ravel(x) - data[:, 1] = np.ravel(y) - data[:, 2] = np.ravel(z) - data[:, 3] = np.ravel(z) - k3d = OrdinaryKriging3D( - data[:, 0], data[:, 1], data[:, 2], data[:, 3], - variogram_model='linear') - k, ss = k3d.execute('grid', np.arange(2., 3., 0.1), np.arange(2., 3., 0.1), - np.arange(0., 4., 1.), backend='vectorized') - assert_allclose(k[0, :, :], 0., atol=0.01) - assert_allclose(k[1, :, :], 1., rtol=1.e-2) - assert_allclose(k[2, :, :], 2., rtol=1.e-2) - assert_allclose(k[3, :, :], 3., rtol=1.e-2) - k, ss = k3d.execute('grid', np.arange(2., 3., 0.1), np.arange(2., 3., 0.1), - np.arange(0., 4., 1.), backend='loop') - assert_allclose(k[0, :, :], 0., atol=0.01) - assert_allclose(k[1, :, :], 1., rtol=1.e-2) - assert_allclose(k[2, :, :], 2., rtol=1.e-2) - assert_allclose(k[3, :, :], 3., rtol=1.e-2) - k3d = OrdinaryKriging3D(data[:, 0], data[:, 1], data[:, 2], data[:, 3], - variogram_model='linear') - k, ss = k3d.execute( - 'points', [2.5, 2.5, 2.5], [2.5, 2.5, 2.5], [1., 2., 3.], - backend='vectorized') - assert_allclose(k[0], 1., atol=0.01) - assert_allclose(k[1], 2., rtol=1.e-2) - assert_allclose(k[2], 3., rtol=1.e-2) - k, ss = k3d.execute( - 'points', [2.5, 2.5, 2.5], [2.5, 2.5, 2.5], [1., 2., 3.], - backend='loop') - assert_allclose(k[0], 1., atol=0.01) - assert_allclose(k[1], 2., rtol=1.e-2) - assert_allclose(k[2], 3., rtol=1.e-2) - - -def test_uk3d_execute(sample_data_3d): - - data, (gridx_ref, gridy_ref, gridz_ref), mask_ref = sample_data_3d - - uk3d = UniversalKriging3D(data[:, 0], data[:, 1], - data[:, 2], data[:, 3]) - - with pytest.raises(ValueError): - uk3d.execute('blurg', gridx_ref, - gridy_ref, gridz_ref) - - k, ss = uk3d.execute('grid', gridx_ref, gridy_ref, - gridz_ref, backend='vectorized') - shape = (gridz_ref.size, gridy_ref.size, gridx_ref.size) - assert k.shape == shape - assert ss.shape == shape - assert np.amax(k) != np.amin(k) - assert np.amax(ss) != np.amin(ss) - assert not np.ma.is_masked(k) - - k, ss = uk3d.execute('grid', gridx_ref, gridy_ref, - gridz_ref, backend='loop') - shape = (gridz_ref.size, gridy_ref.size, gridx_ref.size) - assert k.shape == shape - assert ss.shape == shape - assert np.amax(k) != np.amin(k) - assert np.amax(ss) != np.amin(ss) - assert not np.ma.is_masked(k) - - with pytest.raises(IOError): - uk3d.execute('masked', gridx_ref, gridy_ref, - gridz_ref, backend='vectorized') - mask = np.array([True, False]) - with pytest.raises(ValueError): - uk3d.execute('masked', gridx_ref, gridy_ref, - gridz_ref, mask=mask, backend='vectorized') - k, ss = uk3d.execute('masked', gridx_ref, gridy_ref, gridz_ref, - mask=mask_ref, backend='vectorized') - assert (np.ma.is_masked(k)) - assert (np.ma.is_masked(ss)) - assert k[0, 0, 0] is np.ma.masked - assert ss[0, 0, 0] is np.ma.masked - z, ss = uk3d.execute('masked', gridx_ref, gridy_ref, gridz_ref, - mask=mask_ref.T, backend='vectorized') - assert (np.ma.is_masked(z)) - assert (np.ma.is_masked(ss)) - assert z[0, 0, 0] is np.ma.masked - assert ss[0, 0, 0] is np.ma.masked - - with pytest.raises(IOError): - uk3d.execute('masked', gridx_ref, gridy_ref, - gridz_ref, backend='loop') - mask = np.array([True, False]) - with pytest.raises(ValueError): - uk3d.execute('masked', gridx_ref, gridy_ref, - gridz_ref, mask=mask, backend='loop') - k, ss = uk3d.execute('masked', gridx_ref, gridy_ref, gridz_ref, - mask=mask_ref, backend='loop') - assert (np.ma.is_masked(k)) - assert (np.ma.is_masked(ss)) - assert k[0, 0, 0] is np.ma.masked - assert ss[0, 0, 0] is np.ma.masked - z, ss = uk3d.execute('masked', gridx_ref, gridy_ref, gridz_ref, - mask=mask_ref.T, backend='loop') - assert (np.ma.is_masked(z)) - assert (np.ma.is_masked(ss)) - assert z[0, 0, 0] is np.ma.masked - assert ss[0, 0, 0] is np.ma.masked - - with pytest.raises(ValueError): - uk3d.execute('points', np.array([0.0, 1.0, 2.0]), np.array([0.0, 1.0]), - np.array([1.0]), backend='vectorized') - k, ss = uk3d.execute('points', gridx_ref[0], gridy_ref[0], - gridz_ref[0], backend='vectorized') - assert k.shape == (1,) - assert ss.shape == (1,) - - with pytest.raises(ValueError): - uk3d.execute('points', np.array([0.0, 1.0, 2.0]), np.array([0.0, 1.0]), - np.array([1.0]), backend='loop') - k, ss = uk3d.execute('points', gridx_ref[0], gridy_ref[0], - gridz_ref[0], backend='loop') - assert k.shape == (1,) - assert ss.shape == (1,) - - data = np.zeros((125, 4)) - z, y, x = np.meshgrid(np.arange(0., 5., 1.), np.arange(0., 5., 1.), - np.arange(0., 5., 1.)) - data[:, 0] = np.ravel(x) - data[:, 1] = np.ravel(y) - data[:, 2] = np.ravel(z) - data[:, 3] = np.ravel(z) - k3d = UniversalKriging3D(data[:, 0], data[:, 1], data[:, 2], data[:, 3], - variogram_model='linear') - k, ss = k3d.execute('grid', np.arange(2., 3., 0.1), np.arange(2., 3., 0.1), - np.arange(0., 4., 1.), backend='vectorized') - assert_allclose(k[0, :, :], 0., atol=0.01) - assert_allclose(k[1, :, :], 1., rtol=1.e-2) - assert_allclose(k[2, :, :], 2., rtol=1.e-2) - assert_allclose(k[3, :, :], 3., rtol=1.e-2) - k, ss = k3d.execute('grid', np.arange(2., 3., 0.1), np.arange(2., 3., 0.1), - np.arange(0., 4., 1.), backend='loop') - assert_allclose(k[0, :, :], 0., atol=0.01) - assert_allclose(k[1, :, :], 1., rtol=1.e-2) - assert_allclose(k[2, :, :], 2., rtol=1.e-2) - assert_allclose(k[3, :, :], 3., rtol=1.e-2) - k3d = UniversalKriging3D(data[:, 0], data[:, 1], data[:, 2], data[:, 3], - variogram_model='linear') - k, ss = k3d.execute( - 'points', [2.5, 2.5, 2.5], [2.5, 2.5, 2.5], [1., 2., 3.], - backend='vectorized') - assert_allclose(k[0], 1., atol=0.01) - assert_allclose(k[1], 2., rtol=1.e-2) - assert_allclose(k[2], 3., rtol=1.e-2) - k, ss = k3d.execute( - 'points', [2.5, 2.5, 2.5], [2.5, 2.5, 2.5], [1., 2., 3.], - backend='loop') - assert_allclose(k[0], 1., atol=0.01) - assert_allclose(k[1], 2., rtol=1.e-2) - assert_allclose(k[2], 3., rtol=1.e-2) - - -def test_force_exact_3d(sample_data_3d): - - data, (gridx_ref, gridy_ref, gridz_ref), mask_ref = sample_data_3d - - k3d = OrdinaryKriging3D(data[:, 0], data[:, 1], data[:, 2], - data[:, 3], variogram_model='linear') - k, ss = k3d.execute( - 'grid', [0.1, 0.2, 0.3], [0.1, 0.2, 0.3], [0.1, 0.2, 0.3], - backend='vectorized') - assert k[2, 0, 0] == approx(0.9) - assert ss[2, 0, 0] == approx(0.0) - assert k[0, 2, 0] == approx(0.9) - assert ss[0, 2, 0] == approx(0.0) - assert k[1, 2, 2] == approx(0.7) - assert ss[1, 2, 2] == approx(0.0) - assert ss[2, 2, 2] != approx(0.0) - assert ss[0, 0, 0] != approx(0.0) - - k, ss = k3d.execute( - 'grid', [0.1, 0.2, 0.3], [0.1, 0.2, 0.3], [0.1, 0.2, 0.3], - backend='loop') - assert k[2, 0, 0] == approx(0.9) - assert ss[2, 0, 0] == approx(0.0) - assert k[0, 2, 0] == approx(0.9) - assert ss[0, 2, 0] == approx(0.0) - assert k[1, 2, 2] == approx(0.7) - assert ss[1, 2, 2] == approx(0.0) - assert ss[2, 2, 2] != approx(0.0) - assert ss[0, 0, 0] != approx(0.0) - - k3d = UniversalKriging3D(data[:, 0], data[:, 1], data[:, 2], - data[:, 3], variogram_model='linear') - k, ss = k3d.execute( - 'grid', [0.1, 0.2, 0.3], [0.1, 0.2, 0.3], [0.1, 0.2, 0.3], - backend='vectorized') - assert k[2, 0, 0] == approx(0.9) - assert ss[2, 0, 0] == approx(0.0) - assert k[0, 2, 0] == approx(0.9) - assert ss[0, 2, 0] == approx(0.0) - assert k[1, 2, 2] == approx(0.7) - assert ss[1, 2, 2] == approx(0.0) - assert ss[2, 2, 2] != approx(0.0) - assert ss[0, 0, 0] != approx(0.0) - - k, ss = k3d.execute( - 'grid', [0.1, 0.2, 0.3], [0.1, 0.2, 0.3], [0.1, 0.2, 0.3], - backend='loop') - assert k[2, 0, 0] == approx(0.9) - assert ss[2, 0, 0] == approx(0.0) - assert k[0, 2, 0] == approx(0.9) - assert ss[0, 2, 0] == approx(0.0) - assert k[1, 2, 2] == approx(0.7) - assert ss[1, 2, 2] == approx(0.0) - assert ss[2, 2, 2] != approx(0.0) - assert ss[0, 0, 0] != approx(0.0) - - -def test_uk3d_specified_drift(sample_data_3d): - - data, (gridx_ref, gridy_ref, gridz_ref), mask_ref = sample_data_3d - - zg, yg, xg = np.meshgrid(gridz_ref, gridy_ref, gridx_ref, indexing='ij') - - with pytest.raises(ValueError): - UniversalKriging3D(data[:, 0], data[:, 1], data[:, 2], data[:, 3], - variogram_model='linear', drift_terms=['specified']) - with pytest.raises(TypeError): - UniversalKriging3D(data[:, 0], data[:, 1], data[:, 2], data[:, 3], - variogram_model='linear', - drift_terms=['specified'], - specified_drift=data[:, 0]) - with pytest.raises(ValueError): - UniversalKriging3D(data[:, 0], data[:, 1], data[:, 2], data[:, 3], - variogram_model='linear', drift_terms=['specified'], - specified_drift=[data[:2, 0]]) - - uk_spec = UniversalKriging3D(data[:, 0], data[:, 1], data[:, 2], - data[:, 3], variogram_model='linear', - drift_terms=['specified'], - specified_drift=[data[:, 0], data[:, 1], - data[:, 2]]) - with pytest.raises(ValueError): - uk_spec.execute( - 'grid', gridx_ref, gridy_ref, gridz_ref, - specified_drift_arrays=[gridx_ref, gridy_ref, gridz_ref]) - with pytest.raises(TypeError): - uk_spec.execute('grid', gridx_ref, gridy_ref, gridz_ref, - specified_drift_arrays=gridx_ref) - with pytest.raises(ValueError): - uk_spec.execute('grid', gridx_ref, gridy_ref, gridz_ref, - specified_drift_arrays=[zg]) - z_spec, ss_spec = uk_spec.execute('grid', gridx_ref, gridy_ref, gridz_ref, - specified_drift_arrays=[xg, yg, zg]) - - uk_lin = UniversalKriging3D(data[:, 0], data[:, 1], data[:, 2], data[:, 3], - variogram_model='linear', - drift_terms=['regional_linear']) - z_lin, ss_lin = uk_lin.execute('grid', gridx_ref, gridy_ref, gridz_ref) - - assert_allclose(z_spec, z_lin) - assert_allclose(ss_spec, ss_lin) - - -def test_uk3d_functional_drift(sample_data_3d): - - data, (gridx, gridy, gridz), mask_ref = sample_data_3d - - func_x = lambda x, y, z: x # noqa - func_y = lambda x, y, z: y # noqa - func_z = lambda x, y, z: z # noqa - - with pytest.raises(ValueError): - UniversalKriging3D(data[:, 0], data[:, 1], data[:, 2], data[:, 3], - variogram_model='linear', - drift_terms=['functional']) - with pytest.raises(TypeError): - UniversalKriging3D(data[:, 0], data[:, 1], data[:, 2], data[:, 3], - variogram_model='linear', - drift_terms=['functional'], functional_drift=func_x) - - uk_func = UniversalKriging3D( - data[:, 0], data[:, 1], data[:, 2], data[:, 3], - variogram_model='linear', drift_terms=['functional'], - functional_drift=[func_x, func_y, func_z]) - z_func, ss_func = uk_func.execute('grid', gridx, gridy, gridz) - uk_lin = UniversalKriging3D( - data[:, 0], data[:, 1], data[:, 2], data[:, 3], - variogram_model='linear', drift_terms=['regional_linear']) - z_lin, ss_lin = uk_lin.execute('grid', gridx, gridy, gridz) - assert_allclose(z_func, z_lin) - assert_allclose(ss_func, ss_lin) - - -def test_geometric_code(): - - # Create selected points distributed across the sphere: - N = 4 - lon = np.array([7.0, 7.0, 187.0, 73.231]) - lat = np.array([13.23, 13.2301, -13.23, -79.3]) - - # For the points generated with this reference seed, the distance matrix - # has been calculated using geopy (v. 1.11.0) as follows: - # >>> from geopy.distance import great_circle - # >>> g = great_circle(radius=1.0) - # >>> d = np.zeros((N,N), dtype=float) - # >>> for i in range(N): - # >>> for j in range(N): - # >>> d[i,j] = g.measure((lat[i],lon[i]),(lat[j],lon[j])) - # >>> d *= 180.0/np.pi - # From that distance matrix, the reference values have been obtained. - d_ref = np.array( - [[0.0, 1e-4, 180.0, 98.744848317171801], - [1e-4, 0.0, 179.9999, 98.744946828324345], - [180.0, 179.9999, 0.0, 81.255151682828213], - [98.744848317171801, 98.744946828324345, 81.255151682828213, 0.0]] - ) - - # Calculate distance matrix using the PyKrige code: - d = np.zeros((N, N)) - for i in range(N): - for j in range(N): - d[i, j] = core.great_circle_distance( - lon[i], lat[i], lon[j], lat[j]) - - # Test agains reference values: - assert_allclose(d, d_ref) - - # Test general features: - assert_allclose(d[np.eye(N, dtype=bool)], 0.0) - np.testing.assert_equal(d >= 0.0, np.ones((N, N), dtype=bool)) - assert_allclose(d, d.T) - np.testing.assert_equal(d <= 180.0, np.ones((N, N), dtype=bool)) - - # Test great_circle_distance and euclid3_to_great_circle against each other - lon_ref = lon - lat_ref = lat - for i in range(len(lon_ref)): - lon, lat = np.meshgrid(np.linspace(0, 360.0, 20), - np.linspace(-90.0, 90.0, 20)) - dx = (np.cos(np.pi/180.0*lon)*np.cos(np.pi/180.0*lat) - - np.cos(np.pi/180.0*lon_ref[i])*np.cos(np.pi/180.0*lat_ref[i])) - dy = (np.sin(np.pi/180.0*lon)*np.cos(np.pi/180.0*lat) - - np.sin(np.pi/180.0*lon_ref[i])*np.cos(np.pi/180.0*lat_ref[i])) - dz = np.sin(np.pi/180.0*lat) - np.sin(np.pi/180.0*lat_ref[i]) - assert_allclose( - core.great_circle_distance(lon_ref[i], lat_ref[i], lon, lat), - core.euclid3_to_great_circle(np.sqrt(dx**2+dy**2+dz**2)), - rtol=1e-5) - - -def test_ok_geographic(): - # Generate random data: - np.random.seed(89239413) - lon = 360.0*np.random.rand(50, 1) - lat = 180.0*np.random.rand(50, 1) - 90.0 - z = np.random.rand(50, 1) - - # Generate grid: - grid_lon = 360.0*np.random.rand(120, 1) - grid_lat = 180.0*np.random.rand(120, 1) - 90.0 - - # Create ordinary kriging object: - OK = OrdinaryKriging(lon, lat, z, variogram_model='linear', verbose=False, - enable_plotting=False, coordinates_type='geographic') - - # Execute on grid: - z, ss = OK.execute('grid', grid_lon, grid_lat) - - -def test_ok_geographic_vs_euclid(): - # Generate some random data close to the north pole. - # Then we use a polar projected 2d euclidean coordinate - # system and compare the kriging results in that coordinate - # system with the geographic-option results. - # If data point distance to the north pole is small enough - # (choose maximum 0.01 degrees), the differences due to curvature - # should be negligible. - np.random.seed(89239413) - from_north = 1e-2*np.random.random(5) - lat = 90.0-from_north - lon = 360.0*np.random.random(5) - z = np.random.random(5) - z -= z.mean() - x = from_north * np.cos(np.deg2rad(lon)) - y = from_north * np.sin(np.deg2rad(lon)) - - # Generate grids: - grid_lon = 360.0 * np.linspace(0, 1, 50) - grid_from_north = np.linspace(0,0.01,10) - grid_lat = 90.0 - grid_from_north - grid_x = grid_from_north[:,np.newaxis] * np.cos(np.deg2rad(grid_lon[np.newaxis,:])) - grid_y = grid_from_north[:,np.newaxis] * np.sin(np.deg2rad(grid_lon[np.newaxis,:])) - grid_lon, grid_lat = np.meshgrid(grid_lon, grid_lat, indexing='xy') - - # Flatten the grids: - grid_x = grid_x.flatten() - grid_y = grid_y.flatten() - grid_lon = grid_lon.flatten() - grid_lat = grid_lat.flatten() - - # Calculate and compare distance matrices ensuring that that part - # of the workflow works as intended (tested: 2e-9 is currently the - # match for this setup): - d_eucl = cdist(np.concatenate([x[:,np.newaxis],y[:,np.newaxis]],axis=1), - np.concatenate([grid_x[:,np.newaxis],grid_y[:,np.newaxis]],axis=1)) - d_geo = core.great_circle_distance(lon[:,np.newaxis], lat[:,np.newaxis], - grid_lon[np.newaxis,:], grid_lat[np.newaxis,:]) - assert_allclose(d_eucl,d_geo, rtol=2e-9) - - # Create ordinary kriging objects: - OK_geo = OrdinaryKriging(lon, lat, z, variogram_model='linear', verbose=False, - enable_plotting=False, coordinates_type='geographic') - OK_xy = OrdinaryKriging(x, y, z, variogram_model='linear', verbose=False, - enable_plotting=False) - OK_wrong = OrdinaryKriging(lon, lat, z, variogram_model='linear', verbose=False, - enable_plotting=False) - - # Execute on grid: - zgeo, ss = OK_geo.execute('points', grid_lon, grid_lat) - zxy, ss = OK_xy.execute('points', grid_x, grid_y) - zwrong, ss = OK_wrong.execute('points', grid_lon, grid_lat) - - # Assert equivalence / difference (tested: 2e-5 is currently the - # match for this setup): - assert_allclose(zgeo, zxy, rtol=2e-5) - assert not np.any(zgeo == 0) - assert np.abs((zgeo-zwrong)/zgeo).max() > 1.0 - - -def test_ok_geometric_closest_points(): - # Generate random data: - np.random.seed(89239413) - lon = 360.0*np.random.rand(50, 1) - lat = 180.0*np.random.rand(50, 1) - 90.0 - z = np.random.rand(50, 1) - - # Generate grid: - grid_lon = 360.0*np.random.rand(120, 1) - grid_lat = 180.0*np.random.rand(120, 1) - 90.0 - - # Create ordinary kriging object: - OK = OrdinaryKriging(lon, lat, z, variogram_model='linear', verbose=False, - enable_plotting=False, coordinates_type='geographic') - - # Execute on grid: - with pytest.raises(ValueError): - # Test OK raising ValueError when closest_points == 1: - z, ss = OK.execute('grid', grid_lon, grid_lat, n_closest_points=1, - backend='C') - z, ss = OK.execute('grid', grid_lon, grid_lat, n_closest_points=5, - backend='C') - -@pytest.mark.parametrize("model", [OrdinaryKriging, UniversalKriging]) -def test_gstools_variogram(model): - gstools = pytest.importorskip("gstools") - # test data - data = np.array([[0.3, 1.2, 0.47], - [1.9, 0.6, 0.56], - [1.1, 3.2, 0.74], - [3.3, 4.4, 1.47], - [4.7, 3.8, 1.74]]) - gridx = np.arange(0.0, 5.5, 0.1) - gridy = np.arange(0.0, 6.5, 0.1) - # a GSTools based covariance model - cov_model = gstools.Gaussian(dim=2, len_scale=1, anis=.2, angles=-.5, var=.5, nugget=.1) - # create the krige field - krige = model(data[:, 0], data[:, 1], data[:, 2], cov_model) - z1, ss1 = krige.execute('grid', gridx, gridy) - # check if the field coincides with the data - for i in range(5): - y_id = int(data[i, 1] * 10) - x_id = int(data[i, 0] * 10) - assert np.isclose(z1[y_id, x_id], data[i, 2]) diff --git a/pykrige/tests/test_regression_krige.py b/pykrige/tests/test_regression_krige.py deleted file mode 100644 index ebe1718..0000000 --- a/pykrige/tests/test_regression_krige.py +++ /dev/null @@ -1,89 +0,0 @@ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -from itertools import product -import pytest - -import numpy as np - -from pykrige.rk import RegressionKriging - -try: - from sklearn.svm import SVR - from sklearn.datasets import fetch_california_housing - from sklearn.linear_model import ElasticNet, Lasso - from sklearn.ensemble import RandomForestRegressor - from sklearn.linear_model import LinearRegression - from pykrige.compat import train_test_split - SKLEARN_INSTALLED = True -except ImportError: - SKLEARN_INSTALLED = False - - -def _methods(): - krige_methods = ['ordinary', 'universal'] - ml_methods = [SVR(C=0.01), - RandomForestRegressor(min_samples_split=5, - n_estimators=50), - LinearRegression(), - Lasso(), - ElasticNet() - ] - return product(ml_methods, krige_methods) - - -@pytest.mark.skipif(not SKLEARN_INSTALLED, - reason="requires scikit-learn") -def test_regression_krige(): - np.random.seed(1) - x = np.linspace(-1., 1., 100) - # create a feature matrix with 5 features - X = np.tile(x, reps=(5, 1)).T - y = 1 + 5*X[:, 0] - 2*X[:, 1] - 2*X[:, 2] + 3*X[:, 3] + 4*X[:, 4] + \ - 2*(np.random.rand(100) - 0.5) - - # create lat/lon array - lon = np.linspace(-180., 180.0, 10) - lat = np.linspace(-90., 90., 10) - lon_lat = np.array(list(product(lon, lat))) - - X_train, X_test, y_train, y_test, lon_lat_train, lon_lat_test = \ - train_test_split(X, y, lon_lat, train_size=0.7, random_state=10) - - for ml_model, krige_method in _methods(): - reg_kr_model = RegressionKriging(regression_model=ml_model, - method=krige_method, - n_closest_points=2) - reg_kr_model.fit(X_train, lon_lat_train, y_train) - assert reg_kr_model.score(X_test, lon_lat_test, y_test) > 0.25 - - -@pytest.mark.skipif(not SKLEARN_INSTALLED, - reason="requires scikit-learn") -def test_krige_housing(): - try: - housing = fetch_california_housing() - except PermissionError: - # This can raise permission error on Appveyor - pytest.skip('Failed to load california housing dataset') - - # take only first 1000 - p = housing['data'][:1000, :-2] - x = housing['data'][:1000, -2:] - target = housing['target'][:1000] - - p_train, p_test, y_train, y_test, x_train, x_test = \ - train_test_split(p, target, x, train_size=0.7, - random_state=10) - - for ml_model, krige_method in _methods(): - - reg_kr_model = RegressionKriging(regression_model=ml_model, - method=krige_method, - n_closest_points=2) - reg_kr_model.fit(p_train, x_train, y_train) - if krige_method == 'ordinary': - assert reg_kr_model.score(p_test, x_test, y_test) > 0.5 - else: - assert reg_kr_model.score(p_test, x_test, y_test) > 0.0 diff --git a/pykrige/uk.py b/pykrige/uk.py index 6ec5bb4..1e42e00 100644 --- a/pykrige/uk.py +++ b/pykrige/uk.py @@ -1,19 +1,5 @@ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -import numpy as np -import scipy.linalg -from scipy.spatial.distance import cdist -import matplotlib.pyplot as plt -from . import variogram_models -from . import core -from .core import _adjust_for_anisotropy, _initialize_variogram_model, \ - _make_variogram_parameter_list, _find_statistics -import warnings - -__doc__ = """ +# coding: utf-8 +""" PyKrige ======= @@ -30,8 +16,20 @@ .. [1] P.K. Kitanidis, Introduction to Geostatistcs: Applications in Hydrogeology, (Cambridge University Press, 1997) 272 p. -Copyright (c) 2015-2018, PyKrige Developers +Copyright (c) 2015-2020, PyKrige Developers """ +import numpy as np +import scipy.linalg +from scipy.spatial.distance import cdist +from . import variogram_models +from . import core +from .core import ( + _adjust_for_anisotropy, + _initialize_variogram_model, + _make_variogram_parameter_list, + _find_statistics, +) +import warnings class UniversalKriging: @@ -54,36 +52,34 @@ class UniversalKriging: technically correct for one-dimensional problems. You can also use a `GSTools `_ CovModel. - variogram_parameters: list or dict, optional + variogram_parameters : list or dict, optional Parameters that define the specified variogram model. If not provided, parameters will be automatically calculated using a "soft" L1 norm minimization scheme. For variogram model parameters provided in a dict, the required dict keys vary according to the specified variogram model: :: - linear - {'slope': slope, 'nugget': nugget} - power - {'scale': scale, 'exponent': exponent, 'nugget': nugget} - gaussian - {'sill': s, 'range': r, 'nugget': n} - OR - {'psill': p, 'range': r, 'nugget':n} - spherical - {'sill': s, 'range': r, 'nugget': n} - OR - {'psill': p, 'range': r, 'nugget':n} - exponential - {'sill': s, 'range': r, 'nugget': n} - OR - {'psill': p, 'range': r, 'nugget':n} - hole-effect - {'sill': s, 'range': r, 'nugget': n} - OR - {'psill': p, 'range': r, 'nugget':n} + + # linear + {'slope': slope, 'nugget': nugget} + # power + {'scale': scale, 'exponent': exponent, 'nugget': nugget} + # gaussian, spherical, exponential and hole-effect: + {'sill': s, 'range': r, 'nugget': n} + # OR + {'psill': p, 'range': r, 'nugget': n} + Note that either the full sill or the partial sill (psill = sill - nugget) can be specified in the dict. For variogram model parameters provided in a list, the entries must be as follows: :: - linear - [slope, nugget] - power - [scale, exponent, nugget] - gaussian - [sill, range, nugget] - spherical - [sill, range, nugget] - exponential - [sill, range, nugget] - hole-effect - [sill, range, nugget] + + # linear + [slope, nugget] + # power + [scale, exponent, nugget] + # gaussian, spherical, exponential and hole-effect: + [sill, range, nugget] + Note that the full sill (NOT the partial sill) must be specified in the list format. For a custom variogram model, the parameters are required, as custom @@ -180,26 +176,43 @@ class UniversalKriging: References ---------- .. [1] P.K. Kitanidis, Introduction to Geostatistcs: Applications in - Hydrogeology, (Cambridge University Press, 1997) 272 p. + Hydrogeology, (Cambridge University Press, 1997) 272 p. """ - UNBIAS = True # This can be changed to remove the unbiasedness condition - # Really for testing purposes only... - eps = 1.e-10 # Cutoff for comparison to zero - variogram_dict = {'linear': variogram_models.linear_variogram_model, - 'power': variogram_models.power_variogram_model, - 'gaussian': variogram_models.gaussian_variogram_model, - 'spherical': variogram_models.spherical_variogram_model, - 'exponential': variogram_models.exponential_variogram_model, - 'hole-effect': variogram_models.hole_effect_variogram_model} - - def __init__(self, x, y, z, variogram_model='linear', - variogram_parameters=None, variogram_function=None, nlags=6, - weight=False, anisotropy_scaling=1., anisotropy_angle=0., - drift_terms=None, point_drift=None, external_drift=None, - external_drift_x=None, external_drift_y=None, - specified_drift=None, functional_drift=None, - verbose=False, enable_plotting=False): + UNBIAS = True # This can be changed to remove the unbiasedness condition + # Really for testing purposes only... + eps = 1.0e-10 # Cutoff for comparison to zero + variogram_dict = { + "linear": variogram_models.linear_variogram_model, + "power": variogram_models.power_variogram_model, + "gaussian": variogram_models.gaussian_variogram_model, + "spherical": variogram_models.spherical_variogram_model, + "exponential": variogram_models.exponential_variogram_model, + "hole-effect": variogram_models.hole_effect_variogram_model, + } + + def __init__( + self, + x, + y, + z, + variogram_model="linear", + variogram_parameters=None, + variogram_function=None, + nlags=6, + weight=False, + anisotropy_scaling=1.0, + anisotropy_angle=0.0, + drift_terms=None, + point_drift=None, + external_drift=None, + external_drift_x=None, + external_drift_y=None, + specified_drift=None, + functional_drift=None, + verbose=False, + enable_plotting=False, + ): # Deal with mutable default argument if drift_terms is None: @@ -223,14 +236,18 @@ def __init__(self, x, y, z, variogram_model='linear', variogram_parameters = [] anisotropy_scaling = self.model.pykrige_anis anisotropy_angle = self.model.pykrige_angle - if self.variogram_model not in self.variogram_dict.keys() and \ - self.variogram_model != 'custom': - raise ValueError("Specified variogram model '%s' " - "is not supported." % variogram_model) - elif self.variogram_model == 'custom': + if ( + self.variogram_model not in self.variogram_dict.keys() + and self.variogram_model != "custom" + ): + raise ValueError( + "Specified variogram model '%s' is not supported." % variogram_model + ) + elif self.variogram_model == "custom": if variogram_function is None or not callable(variogram_function): - raise ValueError("Must specify callable function for " - "custom variogram model.") + raise ValueError( + "Must specify callable function for custom variogram model." + ) else: self.variogram_function = variogram_function else: @@ -239,12 +256,13 @@ def __init__(self, x, y, z, variogram_model='linear', # Code assumes 1D input arrays. Ensures that any extraneous dimensions # don't get in the way. Copies are created to avoid any problems with # referencing the original passed arguments. - self.X_ORIG = \ - np.atleast_1d(np.squeeze(np.array(x, copy=True, dtype=np.float64))) - self.Y_ORIG = \ - np.atleast_1d(np.squeeze(np.array(y, copy=True, dtype=np.float64))) - self.Z = \ - np.atleast_1d(np.squeeze(np.array(z, copy=True, dtype=np.float64))) + self.X_ORIG = np.atleast_1d( + np.squeeze(np.array(x, copy=True, dtype=np.float64)) + ) + self.Y_ORIG = np.atleast_1d( + np.squeeze(np.array(y, copy=True, dtype=np.float64)) + ) + self.Z = np.atleast_1d(np.squeeze(np.array(z, copy=True, dtype=np.float64))) self.verbose = verbose self.enable_plotting = enable_plotting @@ -252,68 +270,83 @@ def __init__(self, x, y, z, variogram_model='linear', print("Plotting Enabled\n") # adjust for anisotropy... - self.XCENTER = (np.amax(self.X_ORIG) + np.amin(self.X_ORIG))/2.0 - self.YCENTER = (np.amax(self.Y_ORIG) + np.amin(self.Y_ORIG))/2.0 + self.XCENTER = (np.amax(self.X_ORIG) + np.amin(self.X_ORIG)) / 2.0 + self.YCENTER = (np.amax(self.Y_ORIG) + np.amin(self.Y_ORIG)) / 2.0 self.anisotropy_scaling = anisotropy_scaling self.anisotropy_angle = anisotropy_angle if self.verbose: print("Adjusting data for anisotropy...") - self.X_ADJUSTED, self.Y_ADJUSTED = \ - _adjust_for_anisotropy(np.vstack((self.X_ORIG, self.Y_ORIG)).T, - [self.XCENTER, self.YCENTER], - [self.anisotropy_scaling], - [self.anisotropy_angle]).T + self.X_ADJUSTED, self.Y_ADJUSTED = _adjust_for_anisotropy( + np.vstack((self.X_ORIG, self.Y_ORIG)).T, + [self.XCENTER, self.YCENTER], + [self.anisotropy_scaling], + [self.anisotropy_angle], + ).T if self.verbose: print("Initializing variogram model...") # see comment in ok.py about 'use_psill' kwarg... - vp_temp = _make_variogram_parameter_list(self.variogram_model, - variogram_parameters) - self.lags, self.semivariance, self.variogram_model_parameters = \ - _initialize_variogram_model(np.vstack((self.X_ADJUSTED, - self.Y_ADJUSTED)).T, - self.Z, self.variogram_model, vp_temp, - self.variogram_function, nlags, - weight, 'euclidean') + vp_temp = _make_variogram_parameter_list( + self.variogram_model, variogram_parameters + ) + ( + self.lags, + self.semivariance, + self.variogram_model_parameters, + ) = _initialize_variogram_model( + np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED)).T, + self.Z, + self.variogram_model, + vp_temp, + self.variogram_function, + nlags, + weight, + "euclidean", + ) # TODO extend geographic capabilities to UK... if self.verbose: - if self.variogram_model == 'linear': - print("Using '%s' Variogram Model" % 'linear') + if self.variogram_model == "linear": + print("Using '%s' Variogram Model" % "linear") print("Slope:", self.variogram_model_parameters[0]) - print("Nugget:", self.variogram_model_parameters[1], '\n') - elif self.variogram_model == 'power': - print("Using '%s' Variogram Model" % 'power') + print("Nugget:", self.variogram_model_parameters[1], "\n") + elif self.variogram_model == "power": + print("Using '%s' Variogram Model" % "power") print("Scale:", self.variogram_model_parameters[0]) print("Exponent:", self.variogram_model_parameters[1]) - print("Nugget:", self.variogram_model_parameters[2], '\n') - elif self.variogram_model == 'custom': + print("Nugget:", self.variogram_model_parameters[2], "\n") + elif self.variogram_model == "custom": print("Using Custom Variogram Model") else: print("Using '%s' Variogram Model" % self.variogram_model) print("Partial Sill:", self.variogram_model_parameters[0]) - print("Full Sill:", self.variogram_model_parameters[0] + - self.variogram_model_parameters[2]) + print( + "Full Sill:", + self.variogram_model_parameters[0] + + self.variogram_model_parameters[2], + ) print("Range:", self.variogram_model_parameters[1]) - print("Nugget:", self.variogram_model_parameters[2], '\n') + print("Nugget:", self.variogram_model_parameters[2], "\n") if self.enable_plotting: self.display_variogram_model() if self.verbose: print("Calculating statistics on variogram model fit...") - self.delta, self.sigma, self.epsilon = \ - _find_statistics(np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED)).T, - self.Z, self.variogram_function, - self.variogram_model_parameters, - 'euclidean') + self.delta, self.sigma, self.epsilon = _find_statistics( + np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED)).T, + self.Z, + self.variogram_function, + self.variogram_model_parameters, + "euclidean", + ) self.Q1 = core.calcQ1(self.epsilon) self.Q2 = core.calcQ2(self.epsilon) self.cR = core.calc_cR(self.Q2, self.sigma) if self.verbose: print("Q1 =", self.Q1) print("Q2 =", self.Q2) - print("cR =", self.cR, '\n') + print("cR =", self.cR, "\n") if self.verbose: print("Initializing drift terms...") @@ -321,7 +354,7 @@ def __init__(self, x, y, z, variogram_model='linear', # Note that the regional linear drift values will be based # on the adjusted coordinate system, Really, it doesn't actually # matter which coordinate system is used here. - if 'regional_linear' in drift_terms: + if "regional_linear" in drift_terms: self.regional_linear_drift = True if self.verbose: print("Implementing regional linear drift.") @@ -330,87 +363,108 @@ def __init__(self, x, y, z, variogram_model='linear', # External Z scalars are extracted using the original # (unadjusted) coordinates. - if 'external_Z' in drift_terms: + if "external_Z" in drift_terms: if external_drift is None: raise ValueError("Must specify external Z drift terms.") if external_drift_x is None or external_drift_y is None: - raise ValueError("Must specify coordinates of " - "external Z drift terms.") + raise ValueError("Must specify coordinates of external Z drift terms.") self.external_Z_drift = True - if external_drift.shape[0] != external_drift_y.shape[0] or \ - external_drift.shape[1] != external_drift_x.shape[0]: - if external_drift.shape[0] == external_drift_x.shape[0] and \ - external_drift.shape[1] == external_drift_y.shape[0]: + if ( + external_drift.shape[0] != external_drift_y.shape[0] + or external_drift.shape[1] != external_drift_x.shape[0] + ): + if ( + external_drift.shape[0] == external_drift_x.shape[0] + and external_drift.shape[1] == external_drift_y.shape[0] + ): self.external_Z_drift = np.array(external_drift.T) else: - raise ValueError("External drift dimensions do not match " - "provided x- and y-coordinate dimensions.") + raise ValueError( + "External drift dimensions do not match " + "provided x- and y-coordinate dimensions." + ) else: self.external_Z_array = np.array(external_drift) self.external_Z_array_x = np.array(external_drift_x).flatten() self.external_Z_array_y = np.array(external_drift_y).flatten() - self.z_scalars = self._calculate_data_point_zscalars(self.X_ORIG, - self.Y_ORIG) + self.z_scalars = self._calculate_data_point_zscalars( + self.X_ORIG, self.Y_ORIG + ) if self.verbose: print("Implementing external Z drift.") else: self.external_Z_drift = False # Well coordinates are rotated into adjusted coordinate frame. - if 'point_log' in drift_terms: + if "point_log" in drift_terms: if point_drift is None: - raise ValueError("Must specify location(s) and strength(s) " - "of point drift terms.") + raise ValueError( + "Must specify location(s) and strength(s) of point drift terms." + ) self.point_log_drift = True point_log = np.atleast_2d(np.squeeze(np.array(point_drift, copy=True))) self.point_log_array = np.zeros(point_log.shape) self.point_log_array[:, 2] = point_log[:, 2] - self.point_log_array[:, :2] = \ - _adjust_for_anisotropy(np.vstack((point_log[:, 0], - point_log[:, 1])).T, - [self.XCENTER, self.YCENTER], - [self.anisotropy_scaling], - [self.anisotropy_angle]) + self.point_log_array[:, :2] = _adjust_for_anisotropy( + np.vstack((point_log[:, 0], point_log[:, 1])).T, + [self.XCENTER, self.YCENTER], + [self.anisotropy_scaling], + [self.anisotropy_angle], + ) if self.verbose: - print("Implementing external point-logarithmic drift; " - "number of points =", self.point_log_array.shape[0], '\n') + print( + "Implementing external point-logarithmic drift; " + "number of points =", + self.point_log_array.shape[0], + "\n", + ) else: self.point_log_drift = False - if 'specified' in drift_terms: + if "specified" in drift_terms: if type(specified_drift) is not list: - raise TypeError("Arrays for specified drift terms must be " - "encapsulated in a list.") + raise TypeError( + "Arrays for specified drift terms must be " + "encapsulated in a list." + ) if len(specified_drift) == 0: - raise ValueError("Must provide at least one drift-value array " - "when using the 'specified' drift capability.") + raise ValueError( + "Must provide at least one drift-value array " + "when using the 'specified' drift capability." + ) self.specified_drift = True self.specified_drift_data_arrays = [] for term in specified_drift: specified = np.squeeze(np.array(term, copy=True)) if specified.size != self.X_ORIG.size: - raise ValueError("Must specify the drift values for each " - "data point when using the 'specified' " - "drift capability.") + raise ValueError( + "Must specify the drift values for each " + "data point when using the 'specified' " + "drift capability." + ) self.specified_drift_data_arrays.append(specified) else: self.specified_drift = False # The provided callable functions will be evaluated using # the adjusted coordinates. - if 'functional' in drift_terms: + if "functional" in drift_terms: if type(functional_drift) is not list: - raise TypeError("Callables for functional drift terms must " - "be encapsulated in a list.") + raise TypeError( + "Callables for functional drift terms must " + "be encapsulated in a list." + ) if len(functional_drift) == 0: - raise ValueError("Must provide at least one callable object " - "when using the 'functional' drift capability.") + raise ValueError( + "Must provide at least one callable object " + "when using the 'functional' drift capability." + ) self.functional_drift = True self.functional_drift_terms = functional_drift else: self.functional_drift = False - def _calculate_data_point_zscalars(self, x, y, type_='array'): + def _calculate_data_point_zscalars(self, x, y, type_="array"): """Determines the Z-scalar values at the specified coordinates for use when setting up the kriging matrix. Uses bilinear interpolation. @@ -421,7 +475,7 @@ def _calculate_data_point_zscalars(self, x, y, type_='array'): Z value for that cell in the kriged grid. Rather, the exact Z value right at the coordinate is used.""" - if type_ == 'scalar': + if type_ == "scalar": nx = 1 ny = 1 z_scalars = None @@ -437,7 +491,7 @@ def _calculate_data_point_zscalars(self, x, y, type_='array'): for m in range(ny): for n in range(nx): - if type_ == 'scalar': + if type_ == "scalar": xn = x yn = y else: @@ -448,61 +502,78 @@ def _calculate_data_point_zscalars(self, x, y, type_='array'): xn = x[m, n] yn = y[m, n] - if xn > np.amax(self.external_Z_array_x) or \ - xn < np.amin(self.external_Z_array_x) or \ - yn > np.amax(self.external_Z_array_y) or \ - yn < np.amin(self.external_Z_array_y): - raise ValueError("External drift array does not cover " - "specified kriging domain.") + if ( + xn > np.amax(self.external_Z_array_x) + or xn < np.amin(self.external_Z_array_x) + or yn > np.amax(self.external_Z_array_y) + or yn < np.amin(self.external_Z_array_y) + ): + raise ValueError( + "External drift array does not cover " + "specified kriging domain." + ) # bilinear interpolation - external_x2_index = \ - np.amin(np.where(self.external_Z_array_x >= xn)[0]) - external_x1_index = \ - np.amax(np.where(self.external_Z_array_x <= xn)[0]) - external_y2_index = \ - np.amin(np.where(self.external_Z_array_y >= yn)[0]) - external_y1_index = \ - np.amax(np.where(self.external_Z_array_y <= yn)[0]) + external_x2_index = np.amin(np.where(self.external_Z_array_x >= xn)[0]) + external_x1_index = np.amax(np.where(self.external_Z_array_x <= xn)[0]) + external_y2_index = np.amin(np.where(self.external_Z_array_y >= yn)[0]) + external_y1_index = np.amax(np.where(self.external_Z_array_y <= yn)[0]) if external_y1_index == external_y2_index: if external_x1_index == external_x2_index: z = self.external_Z_array[external_y1_index, external_x1_index] else: - z = (self.external_Z_array[external_y1_index, external_x1_index] * - (self.external_Z_array_x[external_x2_index] - xn) + - self.external_Z_array[external_y2_index, external_x2_index] * - (xn - self.external_Z_array_x[external_x1_index])) / \ - (self.external_Z_array_x[external_x2_index] - - self.external_Z_array_x[external_x1_index]) + z = ( + self.external_Z_array[external_y1_index, external_x1_index] + * (self.external_Z_array_x[external_x2_index] - xn) + + self.external_Z_array[ + external_y2_index, external_x2_index + ] + * (xn - self.external_Z_array_x[external_x1_index]) + ) / ( + self.external_Z_array_x[external_x2_index] + - self.external_Z_array_x[external_x1_index] + ) elif external_x1_index == external_x2_index: if external_y1_index == external_y2_index: z = self.external_Z_array[external_y1_index, external_x1_index] else: - z = (self.external_Z_array[external_y1_index, external_x1_index] * - (self.external_Z_array_y[external_y2_index] - yn) + - self.external_Z_array[external_y2_index, external_x2_index] * - (yn - self.external_Z_array_y[external_y1_index])) / \ - (self.external_Z_array_y[external_y2_index] - - self.external_Z_array_y[external_y1_index]) + z = ( + self.external_Z_array[external_y1_index, external_x1_index] + * (self.external_Z_array_y[external_y2_index] - yn) + + self.external_Z_array[ + external_y2_index, external_x2_index + ] + * (yn - self.external_Z_array_y[external_y1_index]) + ) / ( + self.external_Z_array_y[external_y2_index] + - self.external_Z_array_y[external_y1_index] + ) else: - z = (self.external_Z_array[external_y1_index, external_x1_index] * - (self.external_Z_array_x[external_x2_index] - xn) * - (self.external_Z_array_y[external_y2_index] - yn) + - self.external_Z_array[external_y1_index, external_x2_index] * - (xn - self.external_Z_array_x[external_x1_index]) * - (self.external_Z_array_y[external_y2_index] - yn) + - self.external_Z_array[external_y2_index, external_x1_index] * - (self.external_Z_array_x[external_x2_index] - xn) * - (yn - self.external_Z_array_y[external_y1_index]) + - self.external_Z_array[external_y2_index, external_x2_index] * - (xn - self.external_Z_array_x[external_x1_index]) * - (yn - self.external_Z_array_y[external_y1_index])) / \ - ((self.external_Z_array_x[external_x2_index] - - self.external_Z_array_x[external_x1_index]) * - (self.external_Z_array_y[external_y2_index] - - self.external_Z_array_y[external_y1_index])) - - if type_ == 'scalar': + z = ( + self.external_Z_array[external_y1_index, external_x1_index] + * (self.external_Z_array_x[external_x2_index] - xn) + * (self.external_Z_array_y[external_y2_index] - yn) + + self.external_Z_array[external_y1_index, external_x2_index] + * (xn - self.external_Z_array_x[external_x1_index]) + * (self.external_Z_array_y[external_y2_index] - yn) + + self.external_Z_array[external_y2_index, external_x1_index] + * (self.external_Z_array_x[external_x2_index] - xn) + * (yn - self.external_Z_array_y[external_y1_index]) + + self.external_Z_array[external_y2_index, external_x2_index] + * (xn - self.external_Z_array_x[external_x1_index]) + * (yn - self.external_Z_array_y[external_y1_index]) + ) / ( + ( + self.external_Z_array_x[external_x2_index] + - self.external_Z_array_x[external_x1_index] + ) + * ( + self.external_Z_array_y[external_y2_index] + - self.external_Z_array_y[external_y1_index] + ) + ) + + if type_ == "scalar": z_scalars = z else: if z_scalars.ndim == 1: @@ -512,9 +583,16 @@ def _calculate_data_point_zscalars(self, x, y, type_='array'): return z_scalars - def update_variogram_model(self, variogram_model, variogram_parameters=None, - variogram_function=None, nlags=6, weight=False, - anisotropy_scaling=1., anisotropy_angle=0.): + def update_variogram_model( + self, + variogram_model, + variogram_parameters=None, + variogram_function=None, + nlags=6, + weight=False, + anisotropy_scaling=1.0, + anisotropy_angle=0.0, + ): """Allows user to update variogram type and/or variogram model parameters. @@ -563,89 +641,114 @@ def update_variogram_model(self, variogram_model, variogram_parameters=None, variogram_parameters = [] anisotropy_scaling = self.model.pykrige_anis anisotropy_angle = self.model.pykrige_angle - if self.variogram_model not in self.variogram_dict.keys() and \ - self.variogram_model != 'custom': - raise ValueError("Specified variogram model '%s' " - "is not supported." % variogram_model) - elif self.variogram_model == 'custom': + if ( + self.variogram_model not in self.variogram_dict.keys() + and self.variogram_model != "custom" + ): + raise ValueError( + "Specified variogram model '%s' is not supported." % variogram_model + ) + elif self.variogram_model == "custom": if variogram_function is None or not callable(variogram_function): - raise ValueError("Must specify callable function for " - "custom variogram model.") + raise ValueError( + "Must specify callable function for custom variogram model." + ) else: self.variogram_function = variogram_function else: self.variogram_function = self.variogram_dict[self.variogram_model] - if anisotropy_scaling != self.anisotropy_scaling or \ - anisotropy_angle != self.anisotropy_angle: + if ( + anisotropy_scaling != self.anisotropy_scaling + or anisotropy_angle != self.anisotropy_angle + ): if self.verbose: print("Adjusting data for anisotropy...") self.anisotropy_scaling = anisotropy_scaling self.anisotropy_angle = anisotropy_angle - self.X_ADJUSTED, self.Y_ADJUSTED =\ - _adjust_for_anisotropy(np.vstack((self.X_ORIG, self.Y_ORIG)).T, - [self.XCENTER, self.YCENTER], - [self.anisotropy_scaling], - [self.anisotropy_angle]).T + self.X_ADJUSTED, self.Y_ADJUSTED = _adjust_for_anisotropy( + np.vstack((self.X_ORIG, self.Y_ORIG)).T, + [self.XCENTER, self.YCENTER], + [self.anisotropy_scaling], + [self.anisotropy_angle], + ).T if self.verbose: print("Updating variogram mode...") # See note above about the 'use_psill' kwarg... - vp_temp = _make_variogram_parameter_list(self.variogram_model, - variogram_parameters) - self.lags, self.semivariance, self.variogram_model_parameters = \ - _initialize_variogram_model(np.vstack((self.X_ADJUSTED, - self.Y_ADJUSTED)).T, - self.Z, self.variogram_model, vp_temp, - self.variogram_function, nlags, - weight, 'euclidean') + vp_temp = _make_variogram_parameter_list( + self.variogram_model, variogram_parameters + ) + ( + self.lags, + self.semivariance, + self.variogram_model_parameters, + ) = _initialize_variogram_model( + np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED)).T, + self.Z, + self.variogram_model, + vp_temp, + self.variogram_function, + nlags, + weight, + "euclidean", + ) if self.verbose: - if self.variogram_model == 'linear': - print("Using '%s' Variogram Model" % 'linear') + if self.variogram_model == "linear": + print("Using '%s' Variogram Model" % "linear") print("Slope:", self.variogram_model_parameters[0]) - print("Nugget:", self.variogram_model_parameters[1], '\n') - elif self.variogram_model == 'power': - print("Using '%s' Variogram Model" % 'power') + print("Nugget:", self.variogram_model_parameters[1], "\n") + elif self.variogram_model == "power": + print("Using '%s' Variogram Model" % "power") print("Scale:", self.variogram_model_parameters[0]) print("Exponent:", self.variogram_model_parameters[1]) - print("Nugget:", self.variogram_model_parameters[2], '\n') - elif self.variogram_model == 'custom': + print("Nugget:", self.variogram_model_parameters[2], "\n") + elif self.variogram_model == "custom": print("Using Custom Variogram Model") else: print("Using '%s' Variogram Model" % self.variogram_model) print("Partial Sill:", self.variogram_model_parameters[0]) - print("Full Sill:", self.variogram_model_parameters[0] + - self.variogram_model_parameters[2]) + print( + "Full Sill:", + self.variogram_model_parameters[0] + + self.variogram_model_parameters[2], + ) print("Range:", self.variogram_model_parameters[1]) - print("Nugget:", self.variogram_model_parameters[2], '\n') + print("Nugget:", self.variogram_model_parameters[2], "\n") if self.enable_plotting: self.display_variogram_model() if self.verbose: print("Calculating statistics on variogram model fit...") - self.delta, self.sigma, self.epsilon = \ - _find_statistics(np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED)).T, - self.Z, self.variogram_function, - self.variogram_model_parameters, - 'euclidean') + self.delta, self.sigma, self.epsilon = _find_statistics( + np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED)).T, + self.Z, + self.variogram_function, + self.variogram_model_parameters, + "euclidean", + ) self.Q1 = core.calcQ1(self.epsilon) self.Q2 = core.calcQ2(self.epsilon) self.cR = core.calc_cR(self.Q2, self.sigma) if self.verbose: print("Q1 =", self.Q1) print("Q2 =", self.Q2) - print("cR =", self.cR, '\n') + print("cR =", self.cR, "\n") def display_variogram_model(self): """Displays variogram model with the actual binned data.""" + import matplotlib.pyplot as plt + fig = plt.figure() ax = fig.add_subplot(111) - ax.plot(self.lags, self.semivariance, 'r*') - ax.plot(self.lags, - self.variogram_function(self.variogram_model_parameters, - self.lags), 'k-') + ax.plot(self.lags, self.semivariance, "r*") + ax.plot( + self.lags, + self.variogram_function(self.variogram_model_parameters, self.lags), + "k-", + ) plt.show() def get_variogram_points(self): @@ -663,7 +766,10 @@ def get_variogram_points(self): lags (array) - the lags at which the variogram was evaluated variogram (array) - the variogram function evaluated at the lags """ - return self.lags, self.variogram_function(self.variogram_model_parameters, self.lags) + return ( + self.lags, + self.variogram_function(self.variogram_model_parameters, self.lags), + ) def switch_verbose(self): """Allows user to switch code talk-back on/off. Takes no arguments.""" @@ -679,9 +785,11 @@ def get_epsilon_residuals(self): def plot_epsilon_residuals(self): """Plots the epsilon residuals for the variogram fit.""" + import matplotlib.pyplot as plt + fig = plt.figure() ax = fig.add_subplot(111) - ax.scatter(range(self.epsilon.size), self.epsilon, c='k', marker='*') + ax.scatter(range(self.epsilon.size), self.epsilon, c="k", marker="*") ax.axhline(y=0.0) plt.show() @@ -703,15 +811,16 @@ def print_statistics(self): def _get_kriging_matrix(self, n, n_withdrifts): """Assembles the kriging matrix.""" - xy = np.concatenate((self.X_ADJUSTED[:, np.newaxis], - self.Y_ADJUSTED[:, np.newaxis]), axis=1) - d = cdist(xy, xy, 'euclidean') + xy = np.concatenate( + (self.X_ADJUSTED[:, np.newaxis], self.Y_ADJUSTED[:, np.newaxis]), axis=1 + ) + d = cdist(xy, xy, "euclidean") if self.UNBIAS: - a = np.zeros((n_withdrifts+1, n_withdrifts+1)) + a = np.zeros((n_withdrifts + 1, n_withdrifts + 1)) else: a = np.zeros((n_withdrifts, n_withdrifts)) - a[:n, :n] = - self.variogram_function(self.variogram_model_parameters, d) - np.fill_diagonal(a, 0.) + a[:n, :n] = -self.variogram_function(self.variogram_model_parameters, d) + np.fill_diagonal(a, 0.0) i = n if self.regional_linear_drift: @@ -723,12 +832,16 @@ def _get_kriging_matrix(self, n, n_withdrifts): i += 1 if self.point_log_drift: for well_no in range(self.point_log_array.shape[0]): - log_dist = np.log(np.sqrt((self.X_ADJUSTED - self.point_log_array[well_no, 0])**2 + - (self.Y_ADJUSTED - self.point_log_array[well_no, 1])**2)) + log_dist = np.log( + np.sqrt( + (self.X_ADJUSTED - self.point_log_array[well_no, 0]) ** 2 + + (self.Y_ADJUSTED - self.point_log_array[well_no, 1]) ** 2 + ) + ) if np.any(np.isinf(log_dist)): log_dist[np.isinf(log_dist)] = -100.0 - a[:n, i] = - self.point_log_array[well_no, 2] * log_dist - a[i, :n] = - self.point_log_array[well_no, 2] * log_dist + a[:n, i] = -self.point_log_array[well_no, 2] * log_dist + a[i, :n] = -self.point_log_array[well_no, 2] * log_dist i += 1 if self.external_Z_drift: a[:n, i] = self.z_scalars @@ -745,17 +858,17 @@ def _get_kriging_matrix(self, n, n_withdrifts): a[i, :n] = func(self.X_ADJUSTED, self.Y_ADJUSTED) i += 1 if i != n_withdrifts: - warnings.warn("Error in creating kriging matrix. Kriging may fail.", - RuntimeWarning) + warnings.warn( + "Error in creating kriging matrix. Kriging may fail.", RuntimeWarning + ) if self.UNBIAS: a[n_withdrifts, :n] = 1.0 a[:n, n_withdrifts] = 1.0 - a[n:n_withdrifts + 1, n:n_withdrifts + 1] = 0.0 + a[n : n_withdrifts + 1, n : n_withdrifts + 1] = 0.0 return a - def _exec_vector(self, a, bd, xy, xy_orig, mask, - n_withdrifts, spec_drift_grids): + def _exec_vector(self, a, bd, xy, xy_orig, mask, n_withdrifts, spec_drift_grids): """Solves the kriging system as a vectorized operation. This method can take a lot of memory for large grids and/or large datasets.""" @@ -771,10 +884,10 @@ def _exec_vector(self, a, bd, xy, xy_orig, mask, zero_index = np.where(np.absolute(bd) <= self.eps) if self.UNBIAS: - b = np.zeros((npt, n_withdrifts+1, 1)) + b = np.zeros((npt, n_withdrifts + 1, 1)) else: b = np.zeros((npt, n_withdrifts, 1)) - b[:, :n, 0] = - self.variogram_function(self.variogram_model_parameters, bd) + b[:, :n, 0] = -self.variogram_function(self.variogram_model_parameters, bd) if zero_value: b[zero_index[0], zero_index[1], 0] = 0.0 @@ -786,14 +899,20 @@ def _exec_vector(self, a, bd, xy, xy_orig, mask, i += 1 if self.point_log_drift: for well_no in range(self.point_log_array.shape[0]): - log_dist = np.log(np.sqrt((xy[:, 0] - self.point_log_array[well_no, 0])**2 + - (xy[:, 1] - self.point_log_array[well_no, 1])**2)) + log_dist = np.log( + np.sqrt( + (xy[:, 0] - self.point_log_array[well_no, 0]) ** 2 + + (xy[:, 1] - self.point_log_array[well_no, 1]) ** 2 + ) + ) if np.any(np.isinf(log_dist)): log_dist[np.isinf(log_dist)] = -100.0 - b[:, i, 0] = - self.point_log_array[well_no, 2] * log_dist + b[:, i, 0] = -self.point_log_array[well_no, 2] * log_dist i += 1 if self.external_Z_drift: - b[:, i, 0] = self._calculate_data_point_zscalars(xy_orig[:, 0], xy_orig[:, 1]) + b[:, i, 0] = self._calculate_data_point_zscalars( + xy_orig[:, 0], xy_orig[:, 1] + ) i += 1 if self.specified_drift: for spec_vals in spec_drift_grids: @@ -804,27 +923,36 @@ def _exec_vector(self, a, bd, xy, xy_orig, mask, b[:, i, 0] = func(xy[:, 0], xy[:, 1]) i += 1 if i != n_withdrifts: - warnings.warn("Error in setting up kriging system. " - "Kriging may fail.", RuntimeWarning) + warnings.warn( + "Error in setting up kriging system. Kriging may fail.", RuntimeWarning, + ) if self.UNBIAS: b[:, n_withdrifts, 0] = 1.0 if (~mask).any(): - mask_b = np.repeat(mask[:, np.newaxis, np.newaxis], - n_withdrifts+1, axis=1) + mask_b = np.repeat( + mask[:, np.newaxis, np.newaxis], n_withdrifts + 1, axis=1 + ) b = np.ma.array(b, mask=mask_b) if self.UNBIAS: - x = np.dot(a_inv, b.reshape((npt, n_withdrifts+1)).T).reshape((1, n_withdrifts+1, npt)).T + x = ( + np.dot(a_inv, b.reshape((npt, n_withdrifts + 1)).T) + .reshape((1, n_withdrifts + 1, npt)) + .T + ) else: - x = np.dot(a_inv, b.reshape((npt, n_withdrifts)).T).reshape((1, n_withdrifts, npt)).T + x = ( + np.dot(a_inv, b.reshape((npt, n_withdrifts)).T) + .reshape((1, n_withdrifts, npt)) + .T + ) zvalues = np.sum(x[:, :n, 0] * self.Z, axis=1) sigmasq = np.sum(x[:, :, 0] * -b[:, :, 0], axis=1) return zvalues, sigmasq - def _exec_loop(self, a, bd_all, xy, xy_orig, mask, - n_withdrifts, spec_drift_grids): + def _exec_loop(self, a, bd_all, xy, xy_orig, mask, n_withdrifts, spec_drift_grids): """Solves the kriging system by looping over all specified points. Less memory-intensive, but involves a Python-level loop.""" @@ -835,8 +963,10 @@ def _exec_loop(self, a, bd_all, xy, xy_orig, mask, a_inv = scipy.linalg.inv(a) - for j in np.nonzero(~mask)[0]: # Note that this is the same thing as range(npt) if mask is not defined, - bd = bd_all[j] # otherwise it takes the non-masked elements. + for j in np.nonzero(~mask)[ + 0 + ]: # Note that this is the same thing as range(npt) if mask is not defined, + bd = bd_all[j] # otherwise it takes the non-masked elements. if np.any(np.absolute(bd) <= self.eps): zero_value = True zero_index = np.where(np.absolute(bd) <= self.eps) @@ -845,10 +975,10 @@ def _exec_loop(self, a, bd_all, xy, xy_orig, mask, zero_value = False if self.UNBIAS: - b = np.zeros((n_withdrifts+1, 1)) + b = np.zeros((n_withdrifts + 1, 1)) else: b = np.zeros((n_withdrifts, 1)) - b[:n, 0] = - self.variogram_function(self.variogram_model_parameters, bd) + b[:n, 0] = -self.variogram_function(self.variogram_model_parameters, bd) if zero_value: b[zero_index[0], 0] = 0.0 @@ -860,14 +990,20 @@ def _exec_loop(self, a, bd_all, xy, xy_orig, mask, i += 1 if self.point_log_drift: for well_no in range(self.point_log_array.shape[0]): - log_dist = np.log(np.sqrt((xy[j, 0] - self.point_log_array[well_no, 0])**2 + - (xy[j, 1] - self.point_log_array[well_no, 1])**2)) + log_dist = np.log( + np.sqrt( + (xy[j, 0] - self.point_log_array[well_no, 0]) ** 2 + + (xy[j, 1] - self.point_log_array[well_no, 1]) ** 2 + ) + ) if np.any(np.isinf(log_dist)): log_dist[np.isinf(log_dist)] = -100.0 - b[i, 0] = - self.point_log_array[well_no, 2] * log_dist + b[i, 0] = -self.point_log_array[well_no, 2] * log_dist i += 1 if self.external_Z_drift: - b[i, 0] = self._calculate_data_point_zscalars(xy_orig[j, 0], xy_orig[j, 1], type_='scalar') + b[i, 0] = self._calculate_data_point_zscalars( + xy_orig[j, 0], xy_orig[j, 1], type_="scalar" + ) i += 1 if self.specified_drift: for spec_vals in spec_drift_grids: @@ -878,8 +1014,10 @@ def _exec_loop(self, a, bd_all, xy, xy_orig, mask, b[i, 0] = func(xy[j, 0], xy[j, 1]) i += 1 if i != n_withdrifts: - warnings.warn("Error in setting up kriging system. " - "Kriging may fail.", RuntimeWarning) + warnings.warn( + "Error in setting up kriging system. Kriging may fail.", + RuntimeWarning, + ) if self.UNBIAS: b[n_withdrifts, 0] = 1.0 @@ -889,8 +1027,15 @@ def _exec_loop(self, a, bd_all, xy, xy_orig, mask, return zvalues, sigmasq - def execute(self, style, xpoints, ypoints, mask=None, - backend='vectorized', specified_drift_arrays=None): + def execute( + self, + style, + xpoints, + ypoints, + mask=None, + backend="vectorized", + specified_drift_arrays=None, + ): """Calculates a kriged grid and the associated variance. Includes drift terms. @@ -976,9 +1121,8 @@ def execute(self, style, xpoints, ypoints, mask=None, if self.verbose: print("Executing Universal Kriging...\n") - if style != 'grid' and style != 'masked' and style != 'points': - raise ValueError("style argument must be 'grid', 'points', " - "or 'masked'") + if style != "grid" and style != "masked" and style != "points": + raise ValueError("style argument must be 'grid', 'points', or 'masked'") n = self.X_ADJUSTED.shape[0] n_withdrifts = n @@ -998,111 +1142,144 @@ def execute(self, style, xpoints, ypoints, mask=None, n_withdrifts += len(self.functional_drift_terms) a = self._get_kriging_matrix(n, n_withdrifts) - if style in ['grid', 'masked']: - if style == 'masked': + if style in ["grid", "masked"]: + if style == "masked": if mask is None: - raise IOError("Must specify boolean masking array when " - "style is 'masked'.") + raise IOError( + "Must specify boolean masking array when style is 'masked'." + ) if mask.shape[0] != ny or mask.shape[1] != nx: if mask.shape[0] == nx and mask.shape[1] == ny: mask = mask.T else: - raise ValueError("Mask dimensions do not match " - "specified grid dimensions.") + raise ValueError( + "Mask dimensions do not match specified grid dimensions." + ) mask = mask.flatten() - npt = ny*nx + npt = ny * nx grid_x, grid_y = np.meshgrid(xpts, ypts) xpts = grid_x.flatten() ypts = grid_y.flatten() - elif style == 'points': + elif style == "points": if xpts.size != ypts.size: - raise ValueError("xpoints and ypoints must have same " - "dimensions when treated as listing " - "discrete points.") + raise ValueError( + "xpoints and ypoints must have same " + "dimensions when treated as listing " + "discrete points." + ) npt = nx else: - raise ValueError("style argument must be 'grid', 'points', " - "or 'masked'") + raise ValueError("style argument must be 'grid', 'points', or 'masked'") if specified_drift_arrays is None: specified_drift_arrays = [] spec_drift_grids = [] if self.specified_drift: if len(specified_drift_arrays) == 0: - raise ValueError("Must provide drift values for kriging points " - "when using 'specified' drift capability.") + raise ValueError( + "Must provide drift values for kriging points " + "when using 'specified' drift capability." + ) if type(specified_drift_arrays) is not list: - raise TypeError("Arrays for specified drift terms must be " - "encapsulated in a list.") + raise TypeError( + "Arrays for specified drift terms must be " + "encapsulated in a list." + ) for spec in specified_drift_arrays: - if style in ['grid', 'masked']: + if style in ["grid", "masked"]: if spec.ndim < 2: - raise ValueError("Dimensions of drift values array do " - "not match specified grid dimensions.") + raise ValueError( + "Dimensions of drift values array do " + "not match specified grid dimensions." + ) elif spec.shape[0] != ny or spec.shape[1] != nx: if spec.shape[0] == nx and spec.shape[1] == ny: spec_drift_grids.append(np.squeeze(spec.T)) else: - raise ValueError("Dimensions of drift values array " - "do not match specified grid " - "dimensions.") + raise ValueError( + "Dimensions of drift values array " + "do not match specified grid " + "dimensions." + ) else: spec_drift_grids.append(np.squeeze(spec)) - elif style == 'points': + elif style == "points": if spec.ndim != 1: - raise ValueError("Dimensions of drift values array do " - "not match specified grid dimensions.") + raise ValueError( + "Dimensions of drift values array do " + "not match specified grid dimensions." + ) elif spec.shape[0] != xpts.size: - raise ValueError("Number of supplied drift values in " - "array do not match specified number " - "of kriging points.") + raise ValueError( + "Number of supplied drift values in " + "array do not match specified number " + "of kriging points." + ) else: spec_drift_grids.append(np.squeeze(spec)) if len(spec_drift_grids) != len(self.specified_drift_data_arrays): - raise ValueError("Inconsistent number of specified drift " - "terms supplied.") + raise ValueError( + "Inconsistent number of specified drift terms supplied." + ) else: if len(specified_drift_arrays) != 0: - warnings.warn("Provided specified drift values, but " - "'specified' drift was not initialized during " - "instantiation of UniversalKriging class.", - RuntimeWarning) - - xy_points_original = \ - np.concatenate((xpts[:, np.newaxis], ypts[:, np.newaxis]), axis=1) - xpts, ypts = _adjust_for_anisotropy(np.vstack((xpts, ypts)).T, - [self.XCENTER, self.YCENTER], - [self.anisotropy_scaling], - [self.anisotropy_angle]).T - xy_points = \ - np.concatenate((xpts[:, np.newaxis], ypts[:, np.newaxis]), axis=1) - xy_data = np.concatenate((self.X_ADJUSTED[:, np.newaxis], - self.Y_ADJUSTED[:, np.newaxis]), axis=1) - - if style != 'masked': - mask = np.zeros(npt, dtype='bool') - - bd = cdist(xy_points, xy_data, 'euclidean') - if backend == 'vectorized': - zvalues, sigmasq = self._exec_vector(a, bd, xy_points, - xy_points_original, - mask, n_withdrifts, - spec_drift_grids) - elif backend == 'loop': - zvalues, sigmasq = self._exec_loop(a, bd, xy_points, - xy_points_original, - mask, n_withdrifts, - spec_drift_grids) + warnings.warn( + "Provided specified drift values, but " + "'specified' drift was not initialized during " + "instantiation of UniversalKriging class.", + RuntimeWarning, + ) + + xy_points_original = np.concatenate( + (xpts[:, np.newaxis], ypts[:, np.newaxis]), axis=1 + ) + xpts, ypts = _adjust_for_anisotropy( + np.vstack((xpts, ypts)).T, + [self.XCENTER, self.YCENTER], + [self.anisotropy_scaling], + [self.anisotropy_angle], + ).T + xy_points = np.concatenate((xpts[:, np.newaxis], ypts[:, np.newaxis]), axis=1) + xy_data = np.concatenate( + (self.X_ADJUSTED[:, np.newaxis], self.Y_ADJUSTED[:, np.newaxis]), axis=1 + ) + + if style != "masked": + mask = np.zeros(npt, dtype="bool") + + bd = cdist(xy_points, xy_data, "euclidean") + if backend == "vectorized": + zvalues, sigmasq = self._exec_vector( + a, + bd, + xy_points, + xy_points_original, + mask, + n_withdrifts, + spec_drift_grids, + ) + elif backend == "loop": + zvalues, sigmasq = self._exec_loop( + a, + bd, + xy_points, + xy_points_original, + mask, + n_withdrifts, + spec_drift_grids, + ) else: - raise ValueError('Specified backend {} is not supported ' - 'for 2D universal kriging.'.format(backend)) + raise ValueError( + "Specified backend {} is not supported " + "for 2D universal kriging.".format(backend) + ) - if style == 'masked': + if style == "masked": zvalues = np.ma.array(zvalues, mask=mask) sigmasq = np.ma.array(sigmasq, mask=mask) - if style in ['masked', 'grid']: + if style in ["masked", "grid"]: zvalues = zvalues.reshape((ny, nx)) sigmasq = sigmasq.reshape((ny, nx)) diff --git a/pykrige/uk3d.py b/pykrige/uk3d.py index c320f37..c94664a 100644 --- a/pykrige/uk3d.py +++ b/pykrige/uk3d.py @@ -1,19 +1,5 @@ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -import numpy as np -import scipy.linalg -from scipy.spatial.distance import cdist -import matplotlib.pyplot as plt -from . import variogram_models -from . import core -from .core import _adjust_for_anisotropy, _initialize_variogram_model, \ - _make_variogram_parameter_list, _find_statistics -import warnings - -__doc__ = """ +# coding: utf-8 +""" PyKrige ======= @@ -29,12 +15,24 @@ .. [1] P.K. Kitanidis, Introduction to Geostatistcs: Applications in Hydrogeology, (Cambridge University Press, 1997) 272 p. -Copyright (c) 2015-2018, PyKrige Developers +Copyright (c) 2015-2020, PyKrige Developers """ +import numpy as np +import scipy.linalg +from scipy.spatial.distance import cdist +from . import variogram_models +from . import core +from .core import ( + _adjust_for_anisotropy, + _initialize_variogram_model, + _make_variogram_parameter_list, + _find_statistics, +) +import warnings class UniversalKriging3D: - """Three-dimensional universal kriging + """Three-dimensional universal kriging. Parameters ---------- @@ -61,30 +59,28 @@ class UniversalKriging3D: minimization scheme. For variogram model parameters provided in a dict, the required dict keys vary according to the specified variogram model: :: - linear - {'slope': slope, 'nugget': nugget} - power - {'scale': scale, 'exponent': exponent, 'nugget': nugget} - gaussian - {'sill': s, 'range': r, 'nugget': n} - OR - {'psill': p, 'range': r, 'nugget':n} - spherical - {'sill': s, 'range': r, 'nugget': n} - OR - {'psill': p, 'range': r, 'nugget':n} - exponential - {'sill': s, 'range': r, 'nugget': n} - OR - {'psill': p, 'range': r, 'nugget':n} - hole-effect - {'sill': s, 'range': r, 'nugget': n} - OR - {'psill': p, 'range': r, 'nugget':n} + + # linear + {'slope': slope, 'nugget': nugget} + # power + {'scale': scale, 'exponent': exponent, 'nugget': nugget} + # gaussian, spherical, exponential and hole-effect: + {'sill': s, 'range': r, 'nugget': n} + # OR + {'psill': p, 'range': r, 'nugget': n} + Note that either the full sill or the partial sill (psill = sill - nugget) can be specified in the dict. For variogram model parameters provided in a list, the entries must be as follows: :: - linear - [slope, nugget] - power - [scale, exponent, nugget] - gaussian - [sill, range, nugget] - spherical - [sill, range, nugget] - exponential - [sill, range, nugget] - hole-effect - [sill, range, nugget] + + # linear + [slope, nugget] + # power + [scale, exponent, nugget] + # gaussian, spherical, exponential and hole-effect: + [sill, range, nugget] + Note that the full sill (NOT the partial sill) must be specified in the list format. For a custom variogram model, the parameters are required, as custom @@ -174,25 +170,43 @@ class UniversalKriging3D: References ---------- .. [1] P.K. Kitanidis, Introduction to Geostatistcs: Applications in - Hydrogeology, (Cambridge University Press, 1997) 272 p. + Hydrogeology, (Cambridge University Press, 1997) 272 p. """ - UNBIAS = True # This can be changed to remove the unbiasedness condition - # Really for testing purposes only... - eps = 1.e-10 # Cutoff for comparison to zero - variogram_dict = {'linear': variogram_models.linear_variogram_model, - 'power': variogram_models.power_variogram_model, - 'gaussian': variogram_models.gaussian_variogram_model, - 'spherical': variogram_models.spherical_variogram_model, - 'exponential': variogram_models.exponential_variogram_model, - 'hole-effect': variogram_models.hole_effect_variogram_model} - - def __init__(self, x, y, z, val, variogram_model='linear', - variogram_parameters=None, variogram_function=None, nlags=6, - weight=False, anisotropy_scaling_y=1., anisotropy_scaling_z=1., - anisotropy_angle_x=0., anisotropy_angle_y=0., - anisotropy_angle_z=0., drift_terms=None, specified_drift=None, - functional_drift=None, verbose=False, enable_plotting=False): + UNBIAS = True # This can be changed to remove the unbiasedness condition + # Really for testing purposes only... + eps = 1.0e-10 # Cutoff for comparison to zero + variogram_dict = { + "linear": variogram_models.linear_variogram_model, + "power": variogram_models.power_variogram_model, + "gaussian": variogram_models.gaussian_variogram_model, + "spherical": variogram_models.spherical_variogram_model, + "exponential": variogram_models.exponential_variogram_model, + "hole-effect": variogram_models.hole_effect_variogram_model, + } + + def __init__( + self, + x, + y, + z, + val, + variogram_model="linear", + variogram_parameters=None, + variogram_function=None, + nlags=6, + weight=False, + anisotropy_scaling_y=1.0, + anisotropy_scaling_z=1.0, + anisotropy_angle_x=0.0, + anisotropy_angle_y=0.0, + anisotropy_angle_z=0.0, + drift_terms=None, + specified_drift=None, + functional_drift=None, + verbose=False, + enable_plotting=False, + ): # Deal with mutable default argument if drift_terms is None: @@ -219,14 +233,18 @@ def __init__(self, x, y, z, val, variogram_model='linear', anisotropy_angle_x = self.model.pykrige_angle_x anisotropy_angle_y = self.model.pykrige_angle_y anisotropy_angle_z = self.model.pykrige_angle_z - if self.variogram_model not in self.variogram_dict.keys() and \ - self.variogram_model != 'custom': - raise ValueError("Specified variogram model '%s' " - "is not supported." % variogram_model) - elif self.variogram_model == 'custom': + if ( + self.variogram_model not in self.variogram_dict.keys() + and self.variogram_model != "custom" + ): + raise ValueError( + "Specified variogram model '%s' is not supported." % variogram_model + ) + elif self.variogram_model == "custom": if variogram_function is None or not callable(variogram_function): - raise ValueError("Must specify callable function for " - "custom variogram model.") + raise ValueError( + "Must specify callable function for custom variogram model." + ) else: self.variogram_function = variogram_function else: @@ -235,23 +253,27 @@ def __init__(self, x, y, z, val, variogram_model='linear', # Code assumes 1D input arrays. Ensures that any extraneous dimensions # don't get in the way. Copies are created to avoid any problems with # referencing the original passed arguments. - self.X_ORIG = \ - np.atleast_1d(np.squeeze(np.array(x, copy=True, dtype=np.float64))) - self.Y_ORIG = \ - np.atleast_1d(np.squeeze(np.array(y, copy=True, dtype=np.float64))) - self.Z_ORIG = \ - np.atleast_1d(np.squeeze(np.array(z, copy=True, dtype=np.float64))) - self.VALUES = \ - np.atleast_1d(np.squeeze(np.array(val, copy=True, dtype=np.float64))) + self.X_ORIG = np.atleast_1d( + np.squeeze(np.array(x, copy=True, dtype=np.float64)) + ) + self.Y_ORIG = np.atleast_1d( + np.squeeze(np.array(y, copy=True, dtype=np.float64)) + ) + self.Z_ORIG = np.atleast_1d( + np.squeeze(np.array(z, copy=True, dtype=np.float64)) + ) + self.VALUES = np.atleast_1d( + np.squeeze(np.array(val, copy=True, dtype=np.float64)) + ) self.verbose = verbose self.enable_plotting = enable_plotting if self.enable_plotting and self.verbose: print("Plotting Enabled\n") - self.XCENTER = (np.amax(self.X_ORIG) + np.amin(self.X_ORIG))/2.0 - self.YCENTER = (np.amax(self.Y_ORIG) + np.amin(self.Y_ORIG))/2.0 - self.ZCENTER = (np.amax(self.Z_ORIG) + np.amin(self.Z_ORIG))/2.0 + self.XCENTER = (np.amax(self.X_ORIG) + np.amin(self.X_ORIG)) / 2.0 + self.YCENTER = (np.amax(self.Y_ORIG) + np.amin(self.Y_ORIG)) / 2.0 + self.ZCENTER = (np.amax(self.Z_ORIG) + np.amin(self.Z_ORIG)) / 2.0 self.anisotropy_scaling_y = anisotropy_scaling_y self.anisotropy_scaling_z = anisotropy_scaling_z self.anisotropy_angle_x = anisotropy_angle_x @@ -259,62 +281,75 @@ def __init__(self, x, y, z, val, variogram_model='linear', self.anisotropy_angle_z = anisotropy_angle_z if self.verbose: print("Adjusting data for anisotropy...") - self.X_ADJUSTED, self.Y_ADJUSTED, self.Z_ADJUSTED = \ - _adjust_for_anisotropy(np.vstack((self.X_ORIG, self.Y_ORIG, self.Z_ORIG)).T, - [self.XCENTER, self.YCENTER, self.ZCENTER], - [self.anisotropy_scaling_y, self.anisotropy_scaling_z], - [self.anisotropy_angle_x, self.anisotropy_angle_y, self.anisotropy_angle_z]).T + self.X_ADJUSTED, self.Y_ADJUSTED, self.Z_ADJUSTED = _adjust_for_anisotropy( + np.vstack((self.X_ORIG, self.Y_ORIG, self.Z_ORIG)).T, + [self.XCENTER, self.YCENTER, self.ZCENTER], + [self.anisotropy_scaling_y, self.anisotropy_scaling_z], + [self.anisotropy_angle_x, self.anisotropy_angle_y, self.anisotropy_angle_z], + ).T if self.verbose: print("Initializing variogram model...") - vp_temp = _make_variogram_parameter_list(self.variogram_model, - variogram_parameters) - self.lags, self.semivariance, self.variogram_model_parameters = \ - _initialize_variogram_model(np.vstack((self.X_ADJUSTED, - self.Y_ADJUSTED, - self.Z_ADJUSTED)).T, - self.VALUES, self.variogram_model, - vp_temp, self.variogram_function, - nlags, weight, 'euclidean') + vp_temp = _make_variogram_parameter_list( + self.variogram_model, variogram_parameters + ) + ( + self.lags, + self.semivariance, + self.variogram_model_parameters, + ) = _initialize_variogram_model( + np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED, self.Z_ADJUSTED)).T, + self.VALUES, + self.variogram_model, + vp_temp, + self.variogram_function, + nlags, + weight, + "euclidean", + ) if self.verbose: - if self.variogram_model == 'linear': - print("Using '%s' Variogram Model" % 'linear') + if self.variogram_model == "linear": + print("Using '%s' Variogram Model" % "linear") print("Slope:", self.variogram_model_parameters[0]) - print("Nugget:", self.variogram_model_parameters[1], '\n') - elif self.variogram_model == 'power': - print("Using '%s' Variogram Model" % 'power') + print("Nugget:", self.variogram_model_parameters[1], "\n") + elif self.variogram_model == "power": + print("Using '%s' Variogram Model" % "power") print("Scale:", self.variogram_model_parameters[0]) print("Exponent:", self.variogram_model_parameters[1]) - print("Nugget:", self.variogram_model_parameters[2], '\n') - elif self.variogram_model == 'custom': + print("Nugget:", self.variogram_model_parameters[2], "\n") + elif self.variogram_model == "custom": print("Using Custom Variogram Model") else: print("Using '%s' Variogram Model" % self.variogram_model) print("Partial Sill:", self.variogram_model_parameters[0]) - print("Full Sill:", self.variogram_model_parameters[0] + - self.variogram_model_parameters[2]) + print( + "Full Sill:", + self.variogram_model_parameters[0] + + self.variogram_model_parameters[2], + ) print("Range:", self.variogram_model_parameters[1]) - print("Nugget:", self.variogram_model_parameters[2], '\n') + print("Nugget:", self.variogram_model_parameters[2], "\n") if self.enable_plotting: self.display_variogram_model() if self.verbose: print("Calculating statistics on variogram model fit...") - self.delta, self.sigma, self.epsilon = \ - _find_statistics(np.vstack((self.X_ADJUSTED, - self.Y_ADJUSTED, - self.Z_ADJUSTED)).T, - self.VALUES, self.variogram_function, - self.variogram_model_parameters, 'euclidean') + self.delta, self.sigma, self.epsilon = _find_statistics( + np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED, self.Z_ADJUSTED)).T, + self.VALUES, + self.variogram_function, + self.variogram_model_parameters, + "euclidean", + ) self.Q1 = core.calcQ1(self.epsilon) self.Q2 = core.calcQ2(self.epsilon) self.cR = core.calc_cR(self.Q2, self.sigma) if self.verbose: print("Q1 =", self.Q1) print("Q2 =", self.Q2) - print("cR =", self.cR, '\n') + print("cR =", self.cR, "\n") if self.verbose: print("Initializing drift terms...") @@ -322,51 +357,69 @@ def __init__(self, x, y, z, val, variogram_model='linear', # Note that the regional linear drift values will be based on the # adjusted coordinate system. Really, it doesn't actually matter # which coordinate system is used here. - if 'regional_linear' in drift_terms: + if "regional_linear" in drift_terms: self.regional_linear_drift = True if self.verbose: print("Implementing regional linear drift.") else: self.regional_linear_drift = False - if 'specified' in drift_terms: + if "specified" in drift_terms: if type(specified_drift) is not list: - raise TypeError("Arrays for specified drift terms must be " - "encapsulated in a list.") + raise TypeError( + "Arrays for specified drift terms must be " + "encapsulated in a list." + ) if len(specified_drift) == 0: - raise ValueError("Must provide at least one drift-value array " - "when using the 'specified' drift capability.") + raise ValueError( + "Must provide at least one drift-value array " + "when using the 'specified' drift capability." + ) self.specified_drift = True self.specified_drift_data_arrays = [] for term in specified_drift: specified = np.squeeze(np.array(term, copy=True)) if specified.size != self.X_ORIG.size: - raise ValueError("Must specify the drift values for each " - "data point when using the " - "'specified' drift capability.") + raise ValueError( + "Must specify the drift values for each " + "data point when using the " + "'specified' drift capability." + ) self.specified_drift_data_arrays.append(specified) else: self.specified_drift = False # The provided callable functions will be evaluated using # the adjusted coordinates. - if 'functional' in drift_terms: + if "functional" in drift_terms: if type(functional_drift) is not list: - raise TypeError("Callables for functional drift terms must " - "be encapsulated in a list.") + raise TypeError( + "Callables for functional drift terms must " + "be encapsulated in a list." + ) if len(functional_drift) == 0: - raise ValueError("Must provide at least one callable object " - "when using the 'functional' drift capability.") + raise ValueError( + "Must provide at least one callable object " + "when using the 'functional' drift capability." + ) self.functional_drift = True self.functional_drift_terms = functional_drift else: self.functional_drift = False - def update_variogram_model(self, variogram_model, variogram_parameters=None, - variogram_function=None, nlags=6, weight=False, - anisotropy_scaling_y=1., anisotropy_scaling_z=1., - anisotropy_angle_x=0., anisotropy_angle_y=0., - anisotropy_angle_z=0.): + def update_variogram_model( + self, + variogram_model, + variogram_parameters=None, + variogram_function=None, + nlags=6, + weight=False, + anisotropy_scaling_y=1.0, + anisotropy_scaling_z=1.0, + anisotropy_angle_x=0.0, + anisotropy_angle_y=0.0, + anisotropy_angle_z=0.0, + ): """Changes the variogram model and variogram parameters for the kriging system. @@ -432,24 +485,30 @@ def update_variogram_model(self, variogram_model, variogram_parameters=None, anisotropy_angle_x = self.model.pykrige_angle_x anisotropy_angle_y = self.model.pykrige_angle_y anisotropy_angle_z = self.model.pykrige_angle_z - if self.variogram_model not in self.variogram_dict.keys() and \ - self.variogram_model != 'custom': - raise ValueError("Specified variogram model '%s' " - "is not supported." % variogram_model) - elif self.variogram_model == 'custom': + if ( + self.variogram_model not in self.variogram_dict.keys() + and self.variogram_model != "custom" + ): + raise ValueError( + "Specified variogram model '%s' is not supported." % variogram_model + ) + elif self.variogram_model == "custom": if variogram_function is None or not callable(variogram_function): - raise ValueError("Must specify callable function for " - "custom variogram model.") + raise ValueError( + "Must specify callable function for custom variogram model." + ) else: self.variogram_function = variogram_function else: self.variogram_function = self.variogram_dict[self.variogram_model] - if anisotropy_scaling_y != self.anisotropy_scaling_y or \ - anisotropy_scaling_z != self.anisotropy_scaling_z or \ - anisotropy_angle_x != self.anisotropy_angle_x or \ - anisotropy_angle_y != self.anisotropy_angle_y or \ - anisotropy_angle_z != self.anisotropy_angle_z: + if ( + anisotropy_scaling_y != self.anisotropy_scaling_y + or anisotropy_scaling_z != self.anisotropy_scaling_z + or anisotropy_angle_x != self.anisotropy_angle_x + or anisotropy_angle_y != self.anisotropy_angle_y + or anisotropy_angle_z != self.anisotropy_angle_z + ): if self.verbose: print("Adjusting data for anisotropy...") self.anisotropy_scaling_y = anisotropy_scaling_y @@ -457,70 +516,92 @@ def update_variogram_model(self, variogram_model, variogram_parameters=None, self.anisotropy_angle_x = anisotropy_angle_x self.anisotropy_angle_y = anisotropy_angle_y self.anisotropy_angle_z = anisotropy_angle_z - self.X_ADJUSTED, self.Y_ADJUSTED, self.Z_ADJUSTED = \ - _adjust_for_anisotropy(np.vstack((self.X_ORIG, self.Y_ORIG, self.Z_ORIG)).T, - [self.XCENTER, self.YCENTER, self.ZCENTER], - [self.anisotropy_scaling_y, self.anisotropy_scaling_z], - [self.anisotropy_angle_x, self.anisotropy_angle_y, self.anisotropy_angle_z]).T + self.X_ADJUSTED, self.Y_ADJUSTED, self.Z_ADJUSTED = _adjust_for_anisotropy( + np.vstack((self.X_ORIG, self.Y_ORIG, self.Z_ORIG)).T, + [self.XCENTER, self.YCENTER, self.ZCENTER], + [self.anisotropy_scaling_y, self.anisotropy_scaling_z], + [ + self.anisotropy_angle_x, + self.anisotropy_angle_y, + self.anisotropy_angle_z, + ], + ).T if self.verbose: print("Updating variogram mode...") - vp_temp = _make_variogram_parameter_list(self.variogram_model, - variogram_parameters) - self.lags, self.semivariance, self.variogram_model_parameters = \ - _initialize_variogram_model(np.vstack((self.X_ADJUSTED, - self.Y_ADJUSTED, - self.Z_ADJUSTED)).T, - self.VALUES, self.variogram_model, - vp_temp, self.variogram_function, - nlags, weight, 'euclidean') + vp_temp = _make_variogram_parameter_list( + self.variogram_model, variogram_parameters + ) + ( + self.lags, + self.semivariance, + self.variogram_model_parameters, + ) = _initialize_variogram_model( + np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED, self.Z_ADJUSTED)).T, + self.VALUES, + self.variogram_model, + vp_temp, + self.variogram_function, + nlags, + weight, + "euclidean", + ) if self.verbose: - if self.variogram_model == 'linear': - print("Using '%s' Variogram Model" % 'linear') + if self.variogram_model == "linear": + print("Using '%s' Variogram Model" % "linear") print("Slope:", self.variogram_model_parameters[0]) - print("Nugget:", self.variogram_model_parameters[1], '\n') - elif self.variogram_model == 'power': - print("Using '%s' Variogram Model" % 'power') + print("Nugget:", self.variogram_model_parameters[1], "\n") + elif self.variogram_model == "power": + print("Using '%s' Variogram Model" % "power") print("Scale:", self.variogram_model_parameters[0]) print("Exponent:", self.variogram_model_parameters[1]) - print("Nugget:", self.variogram_model_parameters[2], '\n') - elif self.variogram_model == 'custom': + print("Nugget:", self.variogram_model_parameters[2], "\n") + elif self.variogram_model == "custom": print("Using Custom Variogram Model") else: print("Using '%s' Variogram Model" % self.variogram_model) print("Partial Sill:", self.variogram_model_parameters[0]) - print("Full Sill:", self.variogram_model_parameters[0] + - self.variogram_model_parameters[2]) + print( + "Full Sill:", + self.variogram_model_parameters[0] + + self.variogram_model_parameters[2], + ) print("Range:", self.variogram_model_parameters[1]) - print("Nugget:", self.variogram_model_parameters[2], '\n') + print("Nugget:", self.variogram_model_parameters[2], "\n") if self.enable_plotting: self.display_variogram_model() if self.verbose: print("Calculating statistics on variogram model fit...") - self.delta, self.sigma, self.epsilon = \ - _find_statistics(np.vstack((self.X_ADJUSTED, - self.Y_ADJUSTED, - self.Z_ADJUSTED)).T, - self.VALUES, self.variogram_function, - self.variogram_model_parameters, 'euclidean') + self.delta, self.sigma, self.epsilon = _find_statistics( + np.vstack((self.X_ADJUSTED, self.Y_ADJUSTED, self.Z_ADJUSTED)).T, + self.VALUES, + self.variogram_function, + self.variogram_model_parameters, + "euclidean", + ) self.Q1 = core.calcQ1(self.epsilon) self.Q2 = core.calcQ2(self.epsilon) self.cR = core.calc_cR(self.Q2, self.sigma) if self.verbose: print("Q1 =", self.Q1) print("Q2 =", self.Q2) - print("cR =", self.cR, '\n') + print("cR =", self.cR, "\n") def display_variogram_model(self): """Displays semivariogram and variogram model.""" + import matplotlib.pyplot as plt + fig = plt.figure() ax = fig.add_subplot(111) - ax.plot(self.lags, self.semivariance, 'r*') - ax.plot(self.lags, - self.variogram_function(self.variogram_model_parameters, self.lags), 'k-') + ax.plot(self.lags, self.semivariance, "r*") + ax.plot( + self.lags, + self.variogram_function(self.variogram_model_parameters, self.lags), + "k-", + ) plt.show() def switch_verbose(self): @@ -537,9 +618,11 @@ def get_epsilon_residuals(self): def plot_epsilon_residuals(self): """Plots the epsilon residuals for the variogram fit. No arguments.""" + import matplotlib.pyplot as plt + fig = plt.figure() ax = fig.add_subplot(111) - ax.scatter(range(self.epsilon.size), self.epsilon, c='k', marker='*') + ax.scatter(range(self.epsilon.size), self.epsilon, c="k", marker="*") ax.axhline(y=0.0) plt.show() @@ -561,15 +644,21 @@ def print_statistics(self): def _get_kriging_matrix(self, n, n_withdrifts): """Assembles the kriging matrix.""" - xyz = np.concatenate((self.X_ADJUSTED[:, np.newaxis], self.Y_ADJUSTED[:, np.newaxis], - self.Z_ADJUSTED[:, np.newaxis]), axis=1) - d = cdist(xyz, xyz, 'euclidean') + xyz = np.concatenate( + ( + self.X_ADJUSTED[:, np.newaxis], + self.Y_ADJUSTED[:, np.newaxis], + self.Z_ADJUSTED[:, np.newaxis], + ), + axis=1, + ) + d = cdist(xyz, xyz, "euclidean") if self.UNBIAS: - a = np.zeros((n_withdrifts+1, n_withdrifts+1)) + a = np.zeros((n_withdrifts + 1, n_withdrifts + 1)) else: a = np.zeros((n_withdrifts, n_withdrifts)) - a[:n, :n] = - self.variogram_function(self.variogram_model_parameters, d) - np.fill_diagonal(a, 0.) + a[:n, :n] = -self.variogram_function(self.variogram_model_parameters, d) + np.fill_diagonal(a, 0.0) i = n if self.regional_linear_drift: @@ -593,11 +682,13 @@ def _get_kriging_matrix(self, n, n_withdrifts): a[i, :n] = func(self.X_ADJUSTED, self.Y_ADJUSTED, self.Z_ADJUSTED) i += 1 if i != n_withdrifts: - warnings.warn("Error in creating kriging matrix. Kriging may fail.", RuntimeWarning) + warnings.warn( + "Error in creating kriging matrix. Kriging may fail.", RuntimeWarning + ) if self.UNBIAS: a[n_withdrifts, :n] = 1.0 a[:n, n_withdrifts] = 1.0 - a[n:n_withdrifts + 1, n:n_withdrifts + 1] = 0.0 + a[n : n_withdrifts + 1, n : n_withdrifts + 1] = 0.0 return a @@ -617,10 +708,10 @@ def _exec_vector(self, a, bd, xyz, mask, n_withdrifts, spec_drift_grids): zero_index = np.where(np.absolute(bd) <= self.eps) if self.UNBIAS: - b = np.zeros((npt, n_withdrifts+1, 1)) + b = np.zeros((npt, n_withdrifts + 1, 1)) else: b = np.zeros((npt, n_withdrifts, 1)) - b[:, :n, 0] = - self.variogram_function(self.variogram_model_parameters, bd) + b[:, :n, 0] = -self.variogram_function(self.variogram_model_parameters, bd) if zero_value: b[zero_index[0], zero_index[1], 0] = 0.0 @@ -641,20 +732,30 @@ def _exec_vector(self, a, bd, xyz, mask, n_withdrifts, spec_drift_grids): b[:, i, 0] = func(xyz[:, 2], xyz[:, 1], xyz[:, 0]) i += 1 if i != n_withdrifts: - warnings.warn("Error in setting up kriging system. " - "Kriging may fail.", RuntimeWarning) + warnings.warn( + "Error in setting up kriging system. Kriging may fail.", RuntimeWarning, + ) if self.UNBIAS: b[:, n_withdrifts, 0] = 1.0 if (~mask).any(): - mask_b = np.repeat(mask[:, np.newaxis, np.newaxis], - n_withdrifts+1, axis=1) + mask_b = np.repeat( + mask[:, np.newaxis, np.newaxis], n_withdrifts + 1, axis=1 + ) b = np.ma.array(b, mask=mask_b) if self.UNBIAS: - x = np.dot(a_inv, b.reshape((npt, n_withdrifts+1)).T).reshape((1, n_withdrifts+1, npt)).T + x = ( + np.dot(a_inv, b.reshape((npt, n_withdrifts + 1)).T) + .reshape((1, n_withdrifts + 1, npt)) + .T + ) else: - x = np.dot(a_inv, b.reshape((npt, n_withdrifts)).T).reshape((1, n_withdrifts, npt)).T + x = ( + np.dot(a_inv, b.reshape((npt, n_withdrifts)).T) + .reshape((1, n_withdrifts, npt)) + .T + ) kvalues = np.sum(x[:, :n, 0] * self.VALUES, axis=1) sigmasq = np.sum(x[:, :, 0] * -b[:, :, 0], axis=1) @@ -671,8 +772,10 @@ def _exec_loop(self, a, bd_all, xyz, mask, n_withdrifts, spec_drift_grids): a_inv = scipy.linalg.inv(a) - for j in np.nonzero(~mask)[0]: # Note that this is the same thing as range(npt) if mask is not defined, - bd = bd_all[j] # otherwise it takes the non-masked elements. + for j in np.nonzero(~mask)[ + 0 + ]: # Note that this is the same thing as range(npt) if mask is not defined, + bd = bd_all[j] # otherwise it takes the non-masked elements. if np.any(np.absolute(bd) <= self.eps): zero_value = True zero_index = np.where(np.absolute(bd) <= self.eps) @@ -681,10 +784,10 @@ def _exec_loop(self, a, bd_all, xyz, mask, n_withdrifts, spec_drift_grids): zero_index = None if self.UNBIAS: - b = np.zeros((n_withdrifts+1, 1)) + b = np.zeros((n_withdrifts + 1, 1)) else: b = np.zeros((n_withdrifts, 1)) - b[:n, 0] = - self.variogram_function(self.variogram_model_parameters, bd) + b[:n, 0] = -self.variogram_function(self.variogram_model_parameters, bd) if zero_value: b[zero_index[0], 0] = 0.0 @@ -705,8 +808,10 @@ def _exec_loop(self, a, bd_all, xyz, mask, n_withdrifts, spec_drift_grids): b[i, 0] = func(xyz[j, 2], xyz[j, 1], xyz[j, 0]) i += 1 if i != n_withdrifts: - warnings.warn("Error in setting up kriging system. " - "Kriging may fail.", RuntimeWarning) + warnings.warn( + "Error in setting up kriging system. Kriging may fail.", + RuntimeWarning, + ) if self.UNBIAS: b[n_withdrifts, 0] = 1.0 @@ -716,8 +821,16 @@ def _exec_loop(self, a, bd_all, xyz, mask, n_withdrifts, spec_drift_grids): return kvalues, sigmasq - def execute(self, style, xpoints, ypoints, zpoints, mask=None, - backend='vectorized', specified_drift_arrays=None): + def execute( + self, + style, + xpoints, + ypoints, + zpoints, + mask=None, + backend="vectorized", + specified_drift_arrays=None, + ): """Calculates a kriged grid and the associated variance. This is now the method that performs the main kriging calculation. @@ -809,9 +922,8 @@ def execute(self, style, xpoints, ypoints, zpoints, mask=None, if self.verbose: print("Executing Ordinary Kriging...\n") - if style != 'grid' and style != 'masked' and style != 'points': - raise ValueError("style argument must be 'grid', 'points', " - "or 'masked'") + if style != "grid" and style != "masked" and style != "points": + raise ValueError("style argument must be 'grid', 'points', or 'masked'") xpts = np.atleast_1d(np.squeeze(np.array(xpoints, copy=True))) ypts = np.atleast_1d(np.squeeze(np.array(ypoints, copy=True))) @@ -829,111 +941,152 @@ def execute(self, style, xpoints, ypoints, zpoints, mask=None, nz = zpts.size a = self._get_kriging_matrix(n, n_withdrifts) - if style in ['grid', 'masked']: - if style == 'masked': + if style in ["grid", "masked"]: + if style == "masked": if mask is None: - raise IOError("Must specify boolean masking array " - "when style is 'masked'.") + raise IOError( + "Must specify boolean masking array when style is 'masked'." + ) if mask.ndim != 3: raise ValueError("Mask is not three-dimensional.") if mask.shape[0] != nz or mask.shape[1] != ny or mask.shape[2] != nx: - if mask.shape[0] == nx and mask.shape[2] == nz and mask.shape[1] == ny: + if ( + mask.shape[0] == nx + and mask.shape[2] == nz + and mask.shape[1] == ny + ): mask = mask.swapaxes(0, 2) else: - raise ValueError("Mask dimensions do not match " - "specified grid dimensions.") + raise ValueError( + "Mask dimensions do not match specified grid dimensions." + ) mask = mask.flatten() npt = nz * ny * nx - grid_z, grid_y, grid_x = np.meshgrid(zpts, ypts, xpts, indexing='ij') + grid_z, grid_y, grid_x = np.meshgrid(zpts, ypts, xpts, indexing="ij") xpts = grid_x.flatten() ypts = grid_y.flatten() zpts = grid_z.flatten() - elif style == 'points': + elif style == "points": if xpts.size != ypts.size and ypts.size != zpts.size: - raise ValueError("xpoints and ypoints must have same " - "dimensions when treated as listing " - "discrete points.") + raise ValueError( + "xpoints and ypoints must have same " + "dimensions when treated as listing " + "discrete points." + ) npt = nx else: - raise ValueError("style argument must be 'grid', 'points', " - "or 'masked'") + raise ValueError("style argument must be 'grid', 'points', or 'masked'") if specified_drift_arrays is None: specified_drift_arrays = [] spec_drift_grids = [] if self.specified_drift: if len(specified_drift_arrays) == 0: - raise ValueError("Must provide drift values for kriging " - "points when using 'specified' drift " - "capability.") + raise ValueError( + "Must provide drift values for kriging " + "points when using 'specified' drift " + "capability." + ) if type(specified_drift_arrays) is not list: - raise TypeError("Arrays for specified drift terms must " - "be encapsulated in a list.") + raise TypeError( + "Arrays for specified drift terms must " + "be encapsulated in a list." + ) for spec in specified_drift_arrays: - if style in ['grid', 'masked']: + if style in ["grid", "masked"]: if spec.ndim < 3: - raise ValueError("Dimensions of drift values array do " - "not match specified grid dimensions.") - elif spec.shape[0] != nz or spec.shape[1] != ny or spec.shape[2] != nx: - if spec.shape[0] == nx and spec.shape[2] == nz and spec.shape[1] == ny: + raise ValueError( + "Dimensions of drift values array do " + "not match specified grid dimensions." + ) + elif ( + spec.shape[0] != nz + or spec.shape[1] != ny + or spec.shape[2] != nx + ): + if ( + spec.shape[0] == nx + and spec.shape[2] == nz + and spec.shape[1] == ny + ): spec_drift_grids.append(np.squeeze(spec.swapaxes(0, 2))) else: - raise ValueError("Dimensions of drift values array " - "do not match specified grid " - "dimensions.") + raise ValueError( + "Dimensions of drift values array " + "do not match specified grid " + "dimensions." + ) else: spec_drift_grids.append(np.squeeze(spec)) - elif style == 'points': + elif style == "points": if spec.ndim != 1: - raise ValueError("Dimensions of drift values array do " - "not match specified grid dimensions.") + raise ValueError( + "Dimensions of drift values array do " + "not match specified grid dimensions." + ) elif spec.shape[0] != xpts.size: - raise ValueError("Number of supplied drift values in " - "array do not match specified number " - "of kriging points.") + raise ValueError( + "Number of supplied drift values in " + "array do not match specified number " + "of kriging points." + ) else: spec_drift_grids.append(np.squeeze(spec)) if len(spec_drift_grids) != len(self.specified_drift_data_arrays): - raise ValueError("Inconsistent number of specified " - "drift terms supplied.") + raise ValueError( + "Inconsistent number of specified drift terms supplied." + ) else: if len(specified_drift_arrays) != 0: - warnings.warn("Provided specified drift values, but " - "'specified' drift was not initialized during " - "instantiation of UniversalKriging3D class.", - RuntimeWarning) - - xpts, ypts, zpts = _adjust_for_anisotropy(np.vstack((xpts, ypts, zpts)).T, - [self.XCENTER, self.YCENTER, self.ZCENTER], - [self.anisotropy_scaling_y, self.anisotropy_scaling_z], - [self.anisotropy_angle_x, self.anisotropy_angle_y, - self.anisotropy_angle_z]).T - - if style != 'masked': - mask = np.zeros(npt, dtype='bool') - - xyz_points = np.concatenate((zpts[:, np.newaxis], ypts[:, np.newaxis], - xpts[:, np.newaxis]), axis=1) - xyz_data = np.concatenate((self.Z_ADJUSTED[:, np.newaxis], - self.Y_ADJUSTED[:, np.newaxis], - self.X_ADJUSTED[:, np.newaxis]), axis=1) - bd = cdist(xyz_points, xyz_data, 'euclidean') - - if backend == 'vectorized': - kvalues, sigmasq = self._exec_vector(a, bd, xyz_points, mask, - n_withdrifts, spec_drift_grids) - elif backend == 'loop': - kvalues, sigmasq = self._exec_loop(a, bd, xyz_points, mask, - n_withdrifts, spec_drift_grids) + warnings.warn( + "Provided specified drift values, but " + "'specified' drift was not initialized during " + "instantiation of UniversalKriging3D class.", + RuntimeWarning, + ) + + xpts, ypts, zpts = _adjust_for_anisotropy( + np.vstack((xpts, ypts, zpts)).T, + [self.XCENTER, self.YCENTER, self.ZCENTER], + [self.anisotropy_scaling_y, self.anisotropy_scaling_z], + [self.anisotropy_angle_x, self.anisotropy_angle_y, self.anisotropy_angle_z], + ).T + + if style != "masked": + mask = np.zeros(npt, dtype="bool") + + xyz_points = np.concatenate( + (zpts[:, np.newaxis], ypts[:, np.newaxis], xpts[:, np.newaxis]), axis=1 + ) + xyz_data = np.concatenate( + ( + self.Z_ADJUSTED[:, np.newaxis], + self.Y_ADJUSTED[:, np.newaxis], + self.X_ADJUSTED[:, np.newaxis], + ), + axis=1, + ) + bd = cdist(xyz_points, xyz_data, "euclidean") + + if backend == "vectorized": + kvalues, sigmasq = self._exec_vector( + a, bd, xyz_points, mask, n_withdrifts, spec_drift_grids + ) + elif backend == "loop": + kvalues, sigmasq = self._exec_loop( + a, bd, xyz_points, mask, n_withdrifts, spec_drift_grids + ) else: - raise ValueError('Specified backend {} is not supported for ' - '3D ordinary kriging.'.format(backend)) + raise ValueError( + "Specified backend {} is not supported for " + "3D ordinary kriging.".format(backend) + ) - if style == 'masked': + if style == "masked": kvalues = np.ma.array(kvalues, mask=mask) sigmasq = np.ma.array(sigmasq, mask=mask) - if style in ['masked', 'grid']: + if style in ["masked", "grid"]: kvalues = kvalues.reshape((nz, ny, nx)) sigmasq = sigmasq.reshape((nz, ny, nx)) diff --git a/pykrige/variogram_models.py b/pykrige/variogram_models.py index b6bcf39..5aa46ba 100755 --- a/pykrige/variogram_models.py +++ b/pykrige/variogram_models.py @@ -1,11 +1,5 @@ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -import numpy as np - -__doc__ = """ +# coding: utf-8 +""" PyKrige ======= @@ -23,8 +17,9 @@ .. [1] P.K. Kitanidis, Introduction to Geostatistcs: Applications in Hydrogeology, (Cambridge University Press, 1997) 272 p. -Copyright (c) 2015-2018, PyKrige Developers +Copyright (c) 2015-2020, PyKrige Developers """ +import numpy as np def linear_variogram_model(m, d): @@ -39,7 +34,7 @@ def power_variogram_model(m, d): scale = float(m[0]) exponent = float(m[1]) nugget = float(m[2]) - return scale * d**exponent + nugget + return scale * d ** exponent + nugget def gaussian_variogram_model(m, d): @@ -47,7 +42,7 @@ def gaussian_variogram_model(m, d): psill = float(m[0]) range_ = float(m[1]) nugget = float(m[2]) - return psill * (1. - np.exp(-d**2./(range_*4./7.)**2.)) + nugget + return psill * (1.0 - np.exp(-(d ** 2.0) / (range_ * 4.0 / 7.0) ** 2.0)) + nugget def exponential_variogram_model(m, d): @@ -55,7 +50,7 @@ def exponential_variogram_model(m, d): psill = float(m[0]) range_ = float(m[1]) nugget = float(m[2]) - return psill * (1. - np.exp(-d/(range_/3.))) + nugget + return psill * (1.0 - np.exp(-d / (range_ / 3.0))) + nugget def spherical_variogram_model(m, d): @@ -63,8 +58,16 @@ def spherical_variogram_model(m, d): psill = float(m[0]) range_ = float(m[1]) nugget = float(m[2]) - return np.piecewise(d, [d <= range_, d > range_], - [lambda x: psill * ((3.*x)/(2.*range_) - (x**3.)/(2.*range_**3.)) + nugget, psill + nugget]) + return np.piecewise( + d, + [d <= range_, d > range_], + [ + lambda x: psill + * ((3.0 * x) / (2.0 * range_) - (x ** 3.0) / (2.0 * range_ ** 3.0)) + + nugget, + psill + nugget, + ], + ) def hole_effect_variogram_model(m, d): @@ -72,4 +75,7 @@ def hole_effect_variogram_model(m, d): psill = float(m[0]) range_ = float(m[1]) nugget = float(m[2]) - return psill * (1. - (1.-d/(range_/3.)) * np.exp(-d/(range_/3.))) + nugget + return ( + psill * (1.0 - (1.0 - d / (range_ / 3.0)) * np.exp(-d / (range_ / 3.0))) + + nugget + ) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..96dd67e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +numpy>=1.14.5 +scipy>=1.1.0 diff --git a/requirements_setup.txt b/requirements_setup.txt new file mode 100755 index 0000000..7f373ea --- /dev/null +++ b/requirements_setup.txt @@ -0,0 +1,4 @@ +setuptools>=41.0.1 +setuptools_scm>=3.5.0 +cython>=0.28.3 +numpy>=1.14.5 diff --git a/requirements_test.txt b/requirements_test.txt new file mode 100755 index 0000000..4d4fa38 --- /dev/null +++ b/requirements_test.txt @@ -0,0 +1,5 @@ +pytest-cov>=2.8.0 +pytest>=5.3.0 +coveralls>=1.9.0 +scikit-learn>=0.19 +gstools>=1.1.1 diff --git a/setup.cfg b/setup.cfg index 224a779..f48fdad 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,3 @@ [metadata] -description-file = README.md \ No newline at end of file +description-file = README.md +license_file = LICENSE diff --git a/setup.py b/setup.py index e33a2f4..0962fdf 100755 --- a/setup.py +++ b/setup.py @@ -1,151 +1,111 @@ -from __future__ import absolute_import -from __future__ import print_function - -""" -Updated BSM 10/23/2015 -Cython extensions work-around adapted from simplejson setup script: -https://github.com/simplejson/simplejson/blob/0bcdf20cc525c1343b796cb8f247ea5213c6557e/setup.py#L110 -""" - -import sys -from os.path import join -from setuptools import setup, Extension -from distutils.errors import CCompilerError, DistutilsExecError, DistutilsPlatformError -ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError) - -NAME = 'PyKrige' -VERSION = '1.4.1' -AUTHOR = 'Benjamin S. Murphy' -EMAIL = 'bscott.murphy@gmail.com' -URL = 'https://github.com/bsmurphy/PyKrige' -DESC = 'Kriging Toolkit for Python' - -with open('README.rst', 'r') as fh: - LDESC = fh.read() - -PACKAGES = ['pykrige'] -PCKG_DAT = {'pykrige': ['README.rst', 'CHANGELOG.md', 'LICENSE.txt', - 'MANIFEST.in', join('test_data', '*.txt'), - join('test_data', '*.asc')]} -REQ = ['numpy', 'scipy', 'matplotlib'] - -for req in REQ: - try: - __import__(req) - except ImportError: - print("**************************************************") - print("Error: PyKrige relies on the installation of the SciPy stack " - "(Numpy, SciPy, matplotlib) to work. " - "For instructions for installation, please view " - "https://www.scipy.org/install.html." - "\n {} missing".format(req) - ) - print("**************************************************") - raise - sys.exit(1) -# python setup.py install goes through REQ in reverse order than pip - - -CLSF = ['Development Status :: 5 - Production/Stable', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: BSD License', - 'Programming Language :: Python', - 'Topic :: Scientific/Engineering', - 'Topic :: Scientific/Engineering :: GIS'] - -# Removed python 3 switch from here -try: - from Cython.Distutils import build_ext - import Cython.Compiler.Options - Cython.Compiler.Options.annotate = False - try_cython = True -except ImportError: - print("**************************************************") - print("WARNING: Cython is not currently installed. " - "Falling back to pure Python implementation.") - print("**************************************************") - try_cython = False - - -class BuildFailed(Exception): - pass - - -# This is how I was originally trying to get around the -# Cython extension troubles... Keeping it here for reference... -# -# class BuildExtCompilerCheck(build_ext): -# def build_extensions(self): -# if sys.platform == 'win32' and ('MSC' in sys.version or 'MSVC' in sys.version): -# print("-> COMPILER IS", self.compiler.compiler_type) -# from distutils.msvccompiler import MSVCCompiler -# if isinstance(self.compiler, MSVCCompiler): -# build_ext.build_extensions(self) -# else: -# print("WARNING: The C extensions will not be built since the necessary compiler could not be found.\n" -# "See https://github.com/bsmurphy/PyKrige/issues/8") -# else: -# build_ext.build_extensions(self) - - -def run_setup(with_cython): - if with_cython: - import numpy as np - if sys.platform != 'win32': - compile_args = dict(extra_compile_args=['-O2', '-march=core2', - '-mtune=corei7'], - extra_link_args=['-O2', '-march=core2', - '-mtune=corei7']) - else: - compile_args = {} - - ext_modules = [Extension("pykrige.lib.cok", - ["pykrige/lib/cok.pyx"], - **compile_args), - Extension("pykrige.lib.variogram_models", - ["pykrige/lib/variogram_models.pyx"], - **compile_args)] - - # Transfered python 3 switch here. - # On python 3 machines, will use lapack_py3.pyx - # instead of lapack.pyx to build .lib.lapack - if sys.version_info[0] == 3: - ext_modules += [Extension("pykrige.lib.lapack", - ["pykrige/lib/lapack_py3.pyx"], - **compile_args)] - else: - ext_modules += [Extension("pykrige.lib.lapack", - ["pykrige/lib/lapack.pyx"], - **compile_args)] - - class TryBuildExt(build_ext): - def build_extensions(self): - try: - build_ext.build_extensions(self) - except ext_errors: - print("**************************************************") - print("WARNING: Cython extensions failed to build. " - "Falling back to pure Python implementation.\n" - "See https://github.com/bsmurphy/PyKrige/issues/8 " - "for more information.") - print("**************************************************") - raise BuildFailed() - - cmd = {'build_ext': TryBuildExt} - - setup(name=NAME, version=VERSION, author=AUTHOR, author_email=EMAIL, - url=URL, description=DESC, long_description=LDESC, - packages=PACKAGES, package_data=PCKG_DAT, classifiers=CLSF, - ext_modules=ext_modules, include_dirs=[np.get_include()], - cmdclass=cmd) - - else: - setup(name=NAME, version=VERSION, author=AUTHOR, author_email=EMAIL, - url=URL, description=DESC, long_description=LDESC, - packages=PACKAGES, package_data=PCKG_DAT, classifiers=CLSF) - - -try: - run_setup(try_cython) -except BuildFailed: - run_setup(False) +# -*- coding: utf-8 -*- +"""Kriging Toolkit for Python.""" +import os +from setuptools import setup, find_packages, Extension +from Cython.Build import cythonize +import numpy as np + +HERE = os.path.abspath(os.path.dirname(__file__)) + +# cython extensions ########################################################### + +CY_MODULES = [] +CY_MODULES.append( + Extension( + "pykrige.lib.cok", + [os.path.join("pykrige", "lib", "cok.pyx")], + include_dirs=[np.get_include()], + ) +) +CY_MODULES.append( + Extension( + "pykrige.lib.variogram_models", + [os.path.join("pykrige", "lib", "variogram_models.pyx")], + include_dirs=[np.get_include()], + ) +) +EXT_MODULES = cythonize(CY_MODULES) # annotate=True + +# This is an important part. By setting this compiler directive, cython will +# embed signature information in docstrings. Sphinx then knows how to extract +# and use those signatures. +# python setup.py build_ext --inplace --> then sphinx build +for ext_m in EXT_MODULES: + ext_m.cython_directives = {"embedsignature": True} + +# setup ####################################################################### + +with open(os.path.join(HERE, "README.rst"), encoding="utf-8") as f: + README = f.read() +with open(os.path.join(HERE, "requirements.txt"), encoding="utf-8") as f: + REQ = f.read().splitlines() +with open(os.path.join(HERE, "requirements_setup.txt"), encoding="utf-8") as f: + REQ_SETUP = f.read().splitlines() +with open(os.path.join(HERE, "requirements_test.txt"), encoding="utf-8") as f: + REQ_TEST = f.read().splitlines() +with open(os.path.join(HERE, "docs", "requirements_doc.txt"), encoding="utf-8") as f: + REQ_DOC = f.read().splitlines() + +REQ_DEV = REQ_SETUP + REQ_TEST + REQ_DOC + +DOCLINE = __doc__.split("\n")[0] +CLASSIFIERS = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: End Users/Desktop", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD License", + "Natural Language :: English", + "Operating System :: MacOS", + "Operating System :: MacOS :: MacOS X", + "Operating System :: Microsoft", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX", + "Operating System :: Unix", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Scientific/Engineering", + "Topic :: Scientific/Engineering :: GIS", + "Topic :: Utilities", +] + +setup( + name="PyKrige", + description=DOCLINE, + long_description=README, + long_description_content_type="text/x-rst", + author="Benjamin S. Murphy", + author_email="bscott.murphy@gmail.com", + maintainer="Sebastian Mueller, Roman Yurchak", + maintainer_email="info@geostat-framework.org", + url="https://github.com/GeoStat-Framework/PyKrige", + license="BSD (3 clause)", + classifiers=CLASSIFIERS, + platforms=["Windows", "Linux", "Solaris", "Mac OS-X", "Unix"], + include_package_data=True, + python_requires=">=3.5", + use_scm_version={ + "relative_to": __file__, + "write_to": "pykrige/_version.py", + "write_to_template": "__version__ = '{version}'", + "local_scheme": "no-local-version", + "fallback_version": "0.0.0.dev0", + }, + setup_requires=REQ_SETUP, + install_requires=REQ, + extras_require={ + "plot": ["matplotlib"], + "sklearn": ["scikit-learn>=0.19"], + "doc": REQ_DOC, + "test": REQ_TEST, + "dev": REQ_DEV, + }, + packages=find_packages(exclude=["tests*", "docs*"]), + ext_modules=EXT_MODULES, + include_dirs=[np.get_include()], +) diff --git a/tests/test_api.py b/tests/test_api.py new file mode 100644 index 0000000..7905cf8 --- /dev/null +++ b/tests/test_api.py @@ -0,0 +1,44 @@ +from itertools import product + +import numpy as np + +from pykrige.rk import Krige +from pykrige.rk import threed_krige +from pykrige.compat import GridSearchCV + + +def _method_and_vergiogram(): + method = ["ordinary", "universal", "ordinary3d", "universal3d"] + variogram_model = ["linear", "power", "gaussian", "spherical", "exponential"] + return product(method, variogram_model) + + +def test_krige(): + # dummy data + np.random.seed(1) + X = np.random.randint(0, 400, size=(20, 3)).astype(float) + y = 5 * np.random.rand(20) + + for m, v in _method_and_vergiogram(): + param_dict = {"method": [m], "variogram_model": [v]} + + estimator = GridSearchCV( + Krige(), + param_dict, + n_jobs=-1, + pre_dispatch="2*n_jobs", + verbose=False, + cv=5, + ) + # run the gridsearch + if m in ["ordinary", "universal"]: + estimator.fit(X=X[:, :2], y=y) + else: + estimator.fit(X=X, y=y) + if hasattr(estimator, "best_score_"): + if m in threed_krige: + assert estimator.best_score_ > -10.0 + else: + assert estimator.best_score_ > -3.0 + if hasattr(estimator, "cv_results_"): + assert estimator.cv_results_["mean_train_score"] > 0 diff --git a/tests/test_core.py b/tests/test_core.py new file mode 100755 index 0000000..d17038b --- /dev/null +++ b/tests/test_core.py @@ -0,0 +1,2696 @@ +""" +Testing code. +Updated BSM February 2017 +""" +import sys +import os + +import numpy as np +import pytest +from pytest import approx +from numpy.testing import assert_allclose +from scipy.spatial.distance import cdist + +from pykrige import kriging_tools as kt +from pykrige import core +from pykrige import variogram_models +from pykrige.ok import OrdinaryKriging +from pykrige.uk import UniversalKriging +from pykrige.ok3d import OrdinaryKriging3D +from pykrige.uk3d import UniversalKriging3D + +BASE_DIR = os.path.abspath(os.path.dirname(__file__)) + +allclose_pars = {"rtol": 1e-05, "atol": 1e-08} + + +@pytest.fixture +def validation_ref(): + + data = np.genfromtxt(os.path.join(BASE_DIR, "test_data/test_data.txt")) + ok_test_answer, ok_test_gridx, ok_test_gridy, cellsize, no_data = kt.read_asc_grid( + os.path.join(BASE_DIR, "test_data/test1_answer.asc"), footer=2 + ) + uk_test_answer, uk_test_gridx, uk_test_gridy, cellsize, no_data = kt.read_asc_grid( + os.path.join(BASE_DIR, "test_data/test2_answer.asc"), footer=2 + ) + + return ( + data, + (ok_test_answer, ok_test_gridx, ok_test_gridy), + (uk_test_answer, uk_test_gridx, uk_test_gridy), + ) + + +@pytest.fixture +def sample_data_2d(): + + data = np.array( + [ + [0.3, 1.2, 0.47], + [1.9, 0.6, 0.56], + [1.1, 3.2, 0.74], + [3.3, 4.4, 1.47], + [4.7, 3.8, 1.74], + ] + ) + gridx = np.arange(0.0, 6.0, 1.0) + gridx_2 = np.arange(0.0, 5.5, 0.5) + gridy = np.arange(0.0, 5.5, 0.5) + xi, yi = np.meshgrid(gridx, gridy) + mask = np.array(xi == yi) + return data, (gridx, gridy, gridx_2), mask + + +@pytest.fixture +def sample_data_3d(): + data = np.array( + [ + [0.1, 0.1, 0.3, 0.9], + [0.2, 0.1, 0.4, 0.8], + [0.1, 0.3, 0.1, 0.9], + [0.5, 0.4, 0.4, 0.5], + [0.3, 0.3, 0.2, 0.7], + ] + ) + gridx = np.arange(0.0, 0.6, 0.05) + gridy = np.arange(0.0, 0.6, 0.01) + gridz = np.arange(0.0, 0.6, 0.1) + zi, yi, xi = np.meshgrid(gridz, gridy, gridx, indexing="ij") + mask = np.array((xi == yi) & (yi == zi)) + return data, (gridx, gridy, gridz), mask + + +def test_core_adjust_for_anisotropy(): + + X = np.array([[1.0, 0.0, -1.0, 0.0], [0.0, 1.0, 0.0, -1.0]]).T + X_adj = core._adjust_for_anisotropy(X, [0.0, 0.0], [2.0], [90.0]) + assert_allclose(X_adj[:, 0], np.array([0.0, 1.0, 0.0, -1.0]), **allclose_pars) + assert_allclose(X_adj[:, 1], np.array([-2.0, 0.0, 2.0, 0.0]), **allclose_pars) + + +def test_core_adjust_for_anisotropy_3d(): + + # this is a bad examples, as the X matrix is symmetric + # and insensitive to transpositions + X = np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]).T + X_adj = core._adjust_for_anisotropy( + X, [0.0, 0.0, 0.0], [2.0, 2.0], [90.0, 0.0, 0.0] + ) + assert_allclose(X_adj[:, 0], np.array([1.0, 0.0, 0.0]), **allclose_pars) + assert_allclose(X_adj[:, 1], np.array([0.0, 0.0, 2.0]), **allclose_pars) + assert_allclose(X_adj[:, 2], np.array([0.0, -2.0, 0.0]), **allclose_pars) + X_adj = core._adjust_for_anisotropy( + X, [0.0, 0.0, 0.0], [2.0, 2.0], [0.0, 90.0, 0.0] + ) + assert_allclose(X_adj[:, 0], np.array([0.0, 0.0, -1.0]), **allclose_pars) + assert_allclose(X_adj[:, 1], np.array([0.0, 2.0, 0.0]), **allclose_pars) + assert_allclose(X_adj[:, 2], np.array([2.0, 0.0, 0.0]), **allclose_pars) + X_adj = core._adjust_for_anisotropy( + X, [0.0, 0.0, 0.0], [2.0, 2.0], [0.0, 0.0, 90.0] + ) + assert_allclose(X_adj[:, 0], np.array([0.0, 1.0, 0.0]), **allclose_pars) + assert_allclose(X_adj[:, 1], np.array([-2.0, 0.0, 0.0]), **allclose_pars) + assert_allclose(X_adj[:, 2], np.array([0.0, 0.0, 2.0]), **allclose_pars) + + +def test_core_make_variogram_parameter_list(): + + # test of first case - variogram_model_parameters is None + # function should return None unaffected + result = core._make_variogram_parameter_list("linear", None) + assert result is None + + # tests for second case - variogram_model_parameters is dict + with pytest.raises(KeyError): + core._make_variogram_parameter_list("linear", {"tacos": 1.0, "burritos": 2.0}) + result = core._make_variogram_parameter_list( + "linear", {"slope": 1.0, "nugget": 0.0} + ) + assert result == [1.0, 0.0] + + with pytest.raises(KeyError): + core._make_variogram_parameter_list("power", {"frijoles": 1.0}) + result = core._make_variogram_parameter_list( + "power", {"scale": 2.0, "exponent": 1.0, "nugget": 0.0} + ) + assert result == [2.0, 1.0, 0.0] + + with pytest.raises(KeyError): + core._make_variogram_parameter_list("exponential", {"tacos": 1.0}) + with pytest.raises(KeyError): + core._make_variogram_parameter_list( + "exponential", {"range": 1.0, "nugget": 1.0} + ) + result = core._make_variogram_parameter_list( + "exponential", {"sill": 5.0, "range": 2.0, "nugget": 1.0} + ) + assert result == [4.0, 2.0, 1.0] + result = core._make_variogram_parameter_list( + "exponential", {"psill": 4.0, "range": 2.0, "nugget": 1.0} + ) + assert result == [4.0, 2.0, 1.0] + + with pytest.raises(TypeError): + core._make_variogram_parameter_list("custom", {"junk": 1.0}) + with pytest.raises(ValueError): + core._make_variogram_parameter_list("blarg", {"junk": 1.0}) + + # tests for third case - variogram_model_parameters is list + with pytest.raises(ValueError): + core._make_variogram_parameter_list("linear", [1.0, 2.0, 3.0]) + result = core._make_variogram_parameter_list("linear", [1.0, 2.0]) + assert result == [1.0, 2.0] + + with pytest.raises(ValueError): + core._make_variogram_parameter_list("power", [1.0, 2.0]) + + result = core._make_variogram_parameter_list("power", [1.0, 2.0, 3.0]) + assert result == [1.0, 2.0, 3.0] + + with pytest.raises(ValueError): + core._make_variogram_parameter_list("exponential", [1.0, 2.0, 3.0, 4.0]) + result = core._make_variogram_parameter_list("exponential", [5.0, 2.0, 1.0]) + assert result == [4.0, 2.0, 1.0] + + result = core._make_variogram_parameter_list("custom", [1.0, 2.0, 3.0]) + assert result == [1.0, 2.0, 3] + + with pytest.raises(ValueError): + core._make_variogram_parameter_list("junk", [1.0, 1.0, 1.0]) + + # test for last case - make sure function handles incorrect + # variogram_model_parameters type appropriately + with pytest.raises(TypeError): + core._make_variogram_parameter_list("linear", "tacos") + + +def test_core_initialize_variogram_model(validation_ref): + + data, _, _ = validation_ref + + # Note the variogram_function argument is not a string in real life... + # core._initialize_variogram_model also checks the length of input + # lists, which is redundant now because the same tests are done in + # core._make_variogram_parameter_list + with pytest.raises(ValueError): + core._initialize_variogram_model( + np.vstack((data[:, 0], data[:, 1])).T, + data[:, 2], + "linear", + [0.0], + "linear", + 6, + False, + "euclidean", + ) + with pytest.raises(ValueError): + core._initialize_variogram_model( + np.vstack((data[:, 0], data[:, 1])).T, + data[:, 2], + "spherical", + [0.0], + "spherical", + 6, + False, + "euclidean", + ) + + # core._initialize_variogram_model does also check coordinate type, + # this is NOT redundant + with pytest.raises(ValueError): + core._initialize_variogram_model( + np.vstack((data[:, 0], data[:, 1])).T, + data[:, 2], + "spherical", + [0.0, 0.0, 0.0], + "spherical", + 6, + False, + "tacos", + ) + + x = np.array([1.0 + n / np.sqrt(2) for n in range(4)]) + y = np.array([1.0 + n / np.sqrt(2) for n in range(4)]) + z = np.arange(1.0, 5.0, 1.0) + lags, semivariance, variogram_model_parameters = core._initialize_variogram_model( + np.vstack((x, y)).T, z, "linear", [0.0, 0.0], "linear", 6, False, "euclidean" + ) + + assert_allclose(lags, np.array([1.0, 2.0, 3.0])) + assert_allclose(semivariance, np.array([0.5, 2.0, 4.5])) + + +def test_core_initialize_variogram_model_3d(sample_data_3d): + + data, _, _ = sample_data_3d + + # Note the variogram_function argument is not a string in real life... + # again, these checks in core._initialize_variogram_model are redundant + # now because the same tests are done in + # core._make_variogram_parameter_list + with pytest.raises(ValueError): + core._initialize_variogram_model( + np.vstack((data[:, 0], data[:, 1], data[:, 2])).T, + data[:, 3], + "linear", + [0.0], + "linear", + 6, + False, + "euclidean", + ) + + with pytest.raises(ValueError): + core._initialize_variogram_model( + np.vstack((data[:, 0], data[:, 1], data[:, 2])).T, + data[:, 3], + "spherical", + [0.0], + "spherical", + 6, + False, + "euclidean", + ) + + with pytest.raises(ValueError): + core._initialize_variogram_model( + np.vstack((data[:, 0], data[:, 1], data[:, 2])).T, + data[:, 3], + "linear", + [0.0, 0.0], + "linear", + 6, + False, + "geographic", + ) + + lags, semivariance, variogram_model_parameters = core._initialize_variogram_model( + np.vstack( + ( + np.array([1.0, 2.0, 3.0, 4.0]), + np.array([1.0, 2.0, 3.0, 4.0]), + np.array([1.0, 2.0, 3.0, 4.0]), + ) + ).T, + np.array([1.0, 2.0, 3.0, 4.0]), + "linear", + [0.0, 0.0], + "linear", + 3, + False, + "euclidean", + ) + assert_allclose( + lags, np.array([np.sqrt(3.0), 2.0 * np.sqrt(3.0), 3.0 * np.sqrt(3.0)]) + ) + assert_allclose(semivariance, np.array([0.5, 2.0, 4.5])) + + +def test_core_calculate_variogram_model(): + + res = core._calculate_variogram_model( + np.array([1.0, 2.0, 3.0, 4.0]), + np.array([2.05, 2.95, 4.05, 4.95]), + "linear", + variogram_models.linear_variogram_model, + False, + ) + assert_allclose(res, np.array([0.98, 1.05]), 0.01, 0.01) + + res = core._calculate_variogram_model( + np.array([1.0, 2.0, 3.0, 4.0]), + np.array([2.05, 2.95, 4.05, 4.95]), + "linear", + variogram_models.linear_variogram_model, + True, + ) + assert_allclose(res, np.array([0.98, 1.05]), 0.01, 0.01) + + res = core._calculate_variogram_model( + np.array([1.0, 2.0, 3.0, 4.0]), + np.array([1.0, 2.8284271, 5.1961524, 8.0]), + "power", + variogram_models.power_variogram_model, + False, + ) + assert_allclose(res, np.array([1.0, 1.5, 0.0]), 0.001, 0.001) + + res = core._calculate_variogram_model( + np.array([1.0, 2.0, 3.0, 4.0]), + np.array([1.0, 1.4142, 1.7321, 2.0]), + "power", + variogram_models.power_variogram_model, + False, + ) + assert_allclose(res, np.array([1.0, 0.5, 0.0]), 0.001, 0.001) + + res = core._calculate_variogram_model( + np.array([1.0, 2.0, 3.0, 4.0]), + np.array([1.2642, 1.7293, 1.9004, 1.9634]), + "exponential", + variogram_models.exponential_variogram_model, + False, + ) + assert_allclose(res, np.array([2.0, 3.0, 0.0]), 0.001, 0.001) + + res = core._calculate_variogram_model( + np.array([1.0, 2.0, 3.0, 4.0]), + np.array([0.5769, 1.4872, 1.9065, 1.9914]), + "gaussian", + variogram_models.gaussian_variogram_model, + False, + ) + assert_allclose(res, np.array([2.0, 3.0, 0.0]), 0.001, 0.001) + + res = core._calculate_variogram_model( + np.array([1.0, 2.0, 3.0, 4.0]), + np.array([3.33060952, 3.85063879, 3.96667301, 3.99256374]), + "exponential", + variogram_models.exponential_variogram_model, + False, + ) + assert_allclose(res, np.array([3.0, 2.0, 1.0]), 0.001, 0.001) + + res = core._calculate_variogram_model( + np.array([1.0, 2.0, 3.0, 4.0]), + np.array([2.60487044, 3.85968813, 3.99694817, 3.99998564]), + "gaussian", + variogram_models.gaussian_variogram_model, + False, + ) + assert_allclose(res, np.array([3.0, 2.0, 1.0]), 0.001, 0.001) + + +def test_core_krige(): + + # Example 3.2 from Kitanidis + data = np.array([[9.7, 47.6, 1.22], [43.8, 24.6, 2.822]]) + z, ss = core._krige( + np.vstack((data[:, 0], data[:, 1])).T, + data[:, 2], + np.array([18.8, 67.9]), + variogram_models.linear_variogram_model, + [0.006, 0.1], + "euclidean", + ) + assert z == approx(1.6364, rel=1e-4) + assert ss == approx(0.4201, rel=1e-4) + + z, ss = core._krige( + np.vstack((data[:, 0], data[:, 1])).T, + data[:, 2], + np.array([43.8, 24.6]), + variogram_models.linear_variogram_model, + [0.006, 0.1], + "euclidean", + ) + assert z == approx(2.822, rel=1e-3) + assert ss == approx(0.0, rel=1e-3) + + +def test_core_krige_3d(): + + # Adapted from example 3.2 from Kitanidis + data = np.array([[9.7, 47.6, 1.0, 1.22], [43.8, 24.6, 1.0, 2.822]]) + z, ss = core._krige( + np.vstack((data[:, 0], data[:, 1], data[:, 2])).T, + data[:, 3], + np.array([18.8, 67.9, 1.0]), + variogram_models.linear_variogram_model, + [0.006, 0.1], + "euclidean", + ) + assert z == approx(1.6364, rel=1e-4) + assert ss == approx(0.4201, rel=1e-4) + + z, ss = core._krige( + np.vstack((data[:, 0], data[:, 1], data[:, 2])).T, + data[:, 3], + np.array([43.8, 24.6, 1.0]), + variogram_models.linear_variogram_model, + [0.006, 0.1], + "euclidean", + ) + assert z == approx(2.822, rel=1e-3) + assert ss == approx(0.0, rel=1e-3) + + +def test_ok(validation_ref): + + # Test to compare OK results to those obtained using KT3D_H2O. + # (M. Karanovic, M. Tonkin, and D. Wilson, 2009, Groundwater, + # vol. 47, no. 4, 580-586.) + + data, (ok_test_answer, gridx, gridy), _ = validation_ref + + ok = OrdinaryKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="exponential", + variogram_parameters=[500.0, 3000.0, 0.0], + ) + z, ss = ok.execute("grid", gridx, gridy, backend="vectorized") + assert_allclose(z, ok_test_answer) + z, ss = ok.execute("grid", gridx, gridy, backend="loop") + assert_allclose(z, ok_test_answer) + + +def test_ok_update_variogram_model(validation_ref): + + data, (ok_test_answer, gridx, gridy), _ = validation_ref + + with pytest.raises(ValueError): + OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2], variogram_model="blurg") + + ok = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2]) + variogram_model = ok.variogram_model + variogram_parameters = ok.variogram_model_parameters + anisotropy_scaling = ok.anisotropy_scaling + anisotropy_angle = ok.anisotropy_angle + + with pytest.raises(ValueError): + ok.update_variogram_model("blurg") + + ok.update_variogram_model("power", anisotropy_scaling=3.0, anisotropy_angle=45.0) + + # TODO: check that new parameters equal to the set parameters + assert variogram_model != ok.variogram_model + assert not np.array_equal(variogram_parameters, ok.variogram_model_parameters) + assert anisotropy_scaling != ok.anisotropy_scaling + assert anisotropy_angle != ok.anisotropy_angle + + +def test_ok_get_variogram_points(validation_ref): + # Test to compare the variogram of OK results to those obtained using + # KT3D_H2O. + # (M. Karanovic, M. Tonkin, and D. Wilson, 2009, Groundwater, + # vol. 47, no. 4, 580-586.) + + # Variogram parameters + _variogram_parameters = [500.0, 3000.0, 0.0] + + data, _, (ok_test_answer, gridx, gridy) = validation_ref + + ok = OrdinaryKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="exponential", + variogram_parameters=_variogram_parameters, + ) + + # Get the variogram points from the UniversalKriging instance + lags, calculated_variogram = ok.get_variogram_points() + + # Generate the expected variogram points according to the + # exponential variogram model + expected_variogram = variogram_models.exponential_variogram_model( + _variogram_parameters, lags + ) + + assert_allclose(calculated_variogram, expected_variogram) + + +def test_ok_execute(sample_data_2d): + + data, (gridx, gridy, _), mask_ref = sample_data_2d + + ok = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2]) + + with pytest.raises(ValueError): + ok.execute("blurg", gridx, gridy) + + z, ss = ok.execute("grid", gridx, gridy, backend="vectorized") + shape = (gridy.size, gridx.size) + assert z.shape == shape + assert ss.shape == shape + assert np.amax(z) != np.amin(z) + assert np.amax(ss) != np.amin(ss) + assert not np.ma.is_masked(z) + + z, ss = ok.execute("grid", gridx, gridy, backend="loop") + shape = (gridy.size, gridx.size) + assert z.shape == shape + assert ss.shape == shape + assert np.amax(z) != np.amin(z) + assert np.amax(ss) != np.amin(ss) + assert not np.ma.is_masked(z) + + with pytest.raises(IOError): + ok.execute("masked", gridx, gridy, backend="vectorized") + mask = np.array([True, False]) + with pytest.raises(ValueError): + ok.execute("masked", gridx, gridy, mask=mask, backend="vectorized") + z, ss = ok.execute("masked", gridx, gridy, mask=mask_ref, backend="vectorized") + assert np.ma.is_masked(z) + assert np.ma.is_masked(ss) + assert z[0, 0] is np.ma.masked + assert ss[0, 0] is np.ma.masked + z, ss = ok.execute("masked", gridx, gridy, mask=mask_ref.T, backend="vectorized") + assert np.ma.is_masked(z) + assert np.ma.is_masked(ss) + assert z[0, 0] is np.ma.masked + assert ss[0, 0] is np.ma.masked + + with pytest.raises(IOError): + ok.execute("masked", gridx, gridy, backend="loop") + mask = np.array([True, False]) + with pytest.raises(ValueError): + ok.execute("masked", gridx, gridy, mask=mask, backend="loop") + z, ss = ok.execute("masked", gridx, gridy, mask=mask_ref, backend="loop") + assert np.ma.is_masked(z) + assert np.ma.is_masked(ss) + assert z[0, 0] is np.ma.masked + assert ss[0, 0] is np.ma.masked + z, ss = ok.execute("masked", gridx, gridy, mask=mask_ref.T, backend="loop") + assert np.ma.is_masked(z) + assert np.ma.is_masked(ss) + assert z[0, 0] is np.ma.masked + assert ss[0, 0] is np.ma.masked + + with pytest.raises(ValueError): + ok.execute( + "points", + np.array([0.0, 1.0, 2.0]), + np.array([0.0, 1.0]), + backend="vectorized", + ) + z, ss = ok.execute("points", gridx[0], gridy[0], backend="vectorized") + assert z.shape == (1,) + assert ss.shape == (1,) + + with pytest.raises(ValueError): + ok.execute( + "points", np.array([0.0, 1.0, 2.0]), np.array([0.0, 1.0]), backend="loop" + ) + z, ss = ok.execute("points", gridx[0], gridy[0], backend="loop") + assert z.shape == (1,) + assert ss.shape == (1,) + + +def test_cython_ok(sample_data_2d): + data, (gridx, gridy, _), mask_ref = sample_data_2d + + ok = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2]) + z1, ss1 = ok.execute("grid", gridx, gridy, backend="loop") + z2, ss2 = ok.execute("grid", gridx, gridy, backend="C") + assert_allclose(z1, z2) + assert_allclose(ss1, ss2) + + closest_points = 4 + + z1, ss1 = ok.execute( + "grid", gridx, gridy, backend="loop", n_closest_points=closest_points + ) + z2, ss2 = ok.execute( + "grid", gridx, gridy, backend="C", n_closest_points=closest_points + ) + assert_allclose(z1, z2) + assert_allclose(ss1, ss2) + + +def test_uk(validation_ref): + + # Test to compare UK with linear drift to results from KT3D_H2O. + # (M. Karanovic, M. Tonkin, and D. Wilson, 2009, Groundwater, + # vol. 47, no. 4, 580-586.) + + data, _, (uk_test_answer, gridx, gridy) = validation_ref + + uk = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="exponential", + variogram_parameters=[500.0, 3000.0, 0.0], + drift_terms=["regional_linear"], + ) + z, ss = uk.execute("grid", gridx, gridy, backend="vectorized") + assert_allclose(z, uk_test_answer) + z, ss = uk.execute("grid", gridx, gridy, backend="loop") + assert_allclose(z, uk_test_answer) + + +def test_uk_update_variogram_model(sample_data_2d): + + data, (gridx, gridy, _), mask_ref = sample_data_2d + + with pytest.raises(ValueError): + UniversalKriging(data[:, 0], data[:, 1], data[:, 2], variogram_model="blurg") + with pytest.raises(ValueError): + UniversalKriging(data[:, 0], data[:, 1], data[:, 2], drift_terms=["external_Z"]) + with pytest.raises(ValueError): + UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + drift_terms=["external_Z"], + external_drift=np.array([0]), + ) + with pytest.raises(ValueError): + UniversalKriging(data[:, 0], data[:, 1], data[:, 2], drift_terms=["point_log"]) + + uk = UniversalKriging(data[:, 0], data[:, 1], data[:, 2]) + variogram_model = uk.variogram_model + variogram_parameters = uk.variogram_model_parameters + anisotropy_scaling = uk.anisotropy_scaling + anisotropy_angle = uk.anisotropy_angle + + with pytest.raises(ValueError): + uk.update_variogram_model("blurg") + uk.update_variogram_model("power", anisotropy_scaling=3.0, anisotropy_angle=45.0) + # TODO: check that the new parameters are equal to the expected ones + assert variogram_model != uk.variogram_model + assert not np.array_equal(variogram_parameters, uk.variogram_model_parameters) + assert anisotropy_scaling != uk.anisotropy_scaling + assert anisotropy_angle != uk.anisotropy_angle + + +def test_uk_get_variogram_points(validation_ref): + # Test to compare the variogram of UK with linear drift to results from + # KT3D_H2O. + # (M. Karanovic, M. Tonkin, and D. Wilson, 2009, Groundwater, + # vol. 47, no. 4, 580-586.) + + # Variogram parameters + _variogram_parameters = [500.0, 3000.0, 0.0] + + data, _, (uk_test_answer, gridx, gridy) = validation_ref + + uk = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="exponential", + variogram_parameters=_variogram_parameters, + drift_terms=["regional_linear"], + ) + + # Get the variogram points from the UniversalKriging instance + lags, calculated_variogram = uk.get_variogram_points() + + # Generate the expected variogram points according to the + # exponential variogram model + expected_variogram = variogram_models.exponential_variogram_model( + _variogram_parameters, lags + ) + + assert_allclose(calculated_variogram, expected_variogram) + + +def test_uk_calculate_data_point_zscalars(sample_data_2d): + + data, (gridx, gridy, _), mask_ref = sample_data_2d + + dem = np.arange(0.0, 5.1, 0.1) + dem = np.repeat(dem[np.newaxis, :], 6, axis=0) + dem_x = np.arange(0.0, 5.1, 0.1) + dem_y = np.arange(0.0, 6.0, 1.0) + + with pytest.raises(ValueError): + UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + variogram_parameters=[1.0, 0.0], + drift_terms=["external_Z"], + ) + with pytest.raises(ValueError): + UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + variogram_parameters=[1.0, 0.0], + drift_terms=["external_Z"], + external_drift=dem, + ) + with pytest.raises(ValueError): + UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + variogram_parameters=[1.0, 0.0], + drift_terms=["external_Z"], + external_drift=dem, + external_drift_x=dem_x, + external_drift_y=np.arange(0.0, 5.0, 1.0), + ) + + uk = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + variogram_parameters=[1.0, 0.0], + drift_terms=["external_Z"], + external_drift=dem, + external_drift_x=dem_x, + external_drift_y=dem_y, + ) + assert_allclose(uk.z_scalars, data[:, 0]) + + xi, yi = np.meshgrid(np.arange(0.0, 5.3, 0.1), gridy) + with pytest.raises(ValueError): + uk._calculate_data_point_zscalars(xi, yi) + + xi, yi = np.meshgrid(np.arange(0.0, 5.0, 0.1), gridy) + z_scalars = uk._calculate_data_point_zscalars(xi, yi) + assert_allclose(z_scalars[0, :], np.arange(0.0, 5.0, 0.1)) + + +def test_uk_execute_single_point(): + + # Test data and answer from lecture notes by Nicolas Christou, UCLA Stats + data = np.array( + [ + [61.0, 139.0, 477.0], + [63.0, 140.0, 696.0], + [64.0, 129.0, 227.0], + [68.0, 128.0, 646.0], + [71.0, 140.0, 606.0], + [73.0, 141.0, 791.0], + [75.0, 128.0, 783.0], + ] + ) + point = (65.0, 137.0) + z_answer = 567.54 + ss_answer = 9.044 + + uk = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="exponential", + variogram_parameters=[10.0, 9.99, 0.0], + drift_terms=["regional_linear"], + ) + z, ss = uk.execute( + "points", np.array([point[0]]), np.array([point[1]]), backend="vectorized" + ) + assert z_answer == approx(z[0], rel=0.1) + assert ss_answer == approx(ss[0], rel=0.1) + + z, ss = uk.execute( + "points", np.array([61.0]), np.array([139.0]), backend="vectorized" + ) + assert z[0] == approx(477.0, rel=1e-3) + assert ss[0] == approx(0.0, rel=1e-3) + + z, ss = uk.execute("points", np.array([61.0]), np.array([139.0]), backend="loop") + assert z[0] == approx(477.0, rel=1e-3) + assert ss[0] == approx(0.0, rel=1e-3) + + +def test_uk_execute(sample_data_2d): + + data, (gridx, gridy, _), mask_ref = sample_data_2d + + uk = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["regional_linear"], + ) + + with pytest.raises(ValueError): + uk.execute("blurg", gridx, gridy) + with pytest.raises(ValueError): + uk.execute("grid", gridx, gridy, backend="mrow") + + z, ss = uk.execute("grid", gridx, gridy, backend="vectorized") + shape = (gridy.size, gridx.size) + assert z.shape == shape + assert ss.shape == shape + assert np.amax(z) != np.amin(z) + assert np.amax(ss) != np.amin(ss) + assert not np.ma.is_masked(z) + + z, ss = uk.execute("grid", gridx, gridy, backend="loop") + shape = (gridy.size, gridx.size) + assert z.shape == shape + assert ss.shape == shape + assert np.amax(z) != np.amin(z) + assert np.amax(ss) != np.amin(ss) + assert not np.ma.is_masked(z) + + with pytest.raises(IOError): + uk.execute("masked", gridx, gridy, backend="vectorized") + mask = np.array([True, False]) + with pytest.raises(ValueError): + uk.execute("masked", gridx, gridy, mask=mask, backend="vectorized") + z, ss = uk.execute("masked", gridx, gridy, mask=mask_ref, backend="vectorized") + assert np.ma.is_masked(z) + assert np.ma.is_masked(ss) + assert z[0, 0] is np.ma.masked + assert ss[0, 0] is np.ma.masked + z, ss = uk.execute("masked", gridx, gridy, mask=mask_ref.T, backend="vectorized") + assert np.ma.is_masked(z) + assert np.ma.is_masked(ss) + assert z[0, 0] is np.ma.masked + assert ss[0, 0] is np.ma.masked + + with pytest.raises(IOError): + uk.execute("masked", gridx, gridy, backend="loop") + mask = np.array([True, False]) + with pytest.raises(ValueError): + uk.execute("masked", gridx, gridy, mask=mask, backend="loop") + z, ss = uk.execute("masked", gridx, gridy, mask=mask_ref, backend="loop") + assert np.ma.is_masked(z) + assert np.ma.is_masked(ss) + assert z[0, 0] is np.ma.masked + assert ss[0, 0] is np.ma.masked + z, ss = uk.execute("masked", gridx, gridy, mask=mask_ref.T, backend="loop") + assert np.ma.is_masked(z) + assert np.ma.is_masked(ss) + assert z[0, 0] is np.ma.masked + assert ss[0, 0] is np.ma.masked + + with pytest.raises(ValueError): + uk.execute( + "points", + np.array([0.0, 1.0, 2.0]), + np.array([0.0, 1.0]), + backend="vectorized", + ) + z, ss = uk.execute("points", gridx[0], gridy[0], backend="vectorized") + assert z.shape == (1,) + assert ss.shape == (1,) + + with pytest.raises(ValueError): + uk.execute( + "points", np.array([0.0, 1.0, 2.0]), np.array([0.0, 1.0]), backend="loop" + ) + z, ss = uk.execute("points", gridx[0], gridy[0], backend="loop") + assert z.shape == (1,) + assert ss.shape == (1,) + + +def test_ok_uk_produce_same_result(validation_ref): + + data, _, (uk_test_answer, gridx_ref, gridy_ref) = validation_ref + + gridx = np.linspace(1067000.0, 1072000.0, 100) + gridy = np.linspace(241500.0, 244000.0, 100) + ok = OrdinaryKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + verbose=False, + enable_plotting=False, + ) + z_ok, ss_ok = ok.execute("grid", gridx, gridy, backend="vectorized") + uk = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + verbose=False, + enable_plotting=False, + ) + z_uk, ss_uk = uk.execute("grid", gridx, gridy, backend="vectorized") + assert_allclose(z_ok, z_uk) + assert_allclose(ss_ok, ss_uk) + + z_ok, ss_ok = ok.execute("grid", gridx, gridy, backend="loop") + z_uk, ss_uk = uk.execute("grid", gridx, gridy, backend="loop") + assert_allclose(z_ok, z_uk) + assert_allclose(ss_ok, ss_uk) + + +def test_ok_backends_produce_same_result(validation_ref): + + data, _, (uk_test_answer, gridx_ref, gridy_ref) = validation_ref + + gridx = np.linspace(1067000.0, 1072000.0, 100) + gridy = np.linspace(241500.0, 244000.0, 100) + ok = OrdinaryKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + verbose=False, + enable_plotting=False, + ) + z_ok_v, ss_ok_v = ok.execute("grid", gridx, gridy, backend="vectorized") + z_ok_l, ss_ok_l = ok.execute("grid", gridx, gridy, backend="loop") + assert_allclose(z_ok_v, z_ok_l) + assert_allclose(ss_ok_v, ss_ok_l) + + +def test_uk_backends_produce_same_result(validation_ref): + + data, _, (uk_test_answer, gridx_ref, gridy_ref) = validation_ref + + gridx = np.linspace(1067000.0, 1072000.0, 100) + gridy = np.linspace(241500.0, 244000.0, 100) + uk = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + verbose=False, + enable_plotting=False, + ) + z_uk_v, ss_uk_v = uk.execute("grid", gridx, gridy, backend="vectorized") + z_uk_l, ss_uk_l = uk.execute("grid", gridx, gridy, backend="loop") + assert_allclose(z_uk_v, z_uk_l) + assert_allclose(ss_uk_v, ss_uk_l) + + +def test_kriging_tools(sample_data_2d): + + data, (gridx, gridy, gridx_2), mask_ref = sample_data_2d + + ok = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2]) + z_write, ss_write = ok.execute("grid", gridx, gridy) + + kt.write_asc_grid( + gridx, + gridy, + z_write, + filename=os.path.join(BASE_DIR, "test_data/temp.asc"), + style=1, + ) + z_read, x_read, y_read, cellsize, no_data = kt.read_asc_grid( + os.path.join(BASE_DIR, "test_data/temp.asc") + ) + assert_allclose(z_write, z_read, 0.01, 0.01) + assert_allclose(gridx, x_read) + assert_allclose(gridy, y_read) + + z_write, ss_write = ok.execute("masked", gridx, gridy, mask=mask_ref) + kt.write_asc_grid( + gridx, + gridy, + z_write, + filename=os.path.join(BASE_DIR, "test_data/temp.asc"), + style=1, + ) + z_read, x_read, y_read, cellsize, no_data = kt.read_asc_grid( + os.path.join(BASE_DIR, "test_data/temp.asc") + ) + assert np.ma.allclose( + z_write, + np.ma.masked_where(z_read == no_data, z_read), + masked_equal=True, + rtol=0.01, + atol=0.01, + ) + assert_allclose(gridx, x_read) + assert_allclose(gridy, y_read) + + ok = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2]) + z_write, ss_write = ok.execute("grid", gridx_2, gridy) + + kt.write_asc_grid( + gridx_2, + gridy, + z_write, + filename=os.path.join(BASE_DIR, "test_data/temp.asc"), + style=2, + ) + z_read, x_read, y_read, cellsize, no_data = kt.read_asc_grid( + os.path.join(BASE_DIR, "test_data/temp.asc") + ) + assert_allclose(z_write, z_read, 0.01, 0.01) + assert_allclose(gridx_2, x_read) + assert_allclose(gridy, y_read) + + os.remove(os.path.join(BASE_DIR, "test_data/temp.asc")) + + +# http://doc.pytest.org/en/latest/skipping.html#id1 +@pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") +def test_uk_three_primary_drifts(sample_data_2d): + + data, (gridx, gridy, gridx_2), mask_ref = sample_data_2d + + well = np.array([[1.1, 1.1, -1.0]]) + dem = np.arange(0.0, 5.1, 0.1) + dem = np.repeat(dem[np.newaxis, :], 6, axis=0) + dem_x = np.arange(0.0, 5.1, 0.1) + dem_y = np.arange(0.0, 6.0, 1.0) + + uk = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["regional_linear", "external_Z", "point_log"], + point_drift=well, + external_drift=dem, + external_drift_x=dem_x, + external_drift_y=dem_y, + ) + + z, ss = uk.execute("grid", gridx, gridy, backend="vectorized") + assert z.shape == (gridy.shape[0], gridx.shape[0]) + assert ss.shape == (gridy.shape[0], gridx.shape[0]) + assert np.all(np.isfinite(z)) + assert not np.all(np.isnan(z)) + assert np.all(np.isfinite(ss)) + assert not np.all(np.isnan(ss)) + + z, ss = uk.execute("grid", gridx, gridy, backend="loop") + assert z.shape == (gridy.shape[0], gridx.shape[0]) + assert ss.shape == (gridy.shape[0], gridx.shape[0]) + assert np.all(np.isfinite(z)) + assert not np.all(np.isnan(z)) + assert np.all(np.isfinite(ss)) + assert not np.all(np.isnan(ss)) + + +def test_uk_specified_drift(sample_data_2d): + + data, (gridx, gridy, gridx_2), mask_ref = sample_data_2d + + xg, yg = np.meshgrid(gridx, gridy) + well = np.array([[1.1, 1.1, -1.0]]) + point_log = ( + well[0, 2] + * np.log(np.sqrt((xg - well[0, 0]) ** 2.0 + (yg - well[0, 1]) ** 2.0)) + * -1.0 + ) + if np.any(np.isinf(point_log)): + point_log[np.isinf(point_log)] = -100.0 * well[0, 2] * -1.0 + point_log_data = ( + well[0, 2] + * np.log( + np.sqrt((data[:, 0] - well[0, 0]) ** 2.0 + (data[:, 1] - well[0, 1]) ** 2.0) + ) + * -1.0 + ) + if np.any(np.isinf(point_log_data)): + point_log_data[np.isinf(point_log_data)] = -100.0 * well[0, 2] * -1.0 + + with pytest.raises(ValueError): + UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["specified"], + ) + with pytest.raises(TypeError): + UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["specified"], + specified_drift=data[:, 0], + ) + with pytest.raises(ValueError): + UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["specified"], + specified_drift=[data[:2, 0]], + ) + + uk_spec = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["specified"], + specified_drift=[data[:, 0], data[:, 1]], + ) + with pytest.raises(ValueError): + uk_spec.execute("grid", gridx, gridy, specified_drift_arrays=[gridx, gridy]) + with pytest.raises(TypeError): + uk_spec.execute("grid", gridx, gridy, specified_drift_arrays=gridx) + with pytest.raises(ValueError): + uk_spec.execute("grid", gridx, gridy, specified_drift_arrays=[xg]) + z_spec, ss_spec = uk_spec.execute( + "grid", gridx, gridy, specified_drift_arrays=[xg, yg] + ) + + uk_lin = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["regional_linear"], + ) + z_lin, ss_lin = uk_lin.execute("grid", gridx, gridy) + + assert_allclose(z_spec, z_lin) + assert_allclose(ss_spec, ss_lin) + + uk_spec = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["specified"], + specified_drift=[point_log_data], + ) + z_spec, ss_spec = uk_spec.execute( + "grid", gridx, gridy, specified_drift_arrays=[point_log] + ) + + uk_lin = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["point_log"], + point_drift=well, + ) + z_lin, ss_lin = uk_lin.execute("grid", gridx, gridy) + + assert_allclose(z_spec, z_lin) + assert_allclose(ss_spec, ss_lin) + + uk_spec = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["specified"], + specified_drift=[data[:, 0], data[:, 1], point_log_data], + ) + z_spec, ss_spec = uk_spec.execute( + "grid", gridx, gridy, specified_drift_arrays=[xg, yg, point_log] + ) + uk_lin = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["regional_linear", "point_log"], + point_drift=well, + ) + z_lin, ss_lin = uk_lin.execute("grid", gridx, gridy) + + assert_allclose(z_spec, z_lin) + assert_allclose(ss_spec, ss_lin) + + +def test_uk_functional_drift(sample_data_2d): + + data, (gridx, gridy, gridx_2), mask_ref = sample_data_2d + + well = np.array([[1.1, 1.1, -1.0]]) + func_x = lambda x, y: x # noqa + func_y = lambda x, y: y # noqa + + def func_well(x, y): + return -well[0, 2] * np.log( + np.sqrt((x - well[0, 0]) ** 2.0 + (y - well[0, 1]) ** 2.0) + ) + + with pytest.raises(ValueError): + UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["functional"], + ) + with pytest.raises(TypeError): + UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["functional"], + functional_drift=func_x, + ) + + uk_func = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["functional"], + functional_drift=[func_x, func_y], + ) + z_func, ss_func = uk_func.execute("grid", gridx, gridy) + uk_lin = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["regional_linear"], + ) + z_lin, ss_lin = uk_lin.execute("grid", gridx, gridy) + assert_allclose(z_func, z_lin) + assert_allclose(ss_func, ss_lin) + + uk_func = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["functional"], + functional_drift=[func_well], + ) + z_func, ss_func = uk_func.execute("grid", gridx, gridy) + uk_lin = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["point_log"], + point_drift=well, + ) + z_lin, ss_lin = uk_lin.execute("grid", gridx, gridy) + assert_allclose(z_func, z_lin) + assert_allclose(ss_func, ss_lin) + + uk_func = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["functional"], + functional_drift=[func_x, func_y, func_well], + ) + z_func, ss_func = uk_func.execute("grid", gridx, gridy) + uk_lin = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + drift_terms=["regional_linear", "point_log"], + point_drift=well, + ) + z_lin, ss_lin = uk_lin.execute("grid", gridx, gridy) + assert_allclose(z_func, z_lin) + assert_allclose(ss_func, ss_lin) + + +def test_uk_with_external_drift(validation_ref): + + data, _, (uk_test_answer, gridx_ref, gridy_ref) = validation_ref + + dem, demx, demy, cellsize, no_data = kt.read_asc_grid( + os.path.join(BASE_DIR, "test_data/test3_dem.asc") + ) + uk = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="spherical", + variogram_parameters=[500.0, 3000.0, 0.0], + anisotropy_scaling=1.0, + anisotropy_angle=0.0, + drift_terms=["external_Z"], + external_drift=dem, + external_drift_x=demx, + external_drift_y=demy, + verbose=False, + ) + answer, gridx, gridy, cellsize, no_data = kt.read_asc_grid( + os.path.join(BASE_DIR, "test_data/test3_answer.asc") + ) + + z, ss = uk.execute("grid", gridx, gridy, backend="vectorized") + assert_allclose(z, answer, **allclose_pars) + + z, ss = uk.execute("grid", gridx, gridy, backend="loop") + assert_allclose(z, answer, **allclose_pars) + + +def test_force_exact(): + data = np.array([[1.0, 1.0, 2.0], [2.0, 2.0, 1.5], [3.0, 3.0, 1.0]]) + ok = OrdinaryKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + variogram_parameters=[1.0, 1.0], + ) + z, ss = ok.execute("grid", [1.0, 2.0, 3.0], [1.0, 2.0, 3.0], backend="vectorized") + assert z[0, 0] == approx(2.0) + assert ss[0, 0] == approx(0.0) + assert z[1, 1] == approx(1.5) + assert ss[1, 1] == approx(0.0) + assert z[2, 2] == approx(1.0) + assert ss[2, 2] == approx(0.0) + assert ss[0, 2] != approx(0.0) + assert ss[2, 0] != approx(0.0) + z, ss = ok.execute( + "points", [1.0, 2.0, 3.0, 3.0], [2.0, 1.0, 1.0, 3.0], backend="vectorized" + ) + assert ss[0] != approx(0.0) + assert ss[1] != approx(0.0) + assert ss[2] != approx(0.0) + assert z[3] == approx(1.0) + assert ss[3] == approx(0.0) + z, ss = ok.execute( + "grid", np.arange(0.0, 4.0, 0.1), np.arange(0.0, 4.0, 0.1), backend="vectorized" + ) + assert z[10, 10] == approx(2.0) + assert ss[10, 10] == approx(0.0) + assert z[20, 20] == approx(1.5) + assert ss[20, 20] == approx(0.0) + assert z[30, 30] == approx(1.0) + assert ss[30, 30] == approx(0.0) + assert ss[0, 0] != approx(0.0) + assert ss[15, 15] != approx(0.0) + assert ss[10, 0] != approx(0.0) + assert ss[0, 10] != approx(0.0) + assert ss[20, 10] != approx(0.0) + assert ss[10, 20] != approx(0.0) + assert ss[30, 20] != approx(0.0) + assert ss[20, 30] != approx(0.0) + z, ss = ok.execute( + "grid", np.arange(0.0, 3.1, 0.1), np.arange(2.1, 3.1, 0.1), backend="vectorized" + ) + assert np.any(ss <= 1e-15) + assert not np.any(ss[:9, :30] <= 1e-15) + assert not np.allclose(z[:9, :30], 0.0) + z, ss = ok.execute( + "grid", np.arange(0.0, 1.9, 0.1), np.arange(2.1, 3.1, 0.1), backend="vectorized" + ) + assert not np.any(ss <= 1e-15) + z, ss = ok.execute( + "masked", + np.arange(2.5, 3.5, 0.1), + np.arange(2.5, 3.5, 0.25), + backend="vectorized", + mask=np.asarray( + np.meshgrid(np.arange(2.5, 3.5, 0.1), np.arange(2.5, 3.5, 0.25))[0] == 0.0 + ), + ) + assert ss[2, 5] <= 1e-15 + assert not np.allclose(ss, 0.0) + + z, ss = ok.execute("grid", [1.0, 2.0, 3.0], [1.0, 2.0, 3.0], backend="loop") + assert z[0, 0] == approx(2.0) + assert ss[0, 0] == approx(0.0) + assert z[1, 1] == approx(1.5) + assert ss[1, 1] == approx(0.0) + assert z[2, 2] == approx(1.0) + assert ss[2, 2] == approx(0.0) + assert ss[0, 2] != approx(0.0) + assert ss[2, 0] != approx(0.0) + z, ss = ok.execute( + "points", [1.0, 2.0, 3.0, 3.0], [2.0, 1.0, 1.0, 3.0], backend="loop" + ) + assert ss[0] != approx(0.0) + assert ss[1] != approx(0.0) + assert ss[2] != approx(0.0) + assert z[3] == approx(1.0) + assert ss[3] == approx(0.0) + z, ss = ok.execute( + "grid", np.arange(0.0, 4.0, 0.1), np.arange(0.0, 4.0, 0.1), backend="loop" + ) + assert z[10, 10] == approx(2.0) + assert ss[10, 10] == approx(0.0) + assert z[20, 20] == approx(1.5) + assert ss[20, 20] == approx(0.0) + assert z[30, 30] == approx(1.0) + assert ss[30, 30] == approx(0.0) + assert ss[0, 0] != approx(0.0) + assert ss[15, 15] != approx(0.0) + assert ss[10, 0] != approx(0.0) + assert ss[0, 10] != approx(0.0) + assert ss[20, 10] != approx(0.0) + assert ss[10, 20] != approx(0.0) + assert ss[30, 20] != approx(0.0) + assert ss[20, 30] != approx(0.0) + z, ss = ok.execute( + "grid", np.arange(0.0, 3.1, 0.1), np.arange(2.1, 3.1, 0.1), backend="loop" + ) + assert np.any(ss <= 1e-15) + assert not np.any(ss[:9, :30] <= 1e-15) + assert not np.allclose(z[:9, :30], 0.0) + z, ss = ok.execute( + "grid", np.arange(0.0, 1.9, 0.1), np.arange(2.1, 3.1, 0.1), backend="loop" + ) + assert not np.any(ss <= 1e-15) + z, ss = ok.execute( + "masked", + np.arange(2.5, 3.5, 0.1), + np.arange(2.5, 3.5, 0.25), + backend="loop", + mask=np.asarray( + np.meshgrid(np.arange(2.5, 3.5, 0.1), np.arange(2.5, 3.5, 0.25))[0] == 0.0 + ), + ) + assert ss[2, 5] <= 1e-15 + assert not np.allclose(ss, 0.0) + + uk = UniversalKriging(data[:, 0], data[:, 1], data[:, 2]) + z, ss = uk.execute("grid", [1.0, 2.0, 3.0], [1.0, 2.0, 3.0], backend="vectorized") + assert z[0, 0] == approx(2.0) + assert ss[0, 0] == approx(0.0) + assert z[1, 1] == approx(1.5) + assert ss[1, 1] == approx(0.0) + assert z[2, 2] == approx(1.0) + assert ss[2, 2] == approx(0.0) + assert ss[0, 2] != approx(0.0) + assert ss[2, 0] != approx(0.0) + z, ss = uk.execute( + "points", [1.0, 2.0, 3.0, 3.0], [2.0, 1.0, 1.0, 3.0], backend="vectorized" + ) + assert ss[0] != approx(0.0) + assert ss[1] != approx(0.0) + assert ss[2] != approx(0.0) + assert z[3] == approx(1.0) + assert ss[3] == approx(0.0) + z, ss = uk.execute( + "grid", np.arange(0.0, 4.0, 0.1), np.arange(0.0, 4.0, 0.1), backend="vectorized" + ) + assert z[10, 10] == approx(2.0) + assert ss[10, 10] == approx(0.0) + assert z[20, 20] == approx(1.5) + assert ss[20, 20] == approx(0.0) + assert z[30, 30] == approx(1.0) + assert ss[30, 30] == approx(0.0) + assert ss[0, 0] != approx(0.0) + assert ss[15, 15] != approx(0.0) + assert ss[10, 0] != approx(0.0) + assert ss[0, 10] != approx(0.0) + assert ss[20, 10] != approx(0.0) + assert ss[10, 20] != approx(0.0) + assert ss[30, 20] != approx(0.0) + assert ss[20, 30] != approx(0.0) + z, ss = uk.execute( + "grid", np.arange(0.0, 3.1, 0.1), np.arange(2.1, 3.1, 0.1), backend="vectorized" + ) + assert np.any(ss <= 1e-15) + assert not np.any(ss[:9, :30] <= 1e-15) + assert not np.allclose(z[:9, :30], 0.0) + z, ss = uk.execute( + "grid", np.arange(0.0, 1.9, 0.1), np.arange(2.1, 3.1, 0.1), backend="vectorized" + ) + assert not (np.any(ss <= 1e-15)) + z, ss = uk.execute( + "masked", + np.arange(2.5, 3.5, 0.1), + np.arange(2.5, 3.5, 0.25), + backend="vectorized", + mask=np.asarray( + np.meshgrid(np.arange(2.5, 3.5, 0.1), np.arange(2.5, 3.5, 0.25))[0] == 0.0 + ), + ) + assert ss[2, 5] <= 1e-15 + assert not np.allclose(ss, 0.0) + z, ss = uk.execute("grid", [1.0, 2.0, 3.0], [1.0, 2.0, 3.0], backend="loop") + assert z[0, 0] == approx(2.0) + assert ss[0, 0] == approx(0.0) + assert z[1, 1] == approx(1.5) + assert ss[1, 1] == approx(0.0) + assert z[2, 2] == approx(1.0) + assert ss[2, 2] == approx(0.0) + assert ss[0, 2] != approx(0.0) + assert ss[2, 0] != approx(0.0) + z, ss = uk.execute( + "points", [1.0, 2.0, 3.0, 3.0], [2.0, 1.0, 1.0, 3.0], backend="loop" + ) + assert ss[0] != approx(0.0) + assert ss[1] != approx(0.0) + assert ss[2] != approx(0.0) + assert z[3] == approx(1.0) + assert ss[3] == approx(0.0) + z, ss = uk.execute( + "grid", np.arange(0.0, 4.0, 0.1), np.arange(0.0, 4.0, 0.1), backend="loop" + ) + assert z[10, 10] == approx(2.0) + assert ss[10, 10] == approx(0.0) + assert z[20, 20] == approx(1.5) + assert ss[20, 20] == approx(0.0) + assert z[30, 30] == approx(1.0) + assert ss[30, 30] == approx(0.0) + assert ss[0, 0] != approx(0.0) + assert ss[15, 15] != approx(0.0) + assert ss[10, 0] != approx(0.0) + assert ss[0, 10] != approx(0.0) + assert ss[20, 10] != approx(0.0) + assert ss[10, 20] != approx(0.0) + assert ss[30, 20] != approx(0.0) + assert ss[20, 30] != approx(0.0) + z, ss = uk.execute( + "grid", np.arange(0.0, 3.1, 0.1), np.arange(2.1, 3.1, 0.1), backend="loop" + ) + assert np.any(ss <= 1e-15) + assert not np.any(ss[:9, :30] <= 1e-15) + assert not np.allclose(z[:9, :30], 0.0) + z, ss = uk.execute( + "grid", np.arange(0.0, 1.9, 0.1), np.arange(2.1, 3.1, 0.1), backend="loop" + ) + assert not np.any(ss <= 1e-15) + z, ss = uk.execute( + "masked", + np.arange(2.5, 3.5, 0.1), + np.arange(2.5, 3.5, 0.25), + backend="loop", + mask=np.asarray( + np.meshgrid(np.arange(2.5, 3.5, 0.1), np.arange(2.5, 3.5, 0.25))[0] == 0.0 + ), + ) + assert ss[2, 5] <= 1e-15 + assert not np.allclose(ss, 0.0) + + z, ss = core._krige( + np.vstack((data[:, 0], data[:, 1])).T, + data[:, 2], + np.array([1.0, 1.0]), + variogram_models.linear_variogram_model, + [1.0, 1.0], + "euclidean", + ) + assert z == approx(2.0) + assert ss == approx(0.0) + z, ss = core._krige( + np.vstack((data[:, 0], data[:, 1])).T, + data[:, 2], + np.array([1.0, 2.0]), + variogram_models.linear_variogram_model, + [1.0, 1.0], + "euclidean", + ) + assert ss != approx(0.0) + + data = np.zeros((50, 3)) + x, y = np.meshgrid(np.arange(0.0, 10.0, 1.0), np.arange(0.0, 10.0, 2.0)) + data[:, 0] = np.ravel(x) + data[:, 1] = np.ravel(y) + data[:, 2] = np.ravel(x) * np.ravel(y) + ok = OrdinaryKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + variogram_parameters=[100.0, 1.0], + ) + z, ss = ok.execute( + "grid", + np.arange(0.0, 10.0, 1.0), + np.arange(0.0, 10.0, 2.0), + backend="vectorized", + ) + assert_allclose(np.ravel(z), data[:, 2], **allclose_pars) + assert_allclose(ss, 0.0, **allclose_pars) + z, ss = ok.execute( + "grid", + np.arange(0.5, 10.0, 1.0), + np.arange(0.5, 10.0, 2.0), + backend="vectorized", + ) + assert not np.allclose(np.ravel(z), data[:, 2]) + assert not np.allclose(ss, 0.0) + z, ss = ok.execute( + "grid", np.arange(0.0, 10.0, 1.0), np.arange(0.0, 10.0, 2.0), backend="loop" + ) + assert_allclose(np.ravel(z), data[:, 2], **allclose_pars) + assert_allclose(ss, 0.0, **allclose_pars) + z, ss = ok.execute( + "grid", np.arange(0.5, 10.0, 1.0), np.arange(0.5, 10.0, 2.0), backend="loop" + ) + assert not np.allclose(np.ravel(z), data[:, 2]) + assert not np.allclose(ss, 0.0) + + uk = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="linear", + variogram_parameters=[100.0, 1.0], + ) + z, ss = uk.execute( + "grid", + np.arange(0.0, 10.0, 1.0), + np.arange(0.0, 10.0, 2.0), + backend="vectorized", + ) + assert_allclose(np.ravel(z), data[:, 2], **allclose_pars) + assert_allclose(ss, 0.0, **allclose_pars) + z, ss = uk.execute( + "grid", + np.arange(0.5, 10.0, 1.0), + np.arange(0.5, 10.0, 2.0), + backend="vectorized", + ) + assert not np.allclose(np.ravel(z), data[:, 2]) + assert not np.allclose(ss, 0.0) + z, ss = uk.execute( + "grid", np.arange(0.0, 10.0, 1.0), np.arange(0.0, 10.0, 2.0), backend="loop" + ) + assert_allclose(np.ravel(z), data[:, 2], **allclose_pars) + assert_allclose(ss, 0.0, **allclose_pars) + z, ss = uk.execute( + "grid", np.arange(0.5, 10.0, 1.0), np.arange(0.5, 10.0, 2.0), backend="loop" + ) + assert not np.allclose(np.ravel(z), data[:, 2]) + assert not np.allclose(ss, 0.0) + + +def test_custom_variogram(sample_data_2d): + data, (gridx, gridy, gridx_2), mask_ref = sample_data_2d + + def func(params, dist): + return params[0] * np.log10(dist + params[1]) + params[2] + + with pytest.raises(ValueError): + UniversalKriging(data[:, 0], data[:, 1], data[:, 2], variogram_model="mrow") + with pytest.raises(ValueError): + UniversalKriging(data[:, 0], data[:, 1], data[:, 2], variogram_model="custom") + with pytest.raises(ValueError): + UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="custom", + variogram_function=0, + ) + with pytest.raises(ValueError): + UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="custom", + variogram_function=func, + ) + uk = UniversalKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="custom", + variogram_parameters=[1.0, 1.0, 1.0], + variogram_function=func, + ) + assert uk.variogram_function([1.0, 1.0, 1.0], 1.0) == approx(1.3010, rel=1e-4) + uk = UniversalKriging(data[:, 0], data[:, 1], data[:, 2], variogram_model="linear") + uk.update_variogram_model( + "custom", variogram_parameters=[1.0, 1.0, 1.0], variogram_function=func + ) + assert uk.variogram_function([1.0, 1.0, 1.0], 1.0) == approx(1.3010, rel=1e-4) + + with pytest.raises(ValueError): + OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2], variogram_model="mrow") + with pytest.raises(ValueError): + OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2], variogram_model="custom") + with pytest.raises(ValueError): + OrdinaryKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="custom", + variogram_function=0, + ) + with pytest.raises(ValueError): + OrdinaryKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="custom", + variogram_function=func, + ) + ok = OrdinaryKriging( + data[:, 0], + data[:, 1], + data[:, 2], + variogram_model="custom", + variogram_parameters=[1.0, 1.0, 1.0], + variogram_function=func, + ) + assert ok.variogram_function([1.0, 1.0, 1.0], 1.0) == approx(1.3010, rel=1e-4) + ok = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2], variogram_model="linear") + ok.update_variogram_model( + "custom", variogram_parameters=[1.0, 1.0, 1.0], variogram_function=func + ) + assert ok.variogram_function([1.0, 1.0, 1.0], 1.0) == approx(1.3010, rel=1e-4) + + +def test_ok3d(validation_ref): + + data, (ok_test_answer, gridx_ref, gridy_ref), _ = validation_ref + + # Test to compare K3D results to those obtained using KT3D_H2O. + # (M. Karanovic, M. Tonkin, and D. Wilson, 2009, Groundwater, vol. 47, + # no. 4, 580-586.) + k3d = OrdinaryKriging3D( + data[:, 0], + data[:, 1], + np.zeros(data[:, 1].shape), + data[:, 2], + variogram_model="exponential", + variogram_parameters=[500.0, 3000.0, 0.0], + ) + k, ss = k3d.execute( + "grid", gridx_ref, gridy_ref, np.array([0.0]), backend="vectorized" + ) + assert_allclose(np.squeeze(k), ok_test_answer) + k, ss = k3d.execute("grid", gridx_ref, gridy_ref, np.array([0.0]), backend="loop") + assert_allclose(np.squeeze(k), ok_test_answer) + + # Test to compare K3D results to those obtained using KT3D. + data = np.genfromtxt( + os.path.join(BASE_DIR, "test_data", "test3d_data.txt"), skip_header=1 + ) + ans = np.genfromtxt(os.path.join(BASE_DIR, "test_data", "test3d_answer.txt")) + ans_z = ans[:, 0].reshape((10, 10, 10)) + ans_ss = ans[:, 1].reshape((10, 10, 10)) + k3d = OrdinaryKriging3D( + data[:, 0], + data[:, 1], + data[:, 2], + data[:, 3], + variogram_model="linear", + variogram_parameters=[1.0, 0.1], + ) + k, ss = k3d.execute( + "grid", np.arange(10.0), np.arange(10.0), np.arange(10.0), backend="vectorized" + ) + assert_allclose(k, ans_z, rtol=1e-3, atol=1e-8) + assert_allclose(ss, ans_ss, rtol=1e-3, atol=1e-8) + k3d = OrdinaryKriging3D( + data[:, 0], + data[:, 1], + data[:, 2], + data[:, 3], + variogram_model="linear", + variogram_parameters=[1.0, 0.1], + ) + k, ss = k3d.execute( + "grid", np.arange(10.0), np.arange(10.0), np.arange(10.0), backend="loop" + ) + assert_allclose(k, ans_z, rtol=1e-3, atol=1e-8) + assert_allclose(ss, ans_ss, rtol=1e-3, atol=1e-8) + + +def test_ok3d_moving_window(): + + # Test to compare K3D results to those obtained using KT3D. + data = np.genfromtxt( + os.path.join(BASE_DIR, "test_data", "test3d_data.txt"), skip_header=1 + ) + ans = np.genfromtxt(os.path.join(BASE_DIR, "./test_data/test3d_answer.txt")) + ans_z = ans[:, 0].reshape((10, 10, 10)) + ans_ss = ans[:, 1].reshape((10, 10, 10)) + k3d = OrdinaryKriging3D( + data[:, 0], + data[:, 1], + data[:, 2], + data[:, 3], + variogram_model="linear", + variogram_parameters=[1.0, 0.1], + ) + k, ss = k3d.execute( + "grid", + np.arange(10.0), + np.arange(10.0), + np.arange(10.0), + backend="loop", + n_closest_points=10, + ) + assert_allclose(k, ans_z, rtol=1e-3) + assert_allclose(ss, ans_ss, rtol=1e-3) + + +def test_ok3d_uk3d_and_backends_produce_same_results(validation_ref): + + data, _, (uk_test_answer, gridx_ref, gridy_ref) = validation_ref + + ok3d = OrdinaryKriging3D( + data[:, 0], + data[:, 1], + np.zeros(data[:, 1].shape), + data[:, 2], + variogram_model="exponential", + variogram_parameters=[500.0, 3000.0, 0.0], + ) + ok_v, oss_v = ok3d.execute( + "grid", gridx_ref, gridy_ref, np.array([0.0]), backend="vectorized" + ) + ok_l, oss_l = ok3d.execute( + "grid", gridx_ref, gridy_ref, np.array([0.0]), backend="loop" + ) + + uk3d = UniversalKriging3D( + data[:, 0], + data[:, 1], + np.zeros(data[:, 1].shape), + data[:, 2], + variogram_model="exponential", + variogram_parameters=[500.0, 3000.0, 0.0], + ) + uk_v, uss_v = uk3d.execute( + "grid", gridx_ref, gridy_ref, np.array([0.0]), backend="vectorized" + ) + assert_allclose(uk_v, ok_v) + uk_l, uss_l = uk3d.execute( + "grid", gridx_ref, gridy_ref, np.array([0.0]), backend="loop" + ) + assert_allclose(uk_l, ok_l) + assert_allclose(uk_l, uk_v) + assert_allclose(uss_l, uss_v) + + data = np.genfromtxt( + os.path.join(BASE_DIR, "test_data", "test3d_data.txt"), skip_header=1 + ) + ok3d = OrdinaryKriging3D( + data[:, 0], + data[:, 1], + data[:, 2], + data[:, 3], + variogram_model="linear", + variogram_parameters=[1.0, 0.1], + ) + ok_v, oss_v = ok3d.execute( + "grid", np.arange(10.0), np.arange(10.0), np.arange(10.0), backend="vectorized" + ) + ok_l, oss_l = ok3d.execute( + "grid", np.arange(10.0), np.arange(10.0), np.arange(10.0), backend="loop" + ) + + uk3d = UniversalKriging3D( + data[:, 0], + data[:, 1], + data[:, 2], + data[:, 3], + variogram_model="linear", + variogram_parameters=[1.0, 0.1], + ) + uk_v, uss_v = uk3d.execute( + "grid", np.arange(10.0), np.arange(10.0), np.arange(10.0), backend="vectorized" + ) + assert_allclose(uk_v, ok_v) + assert_allclose(uss_v, oss_v) + uk_l, uss_l = uk3d.execute( + "grid", np.arange(10.0), np.arange(10.0), np.arange(10.0), backend="loop" + ) + assert_allclose(uk_l, ok_l) + assert_allclose(uss_l, oss_l) + assert_allclose(uk_l, uk_v) + assert_allclose(uss_l, uss_v) + + +def test_ok3d_update_variogram_model(sample_data_3d): + + data, (gridx_ref, gridy_ref, gridz_ref), mask_ref = sample_data_3d + + with pytest.raises(ValueError): + OrdinaryKriging3D( + data[:, 0], data[:, 1], data[:, 2], data[:, 3], variogram_model="blurg" + ) + + k3d = OrdinaryKriging3D(data[:, 0], data[:, 1], data[:, 2], data[:, 3]) + variogram_model = k3d.variogram_model + variogram_parameters = k3d.variogram_model_parameters + anisotropy_scaling_y = k3d.anisotropy_scaling_y + anisotropy_scaling_z = k3d.anisotropy_scaling_z + anisotropy_angle_x = k3d.anisotropy_angle_x + anisotropy_angle_y = k3d.anisotropy_angle_y + anisotropy_angle_z = k3d.anisotropy_angle_z + + with pytest.raises(ValueError): + k3d.update_variogram_model("blurg") + k3d.update_variogram_model( + "power", + anisotropy_scaling_y=3.0, + anisotropy_scaling_z=3.0, + anisotropy_angle_x=45.0, + anisotropy_angle_y=45.0, + anisotropy_angle_z=45.0, + ) + assert variogram_model != k3d.variogram_model + assert not np.array_equal(variogram_parameters, k3d.variogram_model_parameters) + assert anisotropy_scaling_y != k3d.anisotropy_scaling_y + assert anisotropy_scaling_z != k3d.anisotropy_scaling_z + assert anisotropy_angle_x != k3d.anisotropy_angle_x + assert anisotropy_angle_y != k3d.anisotropy_angle_y + assert anisotropy_angle_z != k3d.anisotropy_angle_z + + +def test_uk3d_update_variogram_model(sample_data_3d): + + data, (gridx_ref, gridy_ref, gridz_ref), mask_ref = sample_data_3d + + with pytest.raises(ValueError): + UniversalKriging3D( + data[:, 0], data[:, 1], data[:, 2], data[:, 3], variogram_model="blurg" + ) + + uk3d = UniversalKriging3D(data[:, 0], data[:, 1], data[:, 2], data[:, 3]) + variogram_model = uk3d.variogram_model + variogram_parameters = uk3d.variogram_model_parameters + anisotropy_scaling_y = uk3d.anisotropy_scaling_y + anisotropy_scaling_z = uk3d.anisotropy_scaling_z + anisotropy_angle_x = uk3d.anisotropy_angle_x + anisotropy_angle_y = uk3d.anisotropy_angle_y + anisotropy_angle_z = uk3d.anisotropy_angle_z + + with pytest.raises(ValueError): + uk3d.update_variogram_model("blurg") + uk3d.update_variogram_model( + "power", + anisotropy_scaling_y=3.0, + anisotropy_scaling_z=3.0, + anisotropy_angle_x=45.0, + anisotropy_angle_y=45.0, + anisotropy_angle_z=45.0, + ) + assert not variogram_model == uk3d.variogram_model + assert not np.array_equal(variogram_parameters, uk3d.variogram_model_parameters) + assert not anisotropy_scaling_y == uk3d.anisotropy_scaling_y + assert not anisotropy_scaling_z == uk3d.anisotropy_scaling_z + assert not anisotropy_angle_x == uk3d.anisotropy_angle_x + assert not anisotropy_angle_y == uk3d.anisotropy_angle_y + assert not anisotropy_angle_z == uk3d.anisotropy_angle_z + + +def test_ok3d_backends_produce_same_result(sample_data_3d): + + data, (gridx_ref, gridy_ref, gridz_ref), mask_ref = sample_data_3d + + k3d = OrdinaryKriging3D( + data[:, 0], data[:, 1], data[:, 2], data[:, 3], variogram_model="linear" + ) + k_k3d_v, ss_k3d_v = k3d.execute( + "grid", gridx_ref, gridy_ref, gridz_ref, backend="vectorized" + ) + k_k3d_l, ss_k3d_l = k3d.execute( + "grid", gridx_ref, gridy_ref, gridz_ref, backend="loop" + ) + assert_allclose(k_k3d_v, k_k3d_l, rtol=1e-05, atol=1e-8) + assert_allclose(ss_k3d_v, ss_k3d_l, rtol=1e-05, atol=1e-8) + + +def test_ok3d_execute(sample_data_3d): + + data, (gridx_ref, gridy_ref, gridz_ref), mask_ref = sample_data_3d + + k3d = OrdinaryKriging3D(data[:, 0], data[:, 1], data[:, 2], data[:, 3]) + + with pytest.raises(ValueError): + k3d.execute("blurg", gridx_ref, gridy_ref, gridz_ref) + + k, ss = k3d.execute("grid", gridx_ref, gridy_ref, gridz_ref, backend="vectorized") + shape = (gridz_ref.size, gridy_ref.size, gridx_ref.size) + assert k.shape == shape + assert ss.shape == shape + assert np.amax(k) != np.amin(k) + assert np.amax(ss) != np.amin(ss) + assert not np.ma.is_masked(k) + + k, ss = k3d.execute("grid", gridx_ref, gridy_ref, gridz_ref, backend="loop") + shape = (gridz_ref.size, gridy_ref.size, gridx_ref.size) + assert k.shape == shape + assert ss.shape == shape + assert np.amax(k) != np.amin(k) + assert np.amax(ss) != np.amin(ss) + assert not np.ma.is_masked(k) + + with pytest.raises(IOError): + k3d.execute("masked", gridx_ref, gridy_ref, gridz_ref, backend="vectorized") + mask = np.array([True, False]) + with pytest.raises(ValueError): + k3d.execute( + "masked", gridx_ref, gridy_ref, gridz_ref, mask=mask, backend="vectorized" + ) + k, ss = k3d.execute( + "masked", gridx_ref, gridy_ref, gridz_ref, mask=mask_ref, backend="vectorized" + ) + assert np.ma.is_masked(k) + assert np.ma.is_masked(ss) + assert k[0, 0, 0] is np.ma.masked + assert ss[0, 0, 0] is np.ma.masked + z, ss = k3d.execute( + "masked", gridx_ref, gridy_ref, gridz_ref, mask=mask_ref.T, backend="vectorized" + ) + assert np.ma.is_masked(z) + assert np.ma.is_masked(ss) + assert z[0, 0, 0] is np.ma.masked + assert ss[0, 0, 0] is np.ma.masked + + with pytest.raises(IOError): + k3d.execute("masked", gridx_ref, gridy_ref, gridz_ref, backend="loop") + mask = np.array([True, False]) + with pytest.raises(ValueError): + k3d.execute( + "masked", gridx_ref, gridy_ref, gridz_ref, mask=mask, backend="loop" + ) + k, ss = k3d.execute( + "masked", gridx_ref, gridy_ref, gridz_ref, mask=mask_ref, backend="loop" + ) + assert np.ma.is_masked(k) + assert np.ma.is_masked(ss) + assert k[0, 0, 0] is np.ma.masked + assert ss[0, 0, 0] is np.ma.masked + z, ss = k3d.execute( + "masked", gridx_ref, gridy_ref, gridz_ref, mask=mask_ref.T, backend="loop" + ) + assert np.ma.is_masked(z) + assert np.ma.is_masked(ss) + assert z[0, 0, 0] is np.ma.masked + assert ss[0, 0, 0] is np.ma.masked + + with pytest.raises(ValueError): + k3d.execute( + "points", + np.array([0.0, 1.0, 2.0]), + np.array([0.0, 1.0]), + np.array([1.0]), + backend="vectorized", + ) + k, ss = k3d.execute( + "points", gridx_ref[0], gridy_ref[0], gridz_ref[0], backend="vectorized" + ) + assert k.shape == (1,) + assert ss.shape == (1,) + + with pytest.raises(ValueError): + k3d.execute( + "points", + np.array([0.0, 1.0, 2.0]), + np.array([0.0, 1.0]), + np.array([1.0]), + backend="loop", + ) + k, ss = k3d.execute( + "points", gridx_ref[0], gridy_ref[0], gridz_ref[0], backend="loop" + ) + assert k.shape == (1,) + assert ss.shape == (1,) + + data = np.zeros((125, 4)) + z, y, x = np.meshgrid( + np.arange(0.0, 5.0, 1.0), np.arange(0.0, 5.0, 1.0), np.arange(0.0, 5.0, 1.0) + ) + data[:, 0] = np.ravel(x) + data[:, 1] = np.ravel(y) + data[:, 2] = np.ravel(z) + data[:, 3] = np.ravel(z) + k3d = OrdinaryKriging3D( + data[:, 0], data[:, 1], data[:, 2], data[:, 3], variogram_model="linear" + ) + k, ss = k3d.execute( + "grid", + np.arange(2.0, 3.0, 0.1), + np.arange(2.0, 3.0, 0.1), + np.arange(0.0, 4.0, 1.0), + backend="vectorized", + ) + assert_allclose(k[0, :, :], 0.0, atol=0.01) + assert_allclose(k[1, :, :], 1.0, rtol=1.0e-2) + assert_allclose(k[2, :, :], 2.0, rtol=1.0e-2) + assert_allclose(k[3, :, :], 3.0, rtol=1.0e-2) + k, ss = k3d.execute( + "grid", + np.arange(2.0, 3.0, 0.1), + np.arange(2.0, 3.0, 0.1), + np.arange(0.0, 4.0, 1.0), + backend="loop", + ) + assert_allclose(k[0, :, :], 0.0, atol=0.01) + assert_allclose(k[1, :, :], 1.0, rtol=1.0e-2) + assert_allclose(k[2, :, :], 2.0, rtol=1.0e-2) + assert_allclose(k[3, :, :], 3.0, rtol=1.0e-2) + k3d = OrdinaryKriging3D( + data[:, 0], data[:, 1], data[:, 2], data[:, 3], variogram_model="linear" + ) + k, ss = k3d.execute( + "points", + [2.5, 2.5, 2.5], + [2.5, 2.5, 2.5], + [1.0, 2.0, 3.0], + backend="vectorized", + ) + assert_allclose(k[0], 1.0, atol=0.01) + assert_allclose(k[1], 2.0, rtol=1.0e-2) + assert_allclose(k[2], 3.0, rtol=1.0e-2) + k, ss = k3d.execute( + "points", [2.5, 2.5, 2.5], [2.5, 2.5, 2.5], [1.0, 2.0, 3.0], backend="loop" + ) + assert_allclose(k[0], 1.0, atol=0.01) + assert_allclose(k[1], 2.0, rtol=1.0e-2) + assert_allclose(k[2], 3.0, rtol=1.0e-2) + + +def test_uk3d_execute(sample_data_3d): + + data, (gridx_ref, gridy_ref, gridz_ref), mask_ref = sample_data_3d + + uk3d = UniversalKriging3D(data[:, 0], data[:, 1], data[:, 2], data[:, 3]) + + with pytest.raises(ValueError): + uk3d.execute("blurg", gridx_ref, gridy_ref, gridz_ref) + + k, ss = uk3d.execute("grid", gridx_ref, gridy_ref, gridz_ref, backend="vectorized") + shape = (gridz_ref.size, gridy_ref.size, gridx_ref.size) + assert k.shape == shape + assert ss.shape == shape + assert np.amax(k) != np.amin(k) + assert np.amax(ss) != np.amin(ss) + assert not np.ma.is_masked(k) + + k, ss = uk3d.execute("grid", gridx_ref, gridy_ref, gridz_ref, backend="loop") + shape = (gridz_ref.size, gridy_ref.size, gridx_ref.size) + assert k.shape == shape + assert ss.shape == shape + assert np.amax(k) != np.amin(k) + assert np.amax(ss) != np.amin(ss) + assert not np.ma.is_masked(k) + + with pytest.raises(IOError): + uk3d.execute("masked", gridx_ref, gridy_ref, gridz_ref, backend="vectorized") + mask = np.array([True, False]) + with pytest.raises(ValueError): + uk3d.execute( + "masked", gridx_ref, gridy_ref, gridz_ref, mask=mask, backend="vectorized" + ) + k, ss = uk3d.execute( + "masked", gridx_ref, gridy_ref, gridz_ref, mask=mask_ref, backend="vectorized" + ) + assert np.ma.is_masked(k) + assert np.ma.is_masked(ss) + assert k[0, 0, 0] is np.ma.masked + assert ss[0, 0, 0] is np.ma.masked + z, ss = uk3d.execute( + "masked", gridx_ref, gridy_ref, gridz_ref, mask=mask_ref.T, backend="vectorized" + ) + assert np.ma.is_masked(z) + assert np.ma.is_masked(ss) + assert z[0, 0, 0] is np.ma.masked + assert ss[0, 0, 0] is np.ma.masked + + with pytest.raises(IOError): + uk3d.execute("masked", gridx_ref, gridy_ref, gridz_ref, backend="loop") + mask = np.array([True, False]) + with pytest.raises(ValueError): + uk3d.execute( + "masked", gridx_ref, gridy_ref, gridz_ref, mask=mask, backend="loop" + ) + k, ss = uk3d.execute( + "masked", gridx_ref, gridy_ref, gridz_ref, mask=mask_ref, backend="loop" + ) + assert np.ma.is_masked(k) + assert np.ma.is_masked(ss) + assert k[0, 0, 0] is np.ma.masked + assert ss[0, 0, 0] is np.ma.masked + z, ss = uk3d.execute( + "masked", gridx_ref, gridy_ref, gridz_ref, mask=mask_ref.T, backend="loop" + ) + assert np.ma.is_masked(z) + assert np.ma.is_masked(ss) + assert z[0, 0, 0] is np.ma.masked + assert ss[0, 0, 0] is np.ma.masked + + with pytest.raises(ValueError): + uk3d.execute( + "points", + np.array([0.0, 1.0, 2.0]), + np.array([0.0, 1.0]), + np.array([1.0]), + backend="vectorized", + ) + k, ss = uk3d.execute( + "points", gridx_ref[0], gridy_ref[0], gridz_ref[0], backend="vectorized" + ) + assert k.shape == (1,) + assert ss.shape == (1,) + + with pytest.raises(ValueError): + uk3d.execute( + "points", + np.array([0.0, 1.0, 2.0]), + np.array([0.0, 1.0]), + np.array([1.0]), + backend="loop", + ) + k, ss = uk3d.execute( + "points", gridx_ref[0], gridy_ref[0], gridz_ref[0], backend="loop" + ) + assert k.shape == (1,) + assert ss.shape == (1,) + + data = np.zeros((125, 4)) + z, y, x = np.meshgrid( + np.arange(0.0, 5.0, 1.0), np.arange(0.0, 5.0, 1.0), np.arange(0.0, 5.0, 1.0) + ) + data[:, 0] = np.ravel(x) + data[:, 1] = np.ravel(y) + data[:, 2] = np.ravel(z) + data[:, 3] = np.ravel(z) + k3d = UniversalKriging3D( + data[:, 0], data[:, 1], data[:, 2], data[:, 3], variogram_model="linear" + ) + k, ss = k3d.execute( + "grid", + np.arange(2.0, 3.0, 0.1), + np.arange(2.0, 3.0, 0.1), + np.arange(0.0, 4.0, 1.0), + backend="vectorized", + ) + assert_allclose(k[0, :, :], 0.0, atol=0.01) + assert_allclose(k[1, :, :], 1.0, rtol=1.0e-2) + assert_allclose(k[2, :, :], 2.0, rtol=1.0e-2) + assert_allclose(k[3, :, :], 3.0, rtol=1.0e-2) + k, ss = k3d.execute( + "grid", + np.arange(2.0, 3.0, 0.1), + np.arange(2.0, 3.0, 0.1), + np.arange(0.0, 4.0, 1.0), + backend="loop", + ) + assert_allclose(k[0, :, :], 0.0, atol=0.01) + assert_allclose(k[1, :, :], 1.0, rtol=1.0e-2) + assert_allclose(k[2, :, :], 2.0, rtol=1.0e-2) + assert_allclose(k[3, :, :], 3.0, rtol=1.0e-2) + k3d = UniversalKriging3D( + data[:, 0], data[:, 1], data[:, 2], data[:, 3], variogram_model="linear" + ) + k, ss = k3d.execute( + "points", + [2.5, 2.5, 2.5], + [2.5, 2.5, 2.5], + [1.0, 2.0, 3.0], + backend="vectorized", + ) + assert_allclose(k[0], 1.0, atol=0.01) + assert_allclose(k[1], 2.0, rtol=1.0e-2) + assert_allclose(k[2], 3.0, rtol=1.0e-2) + k, ss = k3d.execute( + "points", [2.5, 2.5, 2.5], [2.5, 2.5, 2.5], [1.0, 2.0, 3.0], backend="loop" + ) + assert_allclose(k[0], 1.0, atol=0.01) + assert_allclose(k[1], 2.0, rtol=1.0e-2) + assert_allclose(k[2], 3.0, rtol=1.0e-2) + + +def test_force_exact_3d(sample_data_3d): + + data, (gridx_ref, gridy_ref, gridz_ref), mask_ref = sample_data_3d + + k3d = OrdinaryKriging3D( + data[:, 0], data[:, 1], data[:, 2], data[:, 3], variogram_model="linear" + ) + k, ss = k3d.execute( + "grid", [0.1, 0.2, 0.3], [0.1, 0.2, 0.3], [0.1, 0.2, 0.3], backend="vectorized" + ) + assert k[2, 0, 0] == approx(0.9) + assert ss[2, 0, 0] == approx(0.0) + assert k[0, 2, 0] == approx(0.9) + assert ss[0, 2, 0] == approx(0.0) + assert k[1, 2, 2] == approx(0.7) + assert ss[1, 2, 2] == approx(0.0) + assert ss[2, 2, 2] != approx(0.0) + assert ss[0, 0, 0] != approx(0.0) + + k, ss = k3d.execute( + "grid", [0.1, 0.2, 0.3], [0.1, 0.2, 0.3], [0.1, 0.2, 0.3], backend="loop" + ) + assert k[2, 0, 0] == approx(0.9) + assert ss[2, 0, 0] == approx(0.0) + assert k[0, 2, 0] == approx(0.9) + assert ss[0, 2, 0] == approx(0.0) + assert k[1, 2, 2] == approx(0.7) + assert ss[1, 2, 2] == approx(0.0) + assert ss[2, 2, 2] != approx(0.0) + assert ss[0, 0, 0] != approx(0.0) + + k3d = UniversalKriging3D( + data[:, 0], data[:, 1], data[:, 2], data[:, 3], variogram_model="linear" + ) + k, ss = k3d.execute( + "grid", [0.1, 0.2, 0.3], [0.1, 0.2, 0.3], [0.1, 0.2, 0.3], backend="vectorized" + ) + assert k[2, 0, 0] == approx(0.9) + assert ss[2, 0, 0] == approx(0.0) + assert k[0, 2, 0] == approx(0.9) + assert ss[0, 2, 0] == approx(0.0) + assert k[1, 2, 2] == approx(0.7) + assert ss[1, 2, 2] == approx(0.0) + assert ss[2, 2, 2] != approx(0.0) + assert ss[0, 0, 0] != approx(0.0) + + k, ss = k3d.execute( + "grid", [0.1, 0.2, 0.3], [0.1, 0.2, 0.3], [0.1, 0.2, 0.3], backend="loop" + ) + assert k[2, 0, 0] == approx(0.9) + assert ss[2, 0, 0] == approx(0.0) + assert k[0, 2, 0] == approx(0.9) + assert ss[0, 2, 0] == approx(0.0) + assert k[1, 2, 2] == approx(0.7) + assert ss[1, 2, 2] == approx(0.0) + assert ss[2, 2, 2] != approx(0.0) + assert ss[0, 0, 0] != approx(0.0) + + +def test_uk3d_specified_drift(sample_data_3d): + + data, (gridx_ref, gridy_ref, gridz_ref), mask_ref = sample_data_3d + + zg, yg, xg = np.meshgrid(gridz_ref, gridy_ref, gridx_ref, indexing="ij") + + with pytest.raises(ValueError): + UniversalKriging3D( + data[:, 0], + data[:, 1], + data[:, 2], + data[:, 3], + variogram_model="linear", + drift_terms=["specified"], + ) + with pytest.raises(TypeError): + UniversalKriging3D( + data[:, 0], + data[:, 1], + data[:, 2], + data[:, 3], + variogram_model="linear", + drift_terms=["specified"], + specified_drift=data[:, 0], + ) + with pytest.raises(ValueError): + UniversalKriging3D( + data[:, 0], + data[:, 1], + data[:, 2], + data[:, 3], + variogram_model="linear", + drift_terms=["specified"], + specified_drift=[data[:2, 0]], + ) + + uk_spec = UniversalKriging3D( + data[:, 0], + data[:, 1], + data[:, 2], + data[:, 3], + variogram_model="linear", + drift_terms=["specified"], + specified_drift=[data[:, 0], data[:, 1], data[:, 2]], + ) + with pytest.raises(ValueError): + uk_spec.execute( + "grid", + gridx_ref, + gridy_ref, + gridz_ref, + specified_drift_arrays=[gridx_ref, gridy_ref, gridz_ref], + ) + with pytest.raises(TypeError): + uk_spec.execute( + "grid", gridx_ref, gridy_ref, gridz_ref, specified_drift_arrays=gridx_ref + ) + with pytest.raises(ValueError): + uk_spec.execute( + "grid", gridx_ref, gridy_ref, gridz_ref, specified_drift_arrays=[zg] + ) + z_spec, ss_spec = uk_spec.execute( + "grid", gridx_ref, gridy_ref, gridz_ref, specified_drift_arrays=[xg, yg, zg] + ) + + uk_lin = UniversalKriging3D( + data[:, 0], + data[:, 1], + data[:, 2], + data[:, 3], + variogram_model="linear", + drift_terms=["regional_linear"], + ) + z_lin, ss_lin = uk_lin.execute("grid", gridx_ref, gridy_ref, gridz_ref) + + assert_allclose(z_spec, z_lin) + assert_allclose(ss_spec, ss_lin) + + +def test_uk3d_functional_drift(sample_data_3d): + + data, (gridx, gridy, gridz), mask_ref = sample_data_3d + + func_x = lambda x, y, z: x # noqa + func_y = lambda x, y, z: y # noqa + func_z = lambda x, y, z: z # noqa + + with pytest.raises(ValueError): + UniversalKriging3D( + data[:, 0], + data[:, 1], + data[:, 2], + data[:, 3], + variogram_model="linear", + drift_terms=["functional"], + ) + with pytest.raises(TypeError): + UniversalKriging3D( + data[:, 0], + data[:, 1], + data[:, 2], + data[:, 3], + variogram_model="linear", + drift_terms=["functional"], + functional_drift=func_x, + ) + + uk_func = UniversalKriging3D( + data[:, 0], + data[:, 1], + data[:, 2], + data[:, 3], + variogram_model="linear", + drift_terms=["functional"], + functional_drift=[func_x, func_y, func_z], + ) + z_func, ss_func = uk_func.execute("grid", gridx, gridy, gridz) + uk_lin = UniversalKriging3D( + data[:, 0], + data[:, 1], + data[:, 2], + data[:, 3], + variogram_model="linear", + drift_terms=["regional_linear"], + ) + z_lin, ss_lin = uk_lin.execute("grid", gridx, gridy, gridz) + assert_allclose(z_func, z_lin) + assert_allclose(ss_func, ss_lin) + + +def test_geometric_code(): + + # Create selected points distributed across the sphere: + N = 4 + lon = np.array([7.0, 7.0, 187.0, 73.231]) + lat = np.array([13.23, 13.2301, -13.23, -79.3]) + + # For the points generated with this reference seed, the distance matrix + # has been calculated using geopy (v. 1.11.0) as follows: + # >>> from geopy.distance import great_circle + # >>> g = great_circle(radius=1.0) + # >>> d = np.zeros((N,N), dtype=float) + # >>> for i in range(N): + # >>> for j in range(N): + # >>> d[i,j] = g.measure((lat[i],lon[i]),(lat[j],lon[j])) + # >>> d *= 180.0/np.pi + # From that distance matrix, the reference values have been obtained. + d_ref = np.array( + [ + [0.0, 1e-4, 180.0, 98.744848317171801], + [1e-4, 0.0, 179.9999, 98.744946828324345], + [180.0, 179.9999, 0.0, 81.255151682828213], + [98.744848317171801, 98.744946828324345, 81.255151682828213, 0.0], + ] + ) + + # Calculate distance matrix using the PyKrige code: + d = np.zeros((N, N)) + for i in range(N): + for j in range(N): + d[i, j] = core.great_circle_distance(lon[i], lat[i], lon[j], lat[j]) + + # Test agains reference values: + assert_allclose(d, d_ref) + + # Test general features: + assert_allclose(d[np.eye(N, dtype=bool)], 0.0) + np.testing.assert_equal(d >= 0.0, np.ones((N, N), dtype=bool)) + assert_allclose(d, d.T) + np.testing.assert_equal(d <= 180.0, np.ones((N, N), dtype=bool)) + + # Test great_circle_distance and euclid3_to_great_circle against each other + lon_ref = lon + lat_ref = lat + for i in range(len(lon_ref)): + lon, lat = np.meshgrid(np.linspace(0, 360.0, 20), np.linspace(-90.0, 90.0, 20)) + dx = np.cos(np.pi / 180.0 * lon) * np.cos(np.pi / 180.0 * lat) - np.cos( + np.pi / 180.0 * lon_ref[i] + ) * np.cos(np.pi / 180.0 * lat_ref[i]) + dy = np.sin(np.pi / 180.0 * lon) * np.cos(np.pi / 180.0 * lat) - np.sin( + np.pi / 180.0 * lon_ref[i] + ) * np.cos(np.pi / 180.0 * lat_ref[i]) + dz = np.sin(np.pi / 180.0 * lat) - np.sin(np.pi / 180.0 * lat_ref[i]) + assert_allclose( + core.great_circle_distance(lon_ref[i], lat_ref[i], lon, lat), + core.euclid3_to_great_circle(np.sqrt(dx ** 2 + dy ** 2 + dz ** 2)), + rtol=1e-5, + ) + + +def test_ok_geographic(): + # Generate random data: + np.random.seed(89239413) + lon = 360.0 * np.random.rand(50, 1) + lat = 180.0 * np.random.rand(50, 1) - 90.0 + z = np.random.rand(50, 1) + + # Generate grid: + grid_lon = 360.0 * np.random.rand(120, 1) + grid_lat = 180.0 * np.random.rand(120, 1) - 90.0 + + # Create ordinary kriging object: + OK = OrdinaryKriging( + lon, + lat, + z, + variogram_model="linear", + verbose=False, + enable_plotting=False, + coordinates_type="geographic", + ) + + # Execute on grid: + z, ss = OK.execute("grid", grid_lon, grid_lat) + + +def test_ok_geographic_vs_euclid(): + # Generate some random data close to the north pole. + # Then we use a polar projected 2d euclidean coordinate + # system and compare the kriging results in that coordinate + # system with the geographic-option results. + # If data point distance to the north pole is small enough + # (choose maximum 0.01 degrees), the differences due to curvature + # should be negligible. + np.random.seed(89239413) + from_north = 1e-2 * np.random.random(5) + lat = 90.0 - from_north + lon = 360.0 * np.random.random(5) + z = np.random.random(5) + z -= z.mean() + x = from_north * np.cos(np.deg2rad(lon)) + y = from_north * np.sin(np.deg2rad(lon)) + + # Generate grids: + grid_lon = 360.0 * np.linspace(0, 1, 50) + grid_from_north = np.linspace(0, 0.01, 10) + grid_lat = 90.0 - grid_from_north + grid_x = grid_from_north[:, np.newaxis] * np.cos( + np.deg2rad(grid_lon[np.newaxis, :]) + ) + grid_y = grid_from_north[:, np.newaxis] * np.sin( + np.deg2rad(grid_lon[np.newaxis, :]) + ) + grid_lon, grid_lat = np.meshgrid(grid_lon, grid_lat, indexing="xy") + + # Flatten the grids: + grid_x = grid_x.flatten() + grid_y = grid_y.flatten() + grid_lon = grid_lon.flatten() + grid_lat = grid_lat.flatten() + + # Calculate and compare distance matrices ensuring that that part + # of the workflow works as intended (tested: 2e-9 is currently the + # match for this setup): + d_eucl = cdist( + np.concatenate([x[:, np.newaxis], y[:, np.newaxis]], axis=1), + np.concatenate([grid_x[:, np.newaxis], grid_y[:, np.newaxis]], axis=1), + ) + d_geo = core.great_circle_distance( + lon[:, np.newaxis], + lat[:, np.newaxis], + grid_lon[np.newaxis, :], + grid_lat[np.newaxis, :], + ) + assert_allclose(d_eucl, d_geo, rtol=2e-9) + + # Create ordinary kriging objects: + OK_geo = OrdinaryKriging( + lon, + lat, + z, + variogram_model="linear", + verbose=False, + enable_plotting=False, + coordinates_type="geographic", + ) + OK_xy = OrdinaryKriging( + x, y, z, variogram_model="linear", verbose=False, enable_plotting=False + ) + OK_wrong = OrdinaryKriging( + lon, lat, z, variogram_model="linear", verbose=False, enable_plotting=False + ) + + # Execute on grid: + zgeo, ss = OK_geo.execute("points", grid_lon, grid_lat) + zxy, ss = OK_xy.execute("points", grid_x, grid_y) + zwrong, ss = OK_wrong.execute("points", grid_lon, grid_lat) + + # Assert equivalence / difference (tested: 2e-5 is currently the + # match for this setup): + assert_allclose(zgeo, zxy, rtol=2e-5) + assert not np.any(zgeo == 0) + assert np.abs((zgeo - zwrong) / zgeo).max() > 1.0 + + +def test_ok_geometric_closest_points(): + # Generate random data: + np.random.seed(89239413) + lon = 360.0 * np.random.rand(50, 1) + lat = 180.0 * np.random.rand(50, 1) - 90.0 + z = np.random.rand(50, 1) + + # Generate grid: + grid_lon = 360.0 * np.random.rand(120, 1) + grid_lat = 180.0 * np.random.rand(120, 1) - 90.0 + + # Create ordinary kriging object: + OK = OrdinaryKriging( + lon, + lat, + z, + variogram_model="linear", + verbose=False, + enable_plotting=False, + coordinates_type="geographic", + ) + + # Execute on grid: + with pytest.raises(ValueError): + # Test OK raising ValueError when closest_points == 1: + z, ss = OK.execute("grid", grid_lon, grid_lat, n_closest_points=1, backend="C") + z, ss = OK.execute("grid", grid_lon, grid_lat, n_closest_points=5, backend="C") + + +@pytest.mark.parametrize("model", [OrdinaryKriging, UniversalKriging]) +def test_gstools_variogram(model): + gstools = pytest.importorskip("gstools") + # test data + data = np.array( + [ + [0.3, 1.2, 0.47], + [1.9, 0.6, 0.56], + [1.1, 3.2, 0.74], + [3.3, 4.4, 1.47], + [4.7, 3.8, 1.74], + ] + ) + gridx = np.arange(0.0, 5.5, 0.1) + gridy = np.arange(0.0, 6.5, 0.1) + # a GSTools based covariance model + cov_model = gstools.Gaussian( + dim=2, len_scale=1, anis=0.2, angles=-0.5, var=0.5, nugget=0.1 + ) + # create the krige field + krige = model(data[:, 0], data[:, 1], data[:, 2], cov_model) + z1, ss1 = krige.execute("grid", gridx, gridy) + # check if the field coincides with the data + for i in range(5): + y_id = int(data[i, 1] * 10) + x_id = int(data[i, 0] * 10) + assert np.isclose(z1[y_id, x_id], data[i, 2]) diff --git a/pykrige/tests/test_data/test1_answer.asc b/tests/test_data/test1_answer.asc similarity index 100% rename from pykrige/tests/test_data/test1_answer.asc rename to tests/test_data/test1_answer.asc diff --git a/pykrige/tests/test_data/test1_settings.txt b/tests/test_data/test1_settings.txt similarity index 100% rename from pykrige/tests/test_data/test1_settings.txt rename to tests/test_data/test1_settings.txt diff --git a/pykrige/tests/test_data/test2_answer.asc b/tests/test_data/test2_answer.asc similarity index 100% rename from pykrige/tests/test_data/test2_answer.asc rename to tests/test_data/test2_answer.asc diff --git a/pykrige/tests/test_data/test2_settings.txt b/tests/test_data/test2_settings.txt similarity index 100% rename from pykrige/tests/test_data/test2_settings.txt rename to tests/test_data/test2_settings.txt diff --git a/pykrige/tests/test_data/test3_answer.asc b/tests/test_data/test3_answer.asc similarity index 100% rename from pykrige/tests/test_data/test3_answer.asc rename to tests/test_data/test3_answer.asc diff --git a/pykrige/tests/test_data/test3_dem.asc b/tests/test_data/test3_dem.asc similarity index 100% rename from pykrige/tests/test_data/test3_dem.asc rename to tests/test_data/test3_dem.asc diff --git a/pykrige/tests/test_data/test3_settings.txt b/tests/test_data/test3_settings.txt similarity index 100% rename from pykrige/tests/test_data/test3_settings.txt rename to tests/test_data/test3_settings.txt diff --git a/pykrige/tests/test_data/test3d_answer.txt b/tests/test_data/test3d_answer.txt similarity index 100% rename from pykrige/tests/test_data/test3d_answer.txt rename to tests/test_data/test3d_answer.txt diff --git a/pykrige/tests/test_data/test3d_data.txt b/tests/test_data/test3d_data.txt similarity index 100% rename from pykrige/tests/test_data/test3d_data.txt rename to tests/test_data/test3d_data.txt diff --git a/pykrige/tests/test_data/test_data.txt b/tests/test_data/test_data.txt similarity index 100% rename from pykrige/tests/test_data/test_data.txt rename to tests/test_data/test_data.txt diff --git a/tests/test_regression_krige.py b/tests/test_regression_krige.py new file mode 100644 index 0000000..1eeeb21 --- /dev/null +++ b/tests/test_regression_krige.py @@ -0,0 +1,100 @@ +from itertools import product +import pytest + +import numpy as np + +from pykrige.rk import RegressionKriging + +try: + from sklearn.svm import SVR + from sklearn.datasets import fetch_california_housing + from sklearn.linear_model import ElasticNet, Lasso + from sklearn.ensemble import RandomForestRegressor + from sklearn.linear_model import LinearRegression + from pykrige.compat import train_test_split + + SKLEARN_INSTALLED = True +except ImportError: + SKLEARN_INSTALLED = False + + +def _methods(): + krige_methods = ["ordinary", "universal"] + ml_methods = [ + SVR(C=0.01, gamma="auto"), + RandomForestRegressor(min_samples_split=5, n_estimators=50), + LinearRegression(), + Lasso(), + ElasticNet(), + ] + return product(ml_methods, krige_methods) + + +@pytest.mark.skipif(not SKLEARN_INSTALLED, reason="requires scikit-learn") +def test_regression_krige(): + np.random.seed(1) + x = np.linspace(-1.0, 1.0, 100) + # create a feature matrix with 5 features + X = np.tile(x, reps=(5, 1)).T + y = ( + 1 + + 5 * X[:, 0] + - 2 * X[:, 1] + - 2 * X[:, 2] + + 3 * X[:, 3] + + 4 * X[:, 4] + + 2 * (np.random.rand(100) - 0.5) + ) + + # create lat/lon array + lon = np.linspace(-180.0, 180.0, 10) + lat = np.linspace(-90.0, 90.0, 10) + lon_lat = np.array(list(product(lon, lat))) + + X_train, X_test, y_train, y_test, lon_lat_train, lon_lat_test = train_test_split( + X, y, lon_lat, train_size=0.7, random_state=10 + ) + + for ml_model, krige_method in _methods(): + reg_kr_model = RegressionKriging( + regression_model=ml_model, method=krige_method, n_closest_points=2 + ) + reg_kr_model.fit(X_train, lon_lat_train, y_train) + assert reg_kr_model.score(X_test, lon_lat_test, y_test) > 0.25 + + +@pytest.mark.skipif(not SKLEARN_INSTALLED, reason="requires scikit-learn") +def test_krige_housing(): + import ssl + import urllib + + try: + housing = fetch_california_housing() + except (ssl.SSLError, urllib.error.URLError): + ssl._create_default_https_context = ssl._create_unverified_context + try: + housing = fetch_california_housing() + except PermissionError: + # This can raise permission error on Appveyor + pytest.skip("Failed to load california housing dataset") + ssl._create_default_https_context = ssl.create_default_context + + # take only first 1000 + p = housing["data"][:1000, :-2] + x = housing["data"][:1000, -2:] + target = housing["target"][:1000] + + p_train, p_test, y_train, y_test, x_train, x_test = train_test_split( + p, target, x, train_size=0.7, random_state=10 + ) + + for ml_model, krige_method in _methods(): + + reg_kr_model = RegressionKriging( + regression_model=ml_model, method=krige_method, n_closest_points=2 + ) + reg_kr_model.fit(p_train, x_train, y_train) + if krige_method == "ordinary": + assert reg_kr_model.score(p_test, x_test, y_test) > 0.5 + else: + assert reg_kr_model.score(p_test, x_test, y_test) > 0.0 diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 82c750a..0000000 --- a/tox.ini +++ /dev/null @@ -1,58 +0,0 @@ -[tox] -envlist = py34, py35, py27, py35-no_sklearn, py27-no_sklearn, py34-no_sklearn - -[testenv] -setenv = - PYTHONPATH = {toxinidir}:{toxinidir}/PyKrige -changedir = - pykrige -commands = - python test.py - - -[testenv:py35-no_sklearn] -deps = - numpy==1.11.2 - scipy==0.18.1 - cython - matplotlib - - -[testenv:py34-no_sklearn] -deps = - numpy==1.11.2 - scipy==0.18.1 - cython - matplotlib - -[testenv:py27-no_sklearn] -deps = - numpy==1.10.4 - scipy==0.18.1 - cython - matplotlib - -[testenv:py35] -deps = - numpy==1.11.2 - scipy==0.18.1 - cython - matplotlib - scikit-learn==0.18.1 - - -[testenv:py34] -deps = - numpy==1.10.4 - scipy==0.17.1 - cython - matplotlib - scikit-learn==0.17.1 - -[testenv:py27] -deps = - numpy==1.10.4 - scipy==0.17.1 - cython - matplotlib - scikit-learn==0.17.1 \ No newline at end of file