# -*- coding: utf8 -*-
"""
Kriging utilities inspired from the AMBHAS library (http://www.ambhas.com/).
"""
# Copyright or © or Copr. Actimar/IFREMER (2013-2016)
#
# This software is a computer program whose purpose is to provide
# utilities for handling oceanographic and atmospheric data,
# with the ultimate goal of validating the MARS model from IFREMER.
#
# This software is governed by the CeCILL license under French law and
# abiding by the rules of distribution of free software. You can use,
# modify and/ or redistribute the software under the terms of the CeCILL
# license as circulated by CEA, CNRS and INRIA at the following URL
# "http://www.cecill.info".
#
# As a counterpart to the access to the source code and rights to copy,
# modify and redistribute granted by the license, users are provided only
# with a limited warranty and the software's author, the holder of the
# economic rights, and the successive licensors have only limited
# liability.
#
# In this respect, the user's attention is drawn to the risks associated
# with loading, using, modifying and/or developing or reproducing the
# software by the user in light of its specific status of free software,
# that may mean that it is complicated to manipulate, and that also
# therefore means that it is reserved for developers and experienced
# professionals having in-depth computer knowledge. Users are therefore
# encouraged to load and test the software's suitability as regards their
# requirements in conditions enabling the security of their systems and/or
# data to be ensured and, more generally, to use and operate it in the
# same conditions as regards security.
#
# The fact that you are presently reading this means that you have had
# knowledge of the CeCILL license and that you accept its terms.
#
import gc
import multiprocessing
from multiprocessing import Pool, cpu_count
import warnings
import numpy as N
import pylab as P
if not hasattr(N, 'isclose'):
from vacumm.misc import closeto as isclose
else:
isclose = N.isclose
#from scipy.optimize import curve_fit
[docs]def get_blas_func(name):
try:
import scipy.linalg.blas
func = scipy.linalg.blas.get_blas_funcs(name)
except:
import scipy.linalg.fblas
func = getattr(scipy.linalg.fblas, 'd'+name)
return func
blas_dgemv = get_blas_func('gemv')
[docs]def dgemv(a, x): return blas_dgemv(1., a, x)
try:
from _blaslapack import symm, sytri
except:
# print 'Falling back to builtin functions'
dgemm = get_blas_func('gemm')
[docs] def symm(a, b): return dgemm(1., a, b)
sytri = N.linalg.pinv
from ...misc.misc import kwfilter
from .misc import get_distances
[docs]class KrigingError(Exception):
pass
#: Variogram model types
VARIOGRAM_MODEL_TYPES = ['linear', 'exponential', 'spherical', 'gaussian']
VARIOGRAM_MODEL_TYPES = VARIOGRAM_MODEL_TYPES
#: Default variogram model type
DEFAULT_VARIOGRAM_MODEL_TYPE = 'exponential'
[docs]def variogram_model_type(mtype=None):
"""Check the the variogram model type
:Params:
- **mtype**, optional: ``None``, and index or a string matching
an element of :data:`VARIOGRAM_MODEL_TYPES`.
If set to ``None``, it defaults to :data:`DEFAULT_VARIOGRAM_MODEL_TYPE`.
"""
if mtype is True:
return VARIOGRAM_MODEL_TYPES
errmsg = []
for i, vtype in enumerate(VARIOGRAM_MODEL_TYPES):
errmsg += '"%s" (=%i)'%(vtype, i)
errmsg = 'Invalid variogram model type. Please choose one of: '+ ', '.join(errmsg)
if mtype is None:
mtype = DEFAULT_VARIOGRAM_MODEL_TYPE
if isinstance(mtype, int):
if i<0 or i>len(VARIOGRAM_MODEL_TYPES)-1:
raise KrigingError(errmsg)
return VARIOGRAM_MODEL_TYPES[i]
if not isinstance(mtype, basestring):
raise KrigingError(errmsg)
for vtype in VARIOGRAM_MODEL_TYPES:
if vtype.startswith(mtype): return vtype
raise KrigingError(errmsg)
[docs]def variogram_model(mtype, n, s, r, nrelmax=0.2):
"""Get the variogram model function from its name"""
mtype = variogram_model_type(mtype)
n = max(n, 0)
n = min(n, nrelmax*s)
r = max(r, 0)
s = max(s, 0)
if mtype == 'linear':
return lambda h: n + (s-n) * ((h/r)*(h<=r) + 1*(h>r))
if mtype=='exponential':
return lambda h: n + (s-n) * (1 - N.exp(-3*h/r))
if mtype=='spherical':
return lambda h: n + (s-n)*((1.5*h/r - 0.5*(h/r)**3)*(h<=r) + 1*(h>r))
if mtype=='gaussian':
return lambda h: n + (s-n)*(1-N.exp(-3*h**2/r**2))
raise KrigingError(errmsg)
[docs]class VariogramModel(object):
"""Class used when fitting a variogram model to data to better control params"""
param_names = list(variogram_model.func_code.co_varnames[1:])
param_names.remove('nrelmax')
def __init__(self, mtype, **kwargs):
self.mtype = variogram_model_type(mtype)
self.fixed_params = dict([(p, v) for (p, v) in kwargs.items()
if p in self.param_names and v is not None])
[docs] def get_all_kwargs(self, pp):
"""Get arguments list to :func:`variogram_model` by merging variable params `p`
and :attr:`fixed_params`
"""
pp = list(pp)
return dict([(p, (self.fixed_params[p] if p in self.fixed_params else pp.pop(0)))
for p in self.param_names])
[docs] def get_var_args(self, **kwargs):
"""Get variable arguments list from specified params
.. note:: Result cannot contain ``None``
"""
vargs = [kwargs[p] for p in self.param_names if p not in self.fixed_params]
if None in vargs:
raise VariogramModelError('Variable arguments cannot contains Nones')
return vargs
def __call__(self, d, *pp):
"""Call the variogram model function"""
return self.get_variogram_model(pp)(d)
[docs] def get_variogram_model(self, pp):
"""Get the variogram model function using `pp` variable arguments"""
kwargs = self.get_all_kwargs(pp)
return variogram_model(self.mtype, **kwargs)
[docs]class VariogramModelError(KrigingError):
pass
def _get_xyz_(x, y, z=None, check=True, noextra=True, getmask=False):
if not x.ndim==1 and x.shape!=y.shape :
raise KrigingError('x, y must have the same 1D shape')
if z is not None:
if (noextra or z.ndim==1) and x.shape!=z.shape:
raise KrigingError('z must have the same 1D shape as x and y')
elif not noextra and z.ndim!=1 and (z.ndim!=2 or x.shape!=z.shape[1:]):
raise KrigingError('z must have 2 dims with its last dim of same length as x and y')
mask = N.ma.nomask
if N.ma.isMA(z): mask = N.ma.getmaskarray(z)
if N.ma.isMA(x): mask |= N.ma.getmaskarray(x)
if N.ma.isMA(y): mask |= N.ma.getmaskarray(y)
if mask is not N.ma.nomask and mask.any():
if mask.shape==2: # permanent mask
mask = N.logical_and.reduce(mask, axis=0)
good = ~mask
if check and not good.any():
raise KrigingError('All data are masked')
try:
x = x.compress(good)
except:
pass
y = y.compress(good)
z = z.compress(N.resize(good, z.shape))
res = x, y, z
if getmask:
res += mask,
return res
[docs]def variogram(x, y, z, binned=None, nmax=1500, nbindef=30, nbin0=None,
nbmin=10, distmax=None, distfunc='simple', errfunc=None):
"""Estimate variogram from data
:Params:
- **x/y/z**: 1D arrays of positions and data.
- **nmax**, optional: Above this number, size of
the sampe is reduced using undersampling.
- **binned**, optional: If set to a number,
data are arranged in bins to estimate
variogram. If set to ``None``, data are
arranged in bins if the number of pairs
of points is greater than ``nbindef*nbmin``.
- **nbindef**, optional: Default number
of bins (not used if ``binned`` is a number).
- **nbin0**, optional: If set to a number > 1,
the first bin is split into nbin0 sub-bins.
If set to ``None``, it is evaluated with
``min(bins[1]/nbmin, nbin)``.
- **nbmin**, optional: Minimal number of points
in a bin.
- **distmax**, optional: Max distance to consider.
- **distfunc**: Function to compute distances, or a mode argument to
:func:`~vacumm.misc.grid.misc.get_distances`.
- **errfunc**, optional: Callable function to compute "errors" like square
root difference between to z values. It take two arguments and
defaults to :math:`(z1-z0)^2/2`.
"""
x, y, z = _get_xyz_(x, y, z)
npts = x.shape[0]
# Undepsample?
if npts>2*nmax:
samp = npts/nmax
x = x[::samp]
y = y[::samp]
z = z[::samp]
npts = x.shape[0]
# Distances
dd = get_distances(x, y, x, y, mode=distfunc)
# Variogram
if errfunc is None:
errfunc = lambda a0, a1: 0.5*(a1-a0)**2
vv = errfunc(*N.meshgrid(z, z))
# Unique
ii = N.indices(dd.shape)
iup = ii[1]>ii[0]
d = dd[iup]
v = vv[iup]
del dd, vv
# Max distance
if distmax:
valid = d<=distmax
d = d[valid]
v = v[valid]
del valid
# Direct variogram?
if binned is None and len(d)>nbindef*nbmin: binned = True
if binned is True: binned = nbindef
if not binned: return d, v
# Rebin
# - first try
ii = N.argsort(d)
np = d.shape[0]
nbin = binned
edges = N.linspace(0, np-1, nbin+1).astype('i').tolist()
# - more details in first bin
if nbin0 is None: # do we need more details?
NBIN0MAX = 10
nbin0 = min(edges[1]/nbmin, NBIN0MAX)
if nbin0>1: # split first bin
edges = N.linspace(0., edges[1], nbin0+1).astype('i')[:-1].tolist()+edges[1:]
nbin = nbin - 1 + nbin0 # len(edges)-1
# - rebinning
db = N.empty(nbin)
vb = N.empty(nbin)
for ib in xrange(nbin):
iib = ii[edges[ib]:edges[ib+1]+1]
db[ib] = d[iib].mean()
vb[ib] = v[iib].mean()
return db, vb
[docs]def variogram_fit(x, y, z, mtype=None, getall=False, getp=False, geterr=False,
distfunc='simple', errfunc=None, **kwargs):
"""Fit a variogram model to data and return the function
:Example:
>>> vm, errs = variogram_fit(x, y, z, 'linear', n=0, distmax=30e3, geterr=True)
:Params:
- **x/y/z**: Position and data.
- **mtype**: Variogram model type (see ::`variogram_model_type`).
- **getall**: Get verything in a dictionary whose keys are
- ``"func"``: model function,
- ``"err"``: fitting error,
- ``"params"``: all parameters has a dictionary,
- ``"popt"``: parameters than where optimised,
- ``vm"``: :class:`VariogramModel` instance,
- ``"mtype"``: variogram model type.
- **getp**, optional: Only return model parameters. Return them as
a `class:`dict` if equal to ``2``.
- **variogram_<param>**, optional: ``param`` is passed to :func:`variogram`.
- **distfunc**: Function to compute distances, or a mode argument to
:func:`~vacumm.misc.grid.misc.get_distances`.
- **errfunc**, optional: Callable function to compute "errors" like square
root difference between to z values. It take two arguments and
defaults to :math:`\sqrt(z1^2-z0^2)/2`.
.. warning:: use "haversine" if input coordinates are in degrees.
- Extra keywords are those of :func:`variogram_model`.
They can be used to freeze some of the parameters.
>>> variogram_fit(x, y, z, mtype, n=0) # fix the nugget
"""
kwv = kwfilter(kwargs, 'variogram_')
kwv.setdefault("distfunc", distfunc)
kwv.setdefault("errfunc", errfunc)
# Estimated variogram
d, v = variogram(x, y, z, **kwv)
# Variogram model
vm = VariogramModel(mtype, **kwargs)
# First guess of paramaters
imax = N.ma.argmax(v)
p0 = vm.get_var_args(n=0., s=v[imax], r=d[imax])
# Fitting
# p, e = curve_fit(vm, d, v, p0=p0) # old way: no constraint
from scipy.optimize import minimize
func = lambda pp: ((v-vm.get_variogram_model(pp)(d))**2).sum()
warnings.filterwarnings('ignore', 'divide by zero encountered in divide')
p = minimize(func, p0, bounds=[(N.finfo('d').eps, None)]*len(p0),
method='L-BFGS-B')['x']
del warnings.filters[0]
# Output
if getall:
return dict(
func=vm.get_variogram_model(p),
err=(vm.get_variogram_model(p)(d)-v).std(),
params = vm.get_all_kwargs(p),
popt = p)
if int(getp)==2:
res = vm.get_all_kwargs(p)
elif getp:
res = p
else:
res = vm.get_variogram_model(p)
if not geterr:
return res
return res, (vm.get_variogram_model(p)(d)-v).std()
[docs]def variogram_multifit(xx, yy, zz, mtype=None, getall=False, getp=False, **kwargs):
"""Same as :func:`variogram_fit` but with several samples"""
vm = VariogramModel(mtype, **kwargs)
pp = []
for i, (x, y, z) in enumerate(zip(xx, yy, zz)):
x, y, z = _get_xyz_(x, y, z, check=False)
if len(x)==0: continue
p = variogram_fit(x, y, z, mtype, getp=True, **kwargs)
pp.append(p)
pp = N.asarray(pp)
if pp.shape[0]==0:
raise KrigingError('All data are masked')
mp = N.median(pp, axis=0)
if getall:
return dict(
func=vm.get_variogram_model(mp),
err=None,
params = vm.get_all_kwargs(mp),
popt = mp)
if int(getp)==2:
return vm.get_all_kwargs(mp)
if getp:
return mp
return vm.get_variogram_model(mp)
[docs]def cloud_split(x, y, npmax=1000, getdist=True, getcent=True):
"""Split data intot cloud of points of max size npmax:
:Returns: ``None`` if ``len(x)<=npmax``
Else ``indices``
or ``(indices, global_distorsion, distortions)``.
"""
from scipy.cluster.vq import kmeans, vq
# Nothing to do
csize = len(x)
if npmax<2 or csize<=npmax: return
# Loop on the number of clusters
nclust = 2
points = N.vstack((x, y)).T
ii = N.arange(csize)
while csize > npmax:
centroids, global_distorsion = kmeans(points, nclust)
indices, distorsions = vq(points, centroids)
sindices = [ii[indices==nc] for nc in xrange(nclust)]
csizes = [sii.shape[0] for sii in sindices]
order = N.argsort(csizes)[::-1]
csize = csizes[order[0]]
if getdist: sdistorsions = [distorsions[sii] for sii in sindices]
nclust += 1
# Reorder
sindices = [sindices[i] for i in order]
if getdist:
sdistorsions = [sdistorsions[i] for i in order]
dists = global_distorsion, sdistorsions
if getcent:
centroids = centroids[order]
# Output
if not getdist and not getcent: return indices
ret = sindices,
if getcent: ret += centroids,
if getdist: ret += dists,
return ret
[docs]def syminv(A):
"""Invert a symetric matrix
:Params:
- **A**: (np+1,np+1) for variogram matrix
:Return: ``Ainv(np+1,np+1)``
:Raise: :exc:`KrigingError`
"""
res = sytri(A.astype('d'))
if isinstance(res, tuple):
info = res[1]
if info: raise KrigingError('Error during call to Lapack DSYTRI (info=%i)'%info)
return res[0]
else:
return res
[docs]class CloudKriger(object):
"""Ordinary kriger using mutliclouds of points
Big input cloud of points (size > ``npmax``)
are split into smaller
clouds using cluster analysis of distance with
function :func:`cloud_split`.
The problem is solved in this way:
#. Input points are split in clouds if necessary.
#. The input variogram matrix is inverted
for each cloud, possibly using
:mod:`multiprocessing` if ``nproc>1``.
#. Value are computed at output positions
using each the inverted matrix of cloud.
#. Final value is a weighted average of
the values estimated using each cloud.
Weights are inversely proportional to the inverse
of the squared error.
:Params:
- **x/y/z**: Input positions and data (masked array).
- **mtype**, optional: Variogram model type (defaults to 'exp').
See :func:`variogram_model_type` and :func:`variogram_model_type`.
- **vgf**, optional: Variogram function. If not set,
it is estimated using :meth:`variogram_fit`.
- **npmax**, optional: Maxima size of cloud.
- **nproc**, optional: Number of processes to use
to invert matrices. Set it to a number <2 to switch off
parallelisation.
- **exact**, optional: If True, variogram is exactly zero when distance is zero.
- **distfunc**: Function to compute distances, or a mode argument to
:func:`~vacumm.misc.grid.misc.get_distances`.
- **errfunc**, optional: Callable function to compute "errors" like square
root difference between to z values. It take two arguments and
defaults to :math:`\sqrt(z1^2-z0^2)/2`.
- Extra keywords are parameters to the :func:`variogram_model` that must not be
optimized by :func:`variogram_model`. For instance ``n=0`` fix the
- Extra keywords are the parameters to the :func:`variogram_model` that must not be
optimized by :func:`variogram_model`. For instance ``n=0`` fixes the
nugget to zero.
This is used only if ``vfg`` is not passed as an argument.
:Attributes: :attr:`x`, :attr:`y`, :attr:`z`, :attr:`np`,
:attr:`xc`, :attr:`yc`, :attr:`zc`, :attr:`npc`,
:attr:`variogram_function`, :attr:`Ainv`, :attr:`npmax`, :attr:`nproc`.
.. attribute:: x
List of all input x positions.
.. attribute:: y
List of all input y positions.
.. attribute:: z
List of all input data.
.. attribute:: xc
List of input x positions of each cloud.
.. attribute:: yc
List input of y positions of each cloud.
.. attribute:: zc
List of input data of each cloud.
"""
def __init__(self, x, y, z, krigtype, mtype=None, vgf=None, npmax=1000,
nproc=None, exact=False, distfunc='simple', errfunc=None,
mean=None, farvalue=None, **kwargs):
if krigtype is None:
krigtype = 'ordinary'
krigtype = str(krigtype).lower()
assert krigtype in ['simple', 'ordinary'], ('krigtype must be either '
'"simple" or "ordinary"')
self.x, self.y, self.z, self.mask = _get_xyz_(x, y, z, noextra=False, getmask=True)
self.np = self.x.shape[0]
self.nt = 0 if self.z.ndim==1 else z.shape[0]
self.mtype = variogram_model_type(mtype)
self.npmax = npmax
if nproc is None:
nproc = cpu_count()
else:
nproc = max(1, min(cpu_count(), nproc))
self._setup_clouds_()
self.nproc = min(nproc, self.ncloud)
if callable(vgf):
self.variogram_func = vgf
self._kwargs = kwargs
self.variogram_fitting_results = None
self.exact = exact
self.distfunc = distfunc
self.errfunc = errfunc
self.krigtype = krigtype
self._simple = self.krigtype == 'simple'
if not self._simple:
mean = 0.
elif mean is None:
mean = self.z.mean()
self.mean = mean
self.farvalue = farvalue
def __len__(self):
return self.x.shape[0]
def _get_xyz_(self, x=None, y=None, z=None):
if x is None: x = self.x
if y is None: y = self.y
if z is None: z = self.z
return _get_xyz_(x, y, z, noextra=False)
def _setup_clouds_(self):
"""Setup cloud spliting
Estimate:
#. The number of procs to use (sef.nproc).
#. Cloud positions and data (self.xc/yc/zc[ic]).
#. Weight function for each cloud (self.wc[ic](x,y)).
"""
# Split?
self.npc = []
if self.npmax>2 and self.x.shape[0]>self.npmax:
# Split in clouds
indices, centroids, (gdist, dists) = cloud_split(self.x, self.y,
npmax=self.npmax, getdist=True, getcent=True)
self.ncloud = len(indices)
# Loop on clouds
self.xc = []
self.yc = []
self.zc = []
for ic in xrange(self.ncloud):
# Positions and data
for xyz in 'x', 'y', 'z':
getattr(self, xyz+'c').append(getattr(self, xyz)[..., indices[ic]].T)
# Size
self.npc.append(len(indices[ic]))
else: # Single cloud
self.xc = [self.x]
self.yc = [self.y]
self.zc = [self.z.T]
self.ncloud = 1
self.npc = [self.np]
self.cwfunc = [lambda x, y: 1.]
[docs] def plot_clouds(self, marker='o', **kwargs):
"""Quickly Plot inputs points splitted in clouds"""
P.figure()
for x, y in zip(self.xc, self.yc):
P.plot(x, y, marker, **kwargs)
P.show()
[docs] def variogram_fit(self, x=None, y=None, z=None, **kwargs):
"""Estimate the variogram function by using :func:`variogram_fit`"""
kw = self._kwargs.copy()
kw.update(kwargs)
kw['distfunc'] = self.distfunc
kw['errfunc'] = self.errfunc
x, y, z = self._get_xyz_(x, y, z)
if z.ndim==2:
ne = z.shape[0]
res = variogram_multifit([x]*ne, [y]*ne, z, self.mtype, getall=True, **kw)
else:
res = variogram_fit(x, y, z, self.mtype, getall=True, **kw)
self.variogram_func = res['func']
self.variogram_fitting_results = res
return self.variogram_func
[docs] def get_sill(self):
vgf = self.variogram_func
if self.variogram_fitting_results is not None:
return self.variogram_fitting_results['params']['s']
return vgf(1e60)
sill = property(fget=get_sill, doc="Sill")
[docs] def set_variogram_func(self, vgf):
"""Set the variogram function"""
if not callable(vgf):
raise KrigingError("Your variogram function must be callable")
reset = getattr(self, '_vgf', None) is not vgf
self._vgf = vgf
if reset: del self.Ainv
[docs] def get_variogram_func(self):
"""Get the variogram function"""
if not hasattr(self, '_vgf'):
self.variogram_fit()
return self._vgf
[docs] def del_variogram_func(self):
"""Delete the variogram function"""
if hasattr(self, '_vgf'):
del self._vgf
variogram_func = property(get_variogram_func, set_variogram_func,
del_variogram_func, "Variogram function")
[docs] def get_Ainv(self):
"""Get the inverse of A"""
# Already computed
if hasattr(self, '_Ainv'):
return self._Ainv
# Variogram function
vgf = self.variogram_func
# Loop on clouds
if not hasattr(self, '_dd'): self._dd = []
Ainv = []
AA = []
next = int(not self._simple)
for ic in xrange(self.ncloud):
# Get distance between input points
if len(self._dd)<ic+1:
dd = get_distances(self.xc[ic], self.yc[ic],
self.xc[ic], self.yc[ic], mode=self.distfunc)
self._dd.append(dd)
else:
dd = self._dd[ic]
# Form A
np = self.npc[ic]
A = N.empty((np+next, np+next))
A[:np, :np] = vgf(dd)
if self.exact:
N.fill_diagonal(A, 0)
A[:np, :np][isclose(A[:np, :np], 0.)] = 0.
if not self._simple:
A[-1] = 1
A[:, -1] = 1
A[-1, -1] = 0
# Invert for single cloud
if self.nproc==1:
Ainv.append(syminv(A))
else:
AA.append(A)
# Multiprocessing inversion
if self.nproc>1:
pool = Pool(self.nproc)
Ainv = pool.map(syminv, AA, chunksize=1)
pool.close()
# Fortran arrays
Ainv = [N.asfortranarray(ainv, 'd') for ainv in Ainv]
self.Ainv = Ainv
return Ainv
[docs] def set_Ainv(self, Ainv):
"""Set the invert of A"""
self._Ainv = Ainv
[docs] def del_Ainv(self):
"""Delete the invert of A"""
if hasattr(self, '_Ainv'):
del self._Ainv
Ainv = property(get_Ainv, set_Ainv, del_Ainv, doc='Invert of A')
[docs] def interp(self, xo, yo, geterr=False, blockr=None):
"""Interpolate to positions xo,yo
:Params:
- **xo/yo**: Output positions.
- **geterr**, optional: Also return errors.
:Return: ``zo`` or ``zo,eo``
"""
# Inits
xo = N.asarray(xo, 'd')
yo = N.asarray(yo, 'd')
npo = xo.shape[0]
vgf = self.variogram_func
so = (self.nt, npo) if self.nt else npo
zo = N.zeros(so, 'd')
if geterr:
eo = N.zeros(npo, 'd')
if self.ncloud>1 or geterr:
wo = N.zeros(npo, 'd')
# Loop on clouds
Ainv = self.Ainv
next = int(not self._simple)
for ic in xrange(self.ncloud): # TODO: multiproc here?
# Distances to output points
# dd = cdist(N.transpose([xi,yi]),N.transpose([xo,yo])) # TODO: test cdist
dd = get_distances(xo, yo, self.xc[ic], self.yc[ic], mode=self.distfunc)
# Form B
np = self.npc[ic]
B = N.empty((np+next, npo))
B[:self.npc[ic]] = vgf(dd)
if not self._simple:
B[-1] = 1
if self.exact:
B[:np][isclose(B[:np], 0.)] = 0.
del dd
# Block kriging
if blockr:
tree = cKDTree(N.transpose([xo, yo]))
Bb = B.copy()
for i, iineigh in enumerate(tree.query_ball_tree(tree, blockr)):
Bb[:, i] = B[:, iineigh].mean()
B = Bb
# Compute weights
W = N.ascontiguousarray(symm(Ainv[ic], N.asfortranarray(B, 'd')))
# Simple kriging with adjusted mean for long distance values
if self._simple and self.farvalue is not None:
s = self.get_sill()
Ais = self.get_sill() * Ainv[ic].sum(axis=0)
mean = self.farvalue - (self.zc[ic] * Ais).sum()
mean /= (1 - Ais.sum())
else:
mean = self.mean
# Interpolate
z = N.ascontiguousarray(dgemv(N.asfortranarray(W[:np].T, 'd'),
N.asfortranarray(self.zc[ic]-mean, 'd')))
if self._simple:
z += mean
# Simplest case
if not geterr and self.ncloud<2:
zo[:] = z.T
continue
# Get error
# e = (W[:-1]*B[:-1]).sum(axis=0)
e = (W*B).sum(axis=0)
del W, B
# Weigthed contribution based on errors
w = 1/e**2
if self.ncloud>1:
z[:] *= w
wo += w
del w
zo[:] += z.T ; del z
# Error
if geterr:
eo = 1/N.sqrt(wo)
# Normalization
if self.ncloud>1:
zo[:] /= wo
gc.collect()
if geterr: return zo, eo
return zo
__call__ = interp
[docs]class OrdinaryCloudKriger(CloudKriger):
"""Ordinary kriger using cloud splitting"""
def __init__(self, x, y, z, mtype=None, vgf=None, npmax=1000,
nproc=None, exact=False, distfunc='simple', errfunc=None,
**kwargs):
CloudKriger.__init__(self, x, y, z, 'ordinary', mtype=mtype, vgf=vgf, npmax=npmax,
nproc=nproc, exact=False, distfunc=distfunc, errfunc=errfunc,
**kwargs)
OrdinaryKriger = OrdinaryCloudKriger
[docs]class SimpleCloudKriger(CloudKriger):
"""Simple kriger using cloud splitting"""
def __init__(self, x, y, z, mtype=None, vgf=None, npmax=1000,
nproc=None, exact=False, distfunc='simple', errfunc=None,
mean=None, farvalue=None, **kwargs):
CloudKriger.__init__(self, x, y, z, 'simple', mtype=mtype, vgf=vgf, npmax=npmax,
nproc=nproc, exact=False, distfunc=distfunc, errfunc=errfunc,
mean=mean, farvalue=farvalue, **kwargs)
[docs]def krig(xi, yi, zi, xo, yo, vgf=None, geterr=False, **kwargs):
"""Quickly krig data"""
return OrdinaryKriger(xi, yi, zi, vgf=vgf, **kwargs)(xo, yo, geterr=geterr)
[docs]def gauss3(x, y,
x0=-1, y0=0.5, dx0=1, dy0=1, f0=1.,
x1=1, y1=1, dx1=2, dy1=0.5, f1=-1,
x2=0, y2=-1.5, dx2=.5, dy2=.5, f2=-.3,
**kwargs):
"""Create data sample as function position and 3-gaussian function"""
g = P.bivariate_normal(x, y, dx0, dy0, x0, y0)*f0
g+= P.bivariate_normal(x, y, dx1, dy1, x1, y1)*f1
g+= P.bivariate_normal(x, y, dx2, dy2, x2, y2)*f2
g *= 10.
return g
[docs]def gridded_gauss3(nx=100, ny=100, xmin=-3, xmax=3, ymin=-3, ymax=3, mesh=False, **kwargs):
"""Create a data sample on a grid using :func:`gauss3`"""
x = N.linspace(xmin, xmax, nx)
y = N.linspace(ymin, ymax, ny)
xx, yy = N.meshgrid(x, y)
zz = gauss3(xx, yy, **kwargs)
if mesh:
return xx, yy, zz
return x, y, zz
[docs]def random_gauss3(**kwargs):
"""Create a data sample of random points using :func:`gauss3`"""
x, y = random_points(**kwargs)
z = gauss3(x, y, **kwargs)
return x, y, z
[docs]def random_points(np=200, xmin=-3, xmax=3, ymin=-3, ymax=3, **kwargs):
"""Generate random coordinates of points"""
x = P.rand(np)*(xmax-xmin)+xmin
y = P.rand(np)*(ymax-ymin)+ymin
return x, y