diff --git a/meteoinfo-lab/milconfig.xml b/meteoinfo-lab/milconfig.xml index 203e089b..61e3c364 100644 --- a/meteoinfo-lab/milconfig.xml +++ b/meteoinfo-lab/milconfig.xml @@ -1,6 +1,6 @@ - + @@ -12,21 +12,23 @@ - - + + + + @@ -34,5 +36,5 @@
- + diff --git a/meteoinfo-lab/pylib/mipylib/meteolib/calc/tools.py b/meteoinfo-lab/pylib/mipylib/meteolib/calc/tools.py index eae2014d..c874c960 100644 --- a/meteoinfo-lab/pylib/mipylib/meteolib/calc/tools.py +++ b/meteoinfo-lab/pylib/mipylib/meteolib/calc/tools.py @@ -1,5 +1,48 @@ import mipylib.numeric as np +def resample_nn_1d(a, centers): + """Return one-dimensional nearest-neighbor indexes based on user-specified centers. + Parameters + ---------- + a : array-like + 1-dimensional array of numeric values from which to extract indexes of + nearest-neighbors + centers : array-like + 1-dimensional array of numeric values representing a subset of values to approximate + Returns + ------- + A list of indexes (in type given by `array.argmin()`) representing values closest to + given array values. + """ + ix = [] + for center in centers: + index = (np.abs(a - center)).argmin() + if index not in ix: + ix.append(index) + return ix + +def nearest_intersection_idx(a, b): + """Determine the index of the point just before two lines with common x values. + Parameters + ---------- + a : array-like + 1-dimensional array of y-values for line 1 + b : array-like + 1-dimensional array of y-values for line 2 + Returns + ------- + An array of indexes representing the index of the values + just before the intersection(s) of the two lines. + """ + # Difference in the two y-value sets + difference = a - b + + # Determine the point just before the intersection of the lines + # Will return multiple points for multiple intersections + sign_change_idx, = np.nonzero(np.diff(np.sign(difference))) + + return sign_change_idx + def _remove_nans(*variables): """Remove NaNs from arrays that cause issues with calculations. Takes a variable number of arguments and returns masked arrays in the same diff --git a/meteoinfo-lab/pylib/mipylib/numeric/__init__$py.class b/meteoinfo-lab/pylib/mipylib/numeric/__init__$py.class index 05c75087..d41ec291 100644 Binary files a/meteoinfo-lab/pylib/mipylib/numeric/__init__$py.class and b/meteoinfo-lab/pylib/mipylib/numeric/__init__$py.class differ diff --git a/meteoinfo-lab/pylib/mipylib/numeric/__init__.py b/meteoinfo-lab/pylib/mipylib/numeric/__init__.py index 2268c9c4..40ab03d5 100644 --- a/meteoinfo-lab/pylib/mipylib/numeric/__init__.py +++ b/meteoinfo-lab/pylib/mipylib/numeric/__init__.py @@ -4,6 +4,7 @@ from . import lib from .lib import * from . import linalg from . import random +from . import ma from . import fitting from . import stats from . import interpolate @@ -17,6 +18,6 @@ __all__ = [] __all__.extend(['__version__']) __all__.extend(core.__all__) __all__.extend(lib.__all__) -__all__.extend(['linalg', 'fitting', 'random', 'stats', 'interpolate', 'optimize', 'signal', 'spatial', +__all__.extend(['linalg', 'fitting', 'random', 'ma', 'stats', 'interpolate', 'optimize', 'signal', 'spatial', 'special']) __all__.extend(['griddata']) \ No newline at end of file diff --git a/meteoinfo-lab/pylib/mipylib/numeric/ma/__init__.py b/meteoinfo-lab/pylib/mipylib/numeric/ma/__init__.py new file mode 100644 index 00000000..692f3c69 --- /dev/null +++ b/meteoinfo-lab/pylib/mipylib/numeric/ma/__init__.py @@ -0,0 +1 @@ +from .core import masked_array, MaskedArray \ No newline at end of file diff --git a/meteoinfo-lab/pylib/mipylib/numeric/ma/core.py b/meteoinfo-lab/pylib/mipylib/numeric/ma/core.py new file mode 100644 index 00000000..5fd0a281 --- /dev/null +++ b/meteoinfo-lab/pylib/mipylib/numeric/ma/core.py @@ -0,0 +1,165 @@ +from mipylib import numeric as np +from ..core._ndarray import NDArray +from org.meteoinfo.ndarray.math import ArrayUtil + +nomask = False + +class MaskedArray(NDArray): + """ + An array class with possibly masked values. + Masked values of True exclude the corresponding element from any + computation. + Construction:: + x = MaskedArray(data, mask=nomask, dtype=None, copy=False, subok=True, + ndmin=0, fill_value=None, keep_mask=True, hard_mask=None, + shrink=True, order=None) + Parameters + ---------- + data : array_like + Input data. + mask : sequence, optional + Mask. Must be convertible to an array of booleans with the same + shape as `data`. True indicates a masked (i.e. invalid) data. + dtype : dtype, optional + Data type of the output. + If `dtype` is None, the type of the data argument (``data.dtype``) + is used. If `dtype` is not None and different from ``data.dtype``, + a copy is performed. + copy : bool, optional + Whether to copy the input data (True), or to use a reference instead. + Default is False. + subok : bool, optional + Whether to return a subclass of `MaskedArray` if possible (True) or a + plain `MaskedArray`. Default is True. + ndmin : int, optional + Minimum number of dimensions. Default is 0. + fill_value : scalar, optional + Value used to fill in the masked values when necessary. + If None, a default based on the data-type is used. + """ + + def __init__(self, data, mask=nomask, dtype=None, copy=False, + subok=True, ndmin=0, fill_value=None): + if isinstance(data, NDArray): + data = data._array + super(MaskedArray, self).__init__(data) + self._data = NDArray(self._array) + self._baseclass = getattr(data, '_baseclass', type(self._data)) + if mask is nomask: + self._mask = mask + else: + self._mask = np.array(mask) + if self._mask.shape != self._data.shape: + self._mask = self._mask.reshape(self._data.shape) + if self._mask.dtype != np.dtype.bool: + self._mask = self._mask.astype(np.dtype.bool) + self._fill_value = fill_value + + def __str__(self): + r = 'masked_array(data=' + ArrayUtil.convertToString(self._data._array) + ',\n\tmask=' + if self._mask is nomask: + r = r + 'False,' + else: + r = r + ArrayUtil.convertToString(self._mask._array) + ',' + r = r + '\n\tfill_value=' + str(self._fill_value) + ')' + return r + + def __repr__(self): + return self.__str__() + + def filled(self, fill_value=None): + """ + Return a copy of self, with masked values filled with a given value. + **However**, if there are no masked values to fill, self will be + returned instead as an ndarray. + Parameters + ---------- + fill_value : array_like, optional + The value to use for invalid entries. Can be scalar or non-scalar. + If non-scalar, the resulting ndarray must be broadcastable over + input array. Default is None, in which case, the `fill_value` + attribute of the array is used instead. + Returns + ------- + filled_array : ndarray + A copy of ``self`` with invalid entries replaced by *fill_value* + (be it the function argument or the attribute of ``self``), or + ``self`` itself as an ndarray if there are no invalid entries to + be replaced. + Notes + ----- + The result is **not** a MaskedArray! + Examples + -------- + >>> x = np.ma.array([1,2,3,4,5], mask=[0,0,1,0,1], fill_value=-999) + >>> x.filled() + array([ 1, 2, -999, 4, -999]) + >>> x.filled(fill_value=1000) + array([ 1, 2, 1000, 4, 1000]) + >>> type(x.filled()) + + Subclassing is preserved. This means that if, e.g., the data part of + the masked array is a recarray, `filled` returns a recarray: + >>> x = np.array([(-1, 2), (-3, 4)], dtype='i8,i8').view(np.recarray) + >>> m = np.ma.array(x, mask=[(True, False), (False, True)]) + >>> m.filled() + rec.array([(999999, 2), ( -3, 999999)], + dtype=[('f0', '