def _median_with_nan(values, *args, **kwargs):
    """ replace "median" if skipna is False
    
    numpy's median ignore NaNs as long as less than 50% 
    modify this behaviour and return NaN just as any other operation would
    """
    if _hasbottleneck:
        result = bottleneck.median(values, *args, **kwargs)
    else:
        result = np.median(values, *args, **kwargs)

    if anynan(values):
        if np.size(result) == 1: 
            result = np.nan
        else:
            axis = kwargs.pop('axis', None)
            nans = anynan(values, axis=axis) # determine where the nans should be
            result[nans] = np.nan

    return result
    def __call__(self, values, *args, **kwargs):

        # transform numpy array to masked array if needed
        if anynan(values):
            values = np.ma.array(values, mask=np.isnan(values))
            func = getattr(np.ma, self.__name__)

        else:
            func = getattr(np, self.__name__)

        result = func(values, *args, **kwargs) 

        # transform back to numpy array
        if np.ma.isMaskedArray(result):
            result = result.filled(np.nan)

        return result
Exemple #3
0
def _plot2D(self, funcname, *args, **kwargs):
    """ generic plotting function for 2-D plots
    """
    if len(self.dims) != 2:
        raise NotImplementedError(funcname+" can only be called on two-dimensional dimarrays.")

    import matplotlib.pyplot as plt
    
    #ax = plt.gca()
    if 'ax' in kwargs: 
        ax = kwargs.pop('ax')
    else:
        ax = plt.gca()

    colorbar = kwargs.pop('colorbar', False)

    # get the actual plotting function
    function = getattr(ax, funcname)
    # extract information about axis value and labels
    # e.g. transform non-numeric data to float and
    # set appropriate labels afterwards.
    xval, xticks, xticklab, xlab = _get_axis_labels(self.axes[1])
    yval, yticks, yticklab, ylab = _get_axis_labels(self.axes[0])
    values = self.values

    # pcolor does not work with nans
    if (funcname == 'pcolormesh' or funcname == 'pcolor') and anynan(values):
        values = np.ma.array(values, mask=np.isnan(values))

    # make the plot
    pc = function(xval, yval, values, **kwargs)
    # add labels
    if xlab is not None: ax.set_xlabel(xlab)
    if ylab is not None: ax.set_ylabel(ylab)
    if xticklab is not None: ax.set_xticklabels(xticklab)
    if xticks is not None: ax.set_xticks(xticks)
    if yticklab is not None: ax.set_yticklabels(yticklab)
    if yticks is not None: ax.set_yticks(yticks)

    # add colorbar?
    if colorbar:
        plt.colorbar(pc, ax=ax)
    return pc
Exemple #4
0
def assert_equal_dimarrays(actual, expected, metadata=True, approx=False):
    assert isinstance(expected, da.DimArray)
    assert isinstance(actual, da.DimArray)
    assert actual.shape == expected.shape
    assert actual.dims == expected.dims

    # check the values
    if approx or actual.dtype.kind == 'f' and anynan(actual.values): 
        assert_equal_values = assert_almost_equal
    else: 
        assert_equal_values = assert_equal
    assert_equal_values(actual.values, expected.values)

    # check the axes
    for actual_axis, expected_axis in zip(actual.axes, expected.axes):
        assert_equal_axes(actual_axis, expected_axis, metadata=metadata)

    # check the metadata
    if metadata:
        assert_equal_metadata(actual.attrs, expected.attrs)
def test_median(v, axis, use_bottleneck):

    if not anynan(v):
        assert_allclose(v.median(axis=axis), np.median(v.values, axis=axis))
    else:
        assert_allclose(v.median(axis=axis, skipna=True), np.ma.median(v.to_MaskedArray(), axis=axis))
def _get_weights(self, axis=None, mirror_nans=True, weights=None):
    """ Build array of weights based on individual axes' weights attribute

    Parameters
    ----------
    axis : int or str, optional
        if None a N-D weight is created, otherwise 1-D only
    mirror_nans : bool, optional
        mirror `values`'s NaNs in created weights (True by default)
    {weights}

    Returns
    -------
    full_weights : DimArray of weights, or None

    See Also
    --------
    DimArray.mean, DimArray.var, DimArray.std

    Examples
    --------

    Simpler examples are found Dimarray.mean's doc.
    These are mostly meant as doctest.

    >>> from dimarray import DimArray
    >>> np.random.seed(0) # to make results reproducible
    >>> v = DimArray(np.random.rand(3,2), axes=[[-80, 0, 80], [-180, 180]], dims=['lat','lon'])
    >>> v._get_weights() # None
    >>> w = lambda x : np.cos(np.radians(v.lat))
    >>> v.axes['lat'].weights = w
    >>> v._get_weights()
    dimarray: 6 non-null elements (0 null)
    0 / lat (3): -80 to 80
    1 / lon (2): -180 to 180
    array([[ 0.17364818,  0.17364818],
           [ 1.        ,  1.        ],
           [ 0.17364818,  0.17364818]])
    >>> v.axes['lat'].weights = None
    >>> v._get_weights() # None

    Can pass the weigthts as a dictionary 

    >>> v._get_weights(weights={{'lat':w}}) 
    dimarray: 6 non-null elements (0 null)
    0 / lat (3): -80 to 80
    1 / lon (2): -180 to 180
    array([[ 0.17364818,  0.17364818],
           [ 1.        ,  1.        ],
           [ 0.17364818,  0.17364818]])

    Or by indicating the axis
    
    >>> v._get_weights(weights=w, axis='lat') 
    dimarray: 6 non-null elements (0 null)
    0 / lat (3): -80 to 80
    1 / lon (2): -180 to 180
    array([[ 0.17364818,  0.17364818],
           [ 1.        ,  1.        ],
           [ 0.17364818,  0.17364818]])

    It possible to mix up axis and dict-input weights

    >>> v.axes['lat'].weights = w
    >>> v._get_weights(weights={{'lon':lambda x:((x+180)/100)**2}}) 
    dimarray: 6 non-null elements (0 null)
    0 / lat (3): -80 to 80
    1 / lon (2): -180 to 180
    array([[ 0.       ,  1.5628336],
           [ 0.       ,  9.       ],
           [ 0.       ,  1.5628336]])

    But if the transformation is required on one axis only, 
    weights on other axes are not accounted for since they
    would not affect the result of the transformation.

    >>> v._get_weights(weights={{'lon':lambda x:((x+180)/100)**2}}, axis='lat') 
    dimarray: 6 non-null elements (0 null)
    0 / lat (3): -80 to 80
    1 / lon (2): -180 to 180
    array([[ 0.17364818,  0.17364818],
           [ 1.        ,  1.        ],
           [ 0.17364818,  0.17364818]])

    What is provided as argument overrides axis attribute. 
    Here when the weights are provided for one axis only:

    >>> v._get_weights(weights=lambda x:((x+180)/100)**2, axis='lat') 
    dimarray: 6 non-null elements (0 null)
    0 / lat (3): -80 to 80
    1 / lon (2): -180 to 180
    array([[ 1.,  1.],
           [ 1.,  1.],
           [ 4.,  4.]])

    Or when the weight is provided as a numpy array

    >>> w = np.array([[ 1.,  1.],
    ...               [ 1.,  1.],
    ...               [ 4.,  4.]])
    >>> v._get_weights(weights=w)
    dimarray: 6 non-null elements (0 null)
    0 / lat (3): -80 to 80
    1 / lon (2): -180 to 180
    array([[ 1.,  1.],
           [ 1.,  1.],
           [ 4.,  4.]])

    Or even as DimArray (which will be broadcast with the appropriate shape)

    >>> wa = DimArray(w[:,0], axes=[v.axes['lat']])
    >>> v._get_weights(weights=wa)
    dimarray: 6 non-null elements (0 null)
    0 / lat (3): -80 to 80
    1 / lon (2): -180 to 180
    array([[ 1.,  1.],
           [ 1.,  1.],
           [ 4.,  4.]])

    Any nans in the data will induce 0 in the weights, 
    since the element will then be discarded or the result be NaN anyway.

    >>> v.values[1, 0] = np.nan
    >>> v._get_weights(weights=w)
    dimarray: 6 non-null elements (0 null)
    0 / lat (3): -80 to 80
    1 / lon (2): -180 to 180
    array([[ 1.,  1.],
           [ 0.,  1.],
           [ 4.,  4.]])
    """
    # avoid circular import
    from dimarray import DimArray

    # dims: axes from which the weights should be computed
    if axis is None:
        dims = self.dims
    elif type(axis) is tuple:
        dims = axis
    else:
        dims = (axis,)

    # common case: no weight is provided and no Axis has weights
    # if is enough to check on all axes to be reduced, weights on other axes 
    # make no difference to the reduction transformation
    if weights is None and np.all([self.axes[dim].weights is None for dim in dims]):
        return None

    # for 1-D array, same as axis is None is like axis=0
    if axis is None and self.ndim == 1:
        axis = 0

    # check `weights` type

    # special case: if weights is DimArray, just use it as full weight
    if isinstance(weights, DimArray):
        full_weights = weights.broadcast(self.axes) # broadcast weights on appropriate dimension

    # special case II: numpy array of appropriate dimension ==> just use it as full weight
    elif isinstance(weights, np.ndarray) and weights.ndim > 1:
        assert weights.shape == self.shape, "`weights` must be either 1-D or have same shape as DimArray"
        full_weights = DimArray(weights, self.axes)

    # make axis weights based by combining single-axis weights (standard form)
    else:
        array_type = isinstance(weights, np.ndarray) and weights.dim == 1
        dict_type = isinstance(weights, dict)
        assert weights is None or callable(weights) or array_type or dict_type, "unexpected type for `weights`"
        assert weights is None or not (not dict_type and axis is None), "`weights` type incompatible with flattening operation, provide `axis` or use dict-type weights"
        assert weights is None or not (not dict_type and type(axis) is tuple), "`weights` type incompatible with flattening operation, use dict-type weights"

        # make weights dict-like  (operation on single axis)
        if not dict_type: 
            weights = {axis: weights}

        # Create weights from the axes (only compute for the axes to be reduced)
        full_weights = 1.
        for axis in dims:
            ax = self.axes[axis]

            # get proper weights array: from `Axis` or user-provided on-the-fly
            if weights is not None and axis in weights.keys():
                axis_weights = weights[axis]
            else:
                axis_weights = ax.weights

            # if weight is None, just skip it
            if axis_weights is None: 
                continue

            # broadcast and multiply weights for all axes involved 
            full_weights = ax._get_weights(axis_weights).reshape(dims) * full_weights

        # SPECIAL CASE: NO weights (e.g. weights parameter provided as {} or {'lat':None} and no axis weights attributes):
        if full_weights is 1.:
            return None

        full_weights = full_weights.broadcast(self.axes)

    # set 0 in a where there are NaNs. 
    if mirror_nans and anynan(self.values):
        full_weights.values[np.isnan(self.values)] = 0

    assert full_weights is not None
    
    return full_weights