├── .github └── workflows │ └── main.yml ├── .gitignore ├── .readthedocs.yml ├── .zenodo.json ├── CHANGELOG.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── benchmarks └── kriging_benchmarks.py ├── docs ├── Makefile └── source │ ├── _templates │ ├── autosummary │ │ └── class.rst │ └── layout.html │ ├── api.rst │ ├── changelog.rst │ ├── conf.py │ ├── contents.rst │ ├── index.rst │ ├── pics │ ├── PyKrige.ico │ ├── PyKrige.png │ └── PyKrige_150.png │ ├── sphinxext │ └── github_link.py │ └── variogram_models.rst ├── examples ├── 00_ordinary.py ├── 01_universal.py ├── 02_kriging3D.py ├── 03_gstools_covmodel.py ├── 04_krige_geometric.py ├── 05_kriging_1D.py ├── 06_exact_values_example_1D.py ├── 07_regression_kriging2d.py ├── 08_krige_cv.py ├── 09_kriging_meuse.ipynb ├── 10_classification_kriging2d.py └── README.txt ├── pyproject.toml ├── setup.py ├── src └── pykrige │ ├── __init__.py │ ├── ck.py │ ├── compat.py │ ├── compat_gstools.py │ ├── core.py │ ├── kriging_tools.py │ ├── lib │ ├── __init__.py │ ├── cok.pyx │ ├── variogram_models.pxd │ └── variogram_models.pyx │ ├── ok.py │ ├── ok3d.py │ ├── rk.py │ ├── uk.py │ ├── uk3d.py │ └── variogram_models.py └── tests ├── test_api.py ├── test_classification_krige.py ├── test_core.py ├── test_data ├── test1_answer.asc ├── test1_settings.txt ├── test2_answer.asc ├── test2_settings.txt ├── test3_answer.asc ├── test3_dem.asc ├── test3_settings.txt ├── test3d_answer.txt ├── test3d_data.txt └── test_data.txt └── test_regression_krige.py /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | tags: 8 | - "*" 9 | pull_request: 10 | branches: 11 | - "main" 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | jobs: 16 | source_check: 17 | name: source check 18 | runs-on: ubuntu-latest 19 | strategy: 20 | fail-fast: false 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | 25 | - name: Set up Python 3.9 26 | uses: actions/setup-python@v5 27 | with: 28 | python-version: 3.9 29 | 30 | - name: Install dependencies 31 | run: | 32 | python -m pip install --upgrade pip 33 | pip install -v --editable .[lint] 34 | 35 | - name: black check 36 | run: | 37 | python -m black --check --diff --color . 38 | 39 | - name: isort check 40 | run: | 41 | python -m isort --check --diff --color . 42 | 43 | - name: cython-lint check 44 | run: | 45 | cython-lint src/pykrige/ 46 | 47 | build_wheels: 48 | name: wheels for ${{ matrix.os }} 49 | runs-on: ${{ matrix.os }} 50 | strategy: 51 | fail-fast: false 52 | matrix: 53 | # macos-13 is an intel runner, macos-14 is apple silicon 54 | os: [ubuntu-latest, windows-latest, macos-13, macos-14] 55 | 56 | steps: 57 | - uses: actions/checkout@v4 58 | with: 59 | fetch-depth: '0' 60 | 61 | - name: Build wheels 62 | uses: pypa/cibuildwheel@v2.18.0 63 | with: 64 | output-dir: dist 65 | 66 | - uses: actions/upload-artifact@v3 67 | with: 68 | path: ./dist/*.whl 69 | 70 | build_sdist: 71 | name: sdist on ${{ matrix.os }} with py ${{ matrix.ver.py }} numpy${{ matrix.ver.np }} scipy${{ matrix.ver.sp }} 72 | runs-on: ${{ matrix.os }} 73 | strategy: 74 | fail-fast: false 75 | matrix: 76 | os: [ubuntu-latest, windows-latest, macos-13, macos-14] 77 | # https://github.com/scipy/oldest-supported-numpy/blob/main/setup.cfg 78 | ver: 79 | - {py: '3.8', np: '==1.20.0', sp: '==1.5.4'} 80 | - {py: '3.9', np: '==1.20.0', sp: '==1.5.4'} 81 | - {py: '3.10', np: '==1.21.6', sp: '==1.7.2'} 82 | - {py: '3.11', np: '==1.23.2', sp: '==1.9.2'} 83 | - {py: '3.12', np: '==1.26.2', sp: '==1.11.2'} 84 | - {py: '3.12', np: '>=2.0.0rc1', sp: '>=1.13.0'} 85 | exclude: 86 | - os: macos-14 87 | ver: {py: '3.8', np: '==1.20.0', sp: '==1.5.4'} 88 | - os: macos-14 89 | ver: {py: '3.9', np: '==1.20.0', sp: '==1.5.4'} 90 | - os: macos-14 91 | ver: {py: '3.10', np: '==1.21.6', sp: '==1.7.2'} 92 | steps: 93 | - uses: actions/checkout@v4 94 | with: 95 | fetch-depth: '0' 96 | 97 | - name: Set up Python ${{ matrix.ver.py }} 98 | uses: actions/setup-python@v5 99 | with: 100 | python-version: ${{ matrix.ver.py }} 101 | 102 | - name: Install dependencies 103 | run: | 104 | python -m pip install --upgrade pip 105 | pip install build "coveralls>=3.0.0" 106 | 107 | - name: Install PyKrige 108 | run: | 109 | pip install -v --editable .[test] 110 | 111 | - name: Run tests 112 | env: 113 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 114 | run: | 115 | pip install "numpy${{ matrix.ver.np }}" "scipy${{ matrix.ver.sp }}" 116 | python -m pytest --cov pykrige --cov-report term-missing -v tests/ 117 | python -m coveralls --service=github 118 | 119 | - name: Build sdist 120 | run: | 121 | # PEP 517 package builder from pypa 122 | python -m build --sdist --outdir dist . 123 | 124 | - uses: actions/upload-artifact@v3 125 | if: matrix.os == 'ubuntu-latest' && matrix.ver.py == '3.9' 126 | with: 127 | path: dist/*.tar.gz 128 | 129 | upload_to_pypi: 130 | needs: [build_wheels, build_sdist] 131 | runs-on: ubuntu-latest 132 | 133 | steps: 134 | - uses: actions/download-artifact@v2 135 | with: 136 | name: artifact 137 | path: dist 138 | 139 | - name: Publish to Test PyPI 140 | if: github.ref == 'refs/heads/main' 141 | uses: pypa/gh-action-pypi-publish@release/v1 142 | with: 143 | user: __token__ 144 | password: ${{ secrets.test_pypi_password }} 145 | repository_url: https://test.pypi.org/legacy/ 146 | skip_existing: true 147 | 148 | - name: Publish to PyPI 149 | # only if tagged 150 | if: startsWith(github.ref, 'refs/tags') 151 | uses: pypa/gh-action-pypi-publish@release/v1 152 | with: 153 | user: __token__ 154 | password: ${{ secrets.pypi_password }} 155 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | !src/pykrige/lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | 58 | # Flask stuff: 59 | instance/ 60 | .webassets-cache 61 | 62 | # Scrapy stuff: 63 | .scrapy 64 | 65 | # Sphinx documentation 66 | docs/_build/ 67 | docs/output.txt 68 | docs/source/examples/ 69 | docs/source/generated/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # pyenv 78 | .python-version 79 | 80 | # celery beat schedule file 81 | celerybeat-schedule 82 | 83 | # SageMath parsed files 84 | *.sage.py 85 | 86 | # dotenv 87 | .env 88 | 89 | # virtualenv 90 | .venv 91 | venv/ 92 | ENV/ 93 | 94 | # Spyder project settings 95 | .spyderproject 96 | .spyproject 97 | 98 | # Rope project settings 99 | .ropeproject 100 | 101 | # mkdocs documentation 102 | /site 103 | 104 | # mypy 105 | .mypy_cache/ 106 | 107 | tags 108 | /test_* 109 | 110 | # own stuff 111 | info/ 112 | src/pykrige/_version.py 113 | .vscode/ 114 | 115 | # Cython generated C code 116 | *.c 117 | *.cpp 118 | 119 | # special 120 | *.DS_Store 121 | *.zip 122 | *.vtu 123 | *.vtr 124 | examples/meuse_example_data/* 125 | *.ipynb_checkpoints/* 126 | examples/output.asc 127 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-22.04 5 | tools: 6 | python: "3.11" 7 | 8 | sphinx: 9 | configuration: docs/source/conf.py 10 | 11 | formats: [pdf] 12 | 13 | python: 14 | install: 15 | - method: pip 16 | path: . 17 | extra_requirements: 18 | - doc 19 | -------------------------------------------------------------------------------- /.zenodo.json: -------------------------------------------------------------------------------- 1 | { 2 | "license": "BSD-3-Clause", 3 | "contributors": [ 4 | { 5 | "type": "Other", 6 | "name": "Malte Ziebarth" 7 | }, 8 | { 9 | "type": "Other", 10 | "name": "Sudipta Basak" 11 | }, 12 | { 13 | "type": "Other", 14 | "name": "Matthew Peveler" 15 | }, 16 | { 17 | "type": "Other", 18 | "name": "kvanlombeek" 19 | }, 20 | { 21 | "type": "Other", 22 | "name": "Will Chang" 23 | }, 24 | { 25 | "type": "Other", 26 | "name": "Harry Matchette-Downes" 27 | }, 28 | { 29 | "type": "Other", 30 | "name": "Daniel Mej\u00eda Raigosa" 31 | }, 32 | { 33 | "type": "Other", 34 | "name": "Marcelo Albuquerque" 35 | } 36 | ], 37 | "language": "eng", 38 | "keywords": [ 39 | "kriging", 40 | "ordinary kriging", 41 | "universal kriging", 42 | "external drift kriging", 43 | "regression kriging", 44 | "classification kriging", 45 | "variogram", 46 | "geostatistics", 47 | "Python", 48 | "GeoStat-Framework" 49 | ], 50 | "creators": [ 51 | { 52 | "orcid": "0000-0001-7636-3711", 53 | "affiliation": "United State Geological Survey: Golden, CO, US", 54 | "name": "Benjamin Murphy" 55 | }, 56 | { 57 | "orcid": "0000-0002-2565-4444", 58 | "affiliation": "Symerio", 59 | "name": "Roman Yurchak" 60 | }, 61 | { 62 | "orcid": "0000-0001-9060-4008", 63 | "affiliation": "Helmholtz Centre for Environmental Research - UFZ", 64 | "name": "Sebastian M\u00fcller" 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | 5 | Version 1.7.2 6 | ------------- 7 | *May 27, 2024* 8 | 9 | **New features** 10 | 11 | * added support for numpy 2 ([#290](https://github.com/GeoStat-Framework/PyKrige/pull/290)) 12 | 13 | **Changes** 14 | 15 | * remove universal2 wheels for macos (we already provide separate intel and arm64 wheels) ([#290](https://github.com/GeoStat-Framework/PyKrige/pull/290)) 16 | 17 | **Bug fixes** 18 | 19 | * fixed cython long / longlong issue on windows ([#290](https://github.com/GeoStat-Framework/PyKrige/issues/290)) 20 | 21 | 22 | Version 1.7.1 23 | ------------- 24 | *October 14, 2023* 25 | 26 | **New features** 27 | 28 | * added wheels for Python v3.11 and v3.12 ([#277](https://github.com/GeoStat-Framework/PyKrige/pull/277)) 29 | 30 | **Changes** 31 | 32 | * dropped Python 3.7 support ([#277](https://github.com/GeoStat-Framework/PyKrige/pull/277)) 33 | 34 | **Bug fixes** 35 | 36 | * fixed print statement in uk3d ([#272](https://github.com/GeoStat-Framework/PyKrige/issues/272)) 37 | * fixed exact_values behavior in C backend ([#256](https://github.com/GeoStat-Framework/PyKrige/pull/256)) 38 | 39 | 40 | Version 1.7.0 41 | ------------- 42 | *August 18, 2022* 43 | 44 | **New features** 45 | 46 | * added support for GSTools latlon models ([#245](https://github.com/GeoStat-Framework/PyKrige/pull/245)) 47 | 48 | **Changes** 49 | 50 | * drop Python 3.6 support (setuptools>60 needs py>3.7) ([#242](https://github.com/GeoStat-Framework/PyKrige/pull/242)) 51 | * move `setup.cfg` content to `pyproject.toml` ([PEP 621](https://peps.python.org/pep-0621/)) ([#242](https://github.com/GeoStat-Framework/PyKrige/pull/242)) 52 | * move to `src/` based package structure (better for testing, building and structure) ([#242](https://github.com/GeoStat-Framework/PyKrige/pull/242)) 53 | * build wheels for apple silicon ([#242](https://github.com/GeoStat-Framework/PyKrige/pull/242)) 54 | * apply isort and add check to CI ([#242](https://github.com/GeoStat-Framework/PyKrige/pull/242)) 55 | * fix documentation ([#242](https://github.com/GeoStat-Framework/PyKrige/pull/242), [#252](https://github.com/GeoStat-Framework/PyKrige/pull/252)) 56 | 57 | **Bug fixes** 58 | 59 | * fix AttributeError: 'UniversalKriging' object has no attribute 'external_Z_array' ([#247](https://github.com/GeoStat-Framework/PyKrige/pull/247)) 60 | * remove deprecated scipy (v1.9) method pinv2 ([#237](https://github.com/GeoStat-Framework/PyKrige/pull/237)) 61 | * correcting partial sill in C backend ([#226](https://github.com/GeoStat-Framework/PyKrige/pull/226)) 62 | 63 | 64 | Version 1.6.1 65 | ------------- 66 | *September 02, 2021* 67 | 68 | **New features** 69 | 70 | * IO routines for zmap files ([#199](https://github.com/GeoStat-Framework/PyKrige/pull/199)) 71 | * `write_asc_grid` got new keyword `no_data` ([#199](https://github.com/GeoStat-Framework/PyKrige/issues/199)) 72 | 73 | **Changes** 74 | 75 | * now using a `pyproject.toml` file ([#211](https://github.com/GeoStat-Framework/PyKrige/pull/211)) 76 | * now using a single `main` branch in the repository ([#212](https://github.com/GeoStat-Framework/PyKrige/pull/212)) 77 | * Fixed typos ([#188](https://github.com/GeoStat-Framework/PyKrige/pull/188), [#189](https://github.com/GeoStat-Framework/PyKrige/pull/189)) 78 | 79 | **Bug fixes** 80 | 81 | * `write_asc_grid` was to strict about dx ([#197](https://github.com/GeoStat-Framework/PyKrige/issues/197)) 82 | 83 | 84 | Version 1.6.0 85 | ------------- 86 | *April 04, 2021* 87 | 88 | **New features** 89 | 90 | * added Classification Kriging ([#165](https://github.com/GeoStat-Framework/PyKrige/pull/165), [#184](https://github.com/GeoStat-Framework/PyKrige/pull/184)) 91 | * added wheels for Python 3.9 ([#175](https://github.com/GeoStat-Framework/PyKrige/pull/175)) 92 | 93 | **Changes** 94 | 95 | * moved scikit-learn compat-class `Krige` to `pykrige.compat` ([#165](https://github.com/GeoStat-Framework/PyKrige/pull/165)) 96 | * dropped Python 3.5 support ([#183](https://github.com/GeoStat-Framework/PyKrige/pull/183)) 97 | * moved CI to GitHub-Actions ([#175](https://github.com/GeoStat-Framework/PyKrige/pull/175), [#183](https://github.com/GeoStat-Framework/PyKrige/pull/183)) 98 | * Fixed Typo in `02_kriging3D.py` example ([#182](https://github.com/GeoStat-Framework/PyKrige/pull/182)) 99 | 100 | 101 | Version 1.5.1 102 | ------------- 103 | *August 20, 2020* 104 | 105 | **New features** 106 | 107 | * update Regression Kriging class to be compatible with all kriging features (#158) 108 | * added option to enable/disable "exact values" to all kriging routines (#153) 109 | * added option to use the pseudo-inverse in all kriging routines (#151) 110 | 111 | **Changes** 112 | 113 | * removed compat-layer for sklearn (#157) 114 | * updated examples in documentation 115 | 116 | 117 | Version 1.5.0 118 | ------------- 119 | *April 04, 2020* 120 | 121 | **New features** 122 | 123 | * support for GSTools covariance models (#125) 124 | * pre-build wheels for py35-py38 on Linux, Windows and MacOS (#142) 125 | * GridSerachCV from the compat module sets iid=False by default (if present in sklearn) 126 | to be future prove (iid will be deprecated) (#144) 127 | 128 | **Changes** 129 | 130 | * dropped py2* and py<3.5 support (#142) 131 | * installation now requires cython (#142) 132 | * codebase was formatted with black (#144) 133 | * internally use of scipys lapack/blas bindings (#142) 134 | * PyKrige is now part of the GeoStat-Framework 135 | 136 | 137 | Version 1.4.1 138 | ------------- 139 | *January 13, 2019* 140 | 141 | **New features** 142 | 143 | * 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) 144 | 145 | **Bug fixes** 146 | 147 | * Fixed OrdinaryKriging readme example. PR[#107](https://github.com/GeoStat-Framework/PyKrige/pull/107) by [Harry Matchette-Downes](https://github.com/harrymd) 148 | * 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) 149 | 150 | 151 | Version 1.4.0 152 | ------------- 153 | *April 24, 2018* 154 | 155 | **New features** 156 | 157 | * Regression kriging algotithm. PR [#27](https://github.com/GeoStat-Framework/PyKrige/pull/27) by [Sudipta Basaks](https://github.com/basaks). 158 | * Support for spherical coordinates. PR [#23](https://github.com/GeoStat-Framework/PyKrige/pull/23) by [Malte Ziebarth](https://github.com/mjziebarth) 159 | * Kriging parameter tuning with scikit-learn. PR [#24](https://github.com/GeoStat-Framework/PyKrige/pull/24) by [Sudipta Basaks](https://github.com/basaks). 160 | * 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). 161 | 162 | **Enhancements** 163 | 164 | * Improved memory usage in variogram calculations. PR [#42](https://github.com/GeoStat-Framework/PyKrige/pull/42) by [Sudipta Basaks](https://github.com/basaks). 165 | * Added benchmark scripts. PR [#36](https://github.com/GeoStat-Framework/PyKrige/pull/36) by [Roman Yurchak](https://github.com/rth) 166 | * Added an extensive example using the meusegrids dataset. PR [#28](https://github.com/GeoStat-Framework/PyKrige/pull/28) by [kvanlombeek](https://github.com/kvanlombeek). 167 | 168 | **Bug fixes** 169 | 170 | * Statistics calculations in 3D kriging. PR [#45](https://github.com/GeoStat-Framework/PyKrige/pull/45) by [Will Chang](https://github.com/whdc). 171 | * Automatic variogram estimation robustified. PR [#47](https://github.com/GeoStat-Framework/PyKrige/pull/47) by [Benjamin Murphy](https://github.com/bsmurphy). 172 | 173 | 174 | Version 1.3.1 175 | ------------- 176 | *December 10, 2016* 177 | 178 | * More robust setup for building Cython extensions 179 | 180 | 181 | Version 1.3.0 182 | ------------- 183 | *October 23, 2015* 184 | 185 | * Added support for Python 3. 186 | * 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. 187 | 188 | * [closed issue #2]: https://github.com/GeoStat-Framework/PyKrige/issues/2 189 | * [this discussion of using Cython extensions on Windows]: https://github.com/cython/cython/wiki/CythonExtensionsOnWindows 190 | * [discussion in issue #10]: https://github.com/GeoStat-Framework/PyKrige/issues/10 191 | 192 | 193 | Version 1.2.0 194 | ------------- 195 | *August 1, 2015* 196 | 197 | * Updated the execution portion of each class to streamline processing and reduce redundancy in the code. 198 | * Integrated kriging with a moving window for two-dimensional ordinary kriging. Thanks to Roman Yurchak for this addition. This can be very useful for working with very large datasets, as it limits the size of the kriging matrix system. However, note that this approach can also produce unexpected oddities if the spatial covariance of the data does not decay quickly or if the window is too small. (See Kitanidis 1997 for a discussion of potential problems in kriging with a moving window; also see [closed issue #2] for a brief note about important considerations when kriging with a moving window.) 199 | * Integrated a Cython backend for two-dimensional ordinary kriging. Again, thanks to Roman Yurchak for this addition. Note that currently the Cython backend is only implemented for two-dimensional ordinary kriging; it is not implemented in any of the other kriging classes. (I'll gladly accept any pull requests to extend the Cython backend to the other classes.) 200 | * Implemented two new generic drift capabilities that should allow for use of arbitrary user-designed drifts. These generic drifts are referred to as 'specified' and 'functional' in the code. They are available for both two-dimensional and three-dimensional universal kriging (see below). With the 'specified' drift capability, the user specifies the values of the drift term at every data point and every point at which the kriging system is to be evaluated. With the 'functional' drift capability, the user provides callable function(s) of the two or three spatial coordinates that define the drift term(s). The functions must only take the spatial coordinates as arguments. An arbitrary number of 'specified' or 'functional' drift terms may be used. See `UniversalKriging.__doc__` or `UniversalKriging3D.__doc__` for more information. 201 | * Made a few changes to how the drift terms are implemented when the problem is anisotropic. The regional linear drift is applied in the adjusted coordinate frame. For the point logarithmic drift, the point coordinates are transformed into the adjusted coordinate frame and the drift values are calculated in the transformed frame. The external scalar drift values are extracted using the original (i.e., unadjusted) coordinates. Any functions that are used with the 'functional' drift capability are evaluated in the adjusted coordinate frame. Specified drift values are not adjusted as they are taken to be for the exact points provided. 202 | * 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. 203 | 204 | 205 | Version 1.1.0 206 | ------------- 207 | *May 25, 2015* 208 | 209 | * 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. 210 | * Added support for implementing custom variogram models. To do so, set `variogram_model` to `'custom'`. You must then also specify `variogram_parameters` as well as `variogram_function`, which must be a callable object that takes only two arguments, first a list of function parameters and then the distances at which to evaluate the variogram model. Note that currently the code will not automatically fit the custom variogram model to the data. You must provide the `variogram_parameters`, which will be passed to the callable `variogram_function` as the first argument. 211 | * Modified anisotropy rotation so that coordinate system is rotated CCW by specified angle. The sense of rotation for 2D kriging is now the opposite of what it was before. 212 | * 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. 213 | 214 | 215 | Version 1.0.3 216 | ------------- 217 | *February 15, 2015* 218 | 219 | * 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.) 220 | * Changed setup script. 221 | 222 | 223 | Version 1.0 224 | ----------- 225 | *January 25, 2015* 226 | 227 | * Changed license to New BSD. 228 | * Added support for point-specific and masked-grid kriging. Note that the arguments for the `OrdinaryKriging.execute()` and `UniversalKriging.execute()` methods have changed. 229 | * Changed semivariogram binning procedure. 230 | * Boosted execution speed by almost an order of magnitude. 231 | * Fixed some problems with the external drift capabilities. 232 | * Added more comprehensive testing script. 233 | * 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`. 234 | 235 | 236 | Version 0.2.0 237 | ------------- 238 | *November 23, 2014* 239 | 240 | * 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. 241 | * Fixed a few glaring mistakes in the code. 242 | * Added more documentation. 243 | 244 | 245 | Version 0.1.2 246 | ------------- 247 | *October 27, 2014* 248 | 249 | * First complete release. 250 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2024, PyKrige Developers 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of PyKrige nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | prune ** 2 | recursive-include tests *.py *.txt *.asc 3 | recursive-include src/pykrige *.py *.pyx *.pxd 4 | include LICENSE README.md pyproject.toml setup.py 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyKrige 2 | 3 | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3738604.svg)](https://doi.org/10.5281/zenodo.3738604) 4 | [![PyPI version](https://badge.fury.io/py/PyKrige.svg)](https://badge.fury.io/py/PyKrige) 5 | [![Conda Version](https://img.shields.io/conda/vn/conda-forge/pykrige.svg)](https://anaconda.org/conda-forge/pykrige) 6 | [![Build Status](https://github.com/GeoStat-Framework/PyKrige/workflows/Continuous%20Integration/badge.svg?branch=main)](https://github.com/GeoStat-Framework/PyKrige/actions) 7 | [![Coverage Status](https://coveralls.io/repos/github/GeoStat-Framework/PyKrige/badge.svg?branch=main)](https://coveralls.io/github/GeoStat-Framework/PyKrige?branch=main) 8 | [![Documentation Status](https://readthedocs.org/projects/pykrige/badge/?version=stable)](http://pykrige.readthedocs.io/en/stable/?badge=stable) 9 | [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) 10 | 11 | 12 |

13 | PyKrige-LOGO 14 |

15 | 16 | Kriging Toolkit for Python. 17 | 18 | ## Purpose 19 | 20 | The code supports 2D and 3D ordinary and universal kriging. Standard 21 | variogram models (linear, power, spherical, gaussian, exponential) are 22 | built in, but custom variogram models can also be used. The 2D universal 23 | kriging code currently supports regional-linear, point-logarithmic, and 24 | external drift terms, while the 3D universal kriging code supports a 25 | regional-linear drift term in all three spatial dimensions. Both 26 | universal kriging classes also support generic 'specified' and 27 | 'functional' drift capabilities. With the 'specified' drift capability, 28 | the user may manually specify the values of the drift(s) at each data 29 | point and all grid points. With the 'functional' drift capability, the 30 | user may provide callable function(s) of the spatial coordinates that 31 | define the drift(s). The package includes a module that contains 32 | functions that should be useful in working with ASCII grid files (`\*.asc`). 33 | 34 | See the documentation at for more 35 | details and examples. 36 | 37 | ## Installation 38 | 39 | PyKrige requires Python 3.5+ as well as numpy, scipy. It can be 40 | installed from PyPi with, 41 | 42 | ``` bash 43 | pip install pykrige 44 | ``` 45 | 46 | scikit-learn is an optional dependency needed for parameter tuning and 47 | regression kriging. matplotlib is an optional dependency needed for 48 | plotting. 49 | 50 | If you use conda, PyKrige can be installed from the conda-forge channel with, 52 | 53 | ``` bash 54 | conda install -c conda-forge pykrige 55 | ``` 56 | 57 | ## Features 58 | 59 | ### Kriging algorithms 60 | 61 | - `OrdinaryKriging`: 2D ordinary kriging with estimated mean 62 | - `UniversalKriging`: 2D universal kriging providing drift terms 63 | - `OrdinaryKriging3D`: 3D ordinary kriging 64 | - `UniversalKriging3D`: 3D universal kriging 65 | - `RegressionKriging`: An implementation of Regression-Kriging 66 | - `ClassificationKriging`: An implementation of Simplicial Indicator 67 | Kriging 68 | 69 | ### Wrappers 70 | 71 | - `rk.Krige`: A scikit-learn wrapper class for Ordinary and Universal 72 | Kriging 73 | 74 | ### Tools 75 | 76 | - `kriging_tools.write_asc_grid`: Writes gridded data to ASCII grid file (`\*.asc`) 77 | - `kriging_tools.read_asc_grid`: Reads ASCII grid file (`\*.asc`) 78 | - `kriging_tools.write_zmap_grid`: Writes gridded data to zmap file (`\*.zmap`) 79 | - `kriging_tools.read_zmap_grid`: Reads zmap file (`\*.zmap`) 80 | 81 | ### Kriging Parameters Tuning 82 | 83 | A scikit-learn compatible API for parameter tuning by cross-validation 84 | is exposed in 85 | [sklearn.model\_selection.GridSearchCV](http://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html). 86 | See the [Krige 87 | CV](http://pykrige.readthedocs.io/en/latest/examples/08_krige_cv.html#sphx-glr-examples-08-krige-cv-py) 88 | example for a more practical illustration. 89 | 90 | ### Regression Kriging 91 | 92 | [Regression kriging](https://en.wikipedia.org/wiki/Regression-Kriging) 93 | can be performed with 94 | [pykrige.rk.RegressionKriging](http://pykrige.readthedocs.io/en/latest/examples/07_regression_kriging2d.html). 95 | This class takes as parameters a scikit-learn regression model, and 96 | details of either the `OrdinaryKriging` or the `UniversalKriging` 97 | class, and performs a correction step on the ML regression prediction. 98 | 99 | A demonstration of the regression kriging is provided in the 100 | [corresponding 101 | example](http://pykrige.readthedocs.io/en/latest/examples/07_regression_kriging2d.html#sphx-glr-examples-07-regression-kriging2d-py). 102 | 103 | ### Classification Kriging 104 | 105 | [Simplifical Indicator 106 | kriging](https://www.sciencedirect.com/science/article/abs/pii/S1002070508600254) 107 | can be performed with 108 | [pykrige.ck.ClassificationKriging](http://pykrige.readthedocs.io/en/latest/examples/10_classification_kriging2d.html). 109 | This class takes as parameters a scikit-learn classification model, and 110 | details of either the `OrdinaryKriging` or the `UniversalKriging` class, 111 | and performs a correction step on the ML classification prediction. 112 | 113 | A demonstration of the classification kriging is provided in the 114 | [corresponding 115 | example](http://pykrige.readthedocs.io/en/latest/examples/10_classification_kriging2d.html#sphx-glr-examples-10-classification-kriging2d-py). 116 | 117 | ## License 118 | 119 | PyKrige uses the BSD 3-Clause License. 120 | -------------------------------------------------------------------------------- /benchmarks/kriging_benchmarks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Benchmarks.""" 3 | from time import time 4 | 5 | import numpy as np 6 | 7 | from pykrige.ok import OrdinaryKriging 8 | 9 | np.random.seed(19999) 10 | 11 | VARIOGRAM_MODELS = ["power", "gaussian", "spherical", "exponential", "linear"] 12 | BACKENDS = ["vectorized", "loop", "C"] 13 | N_MOVING_WINDOW = [None, 10, 50, 100] 14 | 15 | 16 | def make_benchark(n_train, n_test, n_dim=2): 17 | """Compute the benchmarks for Ordianry Kriging. 18 | 19 | Parameters 20 | ---------- 21 | n_train : int 22 | number of points in the training set 23 | n_test : int 24 | number of points in the test set 25 | n_dim : int 26 | number of dimensions (default=2) 27 | 28 | Returns 29 | ------- 30 | res : dict 31 | a dictionary with the timing results 32 | """ 33 | X_train = np.random.rand(n_train, n_dim) 34 | y_train = np.random.rand(n_train) 35 | X_test = np.random.rand(n_test, n_dim) 36 | 37 | res = {} 38 | 39 | for variogram_model in VARIOGRAM_MODELS: 40 | tic = time() 41 | OK = OrdinaryKriging( 42 | X_train[:, 0], 43 | X_train[:, 1], 44 | y_train, 45 | variogram_model="linear", 46 | verbose=False, 47 | enable_plotting=False, 48 | ) 49 | res["t_train_{}".format(variogram_model)] = time() - tic 50 | 51 | # All the following tests are performed with the linear variogram model 52 | for backend in BACKENDS: 53 | for n_closest_points in N_MOVING_WINDOW: 54 | if backend == "vectorized" and n_closest_points is not None: 55 | continue # this is not supported 56 | 57 | tic = time() 58 | OK.execute( 59 | "points", 60 | X_test[:, 0], 61 | X_test[:, 1], 62 | backend=backend, 63 | n_closest_points=n_closest_points, 64 | ) 65 | res["t_test_{}_{}".format(backend, n_closest_points)] = time() - tic 66 | 67 | return res 68 | 69 | 70 | def print_benchmark(n_train, n_test, n_dim, res): 71 | """Print the benchmarks. 72 | 73 | Parameters 74 | ---------- 75 | n_train : int 76 | number of points in the training set 77 | n_test : int 78 | number of points in the test set 79 | n_dim : int 80 | number of dimensions (default=2) 81 | res : dict 82 | a dictionary with the timing results 83 | """ 84 | print("=" * 80) 85 | print(" " * 10, "N_dim={}, N_train={}, N_test={}".format(n_dim, n_train, n_test)) 86 | print("=" * 80) 87 | print("\n", "# Training the model", "\n") 88 | print("|".join(["{:>11} ".format(el) for el in ["t_train (s)"] + VARIOGRAM_MODELS])) 89 | print("-" * (11 + 2) * (len(VARIOGRAM_MODELS) + 1)) 90 | print( 91 | "|".join( 92 | ["{:>11} ".format("Training")] 93 | + [ 94 | "{:>11.2} ".format(el) 95 | for el in [res["t_train_{}".format(mod)] for mod in VARIOGRAM_MODELS] 96 | ] 97 | ) 98 | ) 99 | 100 | print("\n", "# Predicting kriging points", "\n") 101 | print("|".join(["{:>11} ".format(el) for el in ["t_test (s)"] + BACKENDS])) 102 | print("-" * (11 + 2) * (len(BACKENDS) + 1)) 103 | 104 | for n_closest_points in N_MOVING_WINDOW: 105 | timing_results = [ 106 | res.get("t_test_{}_{}".format(mod, n_closest_points), "") 107 | for mod in BACKENDS 108 | ] 109 | print( 110 | "|".join( 111 | ["{:>11} ".format("N_nn=" + str(n_closest_points))] 112 | + ["{:>11.2} ".format(el) for el in timing_results] 113 | ) 114 | ) 115 | 116 | 117 | if __name__ == "__main__": 118 | for no_train, no_test in [(400, 1000), (400, 2000), (800, 2000)]: 119 | results = make_benchark(no_train, no_test) 120 | print_benchmark(no_train, no_test, 2, results) 121 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python3 -msphinx 7 | SPHINXPROJ = PyKrige 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/source/_templates/autosummary/class.rst: -------------------------------------------------------------------------------- 1 | {{ fullname | escape | underline}} 2 | 3 | .. currentmodule:: {{ module }} 4 | 5 | .. autoclass:: {{ objname }} 6 | :members: 7 | :undoc-members: 8 | :inherited-members: 9 | :show-inheritance: 10 | 11 | .. raw:: latex 12 | 13 | \clearpage 14 | -------------------------------------------------------------------------------- /docs/source/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | {% block menu %} 3 | 4 | {{ super() }} 5 |
6 | 7 | 12 |
13 | 14 | 20 |
21 |
22 | 26 | {% endblock %} 27 | -------------------------------------------------------------------------------- /docs/source/api.rst: -------------------------------------------------------------------------------- 1 | API Reference 2 | ============= 3 | 4 | .. currentmodule:: pykrige 5 | 6 | Krigging algorithms 7 | ------------------- 8 | 9 | 10 | .. autosummary:: 11 | :toctree: ./generated/ 12 | 13 | pykrige.ok.OrdinaryKriging 14 | pykrige.uk.UniversalKriging 15 | pykrige.ok3d.OrdinaryKriging3D 16 | pykrige.uk3d.UniversalKriging3D 17 | pykrige.rk.RegressionKriging 18 | 19 | 20 | Wrappers 21 | -------- 22 | 23 | .. autosummary:: 24 | :toctree: ./generated/ 25 | 26 | pykrige.rk.Krige 27 | 28 | 29 | Tools 30 | ----- 31 | 32 | .. autosummary:: 33 | :toctree: ./generated/ 34 | 35 | pykrige.kriging_tools.write_asc_grid 36 | pykrige.kriging_tools.read_asc_grid 37 | -------------------------------------------------------------------------------- /docs/source/changelog.rst: -------------------------------------------------------------------------------- 1 | 2 | .. mdinclude:: ../../CHANGELOG.md 3 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # PyKrige documentation build configuration file, created by 5 | # sphinx-quickstart on Wed Mar 1 18:34:53 2017. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | import datetime 16 | import os 17 | import shlex 18 | import sys 19 | 20 | import matplotlib 21 | import sphinx_rtd_theme 22 | 23 | matplotlib.use("Agg") 24 | 25 | 26 | # If extensions (or modules to document with autodoc) are in another directory, 27 | # add these directories to sys.path here. If the directory is relative to the 28 | # documentation root, use os.path.abspath to make it absolute, like shown here. 29 | # sys.path.insert(0, os.path.abspath("../../")) 30 | sys.path.insert(0, os.path.abspath("sphinxext")) 31 | 32 | from github_link import make_linkcode_resolve 33 | 34 | import pykrige 35 | 36 | # -- General configuration ------------------------------------------------ 37 | 38 | # If your documentation needs a minimal Sphinx version, state it here. 39 | # needs_sphinx = '1.0' 40 | 41 | # Add any Sphinx extension module names here, as strings. They can be 42 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 43 | # ones. 44 | extensions = [ 45 | "sphinx.ext.autodoc", 46 | "sphinx.ext.doctest", 47 | "sphinx.ext.mathjax", 48 | "sphinx.ext.autodoc", 49 | "sphinx.ext.autosummary", 50 | "sphinx.ext.napoleon", # parameters look better than with numpydoc only 51 | "numpydoc", 52 | "sphinx_gallery.gen_gallery", 53 | "sphinx.ext.linkcode", 54 | "m2r2", 55 | ] 56 | 57 | autodoc_default_flags = ["members", "inherited-members"] 58 | 59 | # autosummaries from source-files 60 | autosummary_generate = True 61 | # dont show __init__ docstring 62 | autoclass_content = "class" 63 | # sort class members 64 | autodoc_member_order = "groupwise" 65 | # autodoc_member_order = 'bysource' 66 | 67 | # Notes in boxes 68 | napoleon_use_admonition_for_notes = True 69 | # Attributes like parameters 70 | # napoleon_use_ivar = True 71 | # this is a nice class-doc layout 72 | numpydoc_show_class_members = True 73 | # class members have no separate file, so they are not in a toctree 74 | numpydoc_class_members_toctree = False 75 | # for the covmodels alot of classmembers show up... 76 | # maybe switch off with: :no-inherited-members: 77 | numpydoc_show_inherited_class_members = True 78 | # Add any paths that contain templates here, relative to this directory. 79 | templates_path = ["_templates"] 80 | 81 | # The suffix(es) of source filenames. 82 | # You can specify multiple suffix as a list of string: 83 | source_suffix = { 84 | ".rst": "restructuredtext", 85 | ".txt": "restructuredtext", 86 | ".md": "markdown", 87 | } 88 | 89 | # The encoding of source files. 90 | # source_encoding = 'utf-8-sig' 91 | 92 | # The master toctree document. 93 | master_doc = "contents" 94 | 95 | 96 | sphinx_gallery_conf = { 97 | # path to your examples scripts 98 | "examples_dirs": "../../examples", 99 | # path where to save gallery generated examples 100 | "gallery_dirs": "examples", 101 | "filename_pattern": "/.*.py", 102 | } 103 | 104 | # General information about the project. 105 | curr_year = datetime.datetime.now().year 106 | project = "PyKrige" 107 | copyright = "2017 - {}, PyKrige developers".format(curr_year) 108 | author = "PyKrige developers" 109 | 110 | # The version info for the project you're documenting, acts as replacement for 111 | # |version| and |release|, also used in various other places throughout the 112 | # built documents. 113 | # 114 | # The short X.Y version. 115 | version = pykrige.__version__ 116 | # The full version, including alpha/beta/rc tags. 117 | release = pykrige.__version__ 118 | 119 | # The language for content autogenerated by Sphinx. Refer to documentation 120 | # for a list of supported languages. 121 | # 122 | # This is also used if you do content translation via gettext catalogs. 123 | # Usually you set "language" from the command line for these cases. 124 | language = "en" 125 | 126 | # There are two options for replacing |today|: either, you set today to some 127 | # non-false value, then it is used: 128 | # today = '' 129 | # Else, today_fmt is used as the format for a strftime call. 130 | # today_fmt = '%B %d, %Y' 131 | 132 | # List of patterns, relative to source directory, that match files and 133 | # directories to ignore when looking for source files. 134 | exclude_patterns = ["_build"] 135 | 136 | # The reST default role (used for this markup: `text`) to use for all 137 | # documents. 138 | # default_role = None 139 | 140 | # If true, '()' will be appended to :func: etc. cross-reference text. 141 | # add_function_parentheses = True 142 | 143 | # If true, the current module name will be prepended to all description 144 | # unit titles (such as .. function::). 145 | # add_module_names = True 146 | 147 | # If true, sectionauthor and moduleauthor directives will be shown in the 148 | # output. They are ignored by default. 149 | # show_authors = False 150 | 151 | # The name of the Pygments (syntax highlighting) style to use. 152 | pygments_style = "sphinx" 153 | 154 | # A list of ignored prefixes for module index sorting. 155 | # modindex_common_prefix = [] 156 | 157 | # If true, keep warnings as "system message" paragraphs in the built documents. 158 | # keep_warnings = False 159 | 160 | # If true, `todo` and `todoList` produce output, else they produce nothing. 161 | todo_include_todos = False 162 | 163 | 164 | # -- Options for HTML output ---------------------------------------------- 165 | 166 | # The theme to use for HTML and HTML Help pages. See the documentation for 167 | # a list of builtin themes. 168 | html_theme = "sphinx_rtd_theme" 169 | 170 | # Theme options are theme-specific and customize the look and feel of a theme 171 | # further. For a list of options available for each theme, see the 172 | # documentation. 173 | # html_theme_options = {} 174 | 175 | # Add any paths that contain custom themes here, relative to this directory. 176 | # html_theme_path = [] 177 | 178 | # The name for this set of Sphinx documents. If None, it defaults to 179 | # " v documentation". 180 | # html_title = None 181 | 182 | # A shorter title for the navigation bar. Default is the same as html_title. 183 | # html_short_title = None 184 | 185 | # The name of an image file (relative to this directory) to place at the top 186 | # of the sidebar. 187 | # html_logo = None 188 | 189 | # The name of an image file (within the static path) to use as favicon of the 190 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 191 | # pixels large. 192 | # html_favicon = None 193 | 194 | # Add any paths that contain custom static files (such as style sheets) here, 195 | # relative to this directory. They are copied after the builtin static files, 196 | # so a file named "default.css" will overwrite the builtin "default.css". 197 | # html_static_path = ["_static"] 198 | 199 | # Add any extra paths that contain custom files (such as robots.txt or 200 | # .htaccess) here, relative to this directory. These files are copied 201 | # directly to the root of the documentation. 202 | # html_extra_path = [] 203 | 204 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 205 | # using the given strftime format. 206 | # html_last_updated_fmt = '%b %d, %Y' 207 | 208 | # If true, SmartyPants will be used to convert quotes and dashes to 209 | # typographically correct entities. 210 | # html_use_smartypants = True 211 | 212 | # Custom sidebar templates, maps document names to template names. 213 | # html_sidebars = {} 214 | 215 | # Additional templates that should be rendered to pages, maps page names to 216 | # template names. 217 | # html_additional_pages = {} 218 | 219 | # If false, no module index is generated. 220 | # html_domain_indices = True 221 | 222 | # If false, no index is generated. 223 | # html_use_index = True 224 | 225 | # If true, the index is split into individual pages for each letter. 226 | # html_split_index = False 227 | 228 | # If true, links to the reST sources are added to the pages. 229 | html_show_sourcelink = True 230 | 231 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 232 | # html_show_sphinx = True 233 | 234 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 235 | # html_show_copyright = True 236 | 237 | # If true, an OpenSearch description file will be output, and all pages will 238 | # contain a tag referring to it. The value of this option must be the 239 | # base URL from which the finished HTML is served. 240 | # html_use_opensearch = '' 241 | 242 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 243 | # html_file_suffix = None 244 | 245 | # Language to be used for generating the HTML full-text search index. 246 | # Sphinx supports the following languages: 247 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' 248 | # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' 249 | # html_search_language = 'en' 250 | 251 | # A dictionary with options for the search language support, empty by default. 252 | # Now only 'ja' uses this config value 253 | # html_search_options = {'type': 'default'} 254 | 255 | # The name of a javascript file (relative to the configuration directory) that 256 | # implements a search results scorer. If empty, the default will be used. 257 | # html_search_scorer = 'scorer.js' 258 | 259 | # Output file base name for HTML help builder. 260 | htmlhelp_basename = "PyKrigedoc" 261 | html_logo = "pics/PyKrige_150.png" 262 | html_favicon = "pics/PyKrige.ico" 263 | 264 | # -- Options for LaTeX output --------------------------------------------- 265 | 266 | latex_logo = "pics/PyKrige_150.png" 267 | 268 | latex_elements = { 269 | # The paper size ('letterpaper' or 'a4paper'). 270 | #'papersize': 'letterpaper', 271 | # The font size ('10pt', '11pt' or '12pt'). 272 | #'pointsize': '10pt', 273 | # Additional stuff for the LaTeX preamble. 274 | #'preamble': '', 275 | # Latex figure (float) alignment 276 | #'figure_align': 'htbp', 277 | } 278 | 279 | # Grouping the document tree into LaTeX files. List of tuples 280 | # (source start file, target name, title, 281 | # author, documentclass [howto, manual, or own class]). 282 | latex_documents = [ 283 | (master_doc, "PyKrige.tex", "PyKrige Documentation", "PyKrige developers", "manual") 284 | ] 285 | 286 | # The name of an image file (relative to this directory) to place at the top of 287 | # the title page. 288 | # latex_logo = None 289 | 290 | # For "manual" documents, if this is true, then toplevel headings are parts, 291 | # not chapters. 292 | # latex_use_parts = False 293 | 294 | # If true, show page references after internal links. 295 | # latex_show_pagerefs = False 296 | 297 | # If true, show URL addresses after external links. 298 | # latex_show_urls = False 299 | 300 | # Documents to append as an appendix to all manuals. 301 | # latex_appendices = [] 302 | 303 | # If false, no module index is generated. 304 | # latex_domain_indices = True 305 | 306 | 307 | # -- Options for manual page output --------------------------------------- 308 | 309 | # One entry per manual page. List of tuples 310 | # (source start file, name, description, authors, manual section). 311 | man_pages = [(master_doc, "pykrige", "PyKrige Documentation", [author], 1)] 312 | 313 | # If true, show URL addresses after external links. 314 | # man_show_urls = False 315 | 316 | 317 | # -- Options for Texinfo output ------------------------------------------- 318 | 319 | # Grouping the document tree into Texinfo files. List of tuples 320 | # (source start file, target name, title, author, 321 | # dir menu entry, description, category) 322 | texinfo_documents = [ 323 | ( 324 | master_doc, 325 | "PyKrige", 326 | "PyKrige Documentation", 327 | author, 328 | "PyKrige", 329 | "One line description of project.", 330 | "Miscellaneous", 331 | ) 332 | ] 333 | 334 | # Documents to append as an appendix to all manuals. 335 | # texinfo_appendices = [] 336 | 337 | # If false, no module index is generated. 338 | # texinfo_domain_indices = True 339 | 340 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 341 | # texinfo_show_urls = 'footnote' 342 | 343 | # If true, do not generate a @detailmenu in the "Top" node's menu. 344 | # texinfo_no_detailmenu = False 345 | # The following is used by sphinx.ext.linkcode to provide links to github 346 | linkcode_resolve = make_linkcode_resolve( 347 | "pykrige", 348 | "https://github.com/GeoStat-Framework/" 349 | "PyKrige/blob/{revision}/" 350 | "{package}/{path}#L{lineno}", 351 | ) 352 | -------------------------------------------------------------------------------- /docs/source/contents.rst: -------------------------------------------------------------------------------- 1 | .. PyKrige documentation master file, created by 2 | sphinx-quickstart on Wed Mar 1 18:34:53 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | 7 | Contents 8 | ^^^^^^^^ 9 | 10 | .. toctree:: 11 | :maxdepth: 3 12 | 13 | index 14 | variogram_models 15 | api 16 | examples/index 17 | changelog 18 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | 2 | .. mdinclude:: ../../README.md 3 | -------------------------------------------------------------------------------- /docs/source/pics/PyKrige.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeoStat-Framework/PyKrige/e02baad442ac99b22f038b09b6290e7abacc17ae/docs/source/pics/PyKrige.ico -------------------------------------------------------------------------------- /docs/source/pics/PyKrige.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeoStat-Framework/PyKrige/e02baad442ac99b22f038b09b6290e7abacc17ae/docs/source/pics/PyKrige.png -------------------------------------------------------------------------------- /docs/source/pics/PyKrige_150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeoStat-Framework/PyKrige/e02baad442ac99b22f038b09b6290e7abacc17ae/docs/source/pics/PyKrige_150.png -------------------------------------------------------------------------------- /docs/source/sphinxext/github_link.py: -------------------------------------------------------------------------------- 1 | # Adapted from scikit learn 2 | 3 | import inspect 4 | import os 5 | import subprocess 6 | import sys 7 | from functools import partial 8 | from operator import attrgetter 9 | 10 | REVISION_CMD = "git rev-parse --short HEAD" 11 | 12 | 13 | def _get_git_revision(): 14 | try: 15 | revision = subprocess.check_output(REVISION_CMD.split()).strip() 16 | except (subprocess.CalledProcessError, OSError): 17 | print("Failed to execute git to get revision") 18 | return None 19 | return revision.decode("utf-8") 20 | 21 | 22 | def _linkcode_resolve(domain, info, package, url_fmt, revision): 23 | """Determine a link to online source for a class/method/function 24 | 25 | This is called by sphinx.ext.linkcode 26 | 27 | An example with a long-untouched module that everyone has 28 | >>> _linkcode_resolve('py', {'module': 'tty', 29 | ... 'fullname': 'setraw'}, 30 | ... package='tty', 31 | ... url_fmt='http://hg.python.org/cpython/file/' 32 | ... '{revision}/Lib/{package}/{path}#L{lineno}', 33 | ... revision='xxxx') 34 | 'http://hg.python.org/cpython/file/xxxx/Lib/tty/tty.py#L18' 35 | """ 36 | 37 | if revision is None: 38 | return 39 | if domain not in ("py", "pyx"): 40 | return 41 | if not info.get("module") or not info.get("fullname"): 42 | return 43 | 44 | class_name = info["fullname"].split(".")[0] 45 | if type(class_name) != str: 46 | # Python 2 only 47 | class_name = class_name.encode("utf-8") 48 | module = __import__(info["module"], fromlist=[class_name]) 49 | obj = attrgetter(info["fullname"])(module) 50 | 51 | try: 52 | fn = inspect.getsourcefile(obj) 53 | except Exception: 54 | fn = None 55 | if not fn: 56 | try: 57 | fn = inspect.getsourcefile(sys.modules[obj.__module__]) 58 | except Exception: 59 | fn = None 60 | if not fn: 61 | return 62 | 63 | fn = os.path.relpath(fn, start=os.path.dirname(__import__(package).__file__)) 64 | try: 65 | lineno = inspect.getsourcelines(obj)[1] 66 | except Exception: 67 | lineno = "" 68 | return url_fmt.format(revision=revision, package=package, path=fn, lineno=lineno) 69 | 70 | 71 | def make_linkcode_resolve(package, url_fmt): 72 | """Returns a linkcode_resolve function for the given URL format 73 | 74 | revision is a git commit reference (hash or name) 75 | 76 | package is the name of the root module of the package 77 | 78 | url_fmt is along the lines of ('https://github.com/USER/PROJECT/' 79 | 'blob/{revision}/{package}/' 80 | '{path}#L{lineno}') 81 | """ 82 | revision = _get_git_revision() 83 | return partial( 84 | _linkcode_resolve, revision=revision, package=package, url_fmt=url_fmt 85 | ) 86 | -------------------------------------------------------------------------------- /docs/source/variogram_models.rst: -------------------------------------------------------------------------------- 1 | Variogram Models 2 | ================ 3 | 4 | PyKrige internally supports the six variogram models listed below. 5 | Additionally, the code supports user-defined variogram models via the 'custom' 6 | variogram model keyword argument. 7 | 8 | * Gaussian Model 9 | 10 | .. math:: 11 | p \cdot (1 - e^{ - \frac{d^2}{(\frac{4}{7} r)^2}}) + n 12 | 13 | * Exponential Model 14 | 15 | .. math:: 16 | p \cdot (1 - e^{ - \frac{d}{r/3}}) + n 17 | 18 | * Spherical Model 19 | 20 | .. math:: 21 | \begin{cases} 22 | p \cdot (\frac{3d}{2r} - \frac{d^3}{2r^3}) + n & d \leq r \\ 23 | p + n & d > r 24 | \end{cases} 25 | 26 | * Linear Model 27 | 28 | .. math:: 29 | 30 | s \cdot d + n 31 | 32 | Where `s` is the slope and `n` is the nugget. 33 | 34 | * Power Model 35 | 36 | .. math:: 37 | 38 | s \cdot d^e + n 39 | 40 | Where `s` is the scaling factor, `e` is the exponent (between 0 and 2), and `n` 41 | is the nugget term. 42 | 43 | * Hole-Effect Model 44 | 45 | .. math:: 46 | p \cdot (1 - (1 - \frac{d}{r / 3}) * e^{ - \frac{d}{r / 3}}) + n 47 | 48 | Variables are defined as: 49 | 50 | :math:`d` = distance values at which to calculate the variogram 51 | 52 | :math:`p` = partial sill (psill = sill - nugget) 53 | 54 | :math:`r` = range 55 | 56 | :math:`n` = nugget 57 | 58 | :math:`s` = scaling factor or slope 59 | 60 | :math:`e` = exponent for power model 61 | 62 | For stationary variogram models (gaussian, exponential, spherical, and 63 | hole-effect models), the partial sill is defined as the difference between 64 | the full sill and the nugget term. The sill represents the asymptotic 65 | maximum spatial variance at longest lags (distances). The range represents 66 | the distance at which the spatial variance has reached ~95% of the 67 | sill variance. The nugget effectively takes up 'noise' in measurements. 68 | It represents the random deviations from an overall smooth spatial data trend. 69 | (The name *nugget* is an allusion to kriging's mathematical origin in 70 | gold exploration; the nugget effect is intended to take into account the 71 | possibility that when sampling you randomly hit a pocket gold that is 72 | anomalously richer than the surrounding area.) 73 | 74 | For nonstationary models (linear and power models, with unbounded spatial 75 | variances), the nugget has the same meaning. The exponent for the power-law 76 | model should be between 0 and 2 [1]_. 77 | 78 | **A few important notes:** 79 | 80 | The PyKrige user interface by default takes the full sill. This default behavior 81 | can be changed with a keyword flag, so that the user can supply the partial sill 82 | instead. The code internally uses the partial sill (psill = sill - nugget) 83 | rather than the full sill, as it's safer to perform automatic variogram 84 | estimation using the partial sill. 85 | 86 | The exact definitions of the variogram models here may differ from those used 87 | elsewhere. Keep that in mind when switching from another kriging code over to 88 | PyKrige. 89 | 90 | According to [1]_, the hole-effect variogram model is only correct for the 91 | 1D case. It's implemented here for completeness and should be used cautiously. 92 | 93 | References 94 | ---------- 95 | .. [1] P.K. Kitanidis, Introduction to Geostatistics: Applications in 96 | Hydrogeology, (Cambridge University Press, 1997) 272 p. 97 | -------------------------------------------------------------------------------- /examples/00_ordinary.py: -------------------------------------------------------------------------------- 1 | """ 2 | Ordinary Kriging Example 3 | ======================== 4 | 5 | First we will create a 2D dataset together with the associated x, y grids. 6 | 7 | """ 8 | 9 | import matplotlib.pyplot as plt 10 | import numpy as np 11 | 12 | import pykrige.kriging_tools as kt 13 | from pykrige.ok import OrdinaryKriging 14 | 15 | data = np.array( 16 | [ 17 | [0.3, 1.2, 0.47], 18 | [1.9, 0.6, 0.56], 19 | [1.1, 3.2, 0.74], 20 | [3.3, 4.4, 1.47], 21 | [4.7, 3.8, 1.74], 22 | ] 23 | ) 24 | 25 | gridx = np.arange(0.0, 5.5, 0.5) 26 | gridy = np.arange(0.0, 5.5, 0.5) 27 | 28 | ############################################################################### 29 | # Create the ordinary kriging object. Required inputs are the X-coordinates of 30 | # the data points, the Y-coordinates of the data points, and the Z-values of the 31 | # data points. If no variogram model is specified, defaults to a linear variogram 32 | # model. If no variogram model parameters are specified, then the code automatically 33 | # calculates the parameters by fitting the variogram model to the binned 34 | # experimental semivariogram. The verbose kwarg controls code talk-back, and 35 | # the enable_plotting kwarg controls the display of the semivariogram. 36 | 37 | OK = OrdinaryKriging( 38 | data[:, 0], 39 | data[:, 1], 40 | data[:, 2], 41 | variogram_model="linear", 42 | verbose=False, 43 | enable_plotting=False, 44 | ) 45 | 46 | ############################################################################### 47 | # Creates the kriged grid and the variance grid. Allows for kriging on a rectangular 48 | # grid of points, on a masked rectangular grid of points, or with arbitrary points. 49 | # (See OrdinaryKriging.__doc__ for more information.) 50 | 51 | z, ss = OK.execute("grid", gridx, gridy) 52 | 53 | ############################################################################### 54 | # Writes the kriged grid to an ASCII grid file and plot it. 55 | 56 | kt.write_asc_grid(gridx, gridy, z, filename="output.asc") 57 | plt.imshow(z) 58 | plt.show() 59 | -------------------------------------------------------------------------------- /examples/01_universal.py: -------------------------------------------------------------------------------- 1 | """ 2 | Universal Kriging Example 3 | ========================= 4 | 5 | In this example we apply a regional linear trend to the kriging system. 6 | 7 | """ 8 | 9 | import matplotlib.pyplot as plt 10 | import numpy as np 11 | 12 | from pykrige.uk import UniversalKriging 13 | 14 | data = np.array( 15 | [ 16 | [0.3, 1.2, 0.47], 17 | [1.9, 0.6, 0.56], 18 | [1.1, 3.2, 0.74], 19 | [3.3, 4.4, 1.47], 20 | [4.7, 3.8, 1.74], 21 | ] 22 | ) 23 | 24 | gridx = np.arange(0.0, 5.5, 0.5) 25 | gridy = np.arange(0.0, 5.5, 0.5) 26 | 27 | ############################################################################### 28 | # Create the universal kriging object. Required inputs are the X-coordinates of 29 | # the data points, the Y-coordinates of the data points, and the Z-values of the 30 | # data points. Variogram is handled as in the ordinary kriging case. 31 | # drift_terms is a list of the drift terms to include; currently supported terms 32 | # are 'regional_linear', 'point_log', and 'external_Z'. Refer to 33 | # UniversalKriging.__doc__ for more information. 34 | 35 | UK = UniversalKriging( 36 | data[:, 0], 37 | data[:, 1], 38 | data[:, 2], 39 | variogram_model="linear", 40 | drift_terms=["regional_linear"], 41 | ) 42 | 43 | ############################################################################### 44 | # Creates the kriged grid and the variance grid. Allows for kriging on a rectangular 45 | # grid of points, on a masked rectangular grid of points, or with arbitrary points. 46 | # (See UniversalKriging.__doc__ for more information.) 47 | 48 | z, ss = UK.execute("grid", gridx, gridy) 49 | plt.imshow(z) 50 | plt.show() 51 | -------------------------------------------------------------------------------- /examples/02_kriging3D.py: -------------------------------------------------------------------------------- 1 | """ 2 | Three-Dimensional Kriging Example 3 | ================================= 4 | 5 | """ 6 | 7 | import numpy as np 8 | from matplotlib import pyplot as plt 9 | 10 | from pykrige.ok3d import OrdinaryKriging3D 11 | from pykrige.uk3d import UniversalKriging3D 12 | 13 | data = np.array( 14 | [ 15 | [0.1, 0.1, 0.3, 0.9], 16 | [0.2, 0.1, 0.4, 0.8], 17 | [0.1, 0.3, 0.1, 0.9], 18 | [0.5, 0.4, 0.4, 0.5], 19 | [0.3, 0.3, 0.2, 0.7], 20 | ] 21 | ) 22 | 23 | gridx = np.arange(0.0, 0.6, 0.05) 24 | gridy = np.arange(0.0, 0.6, 0.01) 25 | gridz = np.arange(0.0, 0.6, 0.1) 26 | 27 | ############################################################################### 28 | # Create the 3D ordinary kriging object and solves for the three-dimension kriged 29 | # volume and variance. Refer to OrdinaryKriging3D.__doc__ for more information. 30 | 31 | ok3d = OrdinaryKriging3D( 32 | data[:, 0], data[:, 1], data[:, 2], data[:, 3], variogram_model="linear" 33 | ) 34 | k3d1, ss3d = ok3d.execute("grid", gridx, gridy, gridz) 35 | 36 | ############################################################################### 37 | # Create the 3D universal kriging object and solves for the three-dimension kriged 38 | # volume and variance. Refer to UniversalKriging3D.__doc__ for more information. 39 | 40 | uk3d = UniversalKriging3D( 41 | data[:, 0], 42 | data[:, 1], 43 | data[:, 2], 44 | data[:, 3], 45 | variogram_model="linear", 46 | drift_terms=["regional_linear"], 47 | ) 48 | k3d2, ss3d = uk3d.execute("grid", gridx, gridy, gridz) 49 | 50 | ############################################################################### 51 | # To use the generic 'specified' drift term, the user must provide the drift values 52 | # at each data point and at every grid point. The following example is equivalent to 53 | # using a linear drift in all three spatial dimensions. Refer to 54 | # UniversalKriging3D.__doc__ for more information. 55 | 56 | zg, yg, xg = np.meshgrid(gridz, gridy, gridx, indexing="ij") 57 | uk3d = UniversalKriging3D( 58 | data[:, 0], 59 | data[:, 1], 60 | data[:, 2], 61 | data[:, 3], 62 | variogram_model="linear", 63 | drift_terms=["specified"], 64 | specified_drift=[data[:, 0], data[:, 1], data[:, 2]], 65 | ) 66 | k3d3, ss3d = uk3d.execute( 67 | "grid", gridx, gridy, gridz, specified_drift_arrays=[xg, yg, zg] 68 | ) 69 | 70 | ############################################################################### 71 | # To use the generic 'functional' drift term, the user must provide a callable 72 | # function that takes only the spatial dimensions as arguments. The following example 73 | # is equivalent to using a linear drift only in the x-direction. Refer to 74 | # UniversalKriging3D.__doc__ for more information. 75 | 76 | func = lambda x, y, z: x 77 | uk3d = UniversalKriging3D( 78 | data[:, 0], 79 | data[:, 1], 80 | data[:, 2], 81 | data[:, 3], 82 | variogram_model="linear", 83 | drift_terms=["functional"], 84 | functional_drift=[func], 85 | ) 86 | k3d4, ss3d = uk3d.execute("grid", gridx, gridy, gridz) 87 | 88 | ############################################################################### 89 | # Note that the use of the 'specified' and 'functional' generic drift capabilities is 90 | # essentially identical in the two-dimensional universal kriging class (except for a 91 | # difference in the number of spatial coordinates for the passed drift functions). 92 | # See UniversalKriging.__doc__ for more information. 93 | 94 | fig, (ax1, ax2, ax3, ax4) = plt.subplots(4) 95 | ax1.imshow(k3d1[:, :, 0], origin="lower") 96 | ax1.set_title("ordinary kriging") 97 | ax2.imshow(k3d2[:, :, 0], origin="lower") 98 | ax2.set_title("regional lin. drift") 99 | ax3.imshow(k3d3[:, :, 0], origin="lower") 100 | ax3.set_title("specified drift") 101 | ax4.imshow(k3d4[:, :, 0], origin="lower") 102 | ax4.set_title("functional drift") 103 | plt.tight_layout() 104 | plt.show() 105 | -------------------------------------------------------------------------------- /examples/03_gstools_covmodel.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | GSTools Interface 4 | ================= 5 | 6 | Example how to use the PyKrige routines with a GSTools CovModel. 7 | """ 8 | import gstools as gs 9 | import numpy as np 10 | from matplotlib import pyplot as plt 11 | 12 | from pykrige.ok import OrdinaryKriging 13 | 14 | # conditioning data 15 | data = np.array( 16 | [ 17 | [0.3, 1.2, 0.47], 18 | [1.9, 0.6, 0.56], 19 | [1.1, 3.2, 0.74], 20 | [3.3, 4.4, 1.47], 21 | [4.7, 3.8, 1.74], 22 | ] 23 | ) 24 | # grid definition for output field 25 | gridx = np.arange(0.0, 5.5, 0.1) 26 | gridy = np.arange(0.0, 6.5, 0.1) 27 | # a GSTools based covariance model 28 | cov_model = gs.Gaussian(dim=2, len_scale=4, anis=0.2, angles=-0.5, var=0.5, nugget=0.1) 29 | # ordinary kriging with pykrige 30 | OK1 = OrdinaryKriging(data[:, 0], data[:, 1], data[:, 2], cov_model) 31 | z1, ss1 = OK1.execute("grid", gridx, gridy) 32 | plt.imshow(z1, origin="lower") 33 | plt.show() 34 | -------------------------------------------------------------------------------- /examples/04_krige_geometric.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Geometric example 4 | ================= 5 | 6 | A small example script showing the usage of the 'geographic' coordinates type 7 | for ordinary kriging on a sphere. 8 | """ 9 | 10 | import numpy as np 11 | from matplotlib import pyplot as plt 12 | 13 | from pykrige.ok import OrdinaryKriging 14 | 15 | # Make this example reproducible: 16 | np.random.seed(89239413) 17 | 18 | # Generate random data following a uniform spatial distribution 19 | # of nodes and a uniform distribution of values in the interval 20 | # [2.0, 5.5]: 21 | N = 7 22 | lon = 360.0 * np.random.random(N) 23 | lat = 180.0 / np.pi * np.arcsin(2 * np.random.random(N) - 1) 24 | z = 3.5 * np.random.rand(N) + 2.0 25 | 26 | # Generate a regular grid with 60° longitude and 30° latitude steps: 27 | grid_lon = np.linspace(0.0, 360.0, 7) 28 | grid_lat = np.linspace(-90.0, 90.0, 7) 29 | 30 | # Create ordinary kriging object: 31 | OK = OrdinaryKriging( 32 | lon, 33 | lat, 34 | z, 35 | variogram_model="linear", 36 | verbose=False, 37 | enable_plotting=False, 38 | coordinates_type="geographic", 39 | ) 40 | 41 | # Execute on grid: 42 | z1, ss1 = OK.execute("grid", grid_lon, grid_lat) 43 | 44 | # Create ordinary kriging object ignoring curvature: 45 | OK = OrdinaryKriging( 46 | lon, lat, z, variogram_model="linear", verbose=False, enable_plotting=False 47 | ) 48 | 49 | # Execute on grid: 50 | z2, ss2 = OK.execute("grid", grid_lon, grid_lat) 51 | 52 | ############################################################################### 53 | # Print data at equator (last longitude index will show periodicity): 54 | 55 | print("Original data:") 56 | print("Longitude:", lon.astype(int)) 57 | print("Latitude: ", lat.astype(int)) 58 | print("z: ", np.array_str(z, precision=2)) 59 | print("\nKrige at 60° latitude:\n======================") 60 | print("Longitude:", grid_lon) 61 | print("Value: ", np.array_str(z1[5, :], precision=2)) 62 | print("Sigma²: ", np.array_str(ss1[5, :], precision=2)) 63 | print("\nIgnoring curvature:\n=====================") 64 | print("Value: ", np.array_str(z2[5, :], precision=2)) 65 | print("Sigma²: ", np.array_str(ss2[5, :], precision=2)) 66 | 67 | ############################################################################### 68 | # We can see that the data point at longitude 122, latitude 50 correctly 69 | # dominates the kriged results, since it is the closest node in spherical 70 | # distance metric, as longitude differences scale with cos(latitude). 71 | # When kriging using longitude / latitude linearly, the value for grid points 72 | # with longitude values further away as longitude is now incorrectly 73 | # weighted equally as latitude. 74 | 75 | fig, (ax1, ax2) = plt.subplots(1, 2) 76 | ax1.imshow(z1, extent=[0, 360, -90, 90], origin="lower") 77 | ax1.set_title("geo-coordinates") 78 | ax2.imshow(z2, extent=[0, 360, -90, 90], origin="lower") 79 | ax2.set_title("non geo-coordinates") 80 | plt.show() 81 | -------------------------------------------------------------------------------- /examples/05_kriging_1D.py: -------------------------------------------------------------------------------- 1 | """ 2 | 1D Kriging 3 | ========== 4 | 5 | An example of 1D kriging with PyKrige 6 | """ 7 | import matplotlib.pyplot as plt 8 | import numpy as np 9 | 10 | from pykrige import OrdinaryKriging 11 | 12 | plt.style.use("ggplot") 13 | 14 | # fmt: off 15 | # Data taken from 16 | # https://blog.dominodatalab.com/fitting-gaussian-process-models-python/ 17 | X, y = np.array([ 18 | [-5.01, 1.06], [-4.90, 0.92], [-4.82, 0.35], [-4.69, 0.49], [-4.56, 0.52], 19 | [-4.52, 0.12], [-4.39, 0.47], [-4.32,-0.19], [-4.19, 0.08], [-4.11,-0.19], 20 | [-4.00,-0.03], [-3.89,-0.03], [-3.78,-0.05], [-3.67, 0.10], [-3.59, 0.44], 21 | [-3.50, 0.66], [-3.39,-0.12], [-3.28, 0.45], [-3.20, 0.14], [-3.07,-0.28], 22 | [-3.01,-0.46], [-2.90,-0.32], [-2.77,-1.58], [-2.69,-1.44], [-2.60,-1.51], 23 | [-2.49,-1.50], [-2.41,-2.04], [-2.28,-1.57], [-2.19,-1.25], [-2.10,-1.50], 24 | [-2.00,-1.42], [-1.91,-1.10], [-1.80,-0.58], [-1.67,-1.08], [-1.61,-0.79], 25 | [-1.50,-1.00], [-1.37,-0.04], [-1.30,-0.54], [-1.19,-0.15], [-1.06,-0.18], 26 | [-0.98,-0.25], [-0.87,-1.20], [-0.78,-0.49], [-0.68,-0.83], [-0.57,-0.15], 27 | [-0.50, 0.00], [-0.38,-1.10], [-0.29,-0.32], [-0.18,-0.60], [-0.09,-0.49], 28 | [0.03 ,-0.50], [0.09 ,-0.02], [0.20 ,-0.47], [0.31 ,-0.11], [0.41 ,-0.28], 29 | [0.53 , 0.40], [0.61 , 0.11], [0.70 , 0.32], [0.94 , 0.42], [1.02 , 0.57], 30 | [1.13 , 0.82], [1.24 , 1.18], [1.30 , 0.86], [1.43 , 1.11], [1.50 , 0.74], 31 | [1.63 , 0.75], [1.74 , 1.15], [1.80 , 0.76], [1.93 , 0.68], [2.03 , 0.03], 32 | [2.12 , 0.31], [2.23 ,-0.14], [2.31 ,-0.88], [2.40 ,-1.25], [2.50 ,-1.62], 33 | [2.63 ,-1.37], [2.72 ,-0.99], [2.80 ,-1.92], [2.83 ,-1.94], [2.91 ,-1.32], 34 | [3.00 ,-1.69], [3.13 ,-1.84], [3.21 ,-2.05], [3.30 ,-1.69], [3.41 ,-0.53], 35 | [3.52 ,-0.55], [3.63 ,-0.92], [3.72 ,-0.76], [3.80 ,-0.41], [3.91 , 0.12], 36 | [4.04 , 0.25], [4.13 , 0.16], [4.24 , 0.26], [4.32 , 0.62], [4.44 , 1.69], 37 | [4.52 , 1.11], [4.65 , 0.36], [4.74 , 0.79], [4.84 , 0.87], [4.93 , 1.01], 38 | [5.02 , 0.55] 39 | ]).T 40 | # fmt: on 41 | 42 | X_pred = np.linspace(-6, 6, 200) 43 | 44 | # pykrige doesn't support 1D data for now, only 2D or 3D 45 | # adapting the 1D input to 2D 46 | uk = OrdinaryKriging(X, np.zeros(X.shape), y, variogram_model="gaussian") 47 | 48 | y_pred, y_std = uk.execute("grid", X_pred, np.array([0.0])) 49 | 50 | y_pred = np.squeeze(y_pred) 51 | y_std = np.squeeze(y_std) 52 | 53 | fig, ax = plt.subplots(1, 1, figsize=(10, 4)) 54 | ax.scatter(X, y, s=40, label="Input data") 55 | 56 | 57 | ax.plot(X_pred, y_pred, label="Predicted values") 58 | ax.fill_between( 59 | X_pred, 60 | y_pred - 3 * y_std, 61 | y_pred + 3 * y_std, 62 | alpha=0.3, 63 | label="Confidence interval", 64 | ) 65 | ax.legend(loc=9) 66 | ax.set_xlabel("x") 67 | ax.set_ylabel("y") 68 | ax.set_xlim(-6, 6) 69 | ax.set_ylim(-2.8, 3.5) 70 | plt.show() 71 | -------------------------------------------------------------------------------- /examples/06_exact_values_example_1D.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Exact Values 4 | ============ 5 | 6 | PyKrige demonstration and usage 7 | as a non-exact interpolator in 1D. 8 | """ 9 | 10 | import matplotlib.pyplot as plt 11 | import numpy as np 12 | 13 | from pykrige.ok import OrdinaryKriging 14 | 15 | plt.style.use("ggplot") 16 | 17 | np.random.seed(42) 18 | 19 | x = np.linspace(0, 12.5, 50) 20 | xpred = np.linspace(0, 12.5, 393) 21 | y = np.sin(x) * np.exp(-0.25 * x) + np.random.normal(-0.25, 0.25, 50) 22 | 23 | # compare OrdinaryKriging as an exact and non exact interpolator 24 | uk = OrdinaryKriging( 25 | x, np.zeros(x.shape), y, variogram_model="linear", exact_values=False 26 | ) 27 | uk_exact = OrdinaryKriging(x, np.zeros(x.shape), y, variogram_model="linear") 28 | 29 | y_pred, y_std = uk.execute("grid", xpred, np.array([0.0]), backend="loop") 30 | y_pred_exact, y_std_exact = uk_exact.execute( 31 | "grid", xpred, np.array([0.0]), backend="loop" 32 | ) 33 | 34 | 35 | y_pred = np.squeeze(y_pred) 36 | y_std = np.squeeze(y_std) 37 | 38 | y_pred_exact = np.squeeze(y_pred_exact) 39 | y_std_exact = np.squeeze(y_std_exact) 40 | 41 | 42 | fig, ax = plt.subplots(1, 1, figsize=(10, 4)) 43 | 44 | ax.scatter(x, y, label="Input Data") 45 | ax.plot(xpred, y_pred_exact, label="Exact Prediction") 46 | ax.plot(xpred, y_pred, label="Non Exact Prediction") 47 | 48 | ax.fill_between( 49 | xpred, 50 | y_pred - 3 * y_std, 51 | y_pred + 3 * y_std, 52 | alpha=0.3, 53 | label="Confidence interval", 54 | ) 55 | ax.legend(loc=9) 56 | ax.set_ylim(-1.8, 1.3) 57 | ax.legend(loc=9) 58 | plt.xlabel("X") 59 | plt.ylabel("Field") 60 | plt.show() 61 | -------------------------------------------------------------------------------- /examples/07_regression_kriging2d.py: -------------------------------------------------------------------------------- 1 | """ 2 | Regression kriging 3 | ------------------ 4 | 5 | An example of regression kriging 6 | """ 7 | 8 | import sys 9 | 10 | from sklearn.datasets import fetch_california_housing 11 | from sklearn.ensemble import RandomForestRegressor 12 | from sklearn.linear_model import LinearRegression 13 | from sklearn.model_selection import train_test_split 14 | from sklearn.svm import SVR 15 | 16 | from pykrige.rk import RegressionKriging 17 | 18 | svr_model = SVR(C=0.1, gamma="auto") 19 | rf_model = RandomForestRegressor(n_estimators=100) 20 | lr_model = LinearRegression(copy_X=True, fit_intercept=False) 21 | 22 | models = [svr_model, rf_model, lr_model] 23 | 24 | try: 25 | housing = fetch_california_housing() 26 | except PermissionError: 27 | # this dataset can occasionally fail to download on Windows 28 | sys.exit(0) 29 | 30 | # take the first 5000 as Kriging is memory intensive 31 | p = housing["data"][:5000, :-2] 32 | x = housing["data"][:5000, -2:] 33 | target = housing["target"][:5000] 34 | 35 | p_train, p_test, x_train, x_test, target_train, target_test = train_test_split( 36 | p, x, target, test_size=0.3, random_state=42 37 | ) 38 | 39 | for m in models: 40 | print("=" * 40) 41 | print("regression model:", m.__class__.__name__) 42 | m_rk = RegressionKriging(regression_model=m, n_closest_points=10) 43 | m_rk.fit(p_train, x_train, target_train) 44 | print("Regression Score: ", m_rk.regression_model.score(p_test, target_test)) 45 | print("RK score: ", m_rk.score(p_test, x_test, target_test)) 46 | -------------------------------------------------------------------------------- /examples/08_krige_cv.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Krige CV 4 | -------- 5 | 6 | Searching for optimal kriging parameters with cross validation 7 | """ 8 | 9 | import numpy as np 10 | from sklearn.model_selection import GridSearchCV 11 | 12 | from pykrige.rk import Krige 13 | 14 | # 2D Kring param opt 15 | 16 | param_dict = { 17 | "method": ["ordinary", "universal"], 18 | "variogram_model": ["linear", "power", "gaussian", "spherical"], 19 | # "nlags": [4, 6, 8], 20 | # "weight": [True, False] 21 | } 22 | 23 | estimator = GridSearchCV(Krige(), param_dict, verbose=True, return_train_score=True) 24 | 25 | # dummy data 26 | X = np.random.randint(0, 400, size=(100, 2)).astype(float) 27 | y = 5 * np.random.rand(100) 28 | 29 | # run the gridsearch 30 | estimator.fit(X=X, y=y) 31 | 32 | 33 | if hasattr(estimator, "best_score_"): 34 | print("best_score R² = {:.3f}".format(estimator.best_score_)) 35 | print("best_params = ", estimator.best_params_) 36 | 37 | print("\nCV results::") 38 | if hasattr(estimator, "cv_results_"): 39 | for key in [ 40 | "mean_test_score", 41 | "mean_train_score", 42 | "param_method", 43 | "param_variogram_model", 44 | ]: 45 | print(" - {} : {}".format(key, estimator.cv_results_[key])) 46 | 47 | # 3D Kring param opt 48 | 49 | param_dict3d = { 50 | "method": ["ordinary3d", "universal3d"], 51 | "variogram_model": ["linear", "power", "gaussian", "spherical"], 52 | # "nlags": [4, 6, 8], 53 | # "weight": [True, False] 54 | } 55 | 56 | estimator = GridSearchCV(Krige(), param_dict3d, verbose=True, return_train_score=True) 57 | 58 | # dummy data 59 | X3 = np.random.randint(0, 400, size=(100, 3)).astype(float) 60 | y = 5 * np.random.rand(100) 61 | 62 | # run the gridsearch 63 | estimator.fit(X=X3, y=y) 64 | 65 | 66 | if hasattr(estimator, "best_score_"): 67 | print("best_score R² = {:.3f}".format(estimator.best_score_)) 68 | print("best_params = ", estimator.best_params_) 69 | 70 | print("\nCV results::") 71 | if hasattr(estimator, "cv_results_"): 72 | for key in [ 73 | "mean_test_score", 74 | "mean_train_score", 75 | "param_method", 76 | "param_variogram_model", 77 | ]: 78 | print(" - {} : {}".format(key, estimator.cv_results_[key])) 79 | -------------------------------------------------------------------------------- /examples/09_kriging_meuse.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# About this notebook\n", 8 | "\n", 9 | "In this notebook the ordinary kriging algorithm is used to predict the amount of lead in the soil of the riverbank Meuse in the Netherlands. This is one of the textbook examples in Spatial statistics. The performance of making predictions with kriging is benchmarked with a simple nearest neighbours model. Finally a map is made by predicting a grid. The map is plot as a heatmap on top on an interactive map." 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": { 16 | "collapsed": true 17 | }, 18 | "outputs": [], 19 | "source": [ 20 | "# This example requires some extra packages compared to the PyKrige package.\n", 21 | "# At the time of the creation, I used the conda package manager\n", 22 | "# and installed the following (with the versions at the time):\n", 23 | "# pandas 0.18.1, geopandas 0.2.1, seaborn 0.7.1, folium 0.2.1, shapely 1.5.16\n", 24 | "# If you use pip, \"pip install geopandas folium seaborn\" should work to.\n", 25 | "import os\n", 26 | "import requests\n", 27 | "import zipfile\n", 28 | "import geopandas as gpd\n", 29 | "import shapely\n", 30 | "import pandas as pd\n", 31 | "import matplotlib.pyplot as plt\n", 32 | "import matplotlib\n", 33 | "import numpy as np\n", 34 | "import folium\n", 35 | "from folium import plugins\n", 36 | "\n", 37 | "%matplotlib inline\n", 38 | "from pykrige.ok import OrdinaryKriging\n", 39 | "from sklearn.neighbors import KNeighborsRegressor\n", 40 | "from sklearn.model_selection import GridSearchCV\n", 41 | "import pykrige.kriging_tools as kt\n", 42 | "import seaborn as sb\n", 43 | "from IPython.core.display import HTML\n", 44 | "\n", 45 | "HTML(\"\")" 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "metadata": {}, 51 | "source": [ 52 | "## Download and import the shapefile\n", 53 | "\n", 54 | "- Download the zipfile from http://spatial-analyst.net/book/meusegrids\n", 55 | "- Extract the zipfile\n", 56 | "- Delete the zipfile" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "metadata": { 63 | "collapsed": false 64 | }, 65 | "outputs": [], 66 | "source": [ 67 | "if ~os.path.isfile(\"meuse.zip\"):\n", 68 | " url = \"http://spatial-analyst.net/book/system/files/meuse.zip\"\n", 69 | " results = requests.get(url)\n", 70 | " print(\"Status code download: {}\".format(results.status_code))\n", 71 | "with open(\"meuse.zip\", \"wb\") as f:\n", 72 | " f.write(results.content)\n", 73 | "zip_ref = zipfile.ZipFile(\"meuse.zip\", \"r\")\n", 74 | "zip_ref.extractall(\"meuse_example_data/\")\n", 75 | "zip_ref.close()\n", 76 | "os.remove(\"meuse.zip\")" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": null, 82 | "metadata": { 83 | "collapsed": false 84 | }, 85 | "outputs": [], 86 | "source": [ 87 | "meuse = gpd.read_file(\"meuse_example_data/meuse.shp\")\n", 88 | "meuse.crs = {\"init\": \"epsg:28992\"}\n", 89 | "meuse[\"x\"] = meuse[\"geometry\"].apply(lambda x: x.x)\n", 90 | "meuse[\"y\"] = meuse[\"geometry\"].apply(lambda x: x.y)\n", 91 | "meuse.sample()" 92 | ] 93 | }, 94 | { 95 | "cell_type": "markdown", 96 | "metadata": {}, 97 | "source": [ 98 | "## Spatial plot" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": null, 104 | "metadata": { 105 | "collapsed": false, 106 | "scrolled": false 107 | }, 108 | "outputs": [], 109 | "source": [ 110 | "feature_to_plot = \"lead\"\n", 111 | "\n", 112 | "meuse_lat_long = meuse.to_crs({\"init\": \"epsg:4326\"})\n", 113 | "meuse_lat_long[\"long\"] = meuse_lat_long.geometry.apply(lambda x: x.x)\n", 114 | "meuse_lat_long[\"lat\"] = meuse_lat_long.geometry.apply(lambda x: x.y)\n", 115 | "mean_long = np.mean(meuse_lat_long[\"long\"])\n", 116 | "mean_lat = np.mean(meuse_lat_long[\"lat\"])\n", 117 | "m = folium.Map([mean_lat, mean_long], zoom_start=13, tiles=\"Stamen Toner\")\n", 118 | "scale = folium.colormap.linear.YlOrRd.scale(\n", 119 | " vmin=0, vmax=meuse_lat_long[feature_to_plot].max()\n", 120 | ")\n", 121 | "for row in meuse_lat_long.iterrows():\n", 122 | " folium.CircleMarker(\n", 123 | " location=[row[1][\"lat\"], row[1][\"long\"]],\n", 124 | " radius=50,\n", 125 | " color=None,\n", 126 | " fill_opacity=1,\n", 127 | " fill_color=scale(row[1][feature_to_plot]),\n", 128 | " ).add_to(m)\n", 129 | "m.add_children(scale)" 130 | ] 131 | }, 132 | { 133 | "cell_type": "markdown", 134 | "metadata": {}, 135 | "source": [ 136 | "## Split the data into test and train\n", 137 | "\n", 138 | "Control the random split with setting a seed so the results are reproducable." 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": null, 144 | "metadata": { 145 | "collapsed": false 146 | }, 147 | "outputs": [], 148 | "source": [ 149 | "np.random.seed(0)\n", 150 | "test_indexes = np.random.choice(\n", 151 | " a=meuse.index, size=int(np.round(len(meuse.index.values) / 4))\n", 152 | ")\n", 153 | "train_indexes = [index for index in meuse.index if index not in test_indexes]\n", 154 | "meuse_test = meuse.loc[test_indexes, :].copy()\n", 155 | "meuse_train = meuse.loc[train_indexes, :].copy()\n", 156 | "print(\n", 157 | " \"Number of observations in training: {}, in test: {}\".format(\n", 158 | " len(meuse_train), len(meuse_test)\n", 159 | " )\n", 160 | ")" 161 | ] 162 | }, 163 | { 164 | "cell_type": "markdown", 165 | "metadata": {}, 166 | "source": [ 167 | "## Ordinary kriging\n", 168 | "\n", 169 | "Variogram parameters are set with trail and error." 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": null, 175 | "metadata": { 176 | "collapsed": false 177 | }, 178 | "outputs": [], 179 | "source": [ 180 | "model = OrdinaryKriging(\n", 181 | " x=meuse_train[\"x\"],\n", 182 | " y=meuse_train[\"y\"],\n", 183 | " z=meuse_train[\"lead\"],\n", 184 | " verbose=True,\n", 185 | " variogram_parameters=[13500, 900, 4000],\n", 186 | " enable_plotting=True,\n", 187 | " nlags=30,\n", 188 | " weight=True,\n", 189 | " variogram_model=\"spherical\",\n", 190 | ")\n", 191 | "meuse_train[\"prediction\"] = model.execute(\n", 192 | " style=\"points\", xpoints=meuse_train[\"x\"], ypoints=meuse_train[\"y\"]\n", 193 | ")[0].data\n", 194 | "meuse_train[\"kriging_residual\"] = meuse_train[\"lead\"] - meuse_train[\"prediction\"]\n", 195 | "meuse_test[\"prediction\"] = model.execute(\n", 196 | " style=\"points\", xpoints=meuse_test[\"x\"], ypoints=meuse_test[\"y\"]\n", 197 | ")[0].data\n", 198 | "meuse_test[\"kriging_residual\"] = meuse_test[\"lead\"] - meuse_test[\"prediction\"]" 199 | ] 200 | }, 201 | { 202 | "cell_type": "markdown", 203 | "metadata": {}, 204 | "source": [ 205 | "## Model performance\n", 206 | "\n", 207 | "- Performance on training data (kriging is an exact interpollator, so perfect prediction)\n", 208 | "- Performance on test data" 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": null, 214 | "metadata": { 215 | "collapsed": false 216 | }, 217 | "outputs": [], 218 | "source": [ 219 | "plt.figure(figsize=(6, 6))\n", 220 | "plt.subplot(221)\n", 221 | "plt.plot(meuse_train[\"prediction\"], meuse_train[\"lead\"], \".\")\n", 222 | "plt.title(\"Training: pred vs obs\")\n", 223 | "plt.xlabel(\"Predictions\")\n", 224 | "plt.ylabel(\"True value\")\n", 225 | "plt.plot([0, 700], [0, 700], \"g--\")\n", 226 | "plt.ylim(0, 700)\n", 227 | "plt.xlim(0, 700)\n", 228 | "plt.subplot(222)\n", 229 | "meuse_train[\"kriging_residual\"].hist()\n", 230 | "plt.title(\n", 231 | " \"Hist training res\\nMedian absolute error: {:.1f}\".format(\n", 232 | " np.median(np.abs(meuse_train[\"kriging_residual\"]))\n", 233 | " )\n", 234 | ")\n", 235 | "plt.subplot(223)\n", 236 | "plt.plot(meuse_test[\"prediction\"], meuse_test[\"lead\"], \".\")\n", 237 | "plt.plot([0, 700], [0, 700], \"g--\")\n", 238 | "plt.title(\"Test: pred vs obs\")\n", 239 | "plt.xlabel(\"Predictions\")\n", 240 | "plt.ylabel(\"True value\")\n", 241 | "plt.ylim(0, 700)\n", 242 | "plt.xlim(0, 700)\n", 243 | "plt.subplot(224)\n", 244 | "meuse_test[\"kriging_residual\"].hist()\n", 245 | "plt.title(\n", 246 | " \"Hist test res\\nMedian absolute error: {:.1f}\".format(\n", 247 | " np.median(np.abs(meuse_test[\"kriging_residual\"]))\n", 248 | " )\n", 249 | ")\n", 250 | "plt.tight_layout()" 251 | ] 252 | }, 253 | { 254 | "cell_type": "markdown", 255 | "metadata": {}, 256 | "source": [ 257 | "## Benchmark with Nearest Neighbours regression\n", 258 | "\n", 259 | "Optimise number of neighbours with sklearn gridsearch" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": null, 265 | "metadata": { 266 | "collapsed": false 267 | }, 268 | "outputs": [], 269 | "source": [ 270 | "parameters = {\"n_neighbors\": np.arange(1, 10)}\n", 271 | "nn_model = KNeighborsRegressor()\n", 272 | "nn_model_cv = GridSearchCV(nn_model, parameters)\n", 273 | "nn_model_cv = nn_model_cv.fit(meuse_train[[\"x\", \"y\"]], meuse_train[\"lead\"])\n", 274 | "print(\"Optimal number of neighbours {}\".format(nn_model_cv.best_params_))\n", 275 | "nn_model = nn_model_cv.best_estimator_\n", 276 | "meuse_test[\"nn_prediction\"] = nn_model.predict(meuse_test[[\"x\", \"y\"]])\n", 277 | "meuse_test[\"nn_residual\"] = meuse_test[\"lead\"] - meuse_test[\"nn_prediction\"]" 278 | ] 279 | }, 280 | { 281 | "cell_type": "code", 282 | "execution_count": null, 283 | "metadata": { 284 | "collapsed": false 285 | }, 286 | "outputs": [], 287 | "source": [ 288 | "sb.set_style(\"whitegrid\")\n", 289 | "plt.figure(figsize=(4, 4))\n", 290 | "sb.boxplot(data=meuse_test[[\"nn_residual\", \"kriging_residual\"]])\n", 291 | "plt.title(\n", 292 | " \"Compairing residuals\\nmedian abs res NN: {:.1f}, Kriging {:.1f}\\nmean abs res NN: {:.1f}, Kriging: {:.1f}\".format(\n", 293 | " np.median(np.abs(meuse_test[\"nn_residual\"])),\n", 294 | " np.median(np.abs(meuse_test[\"kriging_residual\"])),\n", 295 | " np.mean(np.abs(meuse_test[\"nn_residual\"])),\n", 296 | " np.mean(np.abs(meuse_test[\"kriging_residual\"])),\n", 297 | " )\n", 298 | ")" 299 | ] 300 | }, 301 | { 302 | "cell_type": "markdown", 303 | "metadata": {}, 304 | "source": [ 305 | "## Help functions to sample a grid from a polygon\n", 306 | "\n", 307 | "As found on http://portolan.leaffan.net/creating-sample-points-with-ogr-and-shapely-pt-2-regular-grid-sampling/" 308 | ] 309 | }, 310 | { 311 | "cell_type": "code", 312 | "execution_count": null, 313 | "metadata": { 314 | "collapsed": true 315 | }, 316 | "outputs": [], 317 | "source": [ 318 | "class PolygonPointSampler(object):\n", 319 | " def __init__(self, polygon=\"\"):\n", 320 | " \"\"\"\n", 321 | " Initialize a new PolygonPointSampler object using the specified polygon\n", 322 | " object (as allocated by Shapely). If no polygon is given a new empty\n", 323 | " one is created and set as the base polygon.\n", 324 | " \"\"\"\n", 325 | " if polygon:\n", 326 | " self.polygon = polygon\n", 327 | " else:\n", 328 | " self.polygon = Polygon()\n", 329 | " self.samples = list()\n", 330 | " self.sample_count = 0\n", 331 | " self.prepared = False\n", 332 | "\n", 333 | " def add_polygon(self, polygon):\n", 334 | " \"\"\"\n", 335 | " Add another polygon entity to the base polygon by geometrically unifying\n", 336 | " it with the current one.\n", 337 | " \"\"\"\n", 338 | " self.polygon = self.polygon.union(polygon)\n", 339 | " self.prepared = False\n", 340 | "\n", 341 | " def get_spatial_df(self):\n", 342 | " geo_df = pd.DataFrame(self.samples, columns=[\"geometry\"]).set_geometry(\n", 343 | " \"geometry\"\n", 344 | " )\n", 345 | " geo_df[\"x\"] = geo_df[\"geometry\"].apply(lambda x: x.coords[0][0])\n", 346 | " geo_df[\"y\"] = geo_df[\"geometry\"].apply(lambda x: x.coords[0][1])\n", 347 | " return geo_df\n", 348 | "\n", 349 | " def print_samples(self):\n", 350 | " \"\"\"\n", 351 | " Print all sample points using their WKT representation.\n", 352 | " \"\"\"\n", 353 | " for sample_pt in self.samples:\n", 354 | " print(sample_pt)\n", 355 | "\n", 356 | " def prepare_sampling(self):\n", 357 | " \"\"\"\n", 358 | " Prepare the actual sampling procedure by splitting up the specified base\n", 359 | " polygon (that may consist of multiple simple polygons) and appending its\n", 360 | " compartments to a dedicated list.\n", 361 | " \"\"\"\n", 362 | " self.src = list()\n", 363 | " if hasattr(self.polygon, \"geoms\"):\n", 364 | " for py in self.polygon:\n", 365 | " self.src.append(py)\n", 366 | " else:\n", 367 | " self.src.append(self.polygon)\n", 368 | " self.prepared = True\n", 369 | "\n", 370 | " def perform_sampling(self):\n", 371 | " \"\"\"\n", 372 | " Create a stub for the actual sampling procedure.\n", 373 | " \"\"\"\n", 374 | " raise NotImplementedError\n", 375 | "\n", 376 | "\n", 377 | "class RegularGridSampler(PolygonPointSampler):\n", 378 | " def __init__(self, polygon=\"\", x_interval=100, y_interval=100):\n", 379 | " super(self.__class__, self).__init__(polygon)\n", 380 | " self.x_interval = x_interval\n", 381 | " self.y_interval = y_interval\n", 382 | "\n", 383 | " def perform_sampling(self):\n", 384 | " \"\"\"\n", 385 | " Perform sampling by substituting the polygon with a regular grid of\n", 386 | " sample points within it. The distance between the sample points is\n", 387 | " given by x_interval and y_interval.\n", 388 | " \"\"\"\n", 389 | " if not self.prepared:\n", 390 | " self.prepare_sampling()\n", 391 | " ll = self.polygon.bounds[:2]\n", 392 | " ur = self.polygon.bounds[2:]\n", 393 | " low_x = int(ll[0]) / self.x_interval * self.x_interval\n", 394 | " upp_x = int(ur[0]) / self.x_interval * self.x_interval + self.x_interval\n", 395 | " low_y = int(ll[1]) / self.y_interval * self.y_interval\n", 396 | " upp_y = int(ur[1]) / self.y_interval * self.y_interval + self.y_interval\n", 397 | "\n", 398 | " for x in floatrange(low_x, upp_x, self.x_interval):\n", 399 | " for y in floatrange(low_y, upp_y, self.y_interval):\n", 400 | " p = shapely.geometry.Point(x, y)\n", 401 | " if p.within(self.polygon):\n", 402 | " self.samples.append(p)\n", 403 | "\n", 404 | "\n", 405 | "def floatrange(start, stop, step):\n", 406 | " while start < stop:\n", 407 | " yield start\n", 408 | " start += step" 409 | ] 410 | }, 411 | { 412 | "cell_type": "markdown", 413 | "metadata": {}, 414 | "source": [ 415 | "## Sample a grid \n", 416 | "\n", 417 | "- Find the convex hull surrounding the measurements\n", 418 | "- Sample a grid as found with the help functions" 419 | ] 420 | }, 421 | { 422 | "cell_type": "code", 423 | "execution_count": null, 424 | "metadata": { 425 | "collapsed": false 426 | }, 427 | "outputs": [], 428 | "source": [ 429 | "convex_hull = shapely.geometry.MultiPoint(list(meuse.geometry)).convex_hull.buffer(150)\n", 430 | "sampler = RegularGridSampler(convex_hull, x_interval=50, y_interval=50)\n", 431 | "sampler.perform_sampling()\n", 432 | "grid_points = sampler.get_spatial_df()\n", 433 | "plt.figure(figsize=(4, 4))\n", 434 | "plt.plot(grid_points[\"x\"], grid_points[\"y\"], \".\")\n", 435 | "plt.plot(meuse[\"x\"], meuse[\"y\"], \"r.\")\n", 436 | "plt.title(\"Sampled grid\")" 437 | ] 438 | }, 439 | { 440 | "cell_type": "markdown", 441 | "metadata": {}, 442 | "source": [ 443 | "## Krige a value for each grid point" 444 | ] 445 | }, 446 | { 447 | "cell_type": "code", 448 | "execution_count": null, 449 | "metadata": { 450 | "collapsed": false 451 | }, 452 | "outputs": [], 453 | "source": [ 454 | "grid_points[\"prediction\"] = model.execute(\n", 455 | " style=\"points\", xpoints=grid_points[\"x\"], ypoints=grid_points[\"y\"]\n", 456 | ")[0].data" 457 | ] 458 | }, 459 | { 460 | "cell_type": "markdown", 461 | "metadata": {}, 462 | "source": [ 463 | "## Transform data for a map\n", 464 | "\n", 465 | "- Transform to lat long projection\n", 466 | "- From long to wide format\n", 467 | "- Fill in missing values" 468 | ] 469 | }, 470 | { 471 | "cell_type": "code", 472 | "execution_count": null, 473 | "metadata": { 474 | "collapsed": false 475 | }, 476 | "outputs": [], 477 | "source": [ 478 | "grid_points_gpd = grid_points.set_geometry(\"geometry\")\n", 479 | "grid_points_gpd.crs = {\"init\": \"epsg:28992\"}\n", 480 | "grid_points_gpd = grid_points_gpd.to_crs({\"init\": \"epsg:4326\"})\n", 481 | "grid_points_gpd[\"long\"] = grid_points_gpd.geometry.apply(lambda x: x.x)\n", 482 | "grid_points_gpd[\"lat\"] = grid_points_gpd.geometry.apply(lambda x: x.y)" 483 | ] 484 | }, 485 | { 486 | "cell_type": "code", 487 | "execution_count": null, 488 | "metadata": { 489 | "collapsed": false 490 | }, 491 | "outputs": [], 492 | "source": [ 493 | "grid_points_pivot = grid_points_gpd.pivot(\n", 494 | " values=\"prediction\", columns=\"x\", index=\"y\"\n", 495 | ").fillna(0)\n", 496 | "grid_points_pivot = grid_points_pivot.loc[\n", 497 | " :, grid_points_pivot.columns.sort_values(ascending=True)\n", 498 | "]\n", 499 | "grid_points_pivot = grid_points_pivot.loc[\n", 500 | " grid_points_pivot.index.sort_values(ascending=True), :\n", 501 | "]" 502 | ] 503 | }, 504 | { 505 | "cell_type": "code", 506 | "execution_count": null, 507 | "metadata": { 508 | "collapsed": false 509 | }, 510 | "outputs": [], 511 | "source": [ 512 | "plt.contourf(\n", 513 | " np.unique(grid_points_pivot.columns.values),\n", 514 | " np.unique(grid_points_pivot.index.values),\n", 515 | " grid_points_pivot.values / np.nanmax(grid_points_pivot.values),\n", 516 | " 20,\n", 517 | " cmap=\"GnBu\",\n", 518 | ")\n", 519 | "plt.plot(meuse[\"x\"], meuse[\"y\"], \".\")\n", 520 | "plt.title(\"Kriged grid values\")" 521 | ] 522 | }, 523 | { 524 | "cell_type": "markdown", 525 | "metadata": {}, 526 | "source": [ 527 | "## Folium plot" 528 | ] 529 | }, 530 | { 531 | "cell_type": "code", 532 | "execution_count": null, 533 | "metadata": { 534 | "collapsed": false 535 | }, 536 | "outputs": [], 537 | "source": [ 538 | "def color_function(value):\n", 539 | " if (value == 0) | (value == np.nan):\n", 540 | " return (0, 0, 0, 0)\n", 541 | " else:\n", 542 | " color = matplotlib.cm.YlOrRd(value)\n", 543 | " return color" 544 | ] 545 | }, 546 | { 547 | "cell_type": "code", 548 | "execution_count": null, 549 | "metadata": { 550 | "collapsed": false 551 | }, 552 | "outputs": [], 553 | "source": [ 554 | "m = folium.Map([mean_lat, mean_long], zoom_start=13, tiles=\"Stamen Toner\")\n", 555 | "m.add_children(\n", 556 | " plugins.ImageOverlay(\n", 557 | " image=(grid_points_pivot.values / np.nanmax(grid_points_pivot.values)),\n", 558 | " opacity=0.7,\n", 559 | " origin=\"lower\",\n", 560 | " colormap=color_function,\n", 561 | " bounds=[\n", 562 | " [np.min(grid_points_gpd[\"lat\"]), np.min(grid_points_gpd[\"long\"])],\n", 563 | " [np.max(grid_points_gpd[\"lat\"]), np.max(grid_points_gpd[\"long\"])],\n", 564 | " ],\n", 565 | " )\n", 566 | ")\n", 567 | "for row in meuse_lat_long.iterrows():\n", 568 | " folium.CircleMarker(\n", 569 | " location=[row[1][\"lat\"], row[1][\"long\"]],\n", 570 | " radius=50,\n", 571 | " color=None,\n", 572 | " fill_opacity=1,\n", 573 | " fill_color=scale(row[1][feature_to_plot]),\n", 574 | " ).add_to(m)\n", 575 | "m.add_children(scale)" 576 | ] 577 | }, 578 | { 579 | "cell_type": "code", 580 | "execution_count": null, 581 | "metadata": { 582 | "collapsed": true 583 | }, 584 | "outputs": [], 585 | "source": [] 586 | } 587 | ], 588 | "metadata": { 589 | "anaconda-cloud": {}, 590 | "kernelspec": { 591 | "display_name": "Python [pykrige_dev]", 592 | "language": "python", 593 | "name": "Python [pykrige_dev]" 594 | }, 595 | "language_info": { 596 | "codemirror_mode": { 597 | "name": "ipython", 598 | "version": 3 599 | }, 600 | "file_extension": ".py", 601 | "mimetype": "text/x-python", 602 | "name": "python", 603 | "nbconvert_exporter": "python", 604 | "pygments_lexer": "ipython3", 605 | "version": "3.5.2" 606 | } 607 | }, 608 | "nbformat": 4, 609 | "nbformat_minor": 0 610 | } 611 | -------------------------------------------------------------------------------- /examples/10_classification_kriging2d.py: -------------------------------------------------------------------------------- 1 | """ 2 | Classification kriging 3 | ---------------------- 4 | 5 | An example of classification kriging 6 | """ 7 | 8 | import sys 9 | 10 | from sklearn.datasets import fetch_california_housing 11 | from sklearn.ensemble import RandomForestClassifier 12 | from sklearn.linear_model import LogisticRegression 13 | from sklearn.model_selection import train_test_split 14 | from sklearn.preprocessing import KBinsDiscretizer 15 | from sklearn.svm import SVC 16 | 17 | from pykrige.ck import ClassificationKriging 18 | 19 | svc_model = SVC(C=0.1, gamma="auto", probability=True) 20 | rf_model = RandomForestClassifier(n_estimators=100) 21 | lr_model = LogisticRegression(max_iter=10000) 22 | 23 | models = [svc_model, rf_model, lr_model] 24 | 25 | try: 26 | housing = fetch_california_housing() 27 | except PermissionError: 28 | # this dataset can occasionally fail to download on Windows 29 | sys.exit(0) 30 | 31 | # take the first 5000 as Kriging is memory intensive 32 | p = housing["data"][:5000, :-2] 33 | x = housing["data"][:5000, -2:] 34 | target = housing["target"][:5000] 35 | discretizer = KBinsDiscretizer(encode="ordinal") 36 | target = discretizer.fit_transform(target.reshape(-1, 1)) 37 | 38 | p_train, p_test, x_train, x_test, target_train, target_test = train_test_split( 39 | p, x, target, test_size=0.3, random_state=42 40 | ) 41 | 42 | for m in models: 43 | print("=" * 40) 44 | print("classification model:", m.__class__.__name__) 45 | m_ck = ClassificationKriging(classification_model=m, n_closest_points=10) 46 | m_ck.fit(p_train, x_train, target_train) 47 | print( 48 | "Classification Score: ", m_ck.classification_model.score(p_test, target_test) 49 | ) 50 | print("CK score: ", m_ck.score(p_test, x_test, target_test)) 51 | -------------------------------------------------------------------------------- /examples/README.txt: -------------------------------------------------------------------------------- 1 | Examples 2 | ======== 3 | 4 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools>=64", 4 | "setuptools_scm>=7", 5 | "numpy>=2.0.0rc1,<2.3; python_version >= '3.9'", 6 | "scipy>=1.13.0,<2; python_version >= '3.9'", 7 | "oldest-supported-numpy; python_version < '3.9'", 8 | "scipy>=1.3.2,<2; python_version < '3.9'", 9 | "Cython>=3.0.10,<3.1.0", 10 | ] 11 | build-backend = "setuptools.build_meta" 12 | 13 | [project] 14 | requires-python = ">=3.8" 15 | name = "PyKrige" 16 | description = "Kriging Toolkit for Python." 17 | authors = [ 18 | {name = "Benjamin S. Murphy, Sebastian Müller", email = "info@geostat-framework.org"}, 19 | ] 20 | maintainers = [ 21 | {name = "Sebastian Müller, Roman Yurchak", email = "info@geostat-framework.org"}, 22 | ] 23 | readme = "README.md" 24 | license = {text = "BSD-3-Clause"} 25 | dynamic = ["version"] 26 | classifiers = [ 27 | "Development Status :: 5 - Production/Stable", 28 | "Intended Audience :: Developers", 29 | "Intended Audience :: End Users/Desktop", 30 | "Intended Audience :: Science/Research", 31 | "License :: OSI Approved :: BSD License", 32 | "Natural Language :: English", 33 | "Operating System :: Unix", 34 | "Operating System :: Microsoft", 35 | "Operating System :: MacOS", 36 | "Programming Language :: Python", 37 | "Programming Language :: Python :: 3", 38 | "Programming Language :: Python :: 3 :: Only", 39 | "Programming Language :: Python :: 3.8", 40 | "Programming Language :: Python :: 3.9", 41 | "Programming Language :: Python :: 3.10", 42 | "Programming Language :: Python :: 3.11", 43 | "Programming Language :: Python :: 3.12", 44 | "Topic :: Scientific/Engineering", 45 | "Topic :: Scientific/Engineering :: GIS", 46 | "Topic :: Scientific/Engineering :: Mathematics", 47 | "Topic :: Scientific/Engineering :: Physics", 48 | "Topic :: Utilities", 49 | ] 50 | dependencies = [ 51 | "numpy>=1.20.0", 52 | "scipy>=1.1.0,<2", 53 | ] 54 | 55 | [project.optional-dependencies] 56 | doc = [ 57 | "gstools>=1.4,<2", 58 | "pillow", 59 | "scikit-learn>=0.19", 60 | "m2r2>=0.2.8", 61 | "matplotlib>=3", 62 | "numpydoc>=1.1", 63 | "sphinx>=7", 64 | "sphinx-gallery>=0.8", 65 | "sphinx-rtd-theme>=2", 66 | ] 67 | plot = ["matplotlib>=3,<4"] 68 | sklearn = ["scikit-learn>=0.19"] 69 | test = [ 70 | "pytest-cov>=3", 71 | "scikit-learn>=0.19", 72 | "gstools>=1.4,<2", 73 | ] 74 | lint = [ 75 | "black>=23,<24", 76 | "pylint", 77 | "isort[colors]", 78 | "cython-lint", 79 | ] 80 | 81 | [project.urls] 82 | Changelog = "https://github.com/GeoStat-Framework/PyKrige/blob/main/CHANGELOG.md" 83 | Conda-Forge = "https://anaconda.org/conda-forge/pykrige" 84 | Documentation = "https://pykrige.readthedocs.io" 85 | Homepage = "https://github.com/GeoStat-Framework/PyKrige" 86 | Source = "https://github.com/GeoStat-Framework/PyKrige" 87 | Tracker = "https://github.com/GeoStat-Framework/PyKrige/issues" 88 | 89 | [tool.setuptools] 90 | license-files = ["LICENSE"] 91 | 92 | [tool.setuptools_scm] 93 | write_to = "src/pykrige/_version.py" 94 | write_to_template = "__version__ = '{version}'" 95 | local_scheme = "no-local-version" 96 | fallback_version = "0.0.0.dev0" 97 | 98 | [tool.isort] 99 | profile = "black" 100 | multi_line_output = 3 101 | 102 | [tool.black] 103 | target-version = [ 104 | "py38", 105 | "py39", 106 | "py310", 107 | "py311", 108 | "py312", 109 | ] 110 | 111 | [tool.cython-lint] 112 | max-line-length = 100 113 | 114 | [tool.coverage] 115 | [tool.coverage.run] 116 | source = ["pykrige"] 117 | omit = [ 118 | "*docs*", 119 | "*examples*", 120 | "*tests*", 121 | ] 122 | 123 | [tool.coverage.report] 124 | exclude_lines = [ 125 | "pragma: no cover", 126 | "def __repr__", 127 | "def __str__", 128 | ] 129 | 130 | [tool.pylint] 131 | [tool.pylint.master] 132 | extension-pkg-whitelist = [ 133 | "numpy", 134 | "scipy", 135 | ] 136 | ignore = "_version.py" 137 | 138 | [tool.pylint.message_control] 139 | disable = [ 140 | "R0801", 141 | "C0103", # lots of invalid variable names 142 | ] 143 | 144 | [tool.pylint.reports] 145 | output-format = "colorized" 146 | 147 | [tool.pylint.design] 148 | max-args = 20 149 | max-locals = 50 150 | max-branches = 30 151 | max-statements = 80 152 | max-attributes = 25 153 | max-public-methods = 75 154 | 155 | [tool.cibuildwheel] 156 | # Switch to using build 157 | build-frontend = "build" 158 | # Disable building PyPy wheels on all platforms, 32bit and musllinux builds, py3.6/7 159 | skip = ["cp36-*", "cp37-*", "pp*", "*-win32", "*-manylinux_i686", "*-musllinux_*"] 160 | # Run the package tests using `pytest` 161 | test-extras = "test" 162 | test-command = "pytest -v {package}/tests" 163 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Kriging Toolkit for Python.""" 3 | import os 4 | 5 | import numpy as np 6 | from Cython.Build import cythonize 7 | from setuptools import Extension, setup 8 | 9 | # cython extensions 10 | CY_MODULES = [ 11 | Extension( 12 | name=f"pykrige.{ext}", 13 | sources=[os.path.join("src", "pykrige", *ext.split(".")) + ".pyx"], 14 | include_dirs=[np.get_include()], 15 | define_macros=[("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")], 16 | ) 17 | for ext in ["lib.cok", "lib.variogram_models"] 18 | ] 19 | 20 | # setup - do not include package data to ignore .pyx files in wheels 21 | setup(ext_modules=cythonize(CY_MODULES), include_package_data=False) 22 | -------------------------------------------------------------------------------- /src/pykrige/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | PyKrige 3 | ======= 4 | 5 | Code by Benjamin S. Murphy and the PyKrige Developers 6 | bscott.murphy@gmail.com 7 | 8 | Summary 9 | ------- 10 | Kriging toolkit for Python. 11 | 12 | ok: Contains class OrdinaryKriging, which is a convenience class for easy 13 | access to 2D ordinary kriging. 14 | uk: Contains class UniversalKriging, which provides more control over 15 | 2D kriging by utilizing drift terms. Supported drift terms currently 16 | include point-logarithmic, regional linear, and external z-scalar. 17 | Generic functions of the spatial coordinates may also be supplied to 18 | provide drift terms, or the point-by-point values of a drift term 19 | may be supplied. 20 | ok3d: Contains class OrdinaryKriging3D, which provides support for 21 | 3D ordinary kriging. 22 | uk3d: Contains class UniversalKriging3D, which provide support for 23 | 3D universal kriging. A regional linear drift is the only drift term 24 | currently supported, but generic drift functions or point-by-point 25 | values of a drift term may also be supplied. 26 | kriging_tools: Contains a set of functions to work with *.asc files. 27 | variogram_models: Contains the definitions for the implemented variogram 28 | models. Note that the utilized formulas are as presented in Kitanidis, 29 | so the exact definition of the range (specifically, the associated 30 | scaling of that value) may differ slightly from other sources. 31 | core: Contains the backbone functions of the package that are called by both 32 | the various kriging classes. The functions were consolidated here 33 | in order to reduce redundancy in the code. 34 | test: Contains the test script. 35 | 36 | References 37 | ---------- 38 | .. [1] P.K. Kitanidis, Introduction to Geostatistics: Applications in 39 | Hydrogeology, (Cambridge University Press, 1997) 272 p. 40 | 41 | Copyright (c) 2015-2020, PyKrige Developers 42 | """ 43 | 44 | from . import kriging_tools as kt # noqa 45 | from .ok import OrdinaryKriging # noqa 46 | from .ok3d import OrdinaryKriging3D # noqa 47 | from .uk import UniversalKriging # noqa 48 | from .uk3d import UniversalKriging3D # noqa 49 | 50 | try: 51 | from pykrige._version import __version__ 52 | except ImportError: # pragma: nocover 53 | # package is not installed 54 | __version__ = "0.0.0.dev0" 55 | 56 | 57 | __author__ = "Benjamin S. Murphy" 58 | 59 | 60 | __all__ = ["__version__"] 61 | __all__ += ["kt", "ok", "uk", "ok3d", "uk3d", "kriging_tools"] 62 | __all__ += ["OrdinaryKriging"] 63 | __all__ += ["UniversalKriging"] 64 | __all__ += ["OrdinaryKriging3D"] 65 | __all__ += ["UniversalKriging3D"] 66 | -------------------------------------------------------------------------------- /src/pykrige/ck.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | """Classification Kriging.""" 3 | import numpy as np 4 | 5 | from pykrige.compat import Krige, check_sklearn_model, validate_sklearn 6 | 7 | validate_sklearn() 8 | 9 | from scipy.linalg import helmert 10 | from sklearn.metrics import accuracy_score 11 | from sklearn.preprocessing import OneHotEncoder 12 | from sklearn.svm import SVC 13 | 14 | 15 | class ClassificationKriging: 16 | """ 17 | An implementation of Simplicial Indicator Kriging applied to classification ilr transformed residuals. 18 | 19 | Parameters 20 | ---------- 21 | classification_model: machine learning model instance from sklearn 22 | method: str, optional 23 | type of kriging to be performed 24 | variogram_model: str, optional 25 | variogram model to be used during Kriging 26 | n_closest_points: int 27 | number of closest points to be used during Ordinary Kriging 28 | nlags: int 29 | see OK/UK class description 30 | weight: bool 31 | see OK/UK class description 32 | verbose: bool 33 | see OK/UK class description 34 | exact_values : bool 35 | see OK/UK class description 36 | variogram_parameters : list or dict 37 | see OK/UK class description 38 | variogram_function : callable 39 | see OK/UK class description 40 | anisotropy_scaling : tuple 41 | single value for 2D (UK/OK) and two values in 3D (UK3D/OK3D) 42 | anisotropy_angle : tuple 43 | single value for 2D (UK/OK) and three values in 3D (UK3D/OK3D) 44 | enable_statistics : bool 45 | see OK class description 46 | coordinates_type : str 47 | see OK/UK class description 48 | drift_terms : list of strings 49 | see UK/UK3D class description 50 | point_drift : array_like 51 | see UK class description 52 | ext_drift_grid : tuple 53 | Holding the three values external_drift, external_drift_x and 54 | external_drift_z for the UK class 55 | functional_drift : list of callable 56 | see UK/UK3D class description 57 | """ 58 | 59 | def __init__( 60 | self, 61 | classification_model=SVC(), 62 | method="ordinary", 63 | variogram_model="linear", 64 | n_closest_points=10, 65 | nlags=6, 66 | weight=False, 67 | verbose=False, 68 | exact_values=True, 69 | pseudo_inv=False, 70 | pseudo_inv_type="pinv", 71 | variogram_parameters=None, 72 | variogram_function=None, 73 | anisotropy_scaling=(1.0, 1.0), 74 | anisotropy_angle=(0.0, 0.0, 0.0), 75 | enable_statistics=False, 76 | coordinates_type="euclidean", 77 | drift_terms=None, 78 | point_drift=None, 79 | ext_drift_grid=(None, None, None), 80 | functional_drift=None, 81 | ): 82 | check_sklearn_model(classification_model, task="classification") 83 | self.classification_model = classification_model 84 | self.n_closest_points = n_closest_points 85 | self._kriging_kwargs = dict( 86 | method=method, 87 | variogram_model=variogram_model, 88 | nlags=nlags, 89 | weight=weight, 90 | n_closest_points=n_closest_points, 91 | verbose=verbose, 92 | exact_values=exact_values, 93 | pseudo_inv=pseudo_inv, 94 | pseudo_inv_type=pseudo_inv_type, 95 | variogram_parameters=variogram_parameters, 96 | variogram_function=variogram_function, 97 | anisotropy_scaling=anisotropy_scaling, 98 | anisotropy_angle=anisotropy_angle, 99 | enable_statistics=enable_statistics, 100 | coordinates_type=coordinates_type, 101 | drift_terms=drift_terms, 102 | point_drift=point_drift, 103 | ext_drift_grid=ext_drift_grid, 104 | functional_drift=functional_drift, 105 | ) 106 | 107 | def fit(self, p, x, y): 108 | """ 109 | Fit the classification method and also krige the residual. 110 | 111 | Parameters 112 | ---------- 113 | p: ndarray 114 | (Ns, d) array of predictor variables (Ns samples, d dimensions) 115 | for classification 116 | x: ndarray 117 | ndarray of (x, y) points. Needs to be a (Ns, 2) array 118 | corresponding to the lon/lat, for example 2d classification kriging. 119 | array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging 120 | y: ndarray 121 | array of targets (Ns, ) 122 | """ 123 | self.classification_model.fit(p, y.ravel()) 124 | print("Finished learning classification model") 125 | self.classes_ = self.classification_model.classes_ 126 | 127 | self.krige = [] 128 | for i in range(len(self.classes_) - 1): 129 | self.krige.append(Krige(**self._kriging_kwargs)) 130 | 131 | ml_pred = self.classification_model.predict_proba(p) 132 | ml_pred_ilr = ilr_transformation(ml_pred) 133 | 134 | self.onehotencode = OneHotEncoder(categories=[self.classes_]) 135 | y_ohe = np.array(self.onehotencode.fit_transform(y).todense()) 136 | y_ohe_ilr = ilr_transformation(y_ohe) 137 | 138 | for i in range(len(self.classes_) - 1): 139 | self.krige[i].fit(x=x, y=y_ohe_ilr[:, i] - ml_pred_ilr[:, i]) 140 | 141 | print("Finished kriging residuals") 142 | 143 | def predict(self, p, x, **kwargs): 144 | """ 145 | Predict. 146 | 147 | Parameters 148 | ---------- 149 | p: ndarray 150 | (Ns, d) array of predictor variables (Ns samples, d dimensions) 151 | for classification 152 | x: ndarray 153 | ndarray of (x, y) points. Needs to be a (Ns, 2) array 154 | corresponding to the lon/lat, for example. 155 | array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging 156 | 157 | Returns 158 | ------- 159 | pred: ndarray 160 | The expected value of ys for the query inputs, of shape (Ns,). 161 | 162 | """ 163 | 164 | ml_pred = self.classification_model.predict_proba(p) 165 | ml_pred_ilr = ilr_transformation(ml_pred) 166 | 167 | pred_proba_ilr = self.krige_residual(x, **kwargs) + ml_pred_ilr 168 | pred_proba = inverse_ilr_transformation(pred_proba_ilr) 169 | 170 | return np.argmax(pred_proba, axis=1) 171 | 172 | def krige_residual(self, x, **kwargs): 173 | """ 174 | Calculate the residuals. 175 | 176 | Parameters 177 | ---------- 178 | x: ndarray 179 | ndarray of (x, y) points. Needs to be a (Ns, 2) array 180 | corresponding to the lon/lat, for example. 181 | 182 | Returns 183 | ------- 184 | residual: ndarray 185 | kriged residual values 186 | """ 187 | 188 | krig_pred = [ 189 | self.krige[i].predict(x=x, **kwargs) for i in range(len(self.classes_) - 1) 190 | ] 191 | 192 | return np.vstack(krig_pred).T 193 | 194 | def score(self, p, x, y, sample_weight=None, **kwargs): 195 | """ 196 | Overloading default classification score method. 197 | 198 | Parameters 199 | ---------- 200 | p: ndarray 201 | (Ns, d) array of predictor variables (Ns samples, d dimensions) 202 | for classification 203 | x: ndarray 204 | ndarray of (x, y) points. Needs to be a (Ns, 2) array 205 | corresponding to the lon/lat, for example. 206 | array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging 207 | y: ndarray 208 | array of targets (Ns, ) 209 | """ 210 | return accuracy_score( 211 | y_pred=self.predict(p, x, **kwargs), y_true=y, sample_weight=sample_weight 212 | ) 213 | 214 | 215 | def closure(data, k=1.0): 216 | """Apply closure to data, sample-wise. 217 | Adapted from https://github.com/ofgulban/compoda. 218 | 219 | Parameters 220 | ---------- 221 | data : 2d numpy array, shape [n_samples, n_measurements] 222 | Data to be closed to a certain constant. Do not forget to deal with 223 | zeros in the data before this operation. 224 | k : float, positive 225 | Sum of the measurements will be equal to this number. 226 | 227 | Returns 228 | ------- 229 | data : 2d numpy array, shape [n_samples, n_measurements] 230 | Closed data. 231 | 232 | Reference 233 | --------- 234 | [1] Pawlowsky-Glahn, V., Egozcue, J. J., & Tolosana-Delgado, R. 235 | (2015). Modelling and Analysis of Compositional Data, pg. 9. 236 | Chichester, UK: John Wiley & Sons, Ltd. 237 | DOI: 10.1002/9781119003144 238 | """ 239 | 240 | return k * data / np.sum(data, axis=1)[:, np.newaxis] 241 | 242 | 243 | def ilr_transformation(data): 244 | """Isometric logratio transformation (not vectorized). 245 | Adapted from https://github.com/ofgulban/compoda. 246 | 247 | Parameters 248 | ---------- 249 | data : 2d numpy array, shape [n_samples, n_coordinates] 250 | Barycentric coordinates (closed) in simplex space. 251 | 252 | Returns 253 | ------- 254 | out : 2d numpy array, shape [n_samples, n_coordinates-1] 255 | Coordinates in real space. 256 | 257 | Reference 258 | --------- 259 | [1] Pawlowsky-Glahn, V., Egozcue, J. J., & Tolosana-Delgado, R. 260 | (2015). Modelling and Analysis of Compositional Data, pg. 37. 261 | Chichester, UK: John Wiley & Sons, Ltd. 262 | DOI: 10.1002/9781119003144 263 | """ 264 | data = np.maximum(data, np.finfo(float).eps) 265 | 266 | return np.einsum("ij,jk->ik", np.log(data), -helmert(data.shape[1]).T) 267 | 268 | 269 | def inverse_ilr_transformation(data): 270 | """Inverse isometric logratio transformation (not vectorized). 271 | Adapted from https://github.com/ofgulban/compoda. 272 | 273 | Parameters 274 | ---------- 275 | data : 2d numpy array, shape [n_samples, n_coordinates] 276 | Isometric log-ratio transformed coordinates in real space. 277 | 278 | Returns 279 | ------- 280 | out : 2d numpy array, shape [n_samples, n_coordinates+1] 281 | Barycentric coordinates (closed) in simplex space. 282 | 283 | Reference 284 | --------- 285 | [1] Pawlowsky-Glahn, V., Egozcue, J. J., & Tolosana-Delgado, R. 286 | (2015). Modelling and Analysis of Compositional Data, pg. 37. 287 | Chichester, UK: John Wiley & Sons, Ltd. 288 | DOI: 10.1002/9781119003144 289 | """ 290 | 291 | return closure(np.exp(np.einsum("ij,jk->ik", data, -helmert(data.shape[1] + 1)))) 292 | -------------------------------------------------------------------------------- /src/pykrige/compat.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # pylint: disable= invalid-name, unused-import 3 | """For compatibility.""" 4 | from pykrige.ok import OrdinaryKriging 5 | from pykrige.ok3d import OrdinaryKriging3D 6 | from pykrige.uk import UniversalKriging 7 | from pykrige.uk3d import UniversalKriging3D 8 | 9 | # sklearn 10 | try: 11 | # keep train_test_split here for backward compatibility 12 | from sklearn.base import BaseEstimator, ClassifierMixin, RegressorMixin 13 | from sklearn.model_selection import train_test_split 14 | 15 | SKLEARN_INSTALLED = True 16 | 17 | except ImportError: 18 | SKLEARN_INSTALLED = False 19 | 20 | train_test_split = None 21 | 22 | class RegressorMixin: 23 | """Mock RegressorMixin.""" 24 | 25 | class ClassifierMixin: 26 | """Mock ClassifierMixin.""" 27 | 28 | class BaseEstimator: 29 | """Mock BaseEstimator.""" 30 | 31 | 32 | krige_methods = { 33 | "ordinary": OrdinaryKriging, 34 | "universal": UniversalKriging, 35 | "ordinary3d": OrdinaryKriging3D, 36 | "universal3d": UniversalKriging3D, 37 | } 38 | 39 | threed_krige = ("ordinary3d", "universal3d") 40 | 41 | krige_methods_kws = { 42 | "ordinary": [ 43 | "anisotropy_scaling", 44 | "anisotropy_angle", 45 | "enable_statistics", 46 | "coordinates_type", 47 | ], 48 | "universal": [ 49 | "anisotropy_scaling", 50 | "anisotropy_angle", 51 | "drift_terms", 52 | "point_drift", 53 | "external_drift", 54 | "external_drift_x", 55 | "external_drift_y", 56 | "functional_drift", 57 | ], 58 | "ordinary3d": [ 59 | "anisotropy_scaling_y", 60 | "anisotropy_scaling_z", 61 | "anisotropy_angle_x", 62 | "anisotropy_angle_y", 63 | "anisotropy_angle_z", 64 | ], 65 | "universal3d": [ 66 | "anisotropy_scaling_y", 67 | "anisotropy_scaling_z", 68 | "anisotropy_angle_x", 69 | "anisotropy_angle_y", 70 | "anisotropy_angle_z", 71 | "drift_terms", 72 | "functional_drift", 73 | ], 74 | } 75 | 76 | 77 | class SklearnException(Exception): 78 | """Exception for missing scikit-learn.""" 79 | 80 | 81 | def validate_method(method): 82 | """Validate the kriging method in use.""" 83 | if method not in krige_methods.keys(): 84 | raise ValueError( 85 | "Kriging method must be one of {}".format(krige_methods.keys()) 86 | ) 87 | 88 | 89 | def validate_sklearn(): 90 | """Validate presence of scikit-learn.""" 91 | if not SKLEARN_INSTALLED: 92 | raise SklearnException( 93 | "sklearn needs to be installed in order to use this module" 94 | ) 95 | 96 | 97 | class Krige(RegressorMixin, BaseEstimator): 98 | """ 99 | A scikit-learn wrapper class for Ordinary and Universal Kriging. 100 | 101 | This works with both Grid/RandomSearchCv for finding the best 102 | Krige parameters combination for a problem. 103 | 104 | Parameters 105 | ---------- 106 | method: str, optional 107 | type of kriging to be performed 108 | variogram_model: str, optional 109 | variogram model to be used during Kriging 110 | nlags: int 111 | see OK/UK class description 112 | weight: bool 113 | see OK/UK class description 114 | n_closest_points: int 115 | number of closest points to be used during Ordinary Kriging 116 | verbose: bool 117 | see OK/UK class description 118 | exact_values : bool 119 | see OK/UK class description 120 | variogram_parameters : list or dict 121 | see OK/UK class description 122 | variogram_function : callable 123 | see OK/UK class description 124 | anisotropy_scaling : tuple 125 | single value for 2D (UK/OK) and two values in 3D (UK3D/OK3D) 126 | anisotropy_angle : tuple 127 | single value for 2D (UK/OK) and three values in 3D (UK3D/OK3D) 128 | enable_statistics : bool 129 | see OK class description 130 | coordinates_type : str 131 | see OK/UK class description 132 | drift_terms : list of strings 133 | see UK/UK3D class description 134 | point_drift : array_like 135 | see UK class description 136 | ext_drift_grid : tuple 137 | Holding the three values external_drift, external_drift_x and 138 | external_drift_z for the UK class 139 | functional_drift : list of callable 140 | see UK/UK3D class description 141 | """ 142 | 143 | def __init__( 144 | self, 145 | method="ordinary", 146 | variogram_model="linear", 147 | nlags=6, 148 | weight=False, 149 | n_closest_points=10, 150 | verbose=False, 151 | exact_values=True, 152 | pseudo_inv=False, 153 | pseudo_inv_type="pinv", 154 | variogram_parameters=None, 155 | variogram_function=None, 156 | anisotropy_scaling=(1.0, 1.0), 157 | anisotropy_angle=(0.0, 0.0, 0.0), 158 | enable_statistics=False, 159 | coordinates_type="euclidean", 160 | drift_terms=None, 161 | point_drift=None, 162 | ext_drift_grid=(None, None, None), 163 | functional_drift=None, 164 | ): 165 | validate_method(method) 166 | self.variogram_model = variogram_model 167 | self.variogram_parameters = variogram_parameters 168 | self.variogram_function = variogram_function 169 | self.nlags = nlags 170 | self.weight = weight 171 | self.verbose = verbose 172 | self.exact_values = exact_values 173 | self.pseudo_inv = pseudo_inv 174 | self.pseudo_inv_type = pseudo_inv_type 175 | self.anisotropy_scaling = anisotropy_scaling 176 | self.anisotropy_angle = anisotropy_angle 177 | self.enable_statistics = enable_statistics 178 | self.coordinates_type = coordinates_type 179 | self.drift_terms = drift_terms 180 | self.point_drift = point_drift 181 | self.ext_drift_grid = ext_drift_grid 182 | self.functional_drift = functional_drift 183 | self.model = None # not trained 184 | self.n_closest_points = n_closest_points 185 | self.method = method 186 | 187 | def fit(self, x, y, *args, **kwargs): 188 | """ 189 | Fit the current model. 190 | 191 | Parameters 192 | ---------- 193 | x: ndarray 194 | array of Points, (x, y) pairs of shape (N, 2) for 2d kriging 195 | array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging 196 | y: ndarray 197 | array of targets (N, ) 198 | """ 199 | val_kw = "val" if self.method in threed_krige else "z" 200 | setup = dict( 201 | variogram_model=self.variogram_model, 202 | variogram_parameters=self.variogram_parameters, 203 | variogram_function=self.variogram_function, 204 | nlags=self.nlags, 205 | weight=self.weight, 206 | verbose=self.verbose, 207 | exact_values=self.exact_values, 208 | pseudo_inv=self.pseudo_inv, 209 | pseudo_inv_type=self.pseudo_inv_type, 210 | ) 211 | add_setup = dict( 212 | anisotropy_scaling=self.anisotropy_scaling[0], 213 | anisotropy_angle=self.anisotropy_angle[0], 214 | enable_statistics=self.enable_statistics, 215 | coordinates_type=self.coordinates_type, 216 | anisotropy_scaling_y=self.anisotropy_scaling[0], 217 | anisotropy_scaling_z=self.anisotropy_scaling[1], 218 | anisotropy_angle_x=self.anisotropy_angle[0], 219 | anisotropy_angle_y=self.anisotropy_angle[1], 220 | anisotropy_angle_z=self.anisotropy_angle[2], 221 | drift_terms=self.drift_terms, 222 | point_drift=self.point_drift, 223 | external_drift=self.ext_drift_grid[0], 224 | external_drift_x=self.ext_drift_grid[1], 225 | external_drift_y=self.ext_drift_grid[2], 226 | functional_drift=self.functional_drift, 227 | ) 228 | for kw in krige_methods_kws[self.method]: 229 | setup[kw] = add_setup[kw] 230 | input_kw = self._dimensionality_check(x) 231 | input_kw.update(setup) 232 | input_kw[val_kw] = y 233 | self.model = krige_methods[self.method](**input_kw) 234 | 235 | def _dimensionality_check(self, x, ext=""): 236 | if self.method in ("ordinary", "universal"): 237 | if x.shape[1] != 2: 238 | raise ValueError("2d krige can use only 2d points") 239 | else: 240 | return {"x" + ext: x[:, 0], "y" + ext: x[:, 1]} 241 | if self.method in ("ordinary3d", "universal3d"): 242 | if x.shape[1] != 3: 243 | raise ValueError("3d krige can use only 3d points") 244 | else: 245 | return { 246 | "x" + ext: x[:, 0], 247 | "y" + ext: x[:, 1], 248 | "z" + ext: x[:, 2], 249 | } 250 | 251 | def predict(self, x, *args, **kwargs): 252 | """ 253 | Predict. 254 | 255 | Parameters 256 | ---------- 257 | x: ndarray 258 | array of Points, (x, y) pairs of shape (N, 2) for 2d kriging 259 | array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging 260 | Returns 261 | ------- 262 | Prediction array 263 | """ 264 | if not self.model: 265 | raise Exception("Not trained. Train first") 266 | points = self._dimensionality_check(x, ext="points") 267 | return self.execute(points, *args, **kwargs)[0] 268 | 269 | def execute(self, points, *args, **kwargs): 270 | # TODO array of Points, (x, y) pairs of shape (N, 2) 271 | """ 272 | Execute. 273 | 274 | Parameters 275 | ---------- 276 | points: dict 277 | 278 | Returns 279 | ------- 280 | Prediction array 281 | Variance array 282 | """ 283 | default_kw = dict(style="points", backend="loop") 284 | default_kw.update(kwargs) 285 | points.update(default_kw) 286 | if isinstance(self.model, (OrdinaryKriging, OrdinaryKriging3D)): 287 | points.update(dict(n_closest_points=self.n_closest_points)) 288 | else: 289 | print("n_closest_points will be ignored for UniversalKriging") 290 | prediction, variance = self.model.execute(**points) 291 | return prediction, variance 292 | 293 | 294 | def check_sklearn_model(model, task="regression"): 295 | """Check the sklearn method in use.""" 296 | if task == "regression": 297 | if not (isinstance(model, BaseEstimator) and isinstance(model, RegressorMixin)): 298 | raise RuntimeError( 299 | "Needs to supply an instance of a scikit-learn regression class." 300 | ) 301 | elif task == "classification": 302 | if not ( 303 | isinstance(model, BaseEstimator) and isinstance(model, ClassifierMixin) 304 | ): 305 | raise RuntimeError( 306 | "Needs to supply an instance of a scikit-learn classification class." 307 | ) 308 | -------------------------------------------------------------------------------- /src/pykrige/compat_gstools.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # pylint: disable= invalid-name, unused-import 3 | """For GSTools compatibility.""" 4 | 5 | # gstools 6 | try: 7 | import gstools as gs 8 | 9 | GSTOOLS_INSTALLED = True 10 | GSTOOLS_VERSION = list(map(int, gs.__version__.split(".")[:2])) 11 | except ImportError: 12 | gs = None 13 | GSTOOLS_INSTALLED = False 14 | GSTOOLS_VERSION = None 15 | 16 | 17 | class GSToolsException(Exception): 18 | """Exception for GSTools.""" 19 | 20 | 21 | def validate_gstools(model): 22 | """Validate presence of GSTools.""" 23 | if not GSTOOLS_INSTALLED: 24 | raise GSToolsException( 25 | "GSTools needs to be installed in order to use their CovModel class." 26 | ) 27 | if not isinstance(model, gs.CovModel): 28 | raise GSToolsException( 29 | "GSTools: given variogram model is not a CovModel instance." 30 | ) 31 | if GSTOOLS_VERSION < [1, 3]: 32 | raise GSToolsException("GSTools: need at least GSTools v1.3.") 33 | if model.latlon and GSTOOLS_VERSION < [1, 4]: 34 | raise GSToolsException( 35 | "GSTools: latlon models in PyKrige are only supported from GSTools v1.4." 36 | ) 37 | -------------------------------------------------------------------------------- /src/pykrige/kriging_tools.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | """ 3 | PyKrige 4 | ======= 5 | 6 | Code by Benjamin S. Murphy and the PyKrige Developers 7 | bscott.murphy@gmail.com 8 | 9 | Summary 10 | ------- 11 | Methods for reading/writing ASCII grid files. 12 | 13 | Copyright (c) 2015-2020, PyKrige Developers 14 | """ 15 | import datetime 16 | import io 17 | import os 18 | import warnings 19 | 20 | import numpy as np 21 | 22 | 23 | def write_asc_grid(x, y, z, filename="output.asc", no_data=-999.0, style=1): 24 | r"""Writes gridded data to ASCII grid file (\*.asc). 25 | 26 | This is useful for exporting data to a GIS program. 27 | 28 | Parameters 29 | ---------- 30 | x : array_like, shape (N,) or (N, 1) 31 | X-coordinates of grid points at center of cells. 32 | y : array_like, shape (M,) or (M, 1) 33 | Y-coordinates of grid points at center of cells. 34 | z : array_like, shape (M, N) 35 | Gridded data values. May be a masked array. 36 | filename : string, optional 37 | Name of output \*.asc file. Default name is 'output.asc'. 38 | no_data : float, optional 39 | no data value to be used 40 | style : int, optional 41 | Determines how to write the \*.asc file header. 42 | Specifying 1 writes out DX, DY, XLLCENTER, YLLCENTER. 43 | Specifying 2 writes out CELLSIZE (note DX must be the same as DY), 44 | XLLCORNER, YLLCORNER. Default is 1. 45 | """ 46 | 47 | if np.ma.is_masked(z): 48 | z = np.array(z.tolist(no_data)) 49 | 50 | x = np.squeeze(np.array(x)) 51 | y = np.squeeze(np.array(y)) 52 | z = np.squeeze(np.array(z)) 53 | nrows = z.shape[0] 54 | ncols = z.shape[1] 55 | 56 | if z.ndim != 2: 57 | raise ValueError("Two-dimensional grid is required to write *.asc grid.") 58 | if x.ndim > 1 or y.ndim > 1: 59 | raise ValueError( 60 | "Dimensions of X and/or Y coordinate arrays are not " 61 | "as expected. Could not write *.asc grid." 62 | ) 63 | if z.shape != (y.size, x.size): 64 | warnings.warn( 65 | "Grid dimensions are not as expected. " 66 | "Incorrect *.asc file generation may result.", 67 | RuntimeWarning, 68 | ) 69 | if np.amin(x) != x[0] or np.amin(y) != y[0]: 70 | warnings.warn( 71 | "Order of X or Y coordinates is not as expected. " 72 | "Incorrect *.asc file generation may result.", 73 | RuntimeWarning, 74 | ) 75 | 76 | dx = abs(x[1] - x[0]) 77 | dy = abs(y[1] - y[0]) 78 | if not np.isclose(abs((x[-1] - x[0]) / (x.shape[0] - 1)), dx) or not np.isclose( 79 | abs((y[-1] - y[0]) / (y.shape[0] - 1)), dy 80 | ): 81 | raise ValueError( 82 | "X or Y spacing is not constant; *.asc grid cannot be written." 83 | ) 84 | cellsize = -1 85 | if style == 2: 86 | if dx != dy: 87 | raise ValueError( 88 | "X and Y spacing is not the same. " 89 | "Cannot write *.asc file in the specified format." 90 | ) 91 | cellsize = dx 92 | 93 | xllcenter = x[0] 94 | yllcenter = y[0] 95 | 96 | # Note that these values are flagged as -1. If there is a problem in trying 97 | # to write out style 2, the -1 value will appear in the output file. 98 | xllcorner = -1 99 | yllcorner = -1 100 | if style == 2: 101 | xllcorner = xllcenter - dx / 2.0 102 | yllcorner = yllcenter - dy / 2.0 103 | 104 | with io.open(filename, "w") as f: 105 | if style == 1: 106 | f.write("NCOLS " + "{:<10n}".format(ncols) + "\n") 107 | f.write("NROWS " + "{:<10n}".format(nrows) + "\n") 108 | f.write("XLLCENTER " + "{:<10.2f}".format(xllcenter) + "\n") 109 | f.write("YLLCENTER " + "{:<10.2f}".format(yllcenter) + "\n") 110 | f.write("DX " + "{:<10.2f}".format(dx) + "\n") 111 | f.write("DY " + "{:<10.2f}".format(dy) + "\n") 112 | f.write("NODATA_VALUE " + "{:<10.2f}".format(no_data) + "\n") 113 | elif style == 2: 114 | f.write("NCOLS " + "{:<10n}".format(ncols) + "\n") 115 | f.write("NROWS " + "{:<10n}".format(nrows) + "\n") 116 | f.write("XLLCORNER " + "{:<10.2f}".format(xllcorner) + "\n") 117 | f.write("YLLCORNER " + "{:<10.2f}".format(yllcorner) + "\n") 118 | f.write("CELLSIZE " + "{:<10.2f}".format(cellsize) + "\n") 119 | f.write("NODATA_VALUE " + "{:<10.2f}".format(no_data) + "\n") 120 | else: 121 | raise ValueError("style kwarg must be either 1 or 2.") 122 | 123 | for m in range(z.shape[0] - 1, -1, -1): 124 | for n in range(z.shape[1]): 125 | f.write("{:<16.2f}".format(z[m, n])) 126 | if m != 0: 127 | f.write("\n") 128 | 129 | 130 | def read_asc_grid(filename, footer=0): 131 | r"""Reads ASCII grid file (\*.asc). 132 | 133 | Parameters 134 | ---------- 135 | filename : str 136 | Name of \*.asc file. 137 | footer : int, optional 138 | Number of lines at bottom of \*.asc file to skip. 139 | 140 | Returns 141 | ------- 142 | grid_array : numpy array, shape (M, N) 143 | (M, N) array of grid values, where M is number of Y-coordinates and 144 | N is number of X-coordinates. The array entry corresponding to 145 | the lower-left coordinates is at index [M, 0], so that 146 | the array is oriented as it would be in X-Y space. 147 | x : numpy array, shape (N,) 148 | 1D array of N X-coordinates. 149 | y : numpy array, shape (M,) 150 | 1D array of M Y-coordinates. 151 | CELLSIZE : tuple or float 152 | Either a two-tuple of (x-cell size, y-cell size), 153 | or a float that specifies the uniform cell size. 154 | NODATA : float 155 | Value that specifies which entries are not actual data. 156 | """ 157 | 158 | ncols = None 159 | nrows = None 160 | xllcorner = None 161 | xllcenter = None 162 | yllcorner = None 163 | yllcenter = None 164 | cellsize = None 165 | dx = None 166 | dy = None 167 | no_data = None 168 | header_lines = 0 169 | with io.open(filename, "r") as f: 170 | while True: 171 | string, value = f.readline().split() 172 | header_lines += 1 173 | if string.lower() == "ncols": 174 | ncols = int(value) 175 | elif string.lower() == "nrows": 176 | nrows = int(value) 177 | elif string.lower() == "xllcorner": 178 | xllcorner = float(value) 179 | elif string.lower() == "xllcenter": 180 | xllcenter = float(value) 181 | elif string.lower() == "yllcorner": 182 | yllcorner = float(value) 183 | elif string.lower() == "yllcenter": 184 | yllcenter = float(value) 185 | elif string.lower() == "cellsize": 186 | cellsize = float(value) 187 | elif string.lower() == "cell_size": 188 | cellsize = float(value) 189 | elif string.lower() == "dx": 190 | dx = float(value) 191 | elif string.lower() == "dy": 192 | dy = float(value) 193 | elif string.lower() == "nodata_value": 194 | no_data = float(value) 195 | elif string.lower() == "nodatavalue": 196 | no_data = float(value) 197 | else: 198 | raise IOError("could not read *.asc file. Error in header.") 199 | 200 | if ( 201 | (ncols is not None) 202 | and (nrows is not None) 203 | and ( 204 | ((xllcorner is not None) and (yllcorner is not None)) 205 | or ((xllcenter is not None) and (yllcenter is not None)) 206 | ) 207 | and ((cellsize is not None) or ((dx is not None) and (dy is not None))) 208 | and (no_data is not None) 209 | ): 210 | break 211 | 212 | raw_grid_array = np.genfromtxt( 213 | filename, skip_header=header_lines, skip_footer=footer 214 | ) 215 | grid_array = np.flipud(raw_grid_array) 216 | 217 | if nrows != grid_array.shape[0] or ncols != grid_array.shape[1]: 218 | raise IOError( 219 | "Error reading *.asc file. Encountered problem " 220 | "with header: NCOLS and/or NROWS does not match " 221 | "number of columns/rows in data file body." 222 | ) 223 | 224 | if xllcorner is not None and yllcorner is not None: 225 | if dx is not None and dy is not None: 226 | xllcenter = xllcorner + dx / 2.0 227 | yllcenter = yllcorner + dy / 2.0 228 | else: 229 | xllcenter = xllcorner + cellsize / 2.0 230 | yllcenter = yllcorner + cellsize / 2.0 231 | 232 | if dx is not None and dy is not None: 233 | x = np.arange(xllcenter, xllcenter + ncols * dx, dx) 234 | y = np.arange(yllcenter, yllcenter + nrows * dy, dy) 235 | else: 236 | x = np.arange(xllcenter, xllcenter + ncols * cellsize, cellsize) 237 | y = np.arange(yllcenter, yllcenter + nrows * cellsize, cellsize) 238 | 239 | # Sometimes x and y and can be an entry too long due to imprecision 240 | # in calculating the upper cutoff for np.arange(); this bit takes care of 241 | # that potential problem. 242 | if x.size == ncols + 1: 243 | x = x[:-1] 244 | if y.size == nrows + 1: 245 | y = y[:-1] 246 | 247 | if cellsize is None: 248 | cellsize = (dx, dy) 249 | 250 | return grid_array, x, y, cellsize, no_data 251 | 252 | 253 | def write_zmap_grid( 254 | x, y, z, filename="output.zmap", no_data=-999.0, coord_sys="" 255 | ): 256 | r"""Writes gridded data to ASCII grid file in zmap format (\*.zmap). 257 | 258 | This is useful for exporting data to a GIS program, or Petrel 259 | https://gdal.org/drivers/raster/zmap.html 260 | 261 | Parameters 262 | ---------- 263 | x : array_like, shape (N,) or (N, 1) 264 | X-coordinates of grid points at center of cells. 265 | y : array_like, shape (M,) or (M, 1) 266 | Y-coordinates of grid points at center of cells. 267 | z : array_like, shape (M, N) 268 | Gridded data values. May be a masked array. 269 | filename : string, optional 270 | Name of output \*.zmap file. Default name is 'output.zmap'. 271 | no_data : float, optional 272 | no data value to be used 273 | coord_sys : String, optional 274 | coordinate sytem description 275 | """ 276 | 277 | nodes_per_line = 5 278 | field_width = 15 279 | 280 | if np.ma.is_masked(z): 281 | z = np.array(z.tolist(no_data)) 282 | 283 | x = np.squeeze(np.array(x)) 284 | y = np.squeeze(np.array(y)) 285 | z = np.squeeze(np.array(z)) 286 | nx = len(x) 287 | ny = len(y) 288 | 289 | dx = abs(x[1] - x[0]) 290 | dy = abs(y[1] - y[0]) 291 | 292 | if not np.isclose(abs((x[-1] - x[0]) / (x.shape[0] - 1)), dx) or not np.isclose( 293 | abs((y[-1] - y[0]) / (y.shape[0] - 1)), dy 294 | ): 295 | raise ValueError( 296 | "X or Y spacing is not constant; *.asc grid cannot be written." 297 | ) 298 | 299 | xllcenter = x[0] 300 | yllcenter = y[0] 301 | 302 | hix = xllcenter + (nx - 1) * dx 303 | hiy = yllcenter + (ny - 1) * dy 304 | 305 | now = datetime.datetime.now() 306 | 307 | with io.open(filename, "w") as f: 308 | f.write("!" + "\n") 309 | f.write("! ZIMS FILE NAME : " + os.path.basename(filename) + "\n") 310 | f.write( 311 | "! FORMATTED FILE CREATION DATE: " + now.strftime("%d/%m/%Y") + "\n" 312 | ) 313 | f.write( 314 | "! FORMATTED FILE CREATION TIME: " + now.strftime("%H:%M:%S") + "\n" 315 | ) 316 | f.write("! COORDINATE REFERENCE SYSTEM: " + coord_sys + "\n") 317 | f.write("!" + "\n") 318 | f.write("@Grid HEADER, GRID, " + str(nodes_per_line) + "\n") 319 | f.write(" " + str(field_width) + ", " + str(no_data) + ", , 1 , 1" + "\n") 320 | f.write( 321 | " " 322 | + str(ny) 323 | + ", " 324 | + str(nx) 325 | + ", " 326 | + str(xllcenter) 327 | + ", " 328 | + str(hix) 329 | + ", " 330 | + str(yllcenter) 331 | + ", " 332 | + str(hiy) 333 | + "\n" 334 | ) 335 | f.write(" " + str(dx) + ", 0.0, 0.0 " + "\n") 336 | f.write("@" + "\n") 337 | 338 | for n in range(z.shape[1]): 339 | count = 0 340 | for m in range(z.shape[0] - 1, -1, -1): 341 | count += 1 342 | if np.isnan(z[m, n]): 343 | f.write(space_back_to_front(format(no_data, "13.7E") + " ")) 344 | else: 345 | if abs(z[m, n]) >= 1e100: # one tailing space less 346 | f.write(space_back_to_front(format(z[m, n], "13.7E") + " ")) 347 | elif abs(z[m, n]) >= 1e6: 348 | f.write(space_back_to_front(format(z[m, n], "13.7E") + " ")) 349 | else: 350 | f.write(space_back_to_front("{:<13.4f}".format(z[m, n]) + " ")) 351 | if count % nodes_per_line == 0 or m == 0: 352 | f.write("\n") 353 | 354 | 355 | def read_zmap_grid(filename): 356 | r"""Reads ASCII grid file in zmap format (\*.zmap). 357 | https://gdal.org/drivers/raster/zmap.html 358 | 359 | Parameters 360 | ---------- 361 | filename : str 362 | Name of \*.zmap file. 363 | 364 | Returns 365 | ------- 366 | grid_array : numpy array, shape (M, N) 367 | (M, N) array of grid values, where M is number of Y-coordinates and 368 | N is number of X-coordinates. The array entry corresponding to 369 | the lower-left coordinates is at index [M, 0], so that 370 | the array is oriented as it would be in X-Y space. 371 | x : numpy array, shape (N,) 372 | 1D array of N X-coordinates. 373 | y : numpy array, shape (M,) 374 | 1D array of M Y-coordinates. 375 | cellsize : tuple or float 376 | Either a two-tuple of (x-cell size, y-cell size), 377 | or a float that specifies the uniform cell size. 378 | no_data_value : float 379 | Value that specifies which entries are not actual data. 380 | coord_sys : String 381 | Coordinate system name 382 | """ 383 | 384 | no_data_value, nx, ny, originx, originy, maxx, maxy, dx, dy = ( 385 | 0, 386 | 0, 387 | 0, 388 | 0, 389 | 0, 390 | 0, 391 | 0, 392 | 0, 393 | 0, 394 | ) 395 | data_values = np.empty(1) 396 | coord_sys = "" 397 | 398 | i_header_line, i_value = 0, 0 399 | with io.open(filename, "r") as f: 400 | while True: 401 | line = f.readline() 402 | if line.startswith("!"): 403 | line_strings = line.split(":") 404 | if line_strings[0].__contains__("COORDINATE REFERENCE SYSTEM"): 405 | coord_sys = line_strings[1].replace("\n", "") 406 | else: 407 | line_strings = line.split() 408 | line_strings = [string.replace(",", "") for string in line_strings] 409 | 410 | if len(line_strings) == 0: 411 | break 412 | 413 | if i_header_line == -1 and not line_strings[0].startswith("!"): 414 | for i_string in range(len(line_strings)): 415 | data_values[i_value] = float(line_strings[i_string]) 416 | i_value += 1 417 | 418 | if line_strings[0].startswith("@"): 419 | if i_header_line == 0: 420 | i_header_line += 1 421 | else: 422 | i_header_line = -1 423 | 424 | if i_header_line > 0: 425 | if i_header_line == 2: 426 | no_data_value = float(line_strings[1]) 427 | elif i_header_line == 3: 428 | ny = int(line_strings[0]) 429 | nx = int(line_strings[1]) 430 | originx = float(line_strings[2]) 431 | maxx = float(line_strings[3]) 432 | originy = float(line_strings[4]) 433 | maxy = float(line_strings[5]) 434 | data_values = np.empty(ny * nx) 435 | i_header_line += 1 436 | 437 | if nx * ny != len(data_values): 438 | raise IOError( 439 | "Error reading *.zmap file. Encountered problem " 440 | "with header: (nx * ny) does not match with the " 441 | "number items in data file body." 442 | ) 443 | 444 | z = np.empty([ny, nx]) 445 | i_value = 0 446 | for n in range(z.shape[1]): 447 | for m in range(z.shape[0] - 1, -1, -1): 448 | z[m, n] = data_values[i_value] 449 | i_value += 1 450 | 451 | dx = (maxx - originx) / (nx - 1) 452 | dy = (maxy - originy) / (ny - 1) 453 | 454 | gridx = np.arange(originx, originx + nx * dx, dx) 455 | gridy = np.arange(originy, originy + ny * dy, dy) 456 | 457 | cellsize = (dx, dy) 458 | 459 | return z, gridx, gridy, cellsize, no_data_value, coord_sys 460 | 461 | 462 | def space_back_to_front(string): 463 | net = string.replace(" ", "") 464 | return "".join(string.rsplit(net)) + net 465 | -------------------------------------------------------------------------------- /src/pykrige/lib/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["cok", "variogram_models"] 2 | -------------------------------------------------------------------------------- /src/pykrige/lib/cok.pyx: -------------------------------------------------------------------------------- 1 | # cython: language_level=3, boundscheck=False, wraparound=False, cdivision=True 2 | import numpy as np 3 | 4 | cimport numpy as np 5 | 6 | import scipy.linalg 7 | 8 | from scipy.linalg.cython_blas cimport dgemv 9 | from scipy.linalg.cython_lapack cimport dgesv 10 | 11 | from .variogram_models cimport get_variogram_model 12 | 13 | 14 | cpdef _c_exec_loop( 15 | double [:, ::1] a_all, 16 | double [:, ::1] bd_all, 17 | char [::1] mask, 18 | long n, 19 | dict pars 20 | ): 21 | cdef long i, k 22 | 23 | npt = bd_all.shape[0] 24 | 25 | cdef double [::1] zvalues = np.zeros(npt, dtype='float64') 26 | cdef double [::1] sigmasq = np.zeros(npt, dtype='float64') 27 | cdef double [::1] x = np.zeros(n+1, dtype='float64') 28 | cdef double [::1] b = np.zeros(n+1, dtype='float64') 29 | cdef double [::1] Z = pars['Z'] 30 | cdef double [::1] bd 31 | cdef double z_tmp, ss_tmp, eps=pars['eps'] 32 | cdef int nb, inc=1 33 | cdef double alpha=1.0, beta=0.0 34 | 35 | nb = n + 1 36 | 37 | c_variogram_function = get_variogram_model(pars['variogram_function'].__name__) 38 | 39 | cdef double [::1] variogram_model_parameters = np.asarray(pars['variogram_model_parameters']) 40 | 41 | cdef double [::1, :] a_inv = np.asfortranarray(np.empty_like(a_all)) 42 | 43 | if pars['pseudo_inv']: 44 | if pars['pseudo_inv_type'] == "pinv": 45 | a_inv = np.asfortranarray(scipy.linalg.pinv(a_all)) 46 | elif pars['pseudo_inv_type'] == "pinv2": 47 | a_inv = np.asfortranarray(scipy.linalg.pinv2(a_all)) 48 | elif pars['pseudo_inv_type'] == "pinvh": 49 | a_inv = np.asfortranarray(scipy.linalg.pinvh(a_all)) 50 | else: 51 | raise ValueError('Unknown pseudo inverse method selected.') 52 | else: 53 | a_inv = np.asfortranarray(scipy.linalg.inv(a_all)) 54 | 55 | # same thing as range(npt) if mask is not defined, otherwise take the non masked elements 56 | for i in range(npt): 57 | if mask[i]: 58 | continue 59 | bd = bd_all[i] 60 | 61 | c_variogram_function(variogram_model_parameters, n, bd, b) 62 | 63 | for k in range(n): 64 | b[k] *= -1 65 | b[n] = 1.0 66 | 67 | if pars['exact_values']: 68 | check_b_vect(n, bd, b, eps) 69 | 70 | # Do the BLAS matrix-vector multiplication call 71 | dgemv( # Compute y := alpha*A*x + beta*y 72 | 'N', # char *trans, # {'T','C'}: o(A)=A'; {'N'}: o(A)=A 73 | &nb, # int *m, # Rows of A (prior to transpose from *trans) 74 | &nb, # int *n, # Columns of A / min(len(x)) 75 | &alpha, # np.float64_t *alpha, # Scalar multiple 76 | &(a_inv[0, 0]), # np.float64_t *a, # Matrix A: mxn 77 | &nb, # int *lda, # The size of the first dimension of A (in memory) 78 | &(b[0]), # np.float64_t *x, # Vector x, min(len(x)) = n 79 | &inc, # int *incx, # The increment between elements of x (usually 1) 80 | &beta, # np.float64_t *beta, # Scalar multiple 81 | &(x[0]), # np.float64_t *y, # Vector y, min(len(y)) = m 82 | &inc # int *incy # The increment between elements of y (usually 1) 83 | ) 84 | 85 | z_tmp = 0.0 86 | ss_tmp = 0.0 87 | for k in range(n): 88 | z_tmp += x[k]*Z[k] 89 | 90 | for k in range(nb): 91 | ss_tmp += x[k]*b[k] 92 | 93 | zvalues[i] = z_tmp 94 | sigmasq[i] = -ss_tmp 95 | 96 | return zvalues.base, sigmasq.base 97 | 98 | cpdef _c_exec_loop_moving_window( 99 | double [:, ::1] a_all, 100 | double [:, ::1] bd_all, 101 | char [::1] mask, 102 | long [:, ::1] bd_idx, 103 | long n_max, 104 | dict pars 105 | ): 106 | cdef long i, j, k, p_i, p_j, npt, n 107 | 108 | npt = bd_all.shape[0] 109 | n = bd_idx.shape[1] 110 | 111 | cdef double [::1] zvalues = np.zeros(npt, dtype='float64') 112 | cdef double [::1] sigmasq = np.zeros(npt, dtype='float64') 113 | cdef double [::1] x = np.zeros(n_max+1, dtype='float64') 114 | cdef double [::1] tmp = np.zeros(n_max+1, dtype='float64') 115 | cdef double [::1] b = np.zeros(n_max+1, dtype='float64') 116 | cdef double [::1] Z = pars['Z'] 117 | cdef double [::1] a_selection = np.zeros((n_max+1)*(n_max+1), dtype='float64') 118 | cdef double [::1] bd = np.zeros(n_max, dtype='float64') 119 | cdef double z_tmp, ss_tmp, eps=pars['eps'] 120 | cdef int nb, nrhs=1, info 121 | cdef int [::1] ipiv = np.zeros(n_max+1, dtype='int32') 122 | cdef long [::1] bd_idx_sel 123 | 124 | nb = n + 1 125 | 126 | c_variogram_function = get_variogram_model(pars['variogram_function'].__name__) 127 | 128 | cdef double [::1] variogram_model_parameters = np.asarray(pars['variogram_model_parameters']) 129 | 130 | for i in range(npt): 131 | if mask[i]: 132 | continue 133 | 134 | bd = bd_all[i, :] 135 | 136 | bd_idx_sel = bd_idx[i, :] 137 | 138 | for k in range(n): 139 | p_i = bd_idx_sel[k] 140 | for j in range(n): 141 | p_j = bd_idx_sel[j] 142 | a_selection[k + nb*j] = a_all[p_i, p_j] 143 | 144 | for k in range(nb): 145 | a_selection[k*nb + n] = 1.0 146 | a_selection[n*nb + k] = 1.0 147 | a_selection[n*nb + n] = 0.0 148 | 149 | c_variogram_function(variogram_model_parameters, n, bd, tmp) 150 | 151 | for k in range(n): 152 | b[k] = - tmp[k] 153 | b[n] = 1.0 154 | 155 | if pars['exact_values']: 156 | check_b_vect(n, bd, b, eps) 157 | 158 | for k in range(nb): 159 | x[k] = b[k] 160 | 161 | # higher level (and slower) call to do the same thing as dgesv below 162 | # a2D = a_selection.base[:nb*nb].reshape((nb, nb)) 163 | # x = scipy.linalg.solve(a2D, b.base[:nb]) 164 | 165 | dgesv( 166 | &nb, 167 | &nrhs, 168 | &(a_selection[0]), 169 | &nb, 170 | &(ipiv[0]), 171 | &(x[0]), 172 | &nb, 173 | &info 174 | ) 175 | 176 | if info > 0: 177 | raise ValueError('Singular matrix') 178 | elif info < 0: 179 | raise ValueError('Wrong arguments') 180 | 181 | z_tmp = 0.0 182 | ss_tmp = 0.0 183 | for k in range(n): 184 | j = bd_idx_sel[k] 185 | z_tmp += x[k]*Z[j] 186 | 187 | for k in range(nb): 188 | ss_tmp += x[k]*b[k] 189 | 190 | zvalues[i] = z_tmp 191 | sigmasq[i] = -ss_tmp 192 | 193 | return zvalues.base, sigmasq.base 194 | 195 | 196 | cdef int check_b_vect(long n, double [::1] bd, double[::1] b, double eps) nogil: 197 | cdef long k 198 | 199 | for k in range(n): 200 | if bd[k] <= eps: 201 | b[k] = 0.0 202 | 203 | return 0 204 | -------------------------------------------------------------------------------- /src/pykrige/lib/variogram_models.pxd: -------------------------------------------------------------------------------- 1 | # cython: language_level=3, boundscheck=False, wraparound=False, cdivision=True 2 | ctypedef void (*variogram_model_t)(double [::1], long, double [::1], double [::1]) 3 | 4 | cdef variogram_model_t get_variogram_model(function_name) 5 | -------------------------------------------------------------------------------- /src/pykrige/lib/variogram_models.pyx: -------------------------------------------------------------------------------- 1 | # cython: language_level=3, boundscheck=False, wraparound=False, cdivision=True 2 | from libc.math cimport exp 3 | 4 | 5 | # copied from variogram_model.py 6 | cdef variogram_model_t get_variogram_model(name): 7 | cdef variogram_model_t c_func 8 | 9 | if name == 'linear_variogram_model': 10 | c_func = &_c_linear_variogram_model 11 | elif name == 'power_variogram_model': 12 | c_func = &_c_power_variogram_model 13 | elif name == 'gaussian_variogram_model': 14 | c_func = &_c_gaussian_variogram_model 15 | elif name == 'exponential_variogram_model': 16 | c_func = &_c_exponential_variogram_model 17 | elif name == 'spherical_variogram_model': 18 | c_func = &_c_spherical_variogram_model 19 | else: 20 | raise NotImplementedError 21 | 22 | return c_func 23 | 24 | 25 | cdef void _c_linear_variogram_model( 26 | double [::1] params, long n, double [::1] dist, double[::1] out 27 | ) noexcept nogil: 28 | cdef long k 29 | cdef double a, b 30 | a = params[0] 31 | b = params[1] 32 | for k in range(n): 33 | out[k] = a*dist[k] + b 34 | 35 | 36 | cdef void _c_power_variogram_model( 37 | double [::1] params, long n, double [::1] dist, double[::1] out 38 | ) noexcept nogil: 39 | cdef long k 40 | cdef double a, b, c 41 | a = params[0] 42 | b = params[1] 43 | c = params[2] 44 | for k in range(n): 45 | out[k] = a*(dist[k]**b) + c 46 | 47 | 48 | cdef void _c_gaussian_variogram_model( 49 | double [::1] params, long n, double [::1] dist, double [::1] out 50 | ) noexcept nogil: 51 | cdef long k 52 | cdef double a, b, c 53 | a = params[0] 54 | b = params[1] 55 | c = params[2] 56 | for k in range(n): 57 | out[k] = a*(1 - exp(-(dist[k]/(b*4.0/7.0))**2)) + c 58 | 59 | 60 | cdef void _c_exponential_variogram_model( 61 | double [::1] params, long n, double[::1] dist, double[::1] out 62 | ) noexcept nogil: 63 | cdef long k 64 | cdef double a, b, c 65 | a = params[0] 66 | b = params[1] 67 | c = params[2] 68 | for k in range(n): 69 | out[k] = a*(1 - exp(-dist[k]/(b/3.0))) + c 70 | 71 | 72 | cdef void _c_spherical_variogram_model( 73 | double [::1] params, long n, double[::1] dist, double[::1] out 74 | ) noexcept nogil: 75 | cdef long k 76 | cdef double a, b, c 77 | a = params[0] 78 | b = params[1] 79 | c = params[2] 80 | for k in range(n): 81 | if dist[k] < b: 82 | out[k] = a*((3*dist[k])/(2*b) - (dist[k]**3)/(2*b**3)) + c 83 | else: 84 | out[k] = a + c 85 | -------------------------------------------------------------------------------- /src/pykrige/rk.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | """Regression Kriging.""" 3 | from pykrige.compat import Krige, check_sklearn_model, validate_sklearn 4 | 5 | validate_sklearn() 6 | 7 | from sklearn.metrics import r2_score 8 | from sklearn.svm import SVR 9 | 10 | 11 | class RegressionKriging: 12 | """ 13 | An implementation of Regression-Kriging. 14 | 15 | As described here: 16 | https://en.wikipedia.org/wiki/Regression-Kriging 17 | 18 | Parameters 19 | ---------- 20 | regression_model: machine learning model instance from sklearn 21 | method: str, optional 22 | type of kriging to be performed 23 | variogram_model: str, optional 24 | variogram model to be used during Kriging 25 | n_closest_points: int 26 | number of closest points to be used during Ordinary Kriging 27 | nlags: int 28 | see OK/UK class description 29 | weight: bool 30 | see OK/UK class description 31 | verbose: bool 32 | see OK/UK class description 33 | exact_values : bool 34 | see OK/UK class description 35 | variogram_parameters : list or dict 36 | see OK/UK class description 37 | variogram_function : callable 38 | see OK/UK class description 39 | anisotropy_scaling : tuple 40 | single value for 2D (UK/OK) and two values in 3D (UK3D/OK3D) 41 | anisotropy_angle : tuple 42 | single value for 2D (UK/OK) and three values in 3D (UK3D/OK3D) 43 | enable_statistics : bool 44 | see OK class description 45 | coordinates_type : str 46 | see OK/UK class description 47 | drift_terms : list of strings 48 | see UK/UK3D class description 49 | point_drift : array_like 50 | see UK class description 51 | ext_drift_grid : tuple 52 | Holding the three values external_drift, external_drift_x and 53 | external_drift_z for the UK class 54 | functional_drift : list of callable 55 | see UK/UK3D class description 56 | """ 57 | 58 | def __init__( 59 | self, 60 | regression_model=SVR(), 61 | method="ordinary", 62 | variogram_model="linear", 63 | n_closest_points=10, 64 | nlags=6, 65 | weight=False, 66 | verbose=False, 67 | exact_values=True, 68 | pseudo_inv=False, 69 | pseudo_inv_type="pinv", 70 | variogram_parameters=None, 71 | variogram_function=None, 72 | anisotropy_scaling=(1.0, 1.0), 73 | anisotropy_angle=(0.0, 0.0, 0.0), 74 | enable_statistics=False, 75 | coordinates_type="euclidean", 76 | drift_terms=None, 77 | point_drift=None, 78 | ext_drift_grid=(None, None, None), 79 | functional_drift=None, 80 | ): 81 | check_sklearn_model(regression_model) 82 | self.regression_model = regression_model 83 | self.n_closest_points = n_closest_points 84 | self.krige = Krige( 85 | method=method, 86 | variogram_model=variogram_model, 87 | nlags=nlags, 88 | weight=weight, 89 | n_closest_points=n_closest_points, 90 | verbose=verbose, 91 | exact_values=exact_values, 92 | pseudo_inv=pseudo_inv, 93 | pseudo_inv_type=pseudo_inv_type, 94 | variogram_parameters=variogram_parameters, 95 | variogram_function=variogram_function, 96 | anisotropy_scaling=anisotropy_scaling, 97 | anisotropy_angle=anisotropy_angle, 98 | enable_statistics=enable_statistics, 99 | coordinates_type=coordinates_type, 100 | drift_terms=drift_terms, 101 | point_drift=point_drift, 102 | ext_drift_grid=ext_drift_grid, 103 | functional_drift=functional_drift, 104 | ) 105 | 106 | def fit(self, p, x, y): 107 | """ 108 | Fit the regression method and also Krige the residual. 109 | 110 | Parameters 111 | ---------- 112 | p: ndarray 113 | (Ns, d) array of predictor variables (Ns samples, d dimensions) 114 | for regression 115 | x: ndarray 116 | ndarray of (x, y) points. Needs to be a (Ns, 2) array 117 | corresponding to the lon/lat, for example 2d regression kriging. 118 | array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging 119 | y: ndarray 120 | array of targets (Ns, ) 121 | """ 122 | self.regression_model.fit(p, y) 123 | ml_pred = self.regression_model.predict(p) 124 | print("Finished learning regression model") 125 | # residual=y-ml_pred 126 | self.krige.fit(x=x, y=y - ml_pred) 127 | print("Finished kriging residuals") 128 | 129 | def predict(self, p, x, **kwargs): 130 | """ 131 | Predict. 132 | 133 | Parameters 134 | ---------- 135 | p: ndarray 136 | (Ns, d) array of predictor variables (Ns samples, d dimensions) 137 | for regression 138 | x: ndarray 139 | ndarray of (x, y) points. Needs to be a (Ns, 2) array 140 | corresponding to the lon/lat, for example. 141 | array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging 142 | 143 | Returns 144 | ------- 145 | pred: ndarray 146 | The expected value of ys for the query inputs, of shape (Ns,). 147 | 148 | """ 149 | return self.krige_residual(x, **kwargs) + self.regression_model.predict(p) 150 | 151 | def krige_residual(self, x, **kwargs): 152 | """ 153 | Calculate the residuals. 154 | 155 | Parameters 156 | ---------- 157 | x: ndarray 158 | ndarray of (x, y) points. Needs to be a (Ns, 2) array 159 | corresponding to the lon/lat, for example. 160 | 161 | Returns 162 | ------- 163 | residual: ndarray 164 | kriged residual values 165 | """ 166 | return self.krige.predict(x, **kwargs) 167 | 168 | def score(self, p, x, y, sample_weight=None, **kwargs): 169 | """ 170 | Overloading default regression score method. 171 | 172 | Parameters 173 | ---------- 174 | p: ndarray 175 | (Ns, d) array of predictor variables (Ns samples, d dimensions) 176 | for regression 177 | x: ndarray 178 | ndarray of (x, y) points. Needs to be a (Ns, 2) array 179 | corresponding to the lon/lat, for example. 180 | array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging 181 | y: ndarray 182 | array of targets (Ns, ) 183 | """ 184 | return r2_score( 185 | y_pred=self.predict(p, x, **kwargs), y_true=y, sample_weight=sample_weight 186 | ) 187 | -------------------------------------------------------------------------------- /src/pykrige/variogram_models.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | """ 3 | PyKrige 4 | ======= 5 | 6 | Code by Benjamin S. Murphy and the PyKrige Developers 7 | bscott.murphy@gmail.com 8 | 9 | Summary 10 | ------- 11 | Function definitions for variogram models. In each function, m is a list of 12 | defining parameters and d is an array of the distance values at which to 13 | calculate the variogram model. 14 | 15 | References 16 | ---------- 17 | .. [1] P.K. Kitanidis, Introduction to Geostatistcs: Applications in 18 | Hydrogeology, (Cambridge University Press, 1997) 272 p. 19 | 20 | Copyright (c) 2015-2020, PyKrige Developers 21 | """ 22 | import numpy as np 23 | 24 | 25 | def linear_variogram_model(m, d): 26 | """Linear model, m is [slope, nugget]""" 27 | slope = float(m[0]) 28 | nugget = float(m[1]) 29 | return slope * d + nugget 30 | 31 | 32 | def power_variogram_model(m, d): 33 | """Power model, m is [scale, exponent, nugget]""" 34 | scale = float(m[0]) 35 | exponent = float(m[1]) 36 | nugget = float(m[2]) 37 | return scale * d**exponent + nugget 38 | 39 | 40 | def gaussian_variogram_model(m, d): 41 | """Gaussian model, m is [psill, range, nugget]""" 42 | psill = float(m[0]) 43 | range_ = float(m[1]) 44 | nugget = float(m[2]) 45 | return psill * (1.0 - np.exp(-(d**2.0) / (range_ * 4.0 / 7.0) ** 2.0)) + nugget 46 | 47 | 48 | def exponential_variogram_model(m, d): 49 | """Exponential model, m is [psill, range, nugget]""" 50 | psill = float(m[0]) 51 | range_ = float(m[1]) 52 | nugget = float(m[2]) 53 | return psill * (1.0 - np.exp(-d / (range_ / 3.0))) + nugget 54 | 55 | 56 | def spherical_variogram_model(m, d): 57 | """Spherical model, m is [psill, range, nugget]""" 58 | psill = float(m[0]) 59 | range_ = float(m[1]) 60 | nugget = float(m[2]) 61 | return np.piecewise( 62 | d, 63 | [d <= range_, d > range_], 64 | [ 65 | lambda x: psill 66 | * ((3.0 * x) / (2.0 * range_) - (x**3.0) / (2.0 * range_**3.0)) 67 | + nugget, 68 | psill + nugget, 69 | ], 70 | ) 71 | 72 | 73 | def hole_effect_variogram_model(m, d): 74 | """Hole Effect model, m is [psill, range, nugget]""" 75 | psill = float(m[0]) 76 | range_ = float(m[1]) 77 | nugget = float(m[2]) 78 | return ( 79 | psill * (1.0 - (1.0 - d / (range_ / 3.0)) * np.exp(-d / (range_ / 3.0))) 80 | + nugget 81 | ) 82 | -------------------------------------------------------------------------------- /tests/test_api.py: -------------------------------------------------------------------------------- 1 | from itertools import product 2 | 3 | import numpy as np 4 | import pytest 5 | 6 | from pykrige.compat import Krige, threed_krige 7 | 8 | 9 | def _method_and_vergiogram(): 10 | method = ["ordinary", "universal", "ordinary3d", "universal3d"] 11 | variogram_model = ["linear", "power", "gaussian", "spherical", "exponential"] 12 | return product(method, variogram_model) 13 | 14 | 15 | def test_krige(): 16 | # dummy data 17 | pytest.importorskip("sklearn") 18 | from sklearn.model_selection import GridSearchCV 19 | 20 | np.random.seed(1) 21 | X = np.random.randint(0, 400, size=(20, 3)).astype(float) 22 | y = 5 * np.random.rand(20) 23 | 24 | for m, v in _method_and_vergiogram(): 25 | param_dict = {"method": [m], "variogram_model": [v]} 26 | 27 | estimator = GridSearchCV( 28 | Krige(), 29 | param_dict, 30 | n_jobs=-1, 31 | pre_dispatch="2*n_jobs", 32 | verbose=False, 33 | return_train_score=True, 34 | cv=5, 35 | ) 36 | # run the gridsearch 37 | if m in ["ordinary", "universal"]: 38 | estimator.fit(X=X[:, :2], y=y) 39 | else: 40 | estimator.fit(X=X, y=y) 41 | if hasattr(estimator, "best_score_"): 42 | if m in threed_krige: 43 | assert estimator.best_score_ > -10.0 44 | else: 45 | assert estimator.best_score_ > -3.0 46 | if hasattr(estimator, "cv_results_"): 47 | assert estimator.cv_results_["mean_train_score"] > 0 48 | -------------------------------------------------------------------------------- /tests/test_classification_krige.py: -------------------------------------------------------------------------------- 1 | from itertools import product 2 | 3 | import numpy as np 4 | import pytest 5 | 6 | from pykrige.ck import ClassificationKriging 7 | 8 | try: 9 | from sklearn.datasets import fetch_california_housing 10 | from sklearn.ensemble import RandomForestClassifier 11 | from sklearn.model_selection import train_test_split 12 | from sklearn.preprocessing import KBinsDiscretizer 13 | from sklearn.svm import SVC 14 | 15 | SKLEARN_INSTALLED = True 16 | except ImportError: 17 | SKLEARN_INSTALLED = False 18 | 19 | 20 | def _methods(): 21 | krige_methods = ["ordinary", "universal"] 22 | ml_methods = [ 23 | SVC(C=0.01, gamma="auto", probability=True), 24 | RandomForestClassifier(n_estimators=50), 25 | ] 26 | return product(ml_methods, krige_methods) 27 | 28 | 29 | @pytest.mark.skipif(not SKLEARN_INSTALLED, reason="requires scikit-learn") 30 | def test_classification_krige(): 31 | np.random.seed(1) 32 | x = np.linspace(-1.0, 1.0, 100) 33 | # create a feature matrix with 5 features 34 | X = np.tile(x, reps=(5, 1)).T 35 | y = ( 36 | 1 37 | + 5 * X[:, 0] 38 | - 2 * X[:, 1] 39 | - 2 * X[:, 2] 40 | + 3 * X[:, 3] 41 | + 4 * X[:, 4] 42 | + 2 * (np.random.rand(100) - 0.5) 43 | ) 44 | 45 | # create lat/lon array 46 | lon = np.linspace(-180.0, 180.0, 10) 47 | lat = np.linspace(-90.0, 90.0, 10) 48 | lon_lat = np.array(list(product(lon, lat))) 49 | 50 | discretizer = KBinsDiscretizer(encode="ordinal") 51 | y = discretizer.fit_transform(y.reshape(-1, 1)) 52 | 53 | X_train, X_test, y_train, y_test, lon_lat_train, lon_lat_test = train_test_split( 54 | X, y, lon_lat, train_size=0.7, random_state=10 55 | ) 56 | 57 | for ml_model, krige_method in _methods(): 58 | class_model = ClassificationKriging( 59 | classification_model=ml_model, method=krige_method, n_closest_points=2 60 | ) 61 | class_model.fit(X_train, lon_lat_train, y_train) 62 | assert class_model.score(X_test, lon_lat_test, y_test) > 0.25 63 | 64 | 65 | @pytest.mark.skipif(not SKLEARN_INSTALLED, reason="requires scikit-learn") 66 | def test_krige_classification_housing(): 67 | import ssl 68 | import urllib 69 | 70 | try: 71 | housing = fetch_california_housing() 72 | except (ssl.SSLError, urllib.error.URLError): 73 | ssl._create_default_https_context = ssl._create_unverified_context 74 | try: 75 | housing = fetch_california_housing() 76 | except PermissionError: 77 | # This can raise permission error on Appveyor 78 | pytest.skip("Failed to load california housing dataset") 79 | ssl._create_default_https_context = ssl.create_default_context 80 | 81 | # take only first 1000 82 | p = housing["data"][:1000, :-2] 83 | x = housing["data"][:1000, -2:] 84 | target = housing["target"][:1000] 85 | discretizer = KBinsDiscretizer(encode="ordinal") 86 | target = discretizer.fit_transform(target.reshape(-1, 1)) 87 | 88 | p_train, p_test, y_train, y_test, x_train, x_test = train_test_split( 89 | p, target, x, train_size=0.7, random_state=10 90 | ) 91 | 92 | for ml_model, krige_method in _methods(): 93 | class_model = ClassificationKriging( 94 | classification_model=ml_model, method=krige_method, n_closest_points=2 95 | ) 96 | class_model.fit(p_train, x_train, y_train) 97 | if krige_method == "ordinary": 98 | assert class_model.score(p_test, x_test, y_test) > 0.5 99 | else: 100 | assert class_model.score(p_test, x_test, y_test) > 0.0 101 | -------------------------------------------------------------------------------- /tests/test_data/test1_settings.txt: -------------------------------------------------------------------------------- 1 | Using KT3D - Ordinary Kriging, no drift terms 2 | data.txt 3 | NCOL = 100 4 | NROW = 100 5 | XMIN = 1067275 6 | XMAX = 1072225 7 | YMIN = 241459 8 | YMAX = 243934 9 | COLSIZE = 50 10 | ROWSIZE = 25 11 | 12 | Using Exponential Variogram Model 13 | Sill = 500 14 | Range = 3000 15 | Nugget = 0 16 | Anisotropy = 1 -------------------------------------------------------------------------------- /tests/test_data/test2_settings.txt: -------------------------------------------------------------------------------- 1 | Using KT3D - X/Y Linear Drifts 2 | data.txt 3 | NCOL = 100 4 | NROW = 100 5 | XMIN = 1067275 6 | XMAX = 1072225 7 | YMIN = 241459 8 | YMAX = 243934 9 | COLSIZE = 50 10 | ROWSIZE = 25 11 | 12 | Using Exponential Variogram Model 13 | Sill = 500 14 | Range = 3000 15 | Nugget = 0 16 | Anisotropy = 1 -------------------------------------------------------------------------------- /tests/test_data/test3_settings.txt: -------------------------------------------------------------------------------- 1 | Using MEUK - DEM External Drift 2 | data.txt 3 | NCOL = 200 4 | NROW = 100 5 | XMIN = 1067275 6 | XMAX = 1072225 7 | YMIN = 241459 8 | YMAX = 243934 9 | COLSIZE = 25 10 | ROWSIZE = 25 11 | 12 | Using Spherical Variogram Model 13 | Sill = 500 14 | Range = 3000 15 | Nugget = 0 16 | Anisotropy = 1 -------------------------------------------------------------------------------- /tests/test_data/test3d_answer.txt: -------------------------------------------------------------------------------- 1 | 5.966 7.603 2 | 6.085 6.739 3 | 6.265 5.924 4 | 6.505 5.108 5 | 6.799 4.240 6 | 7.157 3.342 7 | 7.633 2.692 8 | 8.280 2.475 9 | 8.999 2.514 10 | 9.682 3.374 11 | 6.287 6.963 12 | 6.406 6.056 13 | 6.587 5.209 14 | 6.828 4.383 15 | 7.122 3.499 16 | 7.460 2.474 17 | 7.913 1.656 18 | 8.644 1.666 19 | 9.440 1.582 20 | 10.154 2.650 21 | 6.716 6.488 22 | 6.842 5.532 23 | 7.031 4.661 24 | 7.291 3.889 25 | 7.620 3.189 26 | 8.012 2.480 27 | 8.522 1.992 28 | 9.239 2.073 29 | 10.014 2.264 30 | 10.722 3.105 31 | 7.255 6.174 32 | 7.397 5.176 33 | 7.607 4.286 34 | 7.899 3.592 35 | 8.286 3.162 36 | 8.764 2.928 37 | 9.340 2.863 38 | 10.015 3.011 39 | 10.721 3.346 40 | 11.384 4.038 41 | 7.893 6.011 42 | 8.065 5.005 43 | 8.307 4.138 44 | 8.640 3.533 45 | 9.080 3.306 46 | 9.615 3.372 47 | 10.219 3.565 48 | 10.864 3.848 49 | 11.509 4.266 50 | 12.114 4.919 51 | 8.606 5.954 52 | 8.818 4.977 53 | 9.103 4.188 54 | 9.479 3.697 55 | 9.955 3.584 56 | 10.515 3.767 57 | 11.119 4.085 58 | 11.734 4.476 59 | 12.328 4.972 60 | 12.874 5.641 61 | 9.361 5.918 62 | 9.612 4.953 63 | 9.937 4.225 64 | 10.348 3.826 65 | 10.848 3.791 66 | 11.414 4.036 67 | 12.007 4.435 68 | 12.591 4.920 69 | 13.138 5.503 70 | 13.630 6.223 71 | 10.122 5.835 72 | 10.404 4.797 73 | 10.754 4.028 74 | 11.187 3.655 75 | 11.701 3.714 76 | 12.270 4.088 77 | 12.850 4.619 78 | 13.405 5.230 79 | 13.910 5.919 80 | 14.354 6.717 81 | 10.866 5.715 82 | 11.164 4.491 83 | 11.518 3.520 84 | 11.954 3.070 85 | 12.480 3.296 86 | 13.055 3.933 87 | 13.625 4.691 88 | 14.154 5.474 89 | 14.623 6.293 90 | 15.027 7.184 91 | 11.583 5.683 92 | 11.885 4.217 93 | 12.226 2.888 94 | 12.643 2.194 95 | 13.183 2.726 96 | 13.768 3.758 97 | 14.327 4.786 98 | 14.831 5.751 99 | 15.267 6.699 100 | 15.635 7.681 101 | 6.052 6.701 102 | 6.192 5.922 103 | 6.406 5.219 104 | 6.687 4.523 105 | 7.024 3.760 106 | 7.416 2.914 107 | 7.924 2.281 108 | 8.622 2.113 109 | 9.385 2.086 110 | 10.091 3.007 111 | 6.381 6.038 112 | 6.520 5.196 113 | 6.733 4.437 114 | 7.018 3.724 115 | 7.359 2.955 116 | 7.724 1.931 117 | 8.142 0.769 118 | 9.004 1.199 119 | 9.877 0.646 120 | 10.606 2.177 121 | 6.836 5.562 122 | 6.977 4.621 123 | 7.194 3.767 124 | 7.495 3.063 125 | 7.880 2.515 126 | 8.323 1.928 127 | 8.865 1.442 128 | 9.661 1.681 129 | 10.492 1.831 130 | 11.221 2.714 131 | 7.419 5.274 132 | 7.574 4.222 133 | 7.799 3.227 134 | 8.123 2.471 135 | 8.581 2.239 136 | 9.144 2.312 137 | 9.790 2.426 138 | 10.520 2.653 139 | 11.262 2.994 140 | 11.938 3.680 141 | 8.119 5.178 142 | 8.306 4.083 143 | 8.560 3.041 144 | 8.921 2.269 145 | 9.441 2.231 146 | 10.076 2.673 147 | 10.757 3.077 148 | 11.451 3.434 149 | 12.120 3.864 150 | 12.727 4.512 151 | 8.905 5.228 152 | 9.142 4.188 153 | 9.454 3.290 154 | 9.874 2.722 155 | 10.426 2.718 156 | 11.072 3.109 157 | 11.746 3.553 158 | 12.402 3.982 159 | 13.009 4.482 160 | 13.547 5.152 161 | 9.731 5.313 162 | 10.021 4.329 163 | 10.390 3.570 164 | 10.858 3.161 165 | 11.429 3.165 166 | 12.067 3.467 167 | 12.718 3.885 168 | 13.332 4.358 169 | 13.882 4.927 170 | 14.356 5.651 171 | 10.556 5.330 172 | 10.884 4.298 173 | 11.288 3.531 174 | 11.783 3.160 175 | 12.366 3.222 176 | 12.998 3.578 177 | 13.626 4.065 178 | 14.203 4.624 179 | 14.702 5.283 180 | 15.120 6.083 181 | 11.350 5.275 182 | 11.697 4.054 183 | 12.106 3.067 184 | 12.602 2.599 185 | 13.196 2.830 186 | 13.831 3.443 187 | 14.443 4.137 188 | 14.987 4.853 189 | 15.444 5.631 190 | 15.816 6.517 191 | 12.100 5.272 192 | 12.452 3.782 193 | 12.837 2.335 194 | 13.294 1.438 195 | 13.916 2.153 196 | 14.565 3.249 197 | 15.161 4.229 198 | 15.675 5.130 199 | 16.096 6.034 200 | 16.430 7.006 201 | 6.186 5.852 202 | 6.363 5.215 203 | 6.635 4.705 204 | 6.989 4.241 205 | 7.407 3.752 206 | 7.888 3.236 207 | 8.464 2.870 208 | 9.159 2.782 209 | 9.897 2.928 210 | 10.589 3.670 211 | 6.523 5.171 212 | 6.701 4.461 213 | 6.979 3.884 214 | 7.346 3.403 215 | 7.785 2.954 216 | 8.282 2.449 217 | 8.877 2.082 218 | 9.636 2.109 219 | 10.433 2.233 220 | 11.151 3.036 221 | 7.010 4.723 222 | 7.191 3.869 223 | 7.466 3.117 224 | 7.844 2.578 225 | 8.336 2.343 226 | 8.908 2.207 227 | 9.563 2.131 228 | 10.334 2.283 229 | 11.119 2.534 230 | 11.823 3.249 231 | 7.652 4.504 232 | 7.842 3.459 233 | 8.104 2.379 234 | 8.473 1.539 235 | 9.050 1.669 236 | 9.741 2.202 237 | 10.467 2.549 238 | 11.219 2.837 239 | 11.946 3.199 240 | 12.598 3.844 241 | 8.428 4.510 242 | 8.650 3.373 243 | 8.930 2.119 244 | 9.297 0.911 245 | 9.954 1.314 246 | 10.725 2.328 247 | 11.481 2.915 248 | 12.204 3.321 249 | 12.866 3.761 250 | 13.445 4.405 251 | 9.296 4.694 252 | 9.579 3.634 253 | 9.939 2.651 254 | 10.416 2.007 255 | 11.064 2.143 256 | 11.809 2.721 257 | 12.546 3.222 258 | 13.226 3.647 259 | 13.821 4.144 260 | 14.323 4.831 261 | 10.200 4.927 262 | 10.548 3.965 263 | 10.984 3.212 264 | 11.527 2.805 265 | 12.180 2.822 266 | 12.896 3.106 267 | 13.597 3.457 268 | 14.227 3.861 269 | 14.755 4.410 270 | 15.183 5.164 271 | 11.089 5.087 272 | 11.486 4.118 273 | 11.967 3.404 274 | 12.542 3.044 275 | 13.203 3.047 276 | 13.901 3.278 277 | 14.569 3.611 278 | 15.151 4.049 279 | 15.621 4.666 280 | 15.984 5.494 281 | 11.932 5.160 282 | 12.355 4.054 283 | 12.850 3.196 284 | 13.432 2.775 285 | 14.095 2.867 286 | 14.782 3.250 287 | 15.422 3.725 288 | 15.962 4.283 289 | 16.384 4.994 290 | 16.696 5.891 291 | 12.714 5.255 292 | 13.146 3.944 293 | 13.632 2.807 294 | 14.196 2.216 295 | 14.857 2.506 296 | 15.534 3.182 297 | 16.146 3.884 298 | 16.649 4.606 299 | 17.032 5.425 300 | 17.307 6.386 301 | 6.367 5.006 302 | 6.599 4.563 303 | 6.956 4.322 304 | 7.408 4.159 305 | 7.928 3.999 306 | 8.503 3.838 307 | 9.136 3.748 308 | 9.820 3.802 309 | 10.514 4.072 310 | 11.169 4.710 311 | 6.706 4.293 312 | 6.950 3.795 313 | 7.329 3.504 314 | 7.813 3.336 315 | 8.374 3.239 316 | 8.992 3.162 317 | 9.667 3.141 318 | 10.393 3.243 319 | 11.117 3.521 320 | 11.785 4.163 321 | 7.237 3.922 322 | 7.490 3.265 323 | 7.872 2.774 324 | 8.374 2.519 325 | 8.987 2.578 326 | 9.673 2.758 327 | 10.398 2.929 328 | 11.144 3.136 329 | 11.865 3.464 330 | 12.513 4.090 331 | 7.961 3.849 332 | 8.225 2.959 333 | 8.591 2.082 334 | 9.079 1.523 335 | 9.767 1.902 336 | 10.544 2.503 337 | 11.318 2.928 338 | 12.062 3.257 339 | 12.745 3.650 340 | 13.343 4.275 341 | 8.833 4.011 342 | 9.132 2.980 343 | 9.509 1.864 344 | 9.990 0.959 345 | 10.748 1.618 346 | 11.583 2.437 347 | 12.377 2.971 348 | 13.097 3.360 349 | 13.722 3.814 350 | 14.246 4.477 351 | 9.791 4.342 352 | 10.153 3.356 353 | 10.608 2.473 354 | 11.185 1.973 355 | 11.925 2.166 356 | 12.737 2.635 357 | 13.513 3.010 358 | 14.190 3.355 359 | 14.743 3.858 360 | 15.179 4.605 361 | 10.770 4.720 362 | 11.201 3.824 363 | 11.731 3.137 364 | 12.371 2.771 365 | 13.114 2.748 366 | 13.900 2.885 367 | 14.643 3.032 368 | 15.267 3.295 369 | 15.743 3.850 370 | 16.090 4.706 371 | 11.718 5.034 372 | 12.204 4.151 373 | 12.782 3.505 374 | 13.456 3.148 375 | 14.203 3.039 376 | 14.968 3.047 377 | 15.674 3.104 378 | 16.246 3.352 379 | 16.652 3.963 380 | 16.925 4.902 381 | 12.602 5.260 382 | 13.121 4.296 383 | 13.721 3.573 384 | 14.404 3.170 385 | 15.146 3.070 386 | 15.888 3.135 387 | 16.551 3.291 388 | 17.070 3.636 389 | 17.425 4.303 390 | 17.646 5.270 391 | 13.408 5.483 392 | 13.940 4.390 393 | 14.542 3.530 394 | 15.215 3.055 395 | 15.939 3.012 396 | 16.648 3.229 397 | 17.266 3.570 398 | 17.738 4.071 399 | 18.054 4.811 400 | 18.244 5.796 401 | 6.597 4.119 402 | 6.900 3.912 403 | 7.368 4.004 404 | 7.933 4.167 405 | 8.551 4.310 406 | 9.200 4.428 407 | 9.867 4.560 408 | 10.540 4.768 409 | 11.193 5.131 410 | 11.802 5.740 411 | 6.915 3.290 412 | 7.253 3.083 413 | 7.777 3.188 414 | 8.402 3.369 415 | 9.078 3.571 416 | 9.780 3.768 417 | 10.492 3.961 418 | 11.196 4.201 419 | 11.865 4.571 420 | 12.474 5.181 421 | 7.501 3.039 422 | 7.860 2.671 423 | 8.405 2.571 424 | 9.059 2.656 425 | 9.784 2.935 426 | 10.541 3.276 427 | 11.292 3.582 428 | 12.011 3.884 429 | 12.672 4.282 430 | 13.254 4.897 431 | 8.345 3.229 432 | 8.720 2.575 433 | 9.253 2.111 434 | 9.910 2.014 435 | 10.676 2.396 436 | 11.485 2.908 437 | 12.266 3.322 438 | 12.981 3.680 439 | 13.606 4.119 440 | 14.135 4.762 441 | 9.337 3.612 442 | 9.747 2.751 443 | 10.288 2.042 444 | 10.955 1.801 445 | 11.751 2.178 446 | 12.593 2.697 447 | 13.385 3.087 448 | 14.075 3.432 449 | 14.640 3.915 450 | 15.089 4.632 451 | 10.387 4.104 452 | 10.856 3.219 453 | 11.443 2.532 454 | 12.149 2.253 455 | 12.962 2.405 456 | 13.809 2.654 457 | 14.597 2.808 458 | 15.246 3.035 459 | 15.729 3.582 460 | 16.075 4.449 461 | 11.433 4.624 462 | 11.965 3.792 463 | 12.609 3.180 464 | 13.358 2.862 465 | 14.186 2.770 466 | 15.034 2.681 467 | 15.813 2.492 468 | 16.418 2.536 469 | 16.801 3.207 470 | 17.034 4.287 471 | 12.426 5.079 472 | 13.012 4.261 473 | 13.700 3.657 474 | 14.478 3.279 475 | 15.316 3.036 476 | 16.154 2.765 477 | 16.907 2.404 478 | 17.460 2.365 479 | 17.757 3.129 480 | 17.899 4.334 481 | 13.337 5.449 482 | 13.958 4.579 483 | 14.669 3.916 484 | 15.460 3.470 485 | 16.292 3.176 486 | 17.099 2.927 487 | 17.790 2.719 488 | 18.274 2.832 489 | 18.526 3.548 490 | 18.623 4.689 491 | 14.153 5.794 492 | 14.788 4.836 493 | 15.503 4.081 494 | 16.284 3.567 495 | 17.093 3.269 496 | 17.855 3.126 497 | 18.480 3.147 498 | 18.902 3.465 499 | 19.122 4.190 500 | 19.199 5.259 501 | 6.905 3.272 502 | 7.293 3.321 503 | 7.883 3.771 504 | 8.555 4.233 505 | 9.252 4.621 506 | 9.948 4.948 507 | 10.630 5.255 508 | 11.285 5.597 509 | 11.899 6.047 510 | 12.460 6.672 511 | 7.133 2.052 512 | 7.612 2.281 513 | 8.328 2.893 514 | 9.093 3.410 515 | 9.861 3.856 516 | 10.613 4.249 517 | 11.335 4.604 518 | 12.012 4.970 519 | 12.631 5.429 520 | 13.182 6.060 521 | 7.786 1.971 522 | 8.296 1.978 523 | 9.042 2.296 524 | 9.848 2.681 525 | 10.662 3.180 526 | 11.458 3.669 527 | 12.207 4.088 528 | 12.890 4.487 529 | 13.492 4.965 530 | 14.008 5.613 531 | 8.817 2.630 532 | 9.317 2.114 533 | 10.019 1.790 534 | 10.818 1.972 535 | 11.655 2.600 536 | 12.481 3.191 537 | 13.245 3.643 538 | 13.914 4.051 539 | 14.475 4.554 540 | 14.930 5.245 541 | 9.944 3.281 542 | 10.472 2.419 543 | 11.161 1.654 544 | 11.958 1.683 545 | 12.811 2.321 546 | 13.656 2.848 547 | 14.425 3.195 548 | 15.064 3.538 549 | 15.557 4.074 550 | 15.924 4.865 551 | 11.076 3.938 552 | 11.655 3.044 553 | 12.369 2.356 554 | 13.186 2.206 555 | 14.061 2.454 556 | 14.926 2.637 557 | 15.702 2.653 558 | 16.303 2.808 559 | 16.700 3.436 560 | 16.949 4.436 561 | 12.168 4.592 562 | 12.804 3.767 563 | 13.560 3.165 564 | 14.410 2.865 565 | 15.310 2.740 566 | 16.200 2.496 567 | 17.005 1.968 568 | 17.585 1.736 569 | 17.838 2.701 570 | 17.944 4.056 571 | 13.185 5.169 572 | 13.871 4.366 573 | 14.665 3.756 574 | 15.544 3.334 575 | 16.463 2.983 576 | 17.361 2.490 577 | 18.171 1.632 578 | 18.740 0.996 579 | 18.839 2.406 580 | 18.831 3.970 581 | 14.105 5.653 582 | 14.824 4.811 583 | 15.640 4.135 584 | 16.534 3.603 585 | 17.457 3.138 586 | 18.330 2.646 587 | 19.045 2.139 588 | 19.478 2.066 589 | 19.596 2.944 590 | 19.555 4.297 591 | 14.915 6.097 592 | 15.645 5.183 593 | 16.464 4.415 594 | 17.351 3.781 595 | 18.259 3.243 596 | 19.093 2.804 597 | 19.724 2.604 598 | 20.068 2.864 599 | 20.163 3.671 600 | 20.113 4.869 601 | 7.409 2.924 602 | 7.866 3.129 603 | 8.540 3.791 604 | 9.276 4.427 605 | 10.013 4.963 606 | 10.726 5.427 607 | 11.402 5.861 608 | 12.030 6.314 609 | 12.603 6.846 610 | 13.117 7.510 611 | 7.506 1.077 612 | 8.170 1.855 613 | 9.033 2.837 614 | 9.883 3.548 615 | 10.699 4.139 616 | 11.467 4.660 617 | 12.176 5.133 618 | 12.817 5.608 619 | 13.385 6.153 620 | 13.878 6.832 621 | 8.244 1.210 622 | 8.921 1.602 623 | 9.821 2.190 624 | 10.718 2.737 625 | 11.580 3.381 626 | 12.384 3.980 627 | 13.111 4.496 628 | 13.747 4.991 629 | 14.288 5.556 630 | 14.740 6.262 631 | 9.464 2.374 632 | 10.066 1.886 633 | 10.885 1.473 634 | 11.772 1.795 635 | 12.650 2.687 636 | 13.473 3.386 637 | 14.201 3.909 638 | 14.813 4.398 639 | 15.304 4.984 640 | 15.689 5.740 641 | 10.672 3.180 642 | 11.297 2.236 643 | 12.094 1.002 644 | 12.975 1.249 645 | 13.864 2.311 646 | 14.699 2.936 647 | 15.421 3.335 648 | 15.993 3.747 649 | 16.411 4.357 650 | 16.702 5.212 651 | 11.842 3.925 652 | 12.518 2.976 653 | 13.332 2.182 654 | 14.231 2.064 655 | 15.145 2.417 656 | 16.000 2.647 657 | 16.724 2.717 658 | 17.252 2.941 659 | 17.569 3.622 660 | 17.740 4.668 661 | 12.948 4.653 662 | 13.677 3.797 663 | 14.527 3.151 664 | 15.457 2.823 665 | 16.402 2.688 666 | 17.287 2.466 667 | 18.036 2.041 668 | 18.534 1.929 669 | 18.714 2.856 670 | 18.743 4.207 671 | 13.964 5.302 672 | 14.738 4.471 673 | 15.622 3.811 674 | 16.581 3.323 675 | 17.554 2.912 676 | 18.461 2.415 677 | 19.217 1.698 678 | 19.678 1.339 679 | 19.724 2.517 680 | 19.635 4.044 681 | 14.871 5.862 682 | 15.673 4.995 683 | 16.578 4.255 684 | 17.556 3.616 685 | 18.552 3.017 686 | 19.465 2.421 687 | 20.153 1.936 688 | 20.490 1.969 689 | 20.500 2.885 690 | 20.361 4.272 691 | 15.659 6.380 692 | 16.468 5.444 693 | 17.372 4.601 694 | 18.349 3.811 695 | 19.355 3.024 696 | 20.283 2.287 697 | 20.915 1.981 698 | 21.128 2.425 699 | 21.077 3.421 700 | 20.912 4.740 701 | 8.204 3.552 702 | 8.679 3.665 703 | 9.346 4.221 704 | 10.080 4.832 705 | 10.813 5.396 706 | 11.513 5.920 707 | 12.164 6.431 708 | 12.756 6.967 709 | 13.285 7.572 710 | 13.751 8.285 711 | 8.537 2.317 712 | 9.115 2.573 713 | 9.919 3.270 714 | 10.757 3.919 715 | 11.565 4.515 716 | 12.314 5.075 717 | 12.991 5.616 718 | 13.587 6.176 719 | 14.102 6.804 720 | 14.541 7.543 721 | 9.268 2.194 722 | 9.900 2.225 723 | 10.758 2.622 724 | 11.651 3.110 725 | 12.507 3.702 726 | 13.287 4.298 727 | 13.971 4.865 728 | 14.551 5.448 729 | 15.031 6.104 730 | 15.422 6.881 731 | 10.349 2.825 732 | 10.998 2.346 733 | 11.842 2.103 734 | 12.749 2.338 735 | 13.628 2.970 736 | 14.422 3.593 737 | 15.095 4.159 738 | 15.639 4.745 739 | 16.062 5.432 740 | 16.381 6.265 741 | 11.513 3.489 742 | 12.206 2.651 743 | 13.057 1.947 744 | 13.979 1.953 745 | 14.879 2.503 746 | 15.680 3.023 747 | 16.332 3.498 748 | 16.823 4.042 749 | 17.166 4.758 750 | 17.392 5.675 751 | 12.659 4.168 752 | 13.408 3.256 753 | 14.291 2.533 754 | 15.244 2.288 755 | 16.174 2.427 756 | 16.987 2.647 757 | 17.620 2.907 758 | 18.052 3.338 759 | 18.300 4.085 760 | 18.415 5.124 761 | 13.740 4.859 762 | 14.542 3.971 763 | 15.462 3.263 764 | 16.448 2.816 765 | 17.411 2.590 766 | 18.245 2.478 767 | 18.875 2.451 768 | 19.255 2.712 769 | 19.398 3.508 770 | 19.395 4.694 771 | 14.727 5.508 772 | 15.568 4.619 773 | 16.517 3.870 774 | 17.528 3.272 775 | 18.519 2.808 776 | 19.381 2.449 777 | 20.009 2.224 778 | 20.332 2.393 779 | 20.376 3.231 780 | 20.270 4.509 781 | 15.601 6.102 782 | 16.463 5.174 783 | 17.425 4.343 784 | 18.452 3.592 785 | 19.478 2.903 786 | 20.383 2.319 787 | 20.997 2.060 788 | 21.226 2.382 789 | 21.175 3.301 790 | 20.992 4.607 791 | 16.352 6.671 792 | 17.214 5.682 793 | 18.170 4.748 794 | 19.197 3.824 795 | 20.256 2.845 796 | 21.262 1.821 797 | 21.896 1.441 798 | 21.940 2.300 799 | 21.775 3.524 800 | 21.539 4.920 801 | 9.134 4.725 802 | 9.609 4.672 803 | 10.231 4.992 804 | 10.922 5.454 805 | 11.618 5.955 806 | 12.283 6.472 807 | 12.894 7.017 808 | 13.442 7.609 809 | 13.926 8.274 810 | 14.347 9.037 811 | 9.625 3.827 812 | 10.170 3.759 813 | 10.884 4.077 814 | 11.659 4.523 815 | 12.420 5.017 816 | 13.126 5.545 817 | 13.755 6.112 818 | 14.300 6.735 819 | 14.762 7.436 820 | 15.151 8.238 821 | 10.365 3.540 822 | 10.974 3.311 823 | 11.754 3.425 824 | 12.591 3.720 825 | 13.401 4.146 826 | 14.132 4.662 827 | 14.760 5.247 828 | 15.280 5.905 829 | 15.701 6.653 830 | 16.036 7.507 831 | 11.328 3.731 832 | 11.998 3.224 833 | 12.821 2.989 834 | 13.703 3.035 835 | 14.550 3.339 836 | 15.292 3.811 837 | 15.898 4.406 838 | 16.369 5.106 839 | 16.726 5.911 840 | 16.988 6.833 841 | 12.399 4.130 842 | 13.132 3.374 843 | 14.003 2.821 844 | 14.935 2.581 845 | 15.824 2.662 846 | 16.569 3.022 847 | 17.132 3.614 848 | 17.534 4.353 849 | 17.806 5.219 850 | 17.979 6.220 851 | 13.478 4.638 852 | 14.275 3.746 853 | 15.199 3.001 854 | 16.188 2.476 855 | 17.132 2.226 856 | 17.880 2.405 857 | 18.391 2.966 858 | 18.715 3.709 859 | 18.894 4.616 860 | 18.967 5.698 861 | 14.504 5.213 862 | 15.353 4.266 863 | 16.318 3.425 864 | 17.345 2.714 865 | 18.323 2.223 866 | 19.084 2.203 867 | 19.572 2.618 868 | 19.838 3.271 869 | 19.930 4.177 870 | 19.904 5.322 871 | 15.440 5.811 872 | 16.320 4.838 873 | 17.303 3.951 874 | 18.337 3.171 875 | 19.318 2.590 876 | 20.105 2.369 877 | 20.610 2.539 878 | 20.838 3.063 879 | 20.853 3.954 880 | 20.741 5.145 881 | 16.264 6.410 882 | 17.156 5.407 883 | 18.138 4.472 884 | 19.168 3.616 885 | 20.162 2.893 886 | 20.993 2.425 887 | 21.517 2.407 888 | 21.690 2.942 889 | 21.624 3.914 890 | 21.439 5.173 891 | 16.968 7.019 892 | 17.852 5.974 893 | 18.817 4.969 894 | 19.833 3.985 895 | 20.847 3.014 896 | 21.753 2.153 897 | 22.301 1.957 898 | 22.366 2.781 899 | 22.208 4.009 900 | 21.971 5.393 901 | 10.051 6.026 902 | 10.526 5.837 903 | 11.107 5.951 904 | 11.745 6.245 905 | 12.391 6.644 906 | 13.007 7.118 907 | 13.571 7.665 908 | 14.074 8.290 909 | 14.513 9.001 910 | 14.892 9.807 911 | 10.622 5.238 912 | 11.159 4.989 913 | 11.813 5.046 914 | 12.522 5.285 915 | 13.222 5.645 916 | 13.872 6.111 917 | 14.447 6.678 918 | 14.939 7.342 919 | 15.353 8.102 920 | 15.696 8.960 921 | 11.365 4.841 922 | 11.969 4.455 923 | 12.693 4.345 924 | 13.466 4.430 925 | 14.215 4.686 926 | 14.886 5.114 927 | 15.452 5.703 928 | 15.914 6.425 929 | 16.282 7.253 930 | 16.572 8.178 931 | 12.261 4.778 932 | 12.937 4.193 933 | 13.728 3.825 934 | 14.565 3.656 935 | 15.361 3.724 936 | 16.042 4.086 937 | 16.577 4.723 938 | 16.982 5.535 939 | 17.283 6.456 940 | 17.501 7.465 941 | 13.246 4.941 942 | 13.995 4.149 943 | 14.858 3.491 944 | 15.774 2.975 945 | 16.636 2.718 946 | 17.316 2.990 947 | 17.787 3.754 948 | 18.108 4.711 949 | 18.323 5.741 950 | 18.456 6.841 951 | 14.246 5.263 952 | 15.061 4.310 953 | 15.994 3.404 954 | 16.999 2.494 955 | 17.974 1.680 956 | 18.644 1.881 957 | 19.005 2.944 958 | 19.229 4.057 959 | 19.352 5.171 960 | 19.396 6.344 961 | 15.201 5.707 962 | 16.065 4.667 963 | 17.039 3.630 964 | 18.087 2.533 965 | 19.120 1.429 966 | 19.790 1.539 967 | 20.101 2.620 968 | 20.267 3.699 969 | 20.317 4.808 970 | 20.279 6.013 971 | 16.071 6.241 972 | 16.959 5.172 973 | 17.938 4.128 974 | 18.961 3.121 975 | 19.915 2.339 976 | 20.614 2.235 977 | 21.006 2.781 978 | 21.169 3.620 979 | 21.169 4.656 980 | 21.065 5.868 981 | 16.837 6.834 982 | 17.726 5.759 983 | 18.690 4.732 984 | 19.677 3.793 985 | 20.596 3.073 986 | 21.322 2.766 987 | 21.762 2.975 988 | 21.920 3.649 989 | 21.876 4.662 990 | 21.720 5.901 991 | 17.490 7.473 992 | 18.363 6.388 993 | 19.298 5.349 994 | 20.253 4.381 995 | 21.158 3.552 996 | 21.905 3.022 997 | 22.362 3.049 998 | 22.495 3.723 999 | 22.412 4.813 1000 | 22.224 6.115 -------------------------------------------------------------------------------- /tests/test_data/test3d_data.txt: -------------------------------------------------------------------------------- 1 | X Y Z VAR 2 | 2.40 3.80 5.93 12.13 3 | 6.83 6.83 5.36 19.03 4 | 3.01 9.71 0.87 13.59 5 | 8.00 1.01 0.76 9.78 6 | 6.03 1.23 0.79 8.06 7 | 5.73 9.77 7.19 22.69 8 | 2.99 3.78 2.59 9.36 9 | 0.11 1.43 5.95 7.49 10 | 3.38 3.82 2.20 9.40 11 | 4.42 5.67 9.52 19.61 -------------------------------------------------------------------------------- /tests/test_data/test_data.txt: -------------------------------------------------------------------------------- 1 | 1069209.0 243400.0 26.0 2 | 1070962.0 243239.0 73.0 3 | 1070957.0 241845.0 96.0 4 | 1070767.0 242652.0 73.0 5 | 1070063.0 242374.0 67.0 6 | 1069440.0 242201.0 39.0 7 | 1071851.0 241678.0 104.0 8 | 1068668.0 243391.0 22.0 9 | 1068897.0 242501.0 20.0 10 | 1068128.0 242816.0 9.0 11 | 1070878.0 243820.0 73.0 12 | 1069805.0 243729.0 48.0 13 | 1071428.0 241868.0 99.0 14 | 1071012.0 241572.0 96.0 15 | 1067493.0 243015.0 5.0 -------------------------------------------------------------------------------- /tests/test_regression_krige.py: -------------------------------------------------------------------------------- 1 | from itertools import product 2 | 3 | import numpy as np 4 | import pytest 5 | 6 | from pykrige.rk import RegressionKriging 7 | 8 | try: 9 | from sklearn.datasets import fetch_california_housing 10 | from sklearn.ensemble import RandomForestRegressor 11 | from sklearn.linear_model import ElasticNet, Lasso, LinearRegression 12 | from sklearn.model_selection import train_test_split 13 | from sklearn.svm import SVR 14 | 15 | SKLEARN_INSTALLED = True 16 | except ImportError: 17 | SKLEARN_INSTALLED = False 18 | 19 | 20 | def _methods(): 21 | krige_methods = ["ordinary", "universal"] 22 | ml_methods = [ 23 | SVR(C=0.01, gamma="auto"), 24 | RandomForestRegressor(min_samples_split=5, n_estimators=50), 25 | LinearRegression(), 26 | Lasso(), 27 | ElasticNet(), 28 | ] 29 | return product(ml_methods, krige_methods) 30 | 31 | 32 | @pytest.mark.skipif(not SKLEARN_INSTALLED, reason="requires scikit-learn") 33 | def test_regression_krige(): 34 | np.random.seed(1) 35 | x = np.linspace(-1.0, 1.0, 100) 36 | # create a feature matrix with 5 features 37 | X = np.tile(x, reps=(5, 1)).T 38 | y = ( 39 | 1 40 | + 5 * X[:, 0] 41 | - 2 * X[:, 1] 42 | - 2 * X[:, 2] 43 | + 3 * X[:, 3] 44 | + 4 * X[:, 4] 45 | + 2 * (np.random.rand(100) - 0.5) 46 | ) 47 | 48 | # create lat/lon array 49 | lon = np.linspace(-180.0, 180.0, 10) 50 | lat = np.linspace(-90.0, 90.0, 10) 51 | lon_lat = np.array(list(product(lon, lat))) 52 | 53 | X_train, X_test, y_train, y_test, lon_lat_train, lon_lat_test = train_test_split( 54 | X, y, lon_lat, train_size=0.7, random_state=10 55 | ) 56 | 57 | for ml_model, krige_method in _methods(): 58 | reg_kr_model = RegressionKriging( 59 | regression_model=ml_model, method=krige_method, n_closest_points=2 60 | ) 61 | reg_kr_model.fit(X_train, lon_lat_train, y_train) 62 | assert reg_kr_model.score(X_test, lon_lat_test, y_test) > 0.25 63 | 64 | 65 | @pytest.mark.skipif(not SKLEARN_INSTALLED, reason="requires scikit-learn") 66 | def test_krige_housing(): 67 | import ssl 68 | import urllib 69 | 70 | try: 71 | housing = fetch_california_housing() 72 | except (ssl.SSLError, urllib.error.URLError): 73 | ssl._create_default_https_context = ssl._create_unverified_context 74 | try: 75 | housing = fetch_california_housing() 76 | except PermissionError: 77 | # This can raise permission error on Appveyor 78 | pytest.skip("Failed to load california housing dataset") 79 | ssl._create_default_https_context = ssl.create_default_context 80 | 81 | # take only first 1000 82 | p = housing["data"][:1000, :-2] 83 | x = housing["data"][:1000, -2:] 84 | target = housing["target"][:1000] 85 | 86 | p_train, p_test, y_train, y_test, x_train, x_test = train_test_split( 87 | p, target, x, train_size=0.7, random_state=10 88 | ) 89 | 90 | for ml_model, krige_method in _methods(): 91 | reg_kr_model = RegressionKriging( 92 | regression_model=ml_model, method=krige_method, n_closest_points=2 93 | ) 94 | reg_kr_model.fit(p_train, x_train, y_train) 95 | if krige_method == "ordinary": 96 | assert reg_kr_model.score(p_test, x_test, y_test) > 0.5 97 | else: 98 | assert reg_kr_model.score(p_test, x_test, y_test) > 0.0 99 | --------------------------------------------------------------------------------