Example #1
0
def _check_axisoptions(x, axisoptions):
    """checks the axis=options to make sure that it is valid"""
    axlist = cdms2.orderparse(axisoptions)
    if Ellipsis in axlist:
        raise AveragerError, 'Error: Ellipsis (...) not allowed in axis options'
    try:
        cdms2.order2index(x.getAxisList(), axisoptions)
        return axlist
    except:
        raise AveragerError, 'Error: You have specified an invalid axis= option.'
Example #2
0
def _check_axisoptions(x, axisoptions):
    """checks the axis=options to make sure that it is valid"""
    axlist = cdms2.orderparse(axisoptions)
    if Ellipsis in axlist:
        raise AveragerError, 'Error: Ellipsis (...) not allowed in axis options'
    try:
        cdms2.order2index(x.getAxisList(), axisoptions)
        return axlist
    except:
        raise AveragerError, 'Error: You have specified an invalid axis= option.'
Example #3
0
def check_order(var, allowed, vertical=None, copy=False, reorder=False,
    extended=None, getorder=False):
    """Check that the axis order of a variable is matches
    at least one the specifed valid orders

    :Params:

        - **var**: MV2 array.
        - **allowed**: A single order string or a list. It should contain one or
          several of these letters:

            - ``x``: longitudes,
            - ``y``: latitudes,
            - ``z``: vertical levels,
            - ``t``: time axis,
            - ``d``: data values (ignored),
            - ``-``: any kind of axis.

    :Return:

        ``var``, or ``var, order, reordered`` if **reorder** is True.
    """

    # Check allowed orders
    # - consistency
    if not isinstance(allowed, (list, tuple)):
        allowed = [allowed]
    else:
        allowed = list(allowed)
    withd = 'd' in allowed[0]
    get_rank = lambda o: len(o.replace('d', ''))
    rank = get_rank(allowed[0])
    for order in allowed:
        try:
            cdms2.orderparse(order.lower().replace('d', ''))
        except:
            raise VACUMMError("Wrong allowed order: "+order)
        if ('d' in order and not withd) or ('d' not in order and withd):
            raise VACUMMError("'d' only partially present in allowed order: %s"%allowed)
        if get_rank(order)!=rank:
            raise VACUMMError("Inconsistent ranks between allowed orders: %s"%[get_rank(o) for o in allowed])
    # - check extended mode
    if extended is None: # extended?
        re_upper = re.compile('[XYZT]').search
        for order in allowed:
            if re_upper(order) is not None:
                extended = True # force extended mode
                break
        else:
            extended = False
    if extended is False: # lower
        allowed = [order.lower() for order in allowed]
    else: #add tolerance for lower case orders
        re_sub = re.compile('[xyzt]').sub
        allowed = allowed+[re_sub('-', order) for order in allowed]
    # - unique and lower case
    _, idx = N.unique(allowed, return_index=True)
    idx = N.sort(idx)
    allowed = N.array(allowed)[idx].tolist()
    # - restrict to vertical or horizontal (1D data only)
    if vertical is not None and len(allowed[0])==2:
        allowed = [oo for oo in allowed if oo[int(vertical)]=='d']

    # Data order
    data_cdms_order = get_order(var)

    # Loop on allowed orders
    from vacumm.misc.grid.misc import get_axis, var2d
    reordered = False
    for allowed_order in allowed:

        # Handle data case
        d = allowed_order.find('d')
        if d!=-1: allowed_order = allowed_order.replace('d', '') # pure axis

        # Check cdms order
        allowed_cdms_order = allowed_order.lower() # lower case
        if order_match(data_cdms_order, allowed_cdms_order, strict='right'): break # It is already good

        # Try to reorder
        if reorder:

            try:
                reordered = cdms2.order2index(var.getAxisList(), allowed_cdms_order)
                new_var = var.reorder(allowed_cdms_order)
                if allowed_cdms_order[-1]=='x' and len(get_axis(new_var, -1).shape)==2: # 2D axes
                    del var
                    var = var2d(new_var,
                        MV2.transpose(get_axis(new_var, 0)),
                        MV2.transpose(get_axis(new_var, -1)), copy=0)
                    set_order(new_var, allowed_cdms_order)
                else:
                    del var
                    var = new_var
                data_cdms_order = get_order(var)
                break # No error so it worked and we leave

            except:
                continue

    else:
        raise VACUMMError('Wrong type of axes. Possible forms are: %s'%', '.join(allowed))

    if not getorder: return var
    if d!=-1:
        data_cdms_order = cdms2.orderparse(data_cdms_order)
        data_cdms_order.insert(d, 'd')
        data_cdms_order = ''.join(data_cdms_order)
    return var, data_cdms_order, reordered
Example #4
0
def generic1d(data, weights, axis=0, mask='same', copy=True, cyclic=False):
    """Generic 1D filter applied to :mod:`MV2` variables using convolution.

    :Params:

        - **data**: Atleast 1D :mod:`MV2` variable.
        - **weights**: integer, 1D weights.
          They are expected to be symmetric and of odd sizes.
        - **axis**, optional: axis on which to operate
        - **mask**, optional: mode of masking.
          The mask is also filtered, and its value helps
          defining the new mask, depending on this parameter:

            - If a float is provided, data are masked if the mask value
              is greater than this parameter.
            - ``"minimal"``: Equivalent to ``1.``. Data is not masked
              if a valid value was used to compute data.
            - ``"maximal"``: Equivalent to ``0.``. Data is masked
              if a invalid value was used to compute data.
            - ``"same"``: Mask is the same as input mask.

        - **copy**, optional: Copy variable before filtering it.

    :Return:

        - The filtered :mod:`MV2` variable.

    :Example:

        >>> generic1d(data, 3.) # running mean using a 3-points block
        >>> generic1d(data, [1.,2.,1], axis=2) # shapiro filter on third axis

    :See also: :func:`scipy.signal.convolve2d`

    """

    # Setup
    # - data
    data = MV2.asarray(data)
    assert data.ndim, 'Input data array must be at least 1D'
    if axis<0: axis += data.ndim
    if axis!=data.ndim-1:
        init_order = data.getOrder(ids=1)
        data = data.reorder('...%i'%axis)
    datan = data.filled(0.).copy()
    nx = datan.shape[-1]
    datan.shape = -1, nx
    # - weights
    if isinstance(weights, int):
        weights = N.ones(weights)
    else:
        weights = N.asarray(weights)
    assert weights.ndim, 'Input weights array must be at least 1D'
    assert weights.shape[-1] % 2 == 1, 'Shape of weights must be of odd size'
    ww = (~N.ma.getmaskarray(data)).astype('i') #  = good 2D
    nw = weights.shape[-1]
    nw2 = weights.shape[-1] / 2
    weights.shape = 1, -1
    ww.shape = datan.shape
    if data.mask is not MV2.nomask:
        one2d = N.ones(datan.shape, 'i')

    # Cyclic case
    if cyclic:
        mode = 'valid'
        fdatan = N.concatenate((datan[:, -nw2:], datan, datan[:, :nw2]), axis=1)
        fww = N.concatenate((ww[:, -nw2:], ww, ww[:, :nw2]), axis=1)
        one1d = N.ones(datan.shape[-1]+nw2*2, 'i')
    else:
        mode = 'same'
        fdatan = datan
        fww = ww
        one1d = N.ones(datan.shape[-1], 'i')

    # Filter
    kwf = dict(mode=mode)
    datan = convolve2d(fdatan, weights, **kwf)
    one2d = N.resize(N.convolve(one1d, weights[0], **kwf), datan.shape)
    del one1d
    if data.mask is MV2.nomask:
        ww = one2d
    else:
        ww = convolve2d(fww, weights, **kwf)
    del fdatan, fww
    bad = ww==0
    ww[bad] = 1
    datan[:] = N.where(bad, datan, datan/ww.astype('d'))
    ww[bad] = 0

    # Set
    if copy:
        datao = data.clone()
    else:
        datao = data
    datan.shape = data.shape
    bad.shape = data.shape
    if data.mask is not MV2.nomask:
        ww.shape = one2d.shape = data.shape
        if mask is 'same':
            bad = data.mask
        else:
            if mask == 'minimal':
                mask = 1.
            elif mask == 'maximal':
                mask = 0.
            else:
                mask = float(mask)
            bad |= (ww/one2d)<(1-mask) # threshold
        datao[:] = N.ma.masked_where(bad, datan, copy=False)
        del ww, one2d, bad
    else:
        datao[:] = datan

    if axis!=data.ndim-1:
        init_order = cdms2.order2index(datao.getAxisList(), init_order)
        return datao.reorder(init_order)
    return datao
Example #5
0
def deriv(data, axis=0, fast=True, fill_value=None, physical=True, lat=None):
    """Derivative along a given axis

    - **data**: Data array (converted to MV array if needed)

    - *axis*: Axis on which the derivative is performed [default: 0]
    - *fast*: Filled masked array before derivating, so use Numeric which is faster than MA or MV [*WARNING* default: True]
    - *physical*: Try physical derivative, taking axis units into account [default: True]
    - *lat*: Latitude for geographical deriviative to convert positions in degrees to meters

    """
##  print 'deriv2d'
    data = MV.masked_array(data)

    # Reordering if needed
    if axis:
        init_order = data.getOrder(ids=1)
        data = data.reorder(str(axis)+'...')
    data_deriv = data.clone()
    data_deriv.id += '_deriv%i'%axis

    # cdms or Numeric variable  to work on?
    if data.mask is MV.nomask:
        fast = True
    if fast:
        if fill_value is None:
            data_to_use = data.filled()
        else:
            data_to_use = data.filled(fill_value)
    else:
        data_to_use = data

    # Derivative
    data_deriv[1:-1] = data_to_use[2:]-data_to_use[:-2]
    data_deriv[0] = data_to_use[1]-data_to_use[0]
    data_deriv[-1] = data_to_use[-2]-data_to_use[-1]
#   if not fast:
#       for i in 0,-1: data_deriv[i] = MV.masked

    # Physical derivative
    if physical:
        pos = N.resize(data.getAxis(0)[:],data.shape[::-1]).transpose()
        if islon(data.getAxis(0)):
            if lat is None:
                for i,lataxis in enumerate(data.getAxisList()):
                    if islat(lataxis):
                        sh = list(data.shape)
                        if i != data.ndim-1:
                            olen = sh[-1]
                            sh[-1] = len(lataxis)
                            sh[i] = olen
                            lat = N.swapaxes(N.resize(lataxis[:],sh),i,-1)
                        else:
                            lat = N.resize(lataxis[:],sh)
                        break
        if islon(data.getAxis(0)) or islat(data.getAxis(0)):
            pos = deg2m(pos,lat)
            units = 'm-1'
        else:
            units = getattr(data.getAxis(0),'units',None)
        data_deriv[1:-1] /= (pos[2:]-pos[:-2])
        data_deriv[0] /= (pos[1]-pos[0])
        data_deriv[-1] /= (pos[-2]-pos[-1])
    else:
        data_deriv[1:-1] = data_deriv[1:-1] * .5
        units = None

    # Mask
    if fast:
        mask = MV.getmaskarray(data)
        dmask = mask.copy()
        dmask[1:-1] = N.logical_or(mask[2:],mask[:-2])
#       for i in 0,-1: dmask[i] = 1
        data_deriv[:] = MV.masked_array(data_deriv,mask=dmask)
    else:
        for i in 0,-1: data_deriv[i] = MV.masked

    # Reordering back
    if axis:
        init_order = cdms2.order2index(data_deriv.getAxisList(), init_order)
        data_deriv = data_deriv.reorder(init_order)

    # Units
    if units is not None:
        data_units = getattr(data_deriv,'units',None)
        if data_units is None:
            data_deriv.units = units
        else:
            data_deriv.units += ' '+units
            if data_deriv.units == 'm m-1':
                del data_deriv.units

    return data_deriv
Example #6
0
def __check_weightoptions(x, axisoptions, weightoptions):
    """
    Checks the weights=options to make sure that it is valid.
    
    Default: 'weighted'
    
    weight options are one of 'weighted', 'unweighted',  an array of weights for each 
    dimension or a MaskedVariable of the same shape as the data x.
    - 'weighted' means use the grid information to generate weights for that
      dimension or if multiple axes are passed, generate goes ahead and
      generates the full masked area weights.
    - 'unweighted' means use equal weights for all the grid points in that axis. 
    - Also an array of weights (of the same shape as the dimension being 
      averaged over or same shape as V) can be passed.
    """
    #
    # Since the _check_axisoptions was passed, the variable x has already been reordered
    # to the required order..... check it anyway.
    #
    #
    __DEBUG__ = 0
    #
    if __DEBUG__: print 'Axis options entering __check_weightoptions = ', axisoptions
    #
    axisindex = cdms2.order2index(x.getAxisList(), axisoptions)
    if __DEBUG__: print 'This should be 0,1,2... etc. is it? ', axisindex
    #
    axislist = cdms2.orderparse(axisoptions)
    if __DEBUG__: print 'axislist = ', axislist
    #
    if not isinstance(weightoptions, types.ListType):
        #
        # We have either 1 axis only or multiple axes and one MV2 of weights
        #
        if MV2.isMaskedVariable(weightoptions):
            #
            # Weight passed is an MV2. Probably OK
            #
            try:
                # We had required that the weights be of the same rank as the variable to be averaged.
                weightoptions = weightoptions(order=axisoptions)
                #
                # KRISHNA : check this for combinewts!!
                #
##                 if x.shape != weightoptions.shape:
##                     raise AveragerError, \
##                           'Error: Shape of weight array does not match shape of data array'
##                 # end of if x.shape != weightoptions.shape:
                if __DEBUG__: print '... definitely OK'
            except:
                raise AveragerError, \
                      'Error: Did not find the axes requested in the Masked variable.'
        elif len(axislist) == 1:
            #
            # Only one axis to reduce over....'
            # figure out if it is an array (numpy or numpy.ma) or 'equal' or 'generate' or something else
            #
            if __DEBUG__: print 'I have only 1 axis and 1 option'
            weightoptions = __check_each_weight_option(x, axislist[0], axisindex[0], weightoptions)
        else:
            #
            # More than 1 axis to reduce over, and not an MV2 of the same dimensionality as x!
            #
            if weightoptions in ['generate','weighted']:
                weightoptions = area_weights(x,axisoptions)
            else:
                # Cannot do this because 'generate' was not passed with multiple axes.
                raise AveragerError, 'Error: Multiple axes passed without weights to match'
            # end of if weightoptions == 'generate':
    else:
        #
        # I have a list of weightoptions i.e more than 1 axis in axisoption
        #
        #
        # We have multiple axes to deal with each with a weight....
        #
        for i in range(len(axislist)):
            weightoptions[i] = __check_each_weight_option(x, axislist[i], axisindex[i], weightoptions[i])
        # end of for i in range(len(axislist)):
        #
        if len(axislist) < len(weightoptions):
            for j in range(len(axislist), len(weightoptions), 1):
                weightoptions[j] = __check_each_weight_option(x, None, j, weightoptions[j])
        # end of if len(axislist) < len(weightoptions):
    # end of if not isinstance(weightoptions, types.ListType):
    #
    if __DEBUG__: print 'Successful with __check_weightoptions'
    #
    return weightoptions
Example #7
0
def __check_weightoptions(x, axisoptions, weightoptions):
    """
    Checks the weights=options to make sure that it is valid.
    
    Default: 'weighted'
    
    weight options are one of 'weighted', 'unweighted',  an array of weights for each 
    dimension or a MaskedVariable of the same shape as the data x.
    - 'weighted' means use the grid information to generate weights for that
      dimension or if multiple axes are passed, generate goes ahead and
      generates the full masked area weights.
    - 'unweighted' means use equal weights for all the grid points in that axis. 
    - Also an array of weights (of the same shape as the dimension being 
      averaged over or same shape as V) can be passed.
    """
    #
    # Since the _check_axisoptions was passed, the variable x has already been reordered
    # to the required order..... check it anyway.
    #
    #
    __DEBUG__ = 0
    #
    if __DEBUG__:
        print 'Axis options entering __check_weightoptions = ', axisoptions
    #
    axisindex = cdms2.order2index(x.getAxisList(), axisoptions)
    if __DEBUG__: print 'This should be 0,1,2... etc. is it? ', axisindex
    #
    axislist = cdms2.orderparse(axisoptions)
    if __DEBUG__: print 'axislist = ', axislist
    #
    if not isinstance(weightoptions, types.ListType):
        #
        # We have either 1 axis only or multiple axes and one MV2 of weights
        #
        if MV2.isMaskedVariable(weightoptions):
            #
            # Weight passed is an MV2. Probably OK
            #
            try:
                # We had required that the weights be of the same rank as the variable to be averaged.
                weightoptions = weightoptions(order=axisoptions)
                #
                # KRISHNA : check this for combinewts!!
                #
                ##                 if x.shape != weightoptions.shape:
                ##                     raise AveragerError, \
                ##                           'Error: Shape of weight array does not match shape of data array'
                ##                 # end of if x.shape != weightoptions.shape:
                if __DEBUG__: print '... definitely OK'
            except:
                raise AveragerError, \
                      'Error: Did not find the axes requested in the Masked variable.'
        elif len(axislist) == 1:
            #
            # Only one axis to reduce over....'
            # figure out if it is an array (numpy or numpy.ma) or 'equal' or 'generate' or something else
            #
            if __DEBUG__: print 'I have only 1 axis and 1 option'
            weightoptions = __check_each_weight_option(x, axislist[0],
                                                       axisindex[0],
                                                       weightoptions)
        else:
            #
            # More than 1 axis to reduce over, and not an MV2 of the same dimensionality as x!
            #
            if weightoptions in ['generate', 'weighted']:
                weightoptions = area_weights(x, axisoptions)
            else:
                # Cannot do this because 'generate' was not passed with multiple axes.
                raise AveragerError, 'Error: Multiple axes passed without weights to match'
            # end of if weightoptions == 'generate':
    else:
        #
        # I have a list of weightoptions i.e more than 1 axis in axisoption
        #
        #
        # We have multiple axes to deal with each with a weight....
        #
        for i in range(len(axislist)):
            weightoptions[i] = __check_each_weight_option(
                x, axislist[i], axisindex[i], weightoptions[i])
        # end of for i in range(len(axislist)):
        #
        if len(axislist) < len(weightoptions):
            for j in range(len(axislist), len(weightoptions), 1):
                weightoptions[j] = __check_each_weight_option(
                    x, None, j, weightoptions[j])
        # end of if len(axislist) < len(weightoptions):
    # end of if not isinstance(weightoptions, types.ListType):
    #
    if __DEBUG__: print 'Successful with __check_weightoptions'
    #
    return weightoptions
Example #8
0
def generic1d(data, weights, axis=0, mask='same', copy=True, cyclic=False):
    """Generic 1D filter applied to :mod:`MV2` variables using convolution.

    :Params:

        - **data**: Atleast 1D :mod:`MV2` variable.
        - **weights**: integer, 1D weights.
          They are expected to be symmetric and of odd sizes.
        - **axis**, optional: axis on which to operate
        - **mask**, optional: mode of masking.
          The mask is also filtered, and its value helps
          defining the new mask, depending on this parameter:

            - If a float is provided, data are masked if the mask value
              is greater than this parameter.
            - ``"minimal"``: Equivalent to ``1.``. Data is not masked
              if a valid value was used to compute data.
            - ``"maximal"``: Equivalent to ``0.``. Data is masked
              if a invalid value was used to compute data.
            - ``"same"``: Mask is the same as input mask.

        - **copy**, optional: Copy variable before filtering it.

    :Return:

        - The filtered :mod:`MV2` variable.

    :Example:

        >>> generic1d(data, 3.) # running mean using a 3-points block
        >>> generic1d(data, [1.,2.,1], axis=2) # shapiro filter on third axis

    :See also: :func:`scipy.signal.convolve2d`

    """

    # Setup
    # - data
    data = MV2.asarray(data)
    assert data.ndim, 'Input data array must be at least 1D'
    if axis < 0: axis += data.ndim
    if axis != data.ndim - 1:
        init_order = data.getOrder(ids=1)
        data = data.reorder('...%i' % axis)
    datan = data.filled(0.).copy()
    nx = datan.shape[-1]
    datan.shape = -1, nx
    # - weights
    if isinstance(weights, int):
        weights = N.ones(weights)
    else:
        weights = N.asarray(weights)
    assert weights.ndim, 'Input weights array must be at least 1D'
    assert weights.shape[-1] % 2 == 1, 'Shape of weights must be of odd size'
    ww = (~N.ma.getmaskarray(data)).astype('i')  #  = good 2D
    nw = weights.shape[-1]
    nw2 = weights.shape[-1] / 2
    weights.shape = 1, -1
    ww.shape = datan.shape
    if data.mask is not MV2.nomask:
        one2d = N.ones(datan.shape, 'i')

    # Cyclic case
    if cyclic:
        mode = 'valid'
        fdatan = N.concatenate((datan[:, -nw2:], datan, datan[:, :nw2]),
                               axis=1)
        fww = N.concatenate((ww[:, -nw2:], ww, ww[:, :nw2]), axis=1)
        one1d = N.ones(datan.shape[-1] + nw2 * 2, 'i')
    else:
        mode = 'same'
        fdatan = datan
        fww = ww
        one1d = N.ones(datan.shape[-1], 'i')

    # Filter
    kwf = dict(mode=mode)
    datan = convolve2d(fdatan, weights, **kwf)
    one2d = N.resize(N.convolve(one1d, weights[0], **kwf), datan.shape)
    del one1d
    if data.mask is MV2.nomask:
        ww = one2d
    else:
        ww = convolve2d(fww, weights, **kwf)
    del fdatan, fww
    bad = ww == 0
    ww[bad] = 1
    datan[:] = N.where(bad, datan, datan / ww.astype('d'))
    ww[bad] = 0

    # Set
    if copy:
        datao = data.clone()
    else:
        datao = data
    datan.shape = data.shape
    bad.shape = data.shape
    if data.mask is not MV2.nomask:
        ww.shape = one2d.shape = data.shape
        if mask is 'same':
            bad = data.mask
        else:
            if mask == 'minimal':
                mask = 1.
            elif mask == 'maximal':
                mask = 0.
            else:
                mask = float(mask)
            bad |= (ww / one2d) < (1 - mask)  # threshold
        datao[:] = N.ma.masked_where(bad, datan, copy=False)
        del ww, one2d, bad
    else:
        datao[:] = datan

    if axis != data.ndim - 1:
        init_order = cdms2.order2index(datao.getAxisList(), init_order)
        return datao.reorder(init_order)
    return datao
Example #9
0
def deriv(data, axis=0, fast=True, fill_value=None, physical=True, lat=None):
    """Derivative along a given axis

    - **data**: Data array (converted to MV array if needed)

    - *axis*: Axis on which the derivative is performed [default: 0]
    - *fast*: Filled masked array before derivating, so use Numeric which is faster than MA or MV [*WARNING* default: True]
    - *physical*: Try physical derivative, taking axis units into account [default: True]
    - *lat*: Latitude for geographical deriviative to convert positions in degrees to meters

    """
    ##  print 'deriv2d'
    data = MV.masked_array(data)

    # Reordering if needed
    if axis:
        init_order = data.getOrder(ids=1)
        data = data.reorder(str(axis) + '...')
    data_deriv = data.clone()
    data_deriv.id += '_deriv%i' % axis

    # cdms or Numeric variable  to work on?
    if data.mask is MV.nomask:
        fast = True
    if fast:
        if fill_value is None:
            data_to_use = data.filled()
        else:
            data_to_use = data.filled(fill_value)
    else:
        data_to_use = data

    # Derivative
    data_deriv[1:-1] = data_to_use[2:] - data_to_use[:-2]
    data_deriv[0] = data_to_use[1] - data_to_use[0]
    data_deriv[-1] = data_to_use[-2] - data_to_use[-1]
    #   if not fast:
    #       for i in 0,-1: data_deriv[i] = MV.masked

    # Physical derivative
    if physical:
        pos = N.resize(data.getAxis(0)[:], data.shape[::-1]).transpose()
        if islon(data.getAxis(0)):
            if lat is None:
                for i, lataxis in enumerate(data.getAxisList()):
                    if islat(lataxis):
                        sh = list(data.shape)
                        if i != data.ndim - 1:
                            olen = sh[-1]
                            sh[-1] = len(lataxis)
                            sh[i] = olen
                            lat = N.swapaxes(N.resize(lataxis[:], sh), i, -1)
                        else:
                            lat = N.resize(lataxis[:], sh)
                        break
        if islon(data.getAxis(0)) or islat(data.getAxis(0)):
            pos = deg2m(pos, lat)
            units = 'm-1'
        else:
            units = getattr(data.getAxis(0), 'units', None)
        data_deriv[1:-1] /= (pos[2:] - pos[:-2])
        data_deriv[0] /= (pos[1] - pos[0])
        data_deriv[-1] /= (pos[-2] - pos[-1])
    else:
        data_deriv[1:-1] = data_deriv[1:-1] * .5
        units = None

    # Mask
    if fast:
        mask = MV.getmaskarray(data)
        dmask = mask.copy()
        dmask[1:-1] = N.logical_or(mask[2:], mask[:-2])
        #       for i in 0,-1: dmask[i] = 1
        data_deriv[:] = MV.masked_array(data_deriv, mask=dmask)
    else:
        for i in 0, -1:
            data_deriv[i] = MV.masked

    # Reordering back
    if axis:
        init_order = cdms2.order2index(data_deriv.getAxisList(), init_order)
        data_deriv = data_deriv.reorder(init_order)

    # Units
    if units is not None:
        data_units = getattr(data_deriv, 'units', None)
        if data_units is None:
            data_deriv.units = units
        else:
            data_deriv.units += ' ' + units
            if data_deriv.units == 'm m-1':
                del data_deriv.units

    return data_deriv
Example #10
0
def check_order(var,
                allowed,
                vertical=None,
                copy=False,
                reorder=False,
                extended=None,
                getorder=False):
    """Check that the axis order of a variable is matches
    at least one the specifed valid orders

    :Params:

        - **var**: MV2 array.
        - **allowed**: A single order string or a list. It should contain one or
          several of these letters:

            - ``x``: longitudes,
            - ``y``: latitudes,
            - ``z``: vertical levels,
            - ``t``: time axis,
            - ``d``: data values (ignored),
            - ``-``: any kind of axis.

    :Return:

        ``var``, or ``var, order, reordered`` if **reorder** is True.
    """

    # Check allowed orders
    # - consistency
    if not isinstance(allowed, (list, tuple)):
        allowed = [allowed]
    else:
        allowed = list(allowed)
    withd = 'd' in allowed[0]
    get_rank = lambda o: len(o.replace('d', ''))
    rank = get_rank(allowed[0])
    for order in allowed:
        try:
            cdms2.orderparse(order.lower().replace('d', ''))
        except:
            raise VACUMMError("Wrong allowed order: " + order)
        if ('d' in order and not withd) or ('d' not in order and withd):
            raise VACUMMError(
                "'d' only partially present in allowed order: %s" % allowed)
        if get_rank(order) != rank:
            raise VACUMMError("Inconsistent ranks between allowed orders: %s" %
                              [get_rank(o) for o in allowed])
    # - check extended mode
    if extended is None:  # extended?
        re_upper = re.compile('[XYZT]').search
        for order in allowed:
            if re_upper(order) is not None:
                extended = True  # force extended mode
                break
        else:
            extended = False
    if extended is False:  # lower
        allowed = [order.lower() for order in allowed]
    else:  #add tolerance for lower case orders
        re_sub = re.compile('[xyzt]').sub
        allowed = allowed + [re_sub('-', order) for order in allowed]
    # - unique and lower case
    _, idx = N.unique(allowed, return_index=True)
    idx = N.sort(idx)
    allowed = N.array(allowed)[idx].tolist()
    # - restrict to vertical or horizontal (1D data only)
    if vertical is not None and len(allowed[0]) == 2:
        allowed = [oo for oo in allowed if oo[int(vertical)] == 'd']

    # Data order
    data_cdms_order = get_order(var)

    # Loop on allowed orders
    from vacumm.misc.grid.misc import get_axis, var2d
    reordered = False
    for allowed_order in allowed:

        # Handle data case
        d = allowed_order.find('d')
        if d != -1: allowed_order = allowed_order.replace('d', '')  # pure axis

        # Check cdms order
        allowed_cdms_order = allowed_order.lower()  # lower case
        if order_match(data_cdms_order, allowed_cdms_order, strict='right'):
            break  # It is already good

        # Try to reorder
        if reorder:

            try:
                reordered = cdms2.order2index(var.getAxisList(),
                                              allowed_cdms_order)
                new_var = var.reorder(allowed_cdms_order)
                if allowed_cdms_order[-1] == 'x' and len(
                        get_axis(new_var, -1).shape) == 2:  # 2D axes
                    del var
                    var = var2d(new_var,
                                MV2.transpose(get_axis(new_var, 0)),
                                MV2.transpose(get_axis(new_var, -1)),
                                copy=0)
                    set_order(new_var, allowed_cdms_order)
                else:
                    del var
                    var = new_var
                data_cdms_order = get_order(var)
                break  # No error so it worked and we leave

            except:
                continue

    else:
        raise VACUMMError('Wrong type of axes. Possible forms are: %s' %
                          ', '.join(allowed))

    if not getorder: return var
    if d != -1:
        data_cdms_order = cdms2.orderparse(data_cdms_order)
        data_cdms_order.insert(d, 'd')
        data_cdms_order = ''.join(data_cdms_order)
    return var, data_cdms_order, reordered