├── .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 | [](https://doi.org/10.5281/zenodo.3738604)
4 | [](https://badge.fury.io/py/PyKrige)
5 | [](https://anaconda.org/conda-forge/pykrige)
6 | [](https://github.com/GeoStat-Framework/PyKrige/actions)
7 | [](https://coveralls.io/github/GeoStat-Framework/PyKrige?branch=main)
8 | [](http://pykrige.readthedocs.io/en/stable/?badge=stable)
9 | [](https://github.com/psf/black)
10 |
11 |
12 |
13 |
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 | Documentation
4 | {{ super() }}
5 |
6 | PyKrige Links
7 |
12 |
13 | GeoStat Framework
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 |
--------------------------------------------------------------------------------