From daa1d3d708dd085c69f6a5e47e503034b24f6430 Mon Sep 17 00:00:00 2001 From: mralbu Date: Sun, 30 Aug 2020 15:39:48 -0300 Subject: [PATCH 01/17] Add ClassificationKriging --- README.rst | 12 + examples/10_classification_kriging2d.py | 49 ++++ pykrige/rk.py | 324 +++++++++++++++++++++++- tests/test_classification_krige.py | 101 ++++++++ 4 files changed, 474 insertions(+), 12 deletions(-) create mode 100644 examples/10_classification_kriging2d.py create mode 100644 tests/test_classification_krige.py diff --git a/README.rst b/README.rst index 822de88..22ce9b4 100644 --- a/README.rst +++ b/README.rst @@ -74,6 +74,7 @@ Kriging algorithms * ``OrdinaryKriging3D``: 3D ordinary kriging * ``UniversalKriging3D``: 3D universal kriging * ``RegressionKriging``: An implementation of Regression-Kriging +* ``ClassificationKriging``: An implementation of Simplicial Indicator Kriging Wrappers @@ -109,6 +110,17 @@ the ``OrdinaryKriging`` or the ``UniversalKriging`` class, and performs a correc A demonstration of the regression kriging is provided in the `corresponding example `_. +Classification Kriging +------------------ + +`Simplifical Indicator kriging `_ can be performed +with `pykrige.rk.ClassificationKriging `_. +This class takes as parameters a scikit-learn classification model, and details of either either +the ``OrdinaryKriging`` or the ``UniversalKriging`` class, and performs a correction steps on the ML classification prediction. + +A demonstration of the classification kriging is provided in the +`corresponding example `_. + License ^^^^^^^ diff --git a/examples/10_classification_kriging2d.py b/examples/10_classification_kriging2d.py new file mode 100644 index 0000000..97ea9f8 --- /dev/null +++ b/examples/10_classification_kriging2d.py @@ -0,0 +1,49 @@ +""" +Classification kriging +------------------ + +An example of classification kriging +""" + +import sys + +from sklearn.svm import SVC +from sklearn.ensemble import RandomForestClassifier +from sklearn.linear_model import LogisticRegression +from sklearn.datasets import fetch_california_housing +from sklearn.preprocessing import KBinsDiscretizer + +from pykrige.rk import ClassificationKriging +from pykrige.compat import train_test_split + +svc_model = SVC(C=0.1, gamma="auto", probability=True) +rf_model = RandomForestClassifier(n_estimators=100) +lr_model = LogisticRegression(max_iter=10000) + +models = [svc_model, rf_model, lr_model] + +try: + housing = fetch_california_housing() +except PermissionError: + # this dataset can occasionally fail to download on Windows + sys.exit(0) + +# take the first 5000 as Kriging is memory intensive +p = housing["data"][:5000, :-2] +x = housing["data"][:5000, -2:] +target = housing["target"][:5000] +discretizer = KBinsDiscretizer(encode='ordinal') +target = discretizer.fit_transform(target.reshape(-1, 1)) + +p_train, p_test, x_train, x_test, target_train, target_test = train_test_split( + p, x, target, test_size=0.3, random_state=42 +) + +for m in models: + print("=" * 40) + print("regression model:", m.__class__.__name__) + m_ck = ClassificationKriging(classification_model=m, n_closest_points=10) + m_ck.fit(p_train, x_train, target_train) + print("Classification Score: ", + m_ck.classification_model.score(p_test, target_test)) + print("CK score: ", m_ck.score(p_test, x_test, target_test)) diff --git a/pykrige/rk.py b/pykrige/rk.py index e7b4760..20c9ecf 100644 --- a/pykrige/rk.py +++ b/pykrige/rk.py @@ -1,14 +1,17 @@ # coding: utf-8 +from scipy.linalg import helmert +import numpy as np +from sklearn.metrics import r2_score, accuracy_score +from sklearn.svm import SVR +from sklearn.base import RegressorMixin, ClassifierMixin, BaseEstimator +from sklearn.preprocessing import OneHotEncoder +from pykrige.uk3d import UniversalKriging3D +from pykrige.ok3d import OrdinaryKriging3D +from pykrige.uk import UniversalKriging +from pykrige.ok import OrdinaryKriging from pykrige.compat import validate_sklearn validate_sklearn() -from pykrige.ok import OrdinaryKriging -from pykrige.uk import UniversalKriging -from pykrige.ok3d import OrdinaryKriging3D -from pykrige.uk3d import UniversalKriging3D -from sklearn.base import RegressorMixin, BaseEstimator -from sklearn.svm import SVR -from sklearn.metrics import r2_score krige_methods = { "ordinary": OrdinaryKriging, @@ -260,12 +263,18 @@ def execute(self, points, *args, **kwargs): return prediction, variance -def check_sklearn_model(model): +def check_sklearn_model(model, task="regression"): """Check the sklearn method in use.""" - if not (isinstance(model, BaseEstimator) and isinstance(model, RegressorMixin)): - raise RuntimeError( - "Needs to supply an instance of a scikit-learn regression class." - ) + if task == "regression": + if not (isinstance(model, BaseEstimator) and isinstance(model, RegressorMixin)): + raise RuntimeError( + "Needs to supply an instance of a scikit-learn regression class." + ) + elif task == "classification": + if not (isinstance(model, BaseEstimator) and isinstance(model, ClassifierMixin)): + raise RuntimeError( + "Needs to supply an instance of a scikit-learn classification class." + ) class RegressionKriging: @@ -444,3 +453,294 @@ def score(self, p, x, y, sample_weight=None, **kwargs): return r2_score( y_pred=self.predict(p, x, **kwargs), y_true=y, sample_weight=sample_weight ) + + +class ClassificationKriging: + """ + An implementation of Simplicial Indicator Kriging. + + Parameters + ---------- + classification_model: machine learning model instance from sklearn + method: str, optional + type of kriging to be performed + variogram_model: str, optional + variogram model to be used during Kriging + n_closest_points: int + number of closest points to be used during Ordinary Kriging + nlags: int + see OK/UK class description + weight: bool + see OK/UK class description + verbose: bool + see OK/UK class description + exact_values : bool + see OK/UK class description + variogram_parameters : list or dict + see OK/UK class description + variogram_function : callable + see OK/UK class description + anisotropy_scaling : tuple + single value for 2D (UK/OK) and two values in 3D (UK3D/OK3D) + anisotropy_angle : tuple + single value for 2D (UK/OK) and three values in 3D (UK3D/OK3D) + enable_statistics : bool + see OK class description + coordinates_type : str + see OK/UK class description + drift_terms : list of strings + see UK/UK3D class description + point_drift : array_like + see UK class description + ext_drift_grid : tuple + Holding the three values external_drift, external_drift_x and + external_drift_z for the UK class + functional_drift : list of callable + see UK/UK3D class description + """ + + def __init__( + self, + classification_model=SVR(), + method="ordinary", + variogram_model="linear", + n_closest_points=10, + nlags=6, + weight=False, + verbose=False, + exact_values=True, + pseudo_inv=False, + pseudo_inv_type="pinv", + variogram_parameters=None, + variogram_function=None, + anisotropy_scaling=(1.0, 1.0), + anisotropy_angle=(0.0, 0.0, 0.0), + enable_statistics=False, + coordinates_type="euclidean", + drift_terms=None, + point_drift=None, + ext_drift_grid=(None, None, None), + functional_drift=None, + ): + check_sklearn_model(classification_model, task="classification") + self.classification_model = classification_model + self.n_closest_points = n_closest_points + self._kriging_kwargs = dict(method=method, + variogram_model=variogram_model, + nlags=nlags, + weight=weight, + n_closest_points=n_closest_points, + verbose=verbose, + exact_values=exact_values, + pseudo_inv=pseudo_inv, + pseudo_inv_type=pseudo_inv_type, + variogram_parameters=variogram_parameters, + variogram_function=variogram_function, + anisotropy_scaling=anisotropy_scaling, + anisotropy_angle=anisotropy_angle, + enable_statistics=enable_statistics, + coordinates_type=coordinates_type, + drift_terms=drift_terms, + point_drift=point_drift, + ext_drift_grid=ext_drift_grid, + functional_drift=functional_drift) + + def fit(self, p, x, y): + """ + Fit the classification method and also krige the residual. + + Parameters + ---------- + p: ndarray + (Ns, d) array of predictor variables (Ns samples, d dimensions) + for classification + x: ndarray + ndarray of (x, y) points. Needs to be a (Ns, 2) array + corresponding to the lon/lat, for example 2d classification kriging. + array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging + y: ndarray + array of targets (Ns, ) + """ + self.classification_model.fit(p, y.ravel()) + print("Finished learning classification model") + self.classes_ = self.classification_model.classes_ + + self.krige = [] + for i in range(len(self.classes_) - 1): + self.krige.append(Krige(**self._kriging_kwargs)) + + ml_pred = self.classification_model.predict_proba(p) + ml_pred_ilr = ilr_transformation(ml_pred) + + self.onehotencode = OneHotEncoder( + categories=[self.classes_] + ) + y_ohe = np.array(self.onehotencode.fit_transform(y).todense()) + y_ohe_ilr = ilr_transformation(y_ohe) + + for i in range(len(self.classes_) - 1): + self.krige[i].fit(x=x, y=y_ohe_ilr[:, i] - ml_pred_ilr[:, i]) + + print("Finished kriging residuals") + + def predict(self, p, x, **kwargs): + """ + Predict. + + Parameters + ---------- + p: ndarray + (Ns, d) array of predictor variables (Ns samples, d dimensions) + for classification + x: ndarray + ndarray of (x, y) points. Needs to be a (Ns, 2) array + corresponding to the lon/lat, for example. + array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging + + Returns + ------- + pred: ndarray + The expected value of ys for the query inputs, of shape (Ns,). + + """ + + ml_pred = self.classification_model.predict_proba(p) + ml_pred_ilr = ilr_transformation(ml_pred) + + pred_proba_ilr = self.krige_residual(x, **kwargs) + ml_pred_ilr + pred_proba = inverse_ilr_transformation(pred_proba_ilr) + + return np.argmax(pred_proba, axis=1) + + def krige_residual(self, x, **kwargs): + """ + Calculate the residuals. + + Parameters + ---------- + x: ndarray + ndarray of (x, y) points. Needs to be a (Ns, 2) array + corresponding to the lon/lat, for example. + + Returns + ------- + residual: ndarray + kriged residual values + """ + + krig_pred = [self.krige[i].predict( + x=x, **kwargs) for i in range(len(self.classes_) - 1)] + + return np.vstack(krig_pred).T + + def score(self, p, x, y, sample_weight=None, **kwargs): + """ + Overloading default classification score method. + + Parameters + ---------- + p: ndarray + (Ns, d) array of predictor variables (Ns samples, d dimensions) + for classification + x: ndarray + ndarray of (x, y) points. Needs to be a (Ns, 2) array + corresponding to the lon/lat, for example. + array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging + y: ndarray + array of targets (Ns, ) + """ + return accuracy_score( + y_pred=self.predict(p, x, **kwargs), y_true=y, sample_weight=sample_weight + ) + + +def closure(data, k=1.0): + """Apply closure to data, sample-wise. + Adapted from https://github.com/ofgulban/compoda. + + Parameters + ---------- + data : 2d numpy array, shape [n_samples, n_measurements] + Data to be closed to a certain constant. Do not forget to deal with + zeros in the data before this operation. + k : float, positive + Sum of the measurements will be equal to this number. + + Returns + ------- + data : 2d numpy array, shape [n_samples, n_measurements] + Closed data. + + Reference + --------- + [1] Pawlowsky-Glahn, V., Egozcue, J. J., & Tolosana-Delgado, R. + (2015). Modelling and Analysis of Compositional Data, pg. 9. + Chichester, UK: John Wiley & Sons, Ltd. + DOI: 10.1002/9781119003144 + """ + data_sum = np.sum(data, axis=1) + out = np.copy(data) + for i in range(data.shape[1]): + out[:, i] = np.divide(out[:, i], data_sum) + out = out * k + return out + + +def ilr_transformation(data): + """Isometric logratio transformation (not vectorized). + Adapted from https://github.com/ofgulban/compoda. + + Parameters + ---------- + data : 2d numpy array, shape [n_samples, n_coordinates] + Barycentric coordinates (closed) in simplex space. + + Returns + ------- + out : 2d numpy array, shape [n_samples, n_coordinates-1] + Coordinates in real space. + + Reference + --------- + [1] Pawlowsky-Glahn, V., Egozcue, J. J., & Tolosana-Delgado, R. + (2015). Modelling and Analysis of Compositional Data, pg. 37. + Chichester, UK: John Wiley & Sons, Ltd. + DOI: 10.1002/9781119003144 + """ + data = data + np.finfo(float).eps + dims = data.shape + out = np.zeros((dims[0], dims[1]-1)) + helmertian = helmert(dims[1]).T + for i in range(data.shape[0]): + out[i, :] = np.dot(np.log(data[i, :]), helmertian) + return out + + +def inverse_ilr_transformation(data): + """Inverse isometric logratio transformation (not vectorized). + Adapted from https://github.com/ofgulban/compoda. + + Parameters + ---------- + data : 2d numpy array, shape [n_samples, n_coordinates] + Isometric log-ratio transformed coordinates in real space. + + Returns + ------- + out : 2d numpy array, shape [n_samples, n_coordinates+1] + Barycentric coordinates (closed) in simplex space. + + Reference + --------- + [1] Pawlowsky-Glahn, V., Egozcue, J. J., & Tolosana-Delgado, R. + (2015). Modelling and Analysis of Compositional Data, pg. 37. + Chichester, UK: John Wiley & Sons, Ltd. + DOI: 10.1002/9781119003144 + """ + data = data + np.finfo(float).eps + dims = data.shape + out = np.zeros((dims[0], dims[1]+1)) + helmertian = helmert(dims[1]+1) + for i in range(data.shape[0]): + out[i, :] = np.exp(np.dot(data[i, :], helmertian)) + return closure(out) diff --git a/tests/test_classification_krige.py b/tests/test_classification_krige.py new file mode 100644 index 0000000..240d3e7 --- /dev/null +++ b/tests/test_classification_krige.py @@ -0,0 +1,101 @@ +from itertools import product +import pytest + +import numpy as np + +from pykrige.rk import ClassificationKriging + +try: + from sklearn.svm import SVC + from sklearn.datasets import fetch_california_housing + from sklearn.ensemble import RandomForestClassifier + from sklearn.preprocessing import KBinsDiscretizer + from pykrige.compat import train_test_split + + SKLEARN_INSTALLED = True +except ImportError: + SKLEARN_INSTALLED = False + + +def _methods(): + krige_methods = ["ordinary", "universal"] + ml_methods = [ + SVC(C=0.01, gamma="auto", probability=True), + RandomForestClassifier(n_estimators=50) + ] + return product(ml_methods, krige_methods) + + +@pytest.mark.skipif(not SKLEARN_INSTALLED, reason="requires scikit-learn") +def test_classification_krige(): + np.random.seed(1) + x = np.linspace(-1.0, 1.0, 100) + # create a feature matrix with 5 features + X = np.tile(x, reps=(5, 1)).T + y = ( + 1 + + 5 * X[:, 0] + - 2 * X[:, 1] + - 2 * X[:, 2] + + 3 * X[:, 3] + + 4 * X[:, 4] + + 2 * (np.random.rand(100) - 0.5) + ) + + # create lat/lon array + lon = np.linspace(-180.0, 180.0, 10) + lat = np.linspace(-90.0, 90.0, 10) + lon_lat = np.array(list(product(lon, lat))) + + discretizer = KBinsDiscretizer(encode='ordinal') + y = discretizer.fit_transform(y.reshape(-1, 1)) + + X_train, X_test, y_train, y_test, lon_lat_train, lon_lat_test = train_test_split( + X, y, lon_lat, train_size=0.7, random_state=10 + ) + + for ml_model, krige_method in _methods(): + reg_class_model = ClassificationKriging( + classification_model=ml_model, method=krige_method, n_closest_points=2 + ) + reg_class_model.fit(X_train, lon_lat_train, y_train) + assert reg_class_model.score(X_test, lon_lat_test, y_test) > 0.25 + + +@pytest.mark.skipif(not SKLEARN_INSTALLED, reason="requires scikit-learn") +def test_krige_classification_housing(): + import ssl + import urllib + + try: + housing = fetch_california_housing() + except (ssl.SSLError, urllib.error.URLError): + ssl._create_default_https_context = ssl._create_unverified_context + try: + housing = fetch_california_housing() + except PermissionError: + # This can raise permission error on Appveyor + pytest.skip("Failed to load california housing dataset") + ssl._create_default_https_context = ssl.create_default_context + + # take only first 1000 + p = housing["data"][:1000, :-2] + x = housing["data"][:1000, -2:] + target = housing["target"][:1000] + discretizer = KBinsDiscretizer(encode='ordinal') + target = discretizer.fit_transform(target.reshape(-1, 1)) + + p_train, p_test, y_train, y_test, x_train, x_test = train_test_split( + p, target, x, train_size=0.7, random_state=10 + ) + + for ml_model, krige_method in _methods(): + + reg_class_model = ClassificationKriging( + classification_model=ml_model, method=krige_method, n_closest_points=2 + ) + reg_class_model.fit(p_train, x_train, y_train) + if krige_method == "ordinary": + assert reg_class_model.score(p_test, x_test, y_test) > 0.5 + else: + assert reg_class_model.score(p_test, x_test, y_test) > 0.0 From 5dd5df11c2e33f86e770c45396dd9e1ede0120df Mon Sep 17 00:00:00 2001 From: mralbu Date: Sat, 12 Sep 2020 19:31:04 -0300 Subject: [PATCH 02/17] Add ClassificationKriging --- README.rst | 4 +- examples/10_classification_kriging2d.py | 11 +- pykrige/ck.py | 289 ++++++++++++ pykrige/compat.py | 270 +++++++++++ pykrige/rk.py | 567 +----------------------- pykrige/uk.py | 3 +- pykrige/uk3d.py | 3 +- tests/test_api.py | 4 +- tests/test_classification_krige.py | 22 +- tests/test_core.py | 7 +- 10 files changed, 593 insertions(+), 587 deletions(-) create mode 100644 pykrige/ck.py diff --git a/README.rst b/README.rst index 22ce9b4..6da0083 100644 --- a/README.rst +++ b/README.rst @@ -111,11 +111,11 @@ A demonstration of the regression kriging is provided in the `corresponding example `_. Classification Kriging ------------------- +---------------------- `Simplifical Indicator kriging `_ can be performed with `pykrige.rk.ClassificationKriging `_. -This class takes as parameters a scikit-learn classification model, and details of either either +This class takes as parameters a scikit-learn classification model, and details of either the ``OrdinaryKriging`` or the ``UniversalKriging`` class, and performs a correction steps on the ML classification prediction. A demonstration of the classification kriging is provided in the diff --git a/examples/10_classification_kriging2d.py b/examples/10_classification_kriging2d.py index 97ea9f8..aba2956 100644 --- a/examples/10_classification_kriging2d.py +++ b/examples/10_classification_kriging2d.py @@ -13,7 +13,7 @@ from sklearn.datasets import fetch_california_housing from sklearn.preprocessing import KBinsDiscretizer -from pykrige.rk import ClassificationKriging +from pykrige.ck import ClassificationKriging from pykrige.compat import train_test_split svc_model = SVC(C=0.1, gamma="auto", probability=True) @@ -32,7 +32,7 @@ p = housing["data"][:5000, :-2] x = housing["data"][:5000, -2:] target = housing["target"][:5000] -discretizer = KBinsDiscretizer(encode='ordinal') +discretizer = KBinsDiscretizer(encode="ordinal") target = discretizer.fit_transform(target.reshape(-1, 1)) p_train, p_test, x_train, x_test, target_train, target_test = train_test_split( @@ -41,9 +41,10 @@ for m in models: print("=" * 40) - print("regression model:", m.__class__.__name__) + print("classification model:", m.__class__.__name__) m_ck = ClassificationKriging(classification_model=m, n_closest_points=10) m_ck.fit(p_train, x_train, target_train) - print("Classification Score: ", - m_ck.classification_model.score(p_test, target_test)) + print( + "Classification Score: ", m_ck.classification_model.score(p_test, target_test) + ) print("CK score: ", m_ck.score(p_test, x_test, target_test)) diff --git a/pykrige/ck.py b/pykrige/ck.py new file mode 100644 index 0000000..feaa771 --- /dev/null +++ b/pykrige/ck.py @@ -0,0 +1,289 @@ +# coding: utf-8 +from pykrige.compat import Krige, validate_sklearn, check_sklearn_model + +validate_sklearn() + +from sklearn.metrics import r2_score, accuracy_score +from sklearn.svm import SVC +from sklearn.preprocessing import OneHotEncoder +import numpy as np +from scipy.linalg import helmert + + +class ClassificationKriging: + """ + An implementation of Simplicial Indicator Kriging applied to classification ilr transformed residuals. + + Parameters + ---------- + classification_model: machine learning model instance from sklearn + method: str, optional + type of kriging to be performed + variogram_model: str, optional + variogram model to be used during Kriging + n_closest_points: int + number of closest points to be used during Ordinary Kriging + nlags: int + see OK/UK class description + weight: bool + see OK/UK class description + verbose: bool + see OK/UK class description + exact_values : bool + see OK/UK class description + variogram_parameters : list or dict + see OK/UK class description + variogram_function : callable + see OK/UK class description + anisotropy_scaling : tuple + single value for 2D (UK/OK) and two values in 3D (UK3D/OK3D) + anisotropy_angle : tuple + single value for 2D (UK/OK) and three values in 3D (UK3D/OK3D) + enable_statistics : bool + see OK class description + coordinates_type : str + see OK/UK class description + drift_terms : list of strings + see UK/UK3D class description + point_drift : array_like + see UK class description + ext_drift_grid : tuple + Holding the three values external_drift, external_drift_x and + external_drift_z for the UK class + functional_drift : list of callable + see UK/UK3D class description + """ + + def __init__( + self, + classification_model=SVC(), + method="ordinary", + variogram_model="linear", + n_closest_points=10, + nlags=6, + weight=False, + verbose=False, + exact_values=True, + pseudo_inv=False, + pseudo_inv_type="pinv", + variogram_parameters=None, + variogram_function=None, + anisotropy_scaling=(1.0, 1.0), + anisotropy_angle=(0.0, 0.0, 0.0), + enable_statistics=False, + coordinates_type="euclidean", + drift_terms=None, + point_drift=None, + ext_drift_grid=(None, None, None), + functional_drift=None, + ): + check_sklearn_model(classification_model, task="classification") + self.classification_model = classification_model + self.n_closest_points = n_closest_points + self._kriging_kwargs = dict( + method=method, + variogram_model=variogram_model, + nlags=nlags, + weight=weight, + n_closest_points=n_closest_points, + verbose=verbose, + exact_values=exact_values, + pseudo_inv=pseudo_inv, + pseudo_inv_type=pseudo_inv_type, + variogram_parameters=variogram_parameters, + variogram_function=variogram_function, + anisotropy_scaling=anisotropy_scaling, + anisotropy_angle=anisotropy_angle, + enable_statistics=enable_statistics, + coordinates_type=coordinates_type, + drift_terms=drift_terms, + point_drift=point_drift, + ext_drift_grid=ext_drift_grid, + functional_drift=functional_drift, + ) + + def fit(self, p, x, y): + """ + Fit the classification method and also krige the residual. + + Parameters + ---------- + p: ndarray + (Ns, d) array of predictor variables (Ns samples, d dimensions) + for classification + x: ndarray + ndarray of (x, y) points. Needs to be a (Ns, 2) array + corresponding to the lon/lat, for example 2d classification kriging. + array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging + y: ndarray + array of targets (Ns, ) + """ + self.classification_model.fit(p, y.ravel()) + print("Finished learning classification model") + self.classes_ = self.classification_model.classes_ + + self.krige = [] + for i in range(len(self.classes_) - 1): + self.krige.append(Krige(**self._kriging_kwargs)) + + ml_pred = self.classification_model.predict_proba(p) + ml_pred_ilr = ilr_transformation(ml_pred) + + self.onehotencode = OneHotEncoder(categories=[self.classes_]) + y_ohe = np.array(self.onehotencode.fit_transform(y).todense()) + y_ohe_ilr = ilr_transformation(y_ohe) + + for i in range(len(self.classes_) - 1): + self.krige[i].fit(x=x, y=y_ohe_ilr[:, i] - ml_pred_ilr[:, i]) + + print("Finished kriging residuals") + + def predict(self, p, x, **kwargs): + """ + Predict. + + Parameters + ---------- + p: ndarray + (Ns, d) array of predictor variables (Ns samples, d dimensions) + for classification + x: ndarray + ndarray of (x, y) points. Needs to be a (Ns, 2) array + corresponding to the lon/lat, for example. + array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging + + Returns + ------- + pred: ndarray + The expected value of ys for the query inputs, of shape (Ns,). + + """ + + ml_pred = self.classification_model.predict_proba(p) + ml_pred_ilr = ilr_transformation(ml_pred) + + pred_proba_ilr = self.krige_residual(x, **kwargs) + ml_pred_ilr + pred_proba = inverse_ilr_transformation(pred_proba_ilr) + + return np.argmax(pred_proba, axis=1) + + def krige_residual(self, x, **kwargs): + """ + Calculate the residuals. + + Parameters + ---------- + x: ndarray + ndarray of (x, y) points. Needs to be a (Ns, 2) array + corresponding to the lon/lat, for example. + + Returns + ------- + residual: ndarray + kriged residual values + """ + + krig_pred = [ + self.krige[i].predict(x=x, **kwargs) for i in range(len(self.classes_) - 1) + ] + + return np.vstack(krig_pred).T + + def score(self, p, x, y, sample_weight=None, **kwargs): + """ + Overloading default classification score method. + + Parameters + ---------- + p: ndarray + (Ns, d) array of predictor variables (Ns samples, d dimensions) + for classification + x: ndarray + ndarray of (x, y) points. Needs to be a (Ns, 2) array + corresponding to the lon/lat, for example. + array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging + y: ndarray + array of targets (Ns, ) + """ + return accuracy_score( + y_pred=self.predict(p, x, **kwargs), y_true=y, sample_weight=sample_weight + ) + + +def closure(data, k=1.0): + """Apply closure to data, sample-wise. + Adapted from https://github.com/ofgulban/compoda. + + Parameters + ---------- + data : 2d numpy array, shape [n_samples, n_measurements] + Data to be closed to a certain constant. Do not forget to deal with + zeros in the data before this operation. + k : float, positive + Sum of the measurements will be equal to this number. + + Returns + ------- + data : 2d numpy array, shape [n_samples, n_measurements] + Closed data. + + Reference + --------- + [1] Pawlowsky-Glahn, V., Egozcue, J. J., & Tolosana-Delgado, R. + (2015). Modelling and Analysis of Compositional Data, pg. 9. + Chichester, UK: John Wiley & Sons, Ltd. + DOI: 10.1002/9781119003144 + """ + + return k * data / np.sum(data, axis=1)[:, np.newaxis] + + +def ilr_transformation(data): + """Isometric logratio transformation (not vectorized). + Adapted from https://github.com/ofgulban/compoda. + + Parameters + ---------- + data : 2d numpy array, shape [n_samples, n_coordinates] + Barycentric coordinates (closed) in simplex space. + + Returns + ------- + out : 2d numpy array, shape [n_samples, n_coordinates-1] + Coordinates in real space. + + Reference + --------- + [1] Pawlowsky-Glahn, V., Egozcue, J. J., & Tolosana-Delgado, R. + (2015). Modelling and Analysis of Compositional Data, pg. 37. + Chichester, UK: John Wiley & Sons, Ltd. + DOI: 10.1002/9781119003144 + """ + data = np.maximum(data, np.finfo(float).eps) + + return np.einsum("ij,jk->ik", np.log(data), -helmert(data.shape[1]).T) + + +def inverse_ilr_transformation(data): + """Inverse isometric logratio transformation (not vectorized). + Adapted from https://github.com/ofgulban/compoda. + + Parameters + ---------- + data : 2d numpy array, shape [n_samples, n_coordinates] + Isometric log-ratio transformed coordinates in real space. + + Returns + ------- + out : 2d numpy array, shape [n_samples, n_coordinates+1] + Barycentric coordinates (closed) in simplex space. + + Reference + --------- + [1] Pawlowsky-Glahn, V., Egozcue, J. J., & Tolosana-Delgado, R. + (2015). Modelling and Analysis of Compositional Data, pg. 37. + Chichester, UK: John Wiley & Sons, Ltd. + DOI: 10.1002/9781119003144 + """ + + return closure(np.exp(np.einsum("ij,jk->ik", data, -helmert(data.shape[1] + 1)))) diff --git a/pykrige/compat.py b/pykrige/compat.py index 73434aa..d3847e4 100644 --- a/pykrige/compat.py +++ b/pykrige/compat.py @@ -3,24 +3,294 @@ """For compatibility""" from functools import partial +from pykrige.uk3d import UniversalKriging3D +from pykrige.ok3d import OrdinaryKriging3D +from pykrige.uk import UniversalKriging +from pykrige.ok import OrdinaryKriging # sklearn try: from sklearn.model_selection import GridSearchCV from sklearn.model_selection import train_test_split + from sklearn.base import RegressorMixin, ClassifierMixin, BaseEstimator SKLEARN_INSTALLED = True except ImportError: SKLEARN_INSTALLED = False +krige_methods = { + "ordinary": OrdinaryKriging, + "universal": UniversalKriging, + "ordinary3d": OrdinaryKriging3D, + "universal3d": UniversalKriging3D, +} + +threed_krige = ("ordinary3d", "universal3d") + +krige_methods_kws = { + "ordinary": [ + "anisotropy_scaling", + "anisotropy_angle", + "enable_statistics", + "coordinates_type", + ], + "universal": [ + "anisotropy_scaling", + "anisotropy_angle", + "drift_terms", + "point_drift", + "external_drift", + "external_drift_x", + "external_drift_y", + "functional_drift", + ], + "ordinary3d": [ + "anisotropy_scaling_y", + "anisotropy_scaling_z", + "anisotropy_angle_x", + "anisotropy_angle_y", + "anisotropy_angle_z", + ], + "universal3d": [ + "anisotropy_scaling_y", + "anisotropy_scaling_z", + "anisotropy_angle_x", + "anisotropy_angle_y", + "anisotropy_angle_z", + "drift_terms", + "functional_drift", + ], +} + class SklearnException(Exception): pass +def validate_method(method): + """Validate the kriging method in use.""" + if method not in krige_methods.keys(): + raise ValueError( + "Kriging method must be one of {}".format(krige_methods.keys()) + ) + + def validate_sklearn(): if not SKLEARN_INSTALLED: raise SklearnException( "sklearn needs to be installed in order to use this module" ) + + +class Krige(RegressorMixin, BaseEstimator): + """ + A scikit-learn wrapper class for Ordinary and Universal Kriging. + + This works with both Grid/RandomSearchCv for finding the best + Krige parameters combination for a problem. + + Parameters + ---------- + method: str, optional + type of kriging to be performed + variogram_model: str, optional + variogram model to be used during Kriging + nlags: int + see OK/UK class description + weight: bool + see OK/UK class description + n_closest_points: int + number of closest points to be used during Ordinary Kriging + verbose: bool + see OK/UK class description + exact_values : bool + see OK/UK class description + variogram_parameters : list or dict + see OK/UK class description + variogram_function : callable + see OK/UK class description + anisotropy_scaling : tuple + single value for 2D (UK/OK) and two values in 3D (UK3D/OK3D) + anisotropy_angle : tuple + single value for 2D (UK/OK) and three values in 3D (UK3D/OK3D) + enable_statistics : bool + see OK class description + coordinates_type : str + see OK/UK class description + drift_terms : list of strings + see UK/UK3D class description + point_drift : array_like + see UK class description + ext_drift_grid : tuple + Holding the three values external_drift, external_drift_x and + external_drift_z for the UK class + functional_drift : list of callable + see UK/UK3D class description + """ + + def __init__( + self, + method="ordinary", + variogram_model="linear", + nlags=6, + weight=False, + n_closest_points=10, + verbose=False, + exact_values=True, + pseudo_inv=False, + pseudo_inv_type="pinv", + variogram_parameters=None, + variogram_function=None, + anisotropy_scaling=(1.0, 1.0), + anisotropy_angle=(0.0, 0.0, 0.0), + enable_statistics=False, + coordinates_type="euclidean", + drift_terms=None, + point_drift=None, + ext_drift_grid=(None, None, None), + functional_drift=None, + ): + validate_method(method) + self.variogram_model = variogram_model + self.variogram_parameters = variogram_parameters + self.variogram_function = variogram_function + self.nlags = nlags + self.weight = weight + self.verbose = verbose + self.exact_values = exact_values + self.pseudo_inv = pseudo_inv + self.pseudo_inv_type = pseudo_inv_type + self.anisotropy_scaling = anisotropy_scaling + self.anisotropy_angle = anisotropy_angle + self.enable_statistics = enable_statistics + self.coordinates_type = coordinates_type + self.drift_terms = drift_terms + self.point_drift = point_drift + self.ext_drift_grid = ext_drift_grid + self.functional_drift = functional_drift + self.model = None # not trained + self.n_closest_points = n_closest_points + self.method = method + + def fit(self, x, y, *args, **kwargs): + """ + Fit the current model. + + Parameters + ---------- + x: ndarray + array of Points, (x, y) pairs of shape (N, 2) for 2d kriging + array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging + y: ndarray + array of targets (N, ) + """ + val_kw = "val" if self.method in threed_krige else "z" + setup = dict( + variogram_model=self.variogram_model, + variogram_parameters=self.variogram_parameters, + variogram_function=self.variogram_function, + nlags=self.nlags, + weight=self.weight, + verbose=self.verbose, + exact_values=self.exact_values, + pseudo_inv=self.pseudo_inv, + pseudo_inv_type=self.pseudo_inv_type, + ) + add_setup = dict( + anisotropy_scaling=self.anisotropy_scaling[0], + anisotropy_angle=self.anisotropy_angle[0], + enable_statistics=self.enable_statistics, + coordinates_type=self.coordinates_type, + anisotropy_scaling_y=self.anisotropy_scaling[0], + anisotropy_scaling_z=self.anisotropy_scaling[1], + anisotropy_angle_x=self.anisotropy_angle[0], + anisotropy_angle_y=self.anisotropy_angle[1], + anisotropy_angle_z=self.anisotropy_angle[2], + drift_terms=self.drift_terms, + point_drift=self.point_drift, + external_drift=self.ext_drift_grid[0], + external_drift_x=self.ext_drift_grid[1], + external_drift_y=self.ext_drift_grid[2], + functional_drift=self.functional_drift, + ) + for kw in krige_methods_kws[self.method]: + setup[kw] = add_setup[kw] + input_kw = self._dimensionality_check(x) + input_kw.update(setup) + input_kw[val_kw] = y + self.model = krige_methods[self.method](**input_kw) + + def _dimensionality_check(self, x, ext=""): + if self.method in ("ordinary", "universal"): + if x.shape[1] != 2: + raise ValueError("2d krige can use only 2d points") + else: + return {"x" + ext: x[:, 0], "y" + ext: x[:, 1]} + if self.method in ("ordinary3d", "universal3d"): + if x.shape[1] != 3: + raise ValueError("3d krige can use only 3d points") + else: + return { + "x" + ext: x[:, 0], + "y" + ext: x[:, 1], + "z" + ext: x[:, 2], + } + + def predict(self, x, *args, **kwargs): + """ + Predict. + + Parameters + ---------- + x: ndarray + array of Points, (x, y) pairs of shape (N, 2) for 2d kriging + array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging + Returns + ------- + Prediction array + """ + if not self.model: + raise Exception("Not trained. Train first") + points = self._dimensionality_check(x, ext="points") + return self.execute(points, *args, **kwargs)[0] + + def execute(self, points, *args, **kwargs): + # TODO array of Points, (x, y) pairs of shape (N, 2) + """ + Execute. + + Parameters + ---------- + points: dict + + Returns + ------- + Prediction array + Variance array + """ + default_kw = dict(style="points", backend="loop") + default_kw.update(kwargs) + points.update(default_kw) + if isinstance(self.model, (OrdinaryKriging, OrdinaryKriging3D)): + points.update(dict(n_closest_points=self.n_closest_points)) + else: + print("n_closest_points will be ignored for UniversalKriging") + prediction, variance = self.model.execute(**points) + return prediction, variance + + +def check_sklearn_model(model, task="regression"): + """Check the sklearn method in use.""" + if task == "regression": + if not (isinstance(model, BaseEstimator) and isinstance(model, RegressorMixin)): + raise RuntimeError( + "Needs to supply an instance of a scikit-learn regression class." + ) + elif task == "classification": + if not ( + isinstance(model, BaseEstimator) and isinstance(model, ClassifierMixin) + ): + raise RuntimeError( + "Needs to supply an instance of a scikit-learn classification class." + ) diff --git a/pykrige/rk.py b/pykrige/rk.py index 20c9ecf..7e6821b 100644 --- a/pykrige/rk.py +++ b/pykrige/rk.py @@ -1,280 +1,10 @@ # coding: utf-8 -from scipy.linalg import helmert -import numpy as np -from sklearn.metrics import r2_score, accuracy_score -from sklearn.svm import SVR -from sklearn.base import RegressorMixin, ClassifierMixin, BaseEstimator -from sklearn.preprocessing import OneHotEncoder -from pykrige.uk3d import UniversalKriging3D -from pykrige.ok3d import OrdinaryKriging3D -from pykrige.uk import UniversalKriging -from pykrige.ok import OrdinaryKriging -from pykrige.compat import validate_sklearn +from pykrige.compat import Krige, validate_sklearn, check_sklearn_model validate_sklearn() -krige_methods = { - "ordinary": OrdinaryKriging, - "universal": UniversalKriging, - "ordinary3d": OrdinaryKriging3D, - "universal3d": UniversalKriging3D, -} - -threed_krige = ("ordinary3d", "universal3d") - -krige_methods_kws = { - "ordinary": [ - "anisotropy_scaling", - "anisotropy_angle", - "enable_statistics", - "coordinates_type", - ], - "universal": [ - "anisotropy_scaling", - "anisotropy_angle", - "drift_terms", - "point_drift", - "external_drift", - "external_drift_x", - "external_drift_y", - "functional_drift", - ], - "ordinary3d": [ - "anisotropy_scaling_y", - "anisotropy_scaling_z", - "anisotropy_angle_x", - "anisotropy_angle_y", - "anisotropy_angle_z", - ], - "universal3d": [ - "anisotropy_scaling_y", - "anisotropy_scaling_z", - "anisotropy_angle_x", - "anisotropy_angle_y", - "anisotropy_angle_z", - "drift_terms", - "functional_drift", - ], -} - - -def validate_method(method): - """Validate the kriging method in use.""" - if method not in krige_methods.keys(): - raise ValueError( - "Kriging method must be one of {}".format(krige_methods.keys()) - ) - - -class Krige(RegressorMixin, BaseEstimator): - """ - A scikit-learn wrapper class for Ordinary and Universal Kriging. - - This works with both Grid/RandomSearchCv for finding the best - Krige parameters combination for a problem. - - Parameters - ---------- - method: str, optional - type of kriging to be performed - variogram_model: str, optional - variogram model to be used during Kriging - nlags: int - see OK/UK class description - weight: bool - see OK/UK class description - n_closest_points: int - number of closest points to be used during Ordinary Kriging - verbose: bool - see OK/UK class description - exact_values : bool - see OK/UK class description - variogram_parameters : list or dict - see OK/UK class description - variogram_function : callable - see OK/UK class description - anisotropy_scaling : tuple - single value for 2D (UK/OK) and two values in 3D (UK3D/OK3D) - anisotropy_angle : tuple - single value for 2D (UK/OK) and three values in 3D (UK3D/OK3D) - enable_statistics : bool - see OK class description - coordinates_type : str - see OK/UK class description - drift_terms : list of strings - see UK/UK3D class description - point_drift : array_like - see UK class description - ext_drift_grid : tuple - Holding the three values external_drift, external_drift_x and - external_drift_z for the UK class - functional_drift : list of callable - see UK/UK3D class description - """ - - def __init__( - self, - method="ordinary", - variogram_model="linear", - nlags=6, - weight=False, - n_closest_points=10, - verbose=False, - exact_values=True, - pseudo_inv=False, - pseudo_inv_type="pinv", - variogram_parameters=None, - variogram_function=None, - anisotropy_scaling=(1.0, 1.0), - anisotropy_angle=(0.0, 0.0, 0.0), - enable_statistics=False, - coordinates_type="euclidean", - drift_terms=None, - point_drift=None, - ext_drift_grid=(None, None, None), - functional_drift=None, - ): - validate_method(method) - self.variogram_model = variogram_model - self.variogram_parameters = variogram_parameters - self.variogram_function = variogram_function - self.nlags = nlags - self.weight = weight - self.verbose = verbose - self.exact_values = exact_values - self.pseudo_inv = pseudo_inv - self.pseudo_inv_type = pseudo_inv_type - self.anisotropy_scaling = anisotropy_scaling - self.anisotropy_angle = anisotropy_angle - self.enable_statistics = enable_statistics - self.coordinates_type = coordinates_type - self.drift_terms = drift_terms - self.point_drift = point_drift - self.ext_drift_grid = ext_drift_grid - self.functional_drift = functional_drift - self.model = None # not trained - self.n_closest_points = n_closest_points - self.method = method - - def fit(self, x, y, *args, **kwargs): - """ - Fit the current model. - - Parameters - ---------- - x: ndarray - array of Points, (x, y) pairs of shape (N, 2) for 2d kriging - array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging - y: ndarray - array of targets (N, ) - """ - val_kw = "val" if self.method in threed_krige else "z" - setup = dict( - variogram_model=self.variogram_model, - variogram_parameters=self.variogram_parameters, - variogram_function=self.variogram_function, - nlags=self.nlags, - weight=self.weight, - verbose=self.verbose, - exact_values=self.exact_values, - pseudo_inv=self.pseudo_inv, - pseudo_inv_type=self.pseudo_inv_type, - ) - add_setup = dict( - anisotropy_scaling=self.anisotropy_scaling[0], - anisotropy_angle=self.anisotropy_angle[0], - enable_statistics=self.enable_statistics, - coordinates_type=self.coordinates_type, - anisotropy_scaling_y=self.anisotropy_scaling[0], - anisotropy_scaling_z=self.anisotropy_scaling[1], - anisotropy_angle_x=self.anisotropy_angle[0], - anisotropy_angle_y=self.anisotropy_angle[1], - anisotropy_angle_z=self.anisotropy_angle[2], - drift_terms=self.drift_terms, - point_drift=self.point_drift, - external_drift=self.ext_drift_grid[0], - external_drift_x=self.ext_drift_grid[1], - external_drift_y=self.ext_drift_grid[2], - functional_drift=self.functional_drift, - ) - for kw in krige_methods_kws[self.method]: - setup[kw] = add_setup[kw] - input_kw = self._dimensionality_check(x) - input_kw.update(setup) - input_kw[val_kw] = y - self.model = krige_methods[self.method](**input_kw) - - def _dimensionality_check(self, x, ext=""): - if self.method in ("ordinary", "universal"): - if x.shape[1] != 2: - raise ValueError("2d krige can use only 2d points") - else: - return {"x" + ext: x[:, 0], "y" + ext: x[:, 1]} - if self.method in ("ordinary3d", "universal3d"): - if x.shape[1] != 3: - raise ValueError("3d krige can use only 3d points") - else: - return { - "x" + ext: x[:, 0], - "y" + ext: x[:, 1], - "z" + ext: x[:, 2], - } - - def predict(self, x, *args, **kwargs): - """ - Predict. - - Parameters - ---------- - x: ndarray - array of Points, (x, y) pairs of shape (N, 2) for 2d kriging - array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging - Returns - ------- - Prediction array - """ - if not self.model: - raise Exception("Not trained. Train first") - points = self._dimensionality_check(x, ext="points") - return self.execute(points, *args, **kwargs)[0] - - def execute(self, points, *args, **kwargs): - # TODO array of Points, (x, y) pairs of shape (N, 2) - """ - Execute. - - Parameters - ---------- - points: dict - - Returns - ------- - Prediction array - Variance array - """ - default_kw = dict(style="points", backend="loop") - default_kw.update(kwargs) - points.update(default_kw) - if isinstance(self.model, (OrdinaryKriging, OrdinaryKriging3D)): - points.update(dict(n_closest_points=self.n_closest_points)) - else: - print("n_closest_points will be ignored for UniversalKriging") - prediction, variance = self.model.execute(**points) - return prediction, variance - - -def check_sklearn_model(model, task="regression"): - """Check the sklearn method in use.""" - if task == "regression": - if not (isinstance(model, BaseEstimator) and isinstance(model, RegressorMixin)): - raise RuntimeError( - "Needs to supply an instance of a scikit-learn regression class." - ) - elif task == "classification": - if not (isinstance(model, BaseEstimator) and isinstance(model, ClassifierMixin)): - raise RuntimeError( - "Needs to supply an instance of a scikit-learn classification class." - ) +from sklearn.metrics import r2_score, accuracy_score +from sklearn.svm import SVR class RegressionKriging: @@ -453,294 +183,3 @@ def score(self, p, x, y, sample_weight=None, **kwargs): return r2_score( y_pred=self.predict(p, x, **kwargs), y_true=y, sample_weight=sample_weight ) - - -class ClassificationKriging: - """ - An implementation of Simplicial Indicator Kriging. - - Parameters - ---------- - classification_model: machine learning model instance from sklearn - method: str, optional - type of kriging to be performed - variogram_model: str, optional - variogram model to be used during Kriging - n_closest_points: int - number of closest points to be used during Ordinary Kriging - nlags: int - see OK/UK class description - weight: bool - see OK/UK class description - verbose: bool - see OK/UK class description - exact_values : bool - see OK/UK class description - variogram_parameters : list or dict - see OK/UK class description - variogram_function : callable - see OK/UK class description - anisotropy_scaling : tuple - single value for 2D (UK/OK) and two values in 3D (UK3D/OK3D) - anisotropy_angle : tuple - single value for 2D (UK/OK) and three values in 3D (UK3D/OK3D) - enable_statistics : bool - see OK class description - coordinates_type : str - see OK/UK class description - drift_terms : list of strings - see UK/UK3D class description - point_drift : array_like - see UK class description - ext_drift_grid : tuple - Holding the three values external_drift, external_drift_x and - external_drift_z for the UK class - functional_drift : list of callable - see UK/UK3D class description - """ - - def __init__( - self, - classification_model=SVR(), - method="ordinary", - variogram_model="linear", - n_closest_points=10, - nlags=6, - weight=False, - verbose=False, - exact_values=True, - pseudo_inv=False, - pseudo_inv_type="pinv", - variogram_parameters=None, - variogram_function=None, - anisotropy_scaling=(1.0, 1.0), - anisotropy_angle=(0.0, 0.0, 0.0), - enable_statistics=False, - coordinates_type="euclidean", - drift_terms=None, - point_drift=None, - ext_drift_grid=(None, None, None), - functional_drift=None, - ): - check_sklearn_model(classification_model, task="classification") - self.classification_model = classification_model - self.n_closest_points = n_closest_points - self._kriging_kwargs = dict(method=method, - variogram_model=variogram_model, - nlags=nlags, - weight=weight, - n_closest_points=n_closest_points, - verbose=verbose, - exact_values=exact_values, - pseudo_inv=pseudo_inv, - pseudo_inv_type=pseudo_inv_type, - variogram_parameters=variogram_parameters, - variogram_function=variogram_function, - anisotropy_scaling=anisotropy_scaling, - anisotropy_angle=anisotropy_angle, - enable_statistics=enable_statistics, - coordinates_type=coordinates_type, - drift_terms=drift_terms, - point_drift=point_drift, - ext_drift_grid=ext_drift_grid, - functional_drift=functional_drift) - - def fit(self, p, x, y): - """ - Fit the classification method and also krige the residual. - - Parameters - ---------- - p: ndarray - (Ns, d) array of predictor variables (Ns samples, d dimensions) - for classification - x: ndarray - ndarray of (x, y) points. Needs to be a (Ns, 2) array - corresponding to the lon/lat, for example 2d classification kriging. - array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging - y: ndarray - array of targets (Ns, ) - """ - self.classification_model.fit(p, y.ravel()) - print("Finished learning classification model") - self.classes_ = self.classification_model.classes_ - - self.krige = [] - for i in range(len(self.classes_) - 1): - self.krige.append(Krige(**self._kriging_kwargs)) - - ml_pred = self.classification_model.predict_proba(p) - ml_pred_ilr = ilr_transformation(ml_pred) - - self.onehotencode = OneHotEncoder( - categories=[self.classes_] - ) - y_ohe = np.array(self.onehotencode.fit_transform(y).todense()) - y_ohe_ilr = ilr_transformation(y_ohe) - - for i in range(len(self.classes_) - 1): - self.krige[i].fit(x=x, y=y_ohe_ilr[:, i] - ml_pred_ilr[:, i]) - - print("Finished kriging residuals") - - def predict(self, p, x, **kwargs): - """ - Predict. - - Parameters - ---------- - p: ndarray - (Ns, d) array of predictor variables (Ns samples, d dimensions) - for classification - x: ndarray - ndarray of (x, y) points. Needs to be a (Ns, 2) array - corresponding to the lon/lat, for example. - array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging - - Returns - ------- - pred: ndarray - The expected value of ys for the query inputs, of shape (Ns,). - - """ - - ml_pred = self.classification_model.predict_proba(p) - ml_pred_ilr = ilr_transformation(ml_pred) - - pred_proba_ilr = self.krige_residual(x, **kwargs) + ml_pred_ilr - pred_proba = inverse_ilr_transformation(pred_proba_ilr) - - return np.argmax(pred_proba, axis=1) - - def krige_residual(self, x, **kwargs): - """ - Calculate the residuals. - - Parameters - ---------- - x: ndarray - ndarray of (x, y) points. Needs to be a (Ns, 2) array - corresponding to the lon/lat, for example. - - Returns - ------- - residual: ndarray - kriged residual values - """ - - krig_pred = [self.krige[i].predict( - x=x, **kwargs) for i in range(len(self.classes_) - 1)] - - return np.vstack(krig_pred).T - - def score(self, p, x, y, sample_weight=None, **kwargs): - """ - Overloading default classification score method. - - Parameters - ---------- - p: ndarray - (Ns, d) array of predictor variables (Ns samples, d dimensions) - for classification - x: ndarray - ndarray of (x, y) points. Needs to be a (Ns, 2) array - corresponding to the lon/lat, for example. - array of Points, (x, y, z) pairs of shape (N, 3) for 3d kriging - y: ndarray - array of targets (Ns, ) - """ - return accuracy_score( - y_pred=self.predict(p, x, **kwargs), y_true=y, sample_weight=sample_weight - ) - - -def closure(data, k=1.0): - """Apply closure to data, sample-wise. - Adapted from https://github.com/ofgulban/compoda. - - Parameters - ---------- - data : 2d numpy array, shape [n_samples, n_measurements] - Data to be closed to a certain constant. Do not forget to deal with - zeros in the data before this operation. - k : float, positive - Sum of the measurements will be equal to this number. - - Returns - ------- - data : 2d numpy array, shape [n_samples, n_measurements] - Closed data. - - Reference - --------- - [1] Pawlowsky-Glahn, V., Egozcue, J. J., & Tolosana-Delgado, R. - (2015). Modelling and Analysis of Compositional Data, pg. 9. - Chichester, UK: John Wiley & Sons, Ltd. - DOI: 10.1002/9781119003144 - """ - data_sum = np.sum(data, axis=1) - out = np.copy(data) - for i in range(data.shape[1]): - out[:, i] = np.divide(out[:, i], data_sum) - out = out * k - return out - - -def ilr_transformation(data): - """Isometric logratio transformation (not vectorized). - Adapted from https://github.com/ofgulban/compoda. - - Parameters - ---------- - data : 2d numpy array, shape [n_samples, n_coordinates] - Barycentric coordinates (closed) in simplex space. - - Returns - ------- - out : 2d numpy array, shape [n_samples, n_coordinates-1] - Coordinates in real space. - - Reference - --------- - [1] Pawlowsky-Glahn, V., Egozcue, J. J., & Tolosana-Delgado, R. - (2015). Modelling and Analysis of Compositional Data, pg. 37. - Chichester, UK: John Wiley & Sons, Ltd. - DOI: 10.1002/9781119003144 - """ - data = data + np.finfo(float).eps - dims = data.shape - out = np.zeros((dims[0], dims[1]-1)) - helmertian = helmert(dims[1]).T - for i in range(data.shape[0]): - out[i, :] = np.dot(np.log(data[i, :]), helmertian) - return out - - -def inverse_ilr_transformation(data): - """Inverse isometric logratio transformation (not vectorized). - Adapted from https://github.com/ofgulban/compoda. - - Parameters - ---------- - data : 2d numpy array, shape [n_samples, n_coordinates] - Isometric log-ratio transformed coordinates in real space. - - Returns - ------- - out : 2d numpy array, shape [n_samples, n_coordinates+1] - Barycentric coordinates (closed) in simplex space. - - Reference - --------- - [1] Pawlowsky-Glahn, V., Egozcue, J. J., & Tolosana-Delgado, R. - (2015). Modelling and Analysis of Compositional Data, pg. 37. - Chichester, UK: John Wiley & Sons, Ltd. - DOI: 10.1002/9781119003144 - """ - data = data + np.finfo(float).eps - dims = data.shape - out = np.zeros((dims[0], dims[1]+1)) - helmertian = helmert(dims[1]+1) - for i in range(data.shape[0]): - out[i, :] = np.exp(np.dot(data[i, :], helmertian)) - return closure(out) diff --git a/pykrige/uk.py b/pykrige/uk.py index 707cf34..6b6863d 100644 --- a/pykrige/uk.py +++ b/pykrige/uk.py @@ -969,7 +969,8 @@ def _exec_vector(self, a, bd, xy, xy_orig, mask, n_withdrifts, spec_drift_grids) i += 1 if i != n_withdrifts: warnings.warn( - "Error in setting up kriging system. Kriging may fail.", RuntimeWarning, + "Error in setting up kriging system. Kriging may fail.", + RuntimeWarning, ) if self.UNBIAS: b[:, n_withdrifts, 0] = 1.0 diff --git a/pykrige/uk3d.py b/pykrige/uk3d.py index 3fe8813..115893c 100644 --- a/pykrige/uk3d.py +++ b/pykrige/uk3d.py @@ -778,7 +778,8 @@ def _exec_vector(self, a, bd, xyz, mask, n_withdrifts, spec_drift_grids): i += 1 if i != n_withdrifts: warnings.warn( - "Error in setting up kriging system. Kriging may fail.", RuntimeWarning, + "Error in setting up kriging system. Kriging may fail.", + RuntimeWarning, ) if self.UNBIAS: b[:, n_withdrifts, 0] = 1.0 diff --git a/tests/test_api.py b/tests/test_api.py index 98d2de0..6c033ca 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -3,8 +3,8 @@ import numpy as np import pytest -from pykrige.rk import Krige -from pykrige.rk import threed_krige +from pykrige.compat import Krige +from pykrige.compat import threed_krige def _method_and_vergiogram(): diff --git a/tests/test_classification_krige.py b/tests/test_classification_krige.py index 240d3e7..429a06e 100644 --- a/tests/test_classification_krige.py +++ b/tests/test_classification_krige.py @@ -3,7 +3,7 @@ import numpy as np -from pykrige.rk import ClassificationKriging +from pykrige.ck import ClassificationKriging try: from sklearn.svm import SVC @@ -21,7 +21,7 @@ def _methods(): krige_methods = ["ordinary", "universal"] ml_methods = [ SVC(C=0.01, gamma="auto", probability=True), - RandomForestClassifier(n_estimators=50) + RandomForestClassifier(n_estimators=50), ] return product(ml_methods, krige_methods) @@ -47,7 +47,7 @@ def test_classification_krige(): lat = np.linspace(-90.0, 90.0, 10) lon_lat = np.array(list(product(lon, lat))) - discretizer = KBinsDiscretizer(encode='ordinal') + discretizer = KBinsDiscretizer(encode="ordinal") y = discretizer.fit_transform(y.reshape(-1, 1)) X_train, X_test, y_train, y_test, lon_lat_train, lon_lat_test = train_test_split( @@ -55,11 +55,11 @@ def test_classification_krige(): ) for ml_model, krige_method in _methods(): - reg_class_model = ClassificationKriging( + class_model = ClassificationKriging( classification_model=ml_model, method=krige_method, n_closest_points=2 ) - reg_class_model.fit(X_train, lon_lat_train, y_train) - assert reg_class_model.score(X_test, lon_lat_test, y_test) > 0.25 + class_model.fit(X_train, lon_lat_train, y_train) + assert class_model.score(X_test, lon_lat_test, y_test) > 0.25 @pytest.mark.skipif(not SKLEARN_INSTALLED, reason="requires scikit-learn") @@ -82,7 +82,7 @@ def test_krige_classification_housing(): p = housing["data"][:1000, :-2] x = housing["data"][:1000, -2:] target = housing["target"][:1000] - discretizer = KBinsDiscretizer(encode='ordinal') + discretizer = KBinsDiscretizer(encode="ordinal") target = discretizer.fit_transform(target.reshape(-1, 1)) p_train, p_test, y_train, y_test, x_train, x_test = train_test_split( @@ -91,11 +91,11 @@ def test_krige_classification_housing(): for ml_model, krige_method in _methods(): - reg_class_model = ClassificationKriging( + class_model = ClassificationKriging( classification_model=ml_model, method=krige_method, n_closest_points=2 ) - reg_class_model.fit(p_train, x_train, y_train) + class_model.fit(p_train, x_train, y_train) if krige_method == "ordinary": - assert reg_class_model.score(p_test, x_test, y_test) > 0.5 + assert class_model.score(p_test, x_test, y_test) > 0.5 else: - assert reg_class_model.score(p_test, x_test, y_test) > 0.0 + assert class_model.score(p_test, x_test, y_test) > 0.0 diff --git a/tests/test_core.py b/tests/test_core.py index cba4e00..6b07f7d 100755 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -439,7 +439,12 @@ def test_core_krige_3d(): def test_non_exact(): # custom data for this test data = np.array( - [[0.0, 0.0, 0.47], [1.5, 1.5, 0.56], [3, 3, 0.74], [4.5, 4.5, 1.47],] + [ + [0.0, 0.0, 0.47], + [1.5, 1.5, 0.56], + [3, 3, 0.74], + [4.5, 4.5, 1.47], + ] ) # construct grid points so diagonal From ec671d01f0e1c147b2b86f85fb2a7ec36129f3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Thu, 3 Dec 2020 18:51:35 +0100 Subject: [PATCH 03/17] CI: move to github actions (#175) * CI: move to github actions * add py39 support * apply updated black * skip gstools testing for now (no py39 support by now) --- .github/workflows/main.yml | 141 +++++++++++++++++++++++++++++++++++++ .travis.yml | 111 ----------------------------- pykrige/uk.py | 3 +- pykrige/uk3d.py | 3 +- setup.py | 1 + tests/test_core.py | 7 +- 6 files changed, 152 insertions(+), 114 deletions(-) create mode 100644 .github/workflows/main.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..f1eecca --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,141 @@ +name: Continuous Integration + +on: + push: + branches: + - "master" + - "develop" + tags: + - "*" + pull_request: + branches: + - "develop" + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +env: + # needed by coveralls + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CIBW_BUILD: "cp35-* cp36-* cp37-* cp38-* cp39-*" + CIBW_BEFORE_BUILD: 'pip install "numpy<=1.19.4" "scipy<=1.5.4" "cython>=0.29.14" setuptools' + CIBW_TEST_REQUIRES: "pytest scikit-learn" + CIBW_TEST_COMMAND: "pytest -v {project}/tests" + +jobs: + format_check: + name: format check + runs-on: ubuntu-latest + strategy: + fail-fast: false + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python 3.8 + uses: actions\setup-python@v2 + with: + python-version: 3.8 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install black + + - name: black check + run: | + python -m black --check pykrige/ examples/ tests/ + + build_sdist: + name: sdist and coveralls + runs-on: ubuntu-latest + strategy: + fail-fast: false + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: '0' + + - name: Set up Python 3.8 + uses: actions\setup-python@v2 + with: + python-version: 3.8 + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements_setup.txt + pip install -r requirements.txt + pip install -r requirements_test.txt + pip install coveralls>=2.0.0 + + - name: Build sdist + run: | + python setup.py sdist -d dist + python setup.py build_ext --inplace + + - name: Run tests + run: | + python -m pytest --cov pykrige --cov-report term-missing -v tests/ + python -m coveralls + + - uses: actions/upload-artifact@v2 + with: + path: dist/*.tar.gz + + build_wheels: + name: wheels on ${{matrix.os}} + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: '0' + + - name: Set up Python + uses: actions\setup-python@v2 + with: + python-version: "3.8" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install cibuildwheel==1.7.0 + - name: Build wheels + run: | + python -m cibuildwheel --output-dir dist + + - uses: actions/upload-artifact@v2 + with: + path: ./dist/*.whl + + upload_to_pypi: + needs: [build_wheels, build_sdist] + runs-on: ubuntu-latest + + steps: + - uses: actions/download-artifact@v2 + with: + name: artifact + path: dist + + - name: Publish to Test PyPI + if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' + uses: pypa/gh-action-pypi-publish@master + with: + user: __token__ + password: ${{ secrets.test_pypi_password }} + repository_url: https://test.pypi.org/legacy/ + skip_existing: true + + - name: Publish to PyPI + # only if tagged + if: startsWith(github.ref, 'refs/tags') + uses: pypa/gh-action-pypi-publish@master + with: + user: __token__ + password: ${{ secrets.pypi_password }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4e41a65..0000000 --- a/.travis.yml +++ /dev/null @@ -1,111 +0,0 @@ -# do not build pull request from base repo twice (only build PRs of forks) -if: type != pull_request OR fork = true - -language: python -python: 3.8 - -# setuptools-scm needs all tags in order to obtain a proper version -git: - depth: false - -env: - global: - - TWINE_USERNAME=geostatframework - - CIBW_BEFORE_BUILD="pip install numpy==1.17.3 scipy==1.3.2 cython==0.29.14 setuptools" - - CIBW_TEST_REQUIRES="pytest scikit-learn gstools" - - CIBW_TEST_COMMAND="pytest -v {project}/tests" - - OMP_NUM_THREADS=2 - -before_install: - - | - if [[ "$TRAVIS_OS_NAME" = windows ]]; then - choco install python --version 3.8.0 - export PATH="/c/Python38:/c/Python38/Scripts:$PATH" - # make sure it's on PATH as 'python3' - ln -s /c/Python38/python.exe /c/Python38/python3.exe - fi - -install: python3 -m pip install cibuildwheel==1.3.0 - -script: python3 -m cibuildwheel --output-dir dist - -after_success: - - | - if [[ $TRAVIS_PULL_REQUEST == 'false' ]]; then - python3 -m pip install twine - python3 -m twine upload --verbose --skip-existing --repository-url https://test.pypi.org/legacy/ dist/* - if [[ $TRAVIS_TAG ]]; then python3 -m twine upload --verbose --skip-existing dist/*; fi - fi - -notifications: - email: - recipients: - - info@geostat-framework.org - -jobs: - include: - - name: "black check" - services: docker - install: python3 -m pip install black - script: python3 -m black --check pykrige/ examples/ tests/ - after_success: echo "all files formatted correctly" - after_failure: echo "some files not formatted correctly" - - - name: "sdist and coverage" - services: docker - install: - - python3 -m pip install -r requirements_setup.txt - - python3 -m pip install -r requirements_test.txt - - python3 -m pip install -r requirements.txt - script: - - python3 setup.py sdist -d dist - - python3 setup.py build_ext --inplace - - python3 -m pytest --cov pykrige --cov-report term-missing -v tests/ - - python3 -m coveralls - - - name: "Linux py35" - services: docker - env: CIBW_BUILD="cp35-*" - - name: "Linux py36" - services: docker - env: CIBW_BUILD="cp36-*" - - name: "Linux py37" - services: docker - env: CIBW_BUILD="cp37-*" - - name: "Linux py38" - services: docker - env: CIBW_BUILD="cp38-*" - - - name: "MacOS py35" - os: osx - language: shell - env: CIBW_BUILD="cp35-*" - - name: "MacOS py36" - os: osx - language: shell - env: CIBW_BUILD="cp36-*" - - name: "MacOS py37" - os: osx - language: shell - env: CIBW_BUILD="cp37-*" - - name: "MacOS py38" - os: osx - language: shell - env: CIBW_BUILD="cp38-*" - - - name: "Win py35" - os: windows - language: shell - env: CIBW_BUILD="cp35-*" - - name: "Win py36" - os: windows - language: shell - env: CIBW_BUILD="cp36-*" - - name: "Win py37" - os: windows - language: shell - env: CIBW_BUILD="cp37-*" - - name: "Win py38" - os: windows - language: shell - env: CIBW_BUILD="cp38-*" diff --git a/pykrige/uk.py b/pykrige/uk.py index 707cf34..6b6863d 100644 --- a/pykrige/uk.py +++ b/pykrige/uk.py @@ -969,7 +969,8 @@ def _exec_vector(self, a, bd, xy, xy_orig, mask, n_withdrifts, spec_drift_grids) i += 1 if i != n_withdrifts: warnings.warn( - "Error in setting up kriging system. Kriging may fail.", RuntimeWarning, + "Error in setting up kriging system. Kriging may fail.", + RuntimeWarning, ) if self.UNBIAS: b[:, n_withdrifts, 0] = 1.0 diff --git a/pykrige/uk3d.py b/pykrige/uk3d.py index 3fe8813..115893c 100644 --- a/pykrige/uk3d.py +++ b/pykrige/uk3d.py @@ -778,7 +778,8 @@ def _exec_vector(self, a, bd, xyz, mask, n_withdrifts, spec_drift_grids): i += 1 if i != n_withdrifts: warnings.warn( - "Error in setting up kriging system. Kriging may fail.", RuntimeWarning, + "Error in setting up kriging system. Kriging may fail.", + RuntimeWarning, ) if self.UNBIAS: b[:, n_withdrifts, 0] = 1.0 diff --git a/setup.py b/setup.py index 0962fdf..5cd6fb2 100755 --- a/setup.py +++ b/setup.py @@ -68,6 +68,7 @@ "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3 :: Only", "Topic :: Scientific/Engineering", "Topic :: Scientific/Engineering :: GIS", diff --git a/tests/test_core.py b/tests/test_core.py index cba4e00..6b07f7d 100755 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -439,7 +439,12 @@ def test_core_krige_3d(): def test_non_exact(): # custom data for this test data = np.array( - [[0.0, 0.0, 0.47], [1.5, 1.5, 0.56], [3, 3, 0.74], [4.5, 4.5, 1.47],] + [ + [0.0, 0.0, 0.47], + [1.5, 1.5, 0.56], + [3, 3, 0.74], + [4.5, 4.5, 1.47], + ] ) # construct grid points so diagonal From cbc52b3fac355dd691194ab198408d7a78e3d3a4 Mon Sep 17 00:00:00 2001 From: Rhilip Date: Wed, 31 Mar 2021 10:37:31 +0800 Subject: [PATCH 04/17] Fix sample error in 02_kriging3D.py the ax4 seems use the wrong data field, change it from `k3d3` to `k3d4` --- examples/02_kriging3D.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/02_kriging3D.py b/examples/02_kriging3D.py index 7cf14ea..09438db 100755 --- a/examples/02_kriging3D.py +++ b/examples/02_kriging3D.py @@ -98,7 +98,7 @@ ax2.set_title("regional lin. drift") ax3.imshow(k3d3[:, :, 0], origin="lower") ax3.set_title("specified drift") -ax4.imshow(k3d3[:, :, 0], origin="lower") +ax4.imshow(k3d4[:, :, 0], origin="lower") ax4.set_title("functional drift") plt.tight_layout() plt.show() From a0c212c3035c554287aabe44289499ea586ddc00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Sat, 3 Apr 2021 15:22:05 +0200 Subject: [PATCH 05/17] CI: coveralls 3.0 fix --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f1eecca..be9a7b2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -67,7 +67,7 @@ jobs: pip install -r requirements_setup.txt pip install -r requirements.txt pip install -r requirements_test.txt - pip install coveralls>=2.0.0 + pip install coveralls>=3.0.0 - name: Build sdist run: | @@ -77,7 +77,7 @@ jobs: - name: Run tests run: | python -m pytest --cov pykrige --cov-report term-missing -v tests/ - python -m coveralls + python -m coveralls --service=github - uses: actions/upload-artifact@v2 with: From f7a0d721ec7134552b158591f2b00e225512fec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Sat, 3 Apr 2021 15:22:24 +0200 Subject: [PATCH 06/17] Benchmarks: blackened --- benchmarks/kriging_benchmarks.py | 93 ++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 39 deletions(-) diff --git a/benchmarks/kriging_benchmarks.py b/benchmarks/kriging_benchmarks.py index 9f03688..927c333 100644 --- a/benchmarks/kriging_benchmarks.py +++ b/benchmarks/kriging_benchmarks.py @@ -3,11 +3,11 @@ from time import time import numpy as np from pykrige.ok import OrdinaryKriging + np.random.seed(19999) -VARIOGRAM_MODELS = ['power', 'gaussian', 'spherical', - 'exponential', 'linear'] -BACKENDS = ['vectorized', 'loop', 'C'] +VARIOGRAM_MODELS = ["power", "gaussian", "spherical", "exponential", "linear"] +BACKENDS = ["vectorized", "loop", "C"] N_MOVING_WINDOW = [None, 10, 50, 100] @@ -36,23 +36,32 @@ def make_benchark(n_train, n_test, n_dim=2): for variogram_model in VARIOGRAM_MODELS: tic = time() - OK = OrdinaryKriging(X_train[:, 0], X_train[:, 1], y_train, - variogram_model='linear', - verbose=False, enable_plotting=False) - res['t_train_{}'.format(variogram_model)] = time() - tic + OK = OrdinaryKriging( + X_train[:, 0], + X_train[:, 1], + y_train, + variogram_model="linear", + verbose=False, + enable_plotting=False, + ) + res["t_train_{}".format(variogram_model)] = time() - tic # All the following tests are performed with the linear variogram model for backend in BACKENDS: for n_closest_points in N_MOVING_WINDOW: - if backend == 'vectorized' and n_closest_points is not None: + if backend == "vectorized" and n_closest_points is not None: continue # this is not supported tic = time() - OK.execute('points', X_test[:, 0], X_test[:, 1], - backend=backend, - n_closest_points=n_closest_points) - res['t_test_{}_{}'.format(backend, n_closest_points)] = time() - tic + OK.execute( + "points", + X_test[:, 0], + X_test[:, 1], + backend=backend, + n_closest_points=n_closest_points, + ) + res["t_test_{}_{}".format(backend, n_closest_points)] = time() - tic return res @@ -71,34 +80,40 @@ def print_benchmark(n_train, n_test, n_dim, res): res : dict a dictionary with the timing results """ - print('='*80) - print(' '*10, 'N_dim={}, N_train={}, N_test={}'.format(n_dim, - n_train, n_test)) - print('='*80) - print('\n', '# Training the model', '\n') - print('|'.join(['{:>11} '.format(el) for el in ['t_train (s)'] + - VARIOGRAM_MODELS])) - print('-' * (11 + 2) * (len(VARIOGRAM_MODELS) + 1)) - print('|'.join(['{:>11} '.format('Training')] + - ['{:>11.2} '.format(el) for el in - [res['t_train_{}'.format(mod)] - for mod in VARIOGRAM_MODELS]])) - - print('\n', '# Predicting kriging points', '\n') - print('|'.join(['{:>11} '.format(el) for el in ['t_test (s)'] + BACKENDS])) - print('-' * (11 + 2) * (len(BACKENDS) + 1)) + print("=" * 80) + print(" " * 10, "N_dim={}, N_train={}, N_test={}".format(n_dim, n_train, n_test)) + print("=" * 80) + print("\n", "# Training the model", "\n") + print("|".join(["{:>11} ".format(el) for el in ["t_train (s)"] + VARIOGRAM_MODELS])) + print("-" * (11 + 2) * (len(VARIOGRAM_MODELS) + 1)) + print( + "|".join( + ["{:>11} ".format("Training")] + + [ + "{:>11.2} ".format(el) + for el in [res["t_train_{}".format(mod)] for mod in VARIOGRAM_MODELS] + ] + ) + ) + + print("\n", "# Predicting kriging points", "\n") + print("|".join(["{:>11} ".format(el) for el in ["t_test (s)"] + BACKENDS])) + print("-" * (11 + 2) * (len(BACKENDS) + 1)) for n_closest_points in N_MOVING_WINDOW: - timing_results = [res.get( - 't_test_{}_{}'.format(mod, n_closest_points), '') - for mod in BACKENDS] - print('|'.join(['{:>11} '.format('N_nn=' + str(n_closest_points))] + - ['{:>11.2} '.format(el) for el in timing_results])) - - -if __name__ == '__main__': - for no_train, no_test in [(400, 1000), - (400, 2000), - (800, 2000)]: + timing_results = [ + res.get("t_test_{}_{}".format(mod, n_closest_points), "") + for mod in BACKENDS + ] + print( + "|".join( + ["{:>11} ".format("N_nn=" + str(n_closest_points))] + + ["{:>11.2} ".format(el) for el in timing_results] + ) + ) + + +if __name__ == "__main__": + for no_train, no_test in [(400, 1000), (400, 2000), (800, 2000)]: results = make_benchark(no_train, no_test) print_benchmark(no_train, no_test, 2, results) From 549893eabc849236260dc53bb902343873205a8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Sat, 3 Apr 2021 15:22:47 +0200 Subject: [PATCH 07/17] OK: fix long long bug for windows --- pykrige/ok.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pykrige/ok.py b/pykrige/ok.py index 358520d..ec51288 100644 --- a/pykrige/ok.py +++ b/pykrige/ok.py @@ -958,7 +958,12 @@ def execute( zvalues, sigmasq = self._exec_loop_moving_window(a, bd, mask, bd_idx) elif backend == "C": zvalues, sigmasq = _c_exec_loop_moving_window( - a, bd, mask.astype("int8"), bd_idx, self.X_ADJUSTED.shape[0], c_pars + a, + bd, + mask.astype("int8"), + bd_idx.astype("int32"), + self.X_ADJUSTED.shape[0], + c_pars, ) else: raise ValueError( From d58de242d390f07bcebb37ec30a4f16b53ee8a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Sat, 3 Apr 2021 15:28:12 +0200 Subject: [PATCH 08/17] CI: use new cibw actions; restructure --- .github/workflows/main.yml | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index be9a7b2..9585a12 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,14 +13,6 @@ on: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: -env: - # needed by coveralls - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CIBW_BUILD: "cp35-* cp36-* cp37-* cp38-* cp39-*" - CIBW_BEFORE_BUILD: 'pip install "numpy<=1.19.4" "scipy<=1.5.4" "cython>=0.29.14" setuptools' - CIBW_TEST_REQUIRES: "pytest scikit-learn" - CIBW_TEST_COMMAND: "pytest -v {project}/tests" - jobs: format_check: name: format check @@ -75,6 +67,8 @@ jobs: python setup.py build_ext --inplace - name: Run tests + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | python -m pytest --cov pykrige --cov-report term-missing -v tests/ python -m coveralls --service=github @@ -96,18 +90,15 @@ jobs: with: fetch-depth: '0' - - name: Set up Python - uses: actions\setup-python@v2 - with: - python-version: "3.8" - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install cibuildwheel==1.7.0 - name: Build wheels - run: | - python -m cibuildwheel --output-dir dist + uses: joerick/cibuildwheel@v1.10.0 + env: + CIBW_BUILD: cp35-* cp36-* cp37-* cp38-* cp39-* + CIBW_BEFORE_BUILD: pip install numpy==1.19.* scipy<=1.5.4 cython==0.29.* setuptools + CIBW_TEST_REQUIRES: pytest scikit-learn + CIBW_TEST_COMMAND: pytest -v {project}/tests + with: + output-dir: dist - uses: actions/upload-artifact@v2 with: From 28f297d978155fc280758198afa145c9c5861ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Sat, 3 Apr 2021 15:35:03 +0200 Subject: [PATCH 09/17] CI: fix scipy version bug --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9585a12..8a2db8f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -94,7 +94,7 @@ jobs: uses: joerick/cibuildwheel@v1.10.0 env: CIBW_BUILD: cp35-* cp36-* cp37-* cp38-* cp39-* - CIBW_BEFORE_BUILD: pip install numpy==1.19.* scipy<=1.5.4 cython==0.29.* setuptools + CIBW_BEFORE_BUILD: pip install numpy==1.19.* scipy==1.5.* cython==0.29.* setuptools CIBW_TEST_REQUIRES: pytest scikit-learn CIBW_TEST_COMMAND: pytest -v {project}/tests with: From b3e51688850b77db9d4a59cd20225e0bae9c5dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Sat, 3 Apr 2021 15:35:22 +0200 Subject: [PATCH 10/17] OK: 2. try long fix --- pykrige/ok.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pykrige/ok.py b/pykrige/ok.py index ec51288..92d42a7 100644 --- a/pykrige/ok.py +++ b/pykrige/ok.py @@ -961,7 +961,7 @@ def execute( a, bd, mask.astype("int8"), - bd_idx.astype("int32"), + bd_idx.astype(int), self.X_ADJUSTED.shape[0], c_pars, ) From 4a3645f90472df3e347cdb44d8d6acb28e608add Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Sat, 3 Apr 2021 16:11:37 +0200 Subject: [PATCH 11/17] Drop Py35 support --- .github/workflows/main.yml | 2 +- setup.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8a2db8f..b2c88fe 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -93,7 +93,7 @@ jobs: - name: Build wheels uses: joerick/cibuildwheel@v1.10.0 env: - CIBW_BUILD: cp35-* cp36-* cp37-* cp38-* cp39-* + CIBW_BUILD: cp36-* cp37-* cp38-* cp39-* CIBW_BEFORE_BUILD: pip install numpy==1.19.* scipy==1.5.* cython==0.29.* setuptools CIBW_TEST_REQUIRES: pytest scikit-learn CIBW_TEST_COMMAND: pytest -v {project}/tests diff --git a/setup.py b/setup.py index 5cd6fb2..ad29c1e 100755 --- a/setup.py +++ b/setup.py @@ -64,7 +64,6 @@ "Operating System :: Unix", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", @@ -89,7 +88,7 @@ classifiers=CLASSIFIERS, platforms=["Windows", "Linux", "Solaris", "Mac OS-X", "Unix"], include_package_data=True, - python_requires=">=3.5", + python_requires=">=3.6", use_scm_version={ "relative_to": __file__, "write_to": "pykrige/_version.py", From 4d4174295cb2c5e195bdef7d5f627731b18dad31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Sat, 3 Apr 2021 16:35:46 +0200 Subject: [PATCH 12/17] CI: fix pypi upload action version --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b2c88fe..3397381 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -116,7 +116,7 @@ jobs: - name: Publish to Test PyPI if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' - uses: pypa/gh-action-pypi-publish@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.test_pypi_password }} @@ -126,7 +126,7 @@ jobs: - name: Publish to PyPI # only if tagged if: startsWith(github.ref, 'refs/tags') - uses: pypa/gh-action-pypi-publish@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.pypi_password }} From ea5a68e81bbd2d42cde7be3ac1a9326e2532f896 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Sat, 3 Apr 2021 21:43:43 +0200 Subject: [PATCH 13/17] Compat: fix Sklearn validation bug --- examples/07_regression_kriging2d.py | 2 +- examples/10_classification_kriging2d.py | 4 ++-- pykrige/ck.py | 5 +++-- pykrige/compat.py | 21 ++++++++++++++++----- pykrige/rk.py | 3 ++- tests/test_classification_krige.py | 2 +- tests/test_regression_krige.py | 2 +- 7 files changed, 26 insertions(+), 13 deletions(-) diff --git a/examples/07_regression_kriging2d.py b/examples/07_regression_kriging2d.py index 19b927d..3716412 100644 --- a/examples/07_regression_kriging2d.py +++ b/examples/07_regression_kriging2d.py @@ -11,9 +11,9 @@ from sklearn.ensemble import RandomForestRegressor from sklearn.linear_model import LinearRegression from sklearn.datasets import fetch_california_housing +from sklearn.model_selection import train_test_split from pykrige.rk import RegressionKriging -from pykrige.compat import train_test_split svr_model = SVR(C=0.1, gamma="auto") rf_model = RandomForestRegressor(n_estimators=100) diff --git a/examples/10_classification_kriging2d.py b/examples/10_classification_kriging2d.py index aba2956..c625754 100644 --- a/examples/10_classification_kriging2d.py +++ b/examples/10_classification_kriging2d.py @@ -1,6 +1,6 @@ """ Classification kriging ------------------- +---------------------- An example of classification kriging """ @@ -12,9 +12,9 @@ from sklearn.linear_model import LogisticRegression from sklearn.datasets import fetch_california_housing from sklearn.preprocessing import KBinsDiscretizer +from sklearn.model_selection import train_test_split from pykrige.ck import ClassificationKriging -from pykrige.compat import train_test_split svc_model = SVC(C=0.1, gamma="auto", probability=True) rf_model = RandomForestClassifier(n_estimators=100) diff --git a/pykrige/ck.py b/pykrige/ck.py index feaa771..ea9ea50 100644 --- a/pykrige/ck.py +++ b/pykrige/ck.py @@ -1,12 +1,13 @@ # coding: utf-8 +"""Classification Kriging.""" +import numpy as np from pykrige.compat import Krige, validate_sklearn, check_sklearn_model validate_sklearn() -from sklearn.metrics import r2_score, accuracy_score +from sklearn.metrics import accuracy_score from sklearn.svm import SVC from sklearn.preprocessing import OneHotEncoder -import numpy as np from scipy.linalg import helmert diff --git a/pykrige/compat.py b/pykrige/compat.py index d3847e4..d092606 100644 --- a/pykrige/compat.py +++ b/pykrige/compat.py @@ -1,8 +1,6 @@ # coding: utf-8 # pylint: disable= invalid-name, unused-import -"""For compatibility""" -from functools import partial - +"""For compatibility.""" from pykrige.uk3d import UniversalKriging3D from pykrige.ok3d import OrdinaryKriging3D from pykrige.uk import UniversalKriging @@ -10,7 +8,7 @@ # sklearn try: - from sklearn.model_selection import GridSearchCV + # keep train_test_split here for backward compatibility from sklearn.model_selection import train_test_split from sklearn.base import RegressorMixin, ClassifierMixin, BaseEstimator @@ -19,6 +17,18 @@ except ImportError: SKLEARN_INSTALLED = False + train_test_split = None + + class RegressorMixin: + """Mock RegressorMixin.""" + + class ClassifierMixin: + """Mock ClassifierMixin.""" + + class BaseEstimator: + """Mock BaseEstimator.""" + + krige_methods = { "ordinary": OrdinaryKriging, "universal": UniversalKriging, @@ -65,7 +75,7 @@ class SklearnException(Exception): - pass + """Exception for missing scikit-learn.""" def validate_method(method): @@ -77,6 +87,7 @@ def validate_method(method): def validate_sklearn(): + """Validate presence of scikit-learn.""" if not SKLEARN_INSTALLED: raise SklearnException( "sklearn needs to be installed in order to use this module" diff --git a/pykrige/rk.py b/pykrige/rk.py index 7e6821b..70fcc14 100644 --- a/pykrige/rk.py +++ b/pykrige/rk.py @@ -1,9 +1,10 @@ # coding: utf-8 +"""Regression Kriging.""" from pykrige.compat import Krige, validate_sklearn, check_sklearn_model validate_sklearn() -from sklearn.metrics import r2_score, accuracy_score +from sklearn.metrics import r2_score from sklearn.svm import SVR diff --git a/tests/test_classification_krige.py b/tests/test_classification_krige.py index 429a06e..3e5e93a 100644 --- a/tests/test_classification_krige.py +++ b/tests/test_classification_krige.py @@ -10,7 +10,7 @@ from sklearn.datasets import fetch_california_housing from sklearn.ensemble import RandomForestClassifier from sklearn.preprocessing import KBinsDiscretizer - from pykrige.compat import train_test_split + from sklearn.model_selection import train_test_split SKLEARN_INSTALLED = True except ImportError: diff --git a/tests/test_regression_krige.py b/tests/test_regression_krige.py index 1eeeb21..631af56 100644 --- a/tests/test_regression_krige.py +++ b/tests/test_regression_krige.py @@ -11,7 +11,7 @@ from sklearn.linear_model import ElasticNet, Lasso from sklearn.ensemble import RandomForestRegressor from sklearn.linear_model import LinearRegression - from pykrige.compat import train_test_split + from sklearn.model_selection import train_test_split SKLEARN_INSTALLED = True except ImportError: From 1bb7dca94c747deff2ef92bd1c58d37252ba6167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Sat, 3 Apr 2021 22:29:41 +0200 Subject: [PATCH 14/17] update .zenodo.json --- .zenodo.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.zenodo.json b/.zenodo.json index 4e384e2..9600bfc 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -28,6 +28,10 @@ { "type": "Other", "name": "Daniel Mej\u00eda Raigosa" + }, + { + "type": "Other", + "name": "Marcelo Albuquerque" } ], "language": "eng", @@ -37,6 +41,7 @@ "universal kriging", "external drift kriging", "regression kriging", + "classification kriging", "variogram", "geostatistics", "Python", From 2b9dc0511b92bda8ad3fc5feeb01d071a3ec5f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Sat, 3 Apr 2021 22:44:02 +0200 Subject: [PATCH 15/17] update changelog --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76f7ba1..6c36d69 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,23 @@ Changelog ========= +Version 1.6.0 +------------- +*April 04, 2021* + +**New features** + +* added Classification Kriging ([#165](https://github.com/GeoStat-Framework/PyKrige/pull/165), [#184](https://github.com/GeoStat-Framework/PyKrige/pull/184)) +* added wheels for Python 3.9 ([#175](https://github.com/GeoStat-Framework/PyKrige/pull/175)) + +**Changes** + +* moved scikit-learn compat-class `Krige` to `pykrige.compat` ([#165](https://github.com/GeoStat-Framework/PyKrige/pull/165)) +* dropped Python 3.5 support ([#183](https://github.com/GeoStat-Framework/PyKrige/pull/183)) +* moved CI to GitHub-Actions ([#175](https://github.com/GeoStat-Framework/PyKrige/pull/175), [#183](https://github.com/GeoStat-Framework/PyKrige/pull/183)) +* Fixed Typo in `02_kriging3D.py` example ([#182](https://github.com/GeoStat-Framework/PyKrige/pull/182)) + + Version 1.5.1 ------------- *August 20, 2020* From bcb80f8f49d27c9c6353e11f232d2f752af089b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Sat, 3 Apr 2021 22:57:58 +0200 Subject: [PATCH 16/17] Doc: correctly use .md files in doc --- README.md | 118 +++++++++++++++++++++++++++++++++++ README.rst | 128 -------------------------------------- docs/requirements_doc.txt | 2 +- docs/source/changelog.rst | 2 +- docs/source/conf.py | 2 +- docs/source/index.rst | 2 +- setup.py | 4 +- 7 files changed, 124 insertions(+), 134 deletions(-) create mode 100644 README.md delete mode 100644 README.rst diff --git a/README.md b/README.md new file mode 100644 index 0000000..766617a --- /dev/null +++ b/README.md @@ -0,0 +1,118 @@ +# PyKrige + +[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3738604.svg)](https://doi.org/10.5281/zenodo.3738604) +[![PyPI version](https://badge.fury.io/py/PyKrige.svg)](https://badge.fury.io/py/PyKrige) +[![Conda Version](https://img.shields.io/conda/vn/conda-forge/pykrige.svg)](https://anaconda.org/conda-forge/pykrige) +[![Build Status](https://github.com/GeoStat-Framework/PyKrige/workflows/Continuous%20Integration/badge.svg?branch=develop)](https://github.com/GeoStat-Framework/PyKrige/actions) +[![Coverage Status](https://coveralls.io/repos/github/GeoStat-Framework/PyKrige/badge.svg?branch=develop)](https://coveralls.io/github/GeoStat-Framework/PyKrige?branch=develop) +[![Documentation Status](https://readthedocs.org/projects/pykrige/badge/?version=stable)](http://pykrige.readthedocs.io/en/stable/?badge=stable) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) + + +

+PyKrige-LOGO +

+ +Kriging Toolkit for Python. + +## Purpose + +The code supports 2D and 3D ordinary and universal kriging. Standard +variogram models (linear, power, spherical, gaussian, exponential) are +built in, but custom variogram models can also be used. The 2D universal +kriging code currently supports regional-linear, point-logarithmic, and +external drift terms, while the 3D universal kriging code supports a +regional-linear drift term in all three spatial dimensions. Both +universal kriging classes also support generic 'specified' and +'functional' drift capabilities. With the 'specified' drift capability, +the user may manually specify the values of the drift(s) at each data +point and all grid points. With the 'functional' drift capability, the +user may provide callable function(s) of the spatial coordinates that +define the drift(s). The package includes a module that contains +functions that should be useful in working with ASCII grid files (`\*.asc`). + +See the documentation at for more +details and examples. + +## Installation + +PyKrige requires Python 3.5+ as well as numpy, scipy. It can be +installed from PyPi with, + +``` bash +pip install pykrige +``` + +scikit-learn is an optional dependency needed for parameter tuning and +regression kriging. matplotlib is an optional dependency needed for +plotting. + +If you use conda, PyKrige can be installed from the conda-forge channel with, + +``` bash +conda install -c conda-forge pykrige +``` + +## Features + +### Kriging algorithms + +- `OrdinaryKriging`: 2D ordinary kriging with estimated mean +- `UniversalKriging`: 2D universal kriging providing drift terms +- `OrdinaryKriging3D`: 3D ordinary kriging +- `UniversalKriging3D`: 3D universal kriging +- `RegressionKriging`: An implementation of Regression-Kriging +- `ClassificationKriging`: An implementation of Simplicial Indicator + Kriging + +### Wrappers + +- `rk.Krige`: A scikit-learn wrapper class for Ordinary and Universal + Kriging + +### Tools + +- `kriging_tools.write_asc_grid`: Writes gridded data to ASCII grid + file (`\*.asc`) +- `kriging_tools.read_asc_grid`: Reads ASCII grid file (`\*.asc`) + +### Kriging Parameters Tuning + +A scikit-learn compatible API for parameter tuning by cross-validation +is exposed in +[sklearn.model\_selection.GridSearchCV](http://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html). +See the [Krige +CV](http://pykrige.readthedocs.io/en/latest/examples/08_krige_cv.html#sphx-glr-examples-08-krige-cv-py) +example for a more practical illustration. + +### Regression Kriging + +[Regression kriging](https://en.wikipedia.org/wiki/Regression-Kriging) +can be performed with +[pykrige.rk.RegressionKriging](http://pykrige.readthedocs.io/en/latest/examples/07_regression_kriging2d.html). +This class takes as parameters a scikit-learn regression model, and +details of either either the `OrdinaryKriging` or the `UniversalKriging` +class, and performs a correction steps on the ML regression prediction. + +A demonstration of the regression kriging is provided in the +[corresponding +example](http://pykrige.readthedocs.io/en/latest/examples/07_regression_kriging2d.html#sphx-glr-examples-07-regression-kriging2d-py). + +### Classification Kriging + +[Simplifical Indicator +kriging](https://www.sciencedirect.com/science/article/abs/pii/S1002070508600254) +can be performed with +[pykrige.rk.ClassificationKriging](http://pykrige.readthedocs.io/en/latest/examples/10_classification_kriging2d.html). +This class takes as parameters a scikit-learn classification model, and +details of either the `OrdinaryKriging` or the `UniversalKriging` class, +and performs a correction steps on the ML classification prediction. + +A demonstration of the classification kriging is provided in the +[corresponding +example](http://pykrige.readthedocs.io/en/latest/examples/10_classification_kriging2d.html#sphx-glr-examples-10-classification-kriging2d-py). + +## License + +PyKrige uses the BSD 3-Clause License. diff --git a/README.rst b/README.rst deleted file mode 100644 index 6da0083..0000000 --- a/README.rst +++ /dev/null @@ -1,128 +0,0 @@ -PyKrige -======= - -.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.3738604.svg - :target: https://doi.org/10.5281/zenodo.3738604 -.. image:: https://badge.fury.io/py/PyKrige.svg - :target: https://badge.fury.io/py/PyKrige -.. image:: https://img.shields.io/conda/vn/conda-forge/pykrige.svg - :target: https://anaconda.org/conda-forge/pykrige -.. image:: https://travis-ci.com/GeoStat-Framework/PyKrige.svg?branch=master - :target: https://travis-ci.com/GeoStat-Framework/PyKrige -.. image:: https://coveralls.io/repos/github/GeoStat-Framework/PyKrige/badge.svg?branch=master - :target: https://coveralls.io/github/GeoStat-Framework/PyKrige?branch=master -.. image:: https://readthedocs.org/projects/pykrige/badge/?version=stable - :target: http://pykrige.readthedocs.io/en/stable/?badge=stable - :alt: Documentation Status -.. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://github.com/psf/black - - -.. figure:: https://github.com/GeoStat-Framework/GeoStat-Framework.github.io/raw/master/docs/source/pics/PyKrige_250.png - :align: center - :alt: PyKrige - :figclass: align-center - - -Kriging Toolkit for Python. - - -Purpose -^^^^^^^ - -The code supports 2D and 3D ordinary and universal kriging. Standard variogram models -(linear, power, spherical, gaussian, exponential) are built in, but custom variogram models can also be used. -The 2D universal kriging code currently supports regional-linear, point-logarithmic, and external drift terms, -while the 3D universal kriging code supports a regional-linear drift term in all three spatial dimensions. -Both universal kriging classes also support generic 'specified' and 'functional' drift capabilities. -With the 'specified' drift capability, the user may manually specify the values of the drift(s) at each data -point and all grid points. With the 'functional' drift capability, the user may provide callable function(s) -of the spatial coordinates that define the drift(s). The package includes a module that contains functions -that should be useful in working with ASCII grid files (`*.asc`). - -See the documentation at `http://pykrige.readthedocs.io/ `_ -for more details and examples. - - -Installation -^^^^^^^^^^^^ - -PyKrige requires Python 3.5+ as well as numpy, scipy. It can be installed from PyPi with, - -.. code:: bash - - pip install pykrige - -scikit-learn is an optional dependency needed for parameter tuning and regression kriging. -matplotlib is an optional dependency needed for plotting. - -If you use conda, PyKrige can be installed from the `conda-forge` channel with, - -.. code:: bash - - conda install -c conda-forge pykrige - - -Features -^^^^^^^^ - -Kriging algorithms ------------------- - -* ``OrdinaryKriging``: 2D ordinary kriging with estimated mean -* ``UniversalKriging``: 2D universal kriging providing drift terms -* ``OrdinaryKriging3D``: 3D ordinary kriging -* ``UniversalKriging3D``: 3D universal kriging -* ``RegressionKriging``: An implementation of Regression-Kriging -* ``ClassificationKriging``: An implementation of Simplicial Indicator Kriging - - -Wrappers --------- - -* ``rk.Krige``: A scikit-learn wrapper class for Ordinary and Universal Kriging - - -Tools ------ - -* ``kriging_tools.write_asc_grid``: Writes gridded data to ASCII grid file (\*.asc) -* ``kriging_tools.read_asc_grid``: Reads ASCII grid file (\*.asc) - - -Kriging Parameters Tuning -------------------------- - -A scikit-learn compatible API for parameter tuning by cross-validation is exposed in -`sklearn.model_selection.GridSearchCV `_. -See the `Krige CV `_ -example for a more practical illustration. - - -Regression Kriging ------------------- - -`Regression kriging `_ can be performed -with `pykrige.rk.RegressionKriging `_. -This class takes as parameters a scikit-learn regression model, and details of either either -the ``OrdinaryKriging`` or the ``UniversalKriging`` class, and performs a correction steps on the ML regression prediction. - -A demonstration of the regression kriging is provided in the -`corresponding example `_. - -Classification Kriging ----------------------- - -`Simplifical Indicator kriging `_ can be performed -with `pykrige.rk.ClassificationKriging `_. -This class takes as parameters a scikit-learn classification model, and details of either -the ``OrdinaryKriging`` or the ``UniversalKriging`` class, and performs a correction steps on the ML classification prediction. - -A demonstration of the classification kriging is provided in the -`corresponding example `_. - - -License -^^^^^^^ - -PyKrige uses the BSD 3-Clause License. diff --git a/docs/requirements_doc.txt b/docs/requirements_doc.txt index 34ee608..2c4ec45 100644 --- a/docs/requirements_doc.txt +++ b/docs/requirements_doc.txt @@ -2,7 +2,7 @@ scikit-learn>=0.19 gstools>=1.1.1 sphinx pillow -recommonmark sphinx_rtd_theme sphinxcontrib-napoleon sphinx-gallery +m2r2 \ No newline at end of file diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 4bb9e62..558bef5 100755 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -1,2 +1,2 @@ -.. include:: ../../CHANGELOG.md +.. mdinclude:: ../../CHANGELOG.md diff --git a/docs/source/conf.py b/docs/source/conf.py index b36d737..bcacc0c 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -48,7 +48,7 @@ "sphinxcontrib.napoleon", "sphinx_gallery.gen_gallery", "sphinx.ext.linkcode", - "recommonmark", + "m2r2", ] diff --git a/docs/source/index.rst b/docs/source/index.rst index 4d2c74b..8a2fb51 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,2 +1,2 @@ -.. include:: ../../README.rst +.. mdinclude:: ../../README.md diff --git a/setup.py b/setup.py index ad29c1e..e9a2364 100755 --- a/setup.py +++ b/setup.py @@ -35,7 +35,7 @@ # setup ####################################################################### -with open(os.path.join(HERE, "README.rst"), encoding="utf-8") as f: +with open(os.path.join(HERE, "README.md"), encoding="utf-8") as f: README = f.read() with open(os.path.join(HERE, "requirements.txt"), encoding="utf-8") as f: REQ = f.read().splitlines() @@ -78,7 +78,7 @@ name="PyKrige", description=DOCLINE, long_description=README, - long_description_content_type="text/x-rst", + long_description_content_type="text/markdown", author="Benjamin S. Murphy", author_email="bscott.murphy@gmail.com", maintainer="Sebastian Mueller, Roman Yurchak", From 666137d67584f53641b9298e11dd8f26e785447f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Sat, 3 Apr 2021 23:10:23 +0200 Subject: [PATCH 17/17] Doc: add latex logo --- docs/source/conf.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index bcacc0c..9ddd224 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -244,6 +244,8 @@ # -- Options for LaTeX output --------------------------------------------- +latex_logo = "pics/PyKrige_150.png" + latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper',