from copy import deepcopy
from re import match as re_match
from re import findall as re_findall
from textwrap import fill as textwrap_fill
from itertools import izip
from cPickle import dumps, loads, PicklingError
from re import search as re_search
from netCDF4 import default_fillvals as _netCDF4_default_fillvals
from operator import truediv as truediv
from operator import itruediv as itruediv
from re import compile as re_compile
from numpy import array as numpy_array
from numpy import result_type as numpy_result_type
from .flags import Flags
from .functions import RTOL, ATOL, equals, set_equals
from .functions import inspect as cf_inspect
from .query import Query
from .units import Units
from .data.data import Data
_units_None = Units()
docstring = {
# ----------------------------------------------------------------
'{+data-like}': '''A data-like object is any object containing array-like or
scalar data which could be used to create a `cf.Data` object.
*Example:*
Instances, ``x``, of following types are all examples of
data-like objects (because ``cf.Data(x)`` creates a valid
`cf.Data` object): :py:obj:`int`, :py:obj:`float`,
:py:obj:`str`, :py:obj:`tuple`, :py:obj:`list`,
`numpy.ndarray`, `cf.Data`, `cf.Coordinate`, `cf.Field`.''',
# ----------------------------------------------------------------
'{+atol}': '''atol : float, optional
The absolute tolerance for all numerical comparisons, By
default the value returned by the `cf.ATOL` function is used.''',
# ----------------------------------------------------------------
'{+rtol}': '''rtol : float, optional
The relative tolerance for all numerical comparisons, By
default the value returned by the `cf.RTOL` function is used.''',
# ----------------------------------------------------------------
'{+axis_selection}': '''Axes are selected with the criteria specified by the keyword
parameters. If no keyword parameters are specified then all axes are
selected.''',
# ----------------------------------------------------------------
'{+item_selection}': '''Items are selected with the criteria specified by the keyword
parameters. By default, if multiple criteria are given then the items
will be the intersection of each keyword's selection (see the
*match_and* parameter). If no keyword parameters are specified then
all items are selected.''',
# ----------------------------------------------------------------
'{+item_criteria}': '''There are two main types of item selection criteria:
============================ ==================================
Selection criteria Parameters
============================ ==================================
Attributes and CF properties *items*
Data array axes *axes*, *axes_all*, *axes_subset*,
*axes_superset*, *ndim*
============================ ==================================''',
# ----------------------------------------------------------------
'{+items_criteria}': '''There are three main types of item selection criteria:
============================ ==================================
Selection criteria Parameters
============================ ==================================
Attributes and CF properties *items*
Data array axes *axes*, *axes_all*, *axes_subset*,
*axes_superset*, *ndim*
Item role *role*
============================ ==================================''',
# ----------------------------------------------------------------
'{+ndim}': '''ndim : *optional*
{+Fef,}Select the items whose number of data array dimensions
satisfy the given condition. A range of data numbers of array
axes may be selected if *ndim* is a `cf.Query` object.
*Example:*
``ndim=1`` selects items which span exactly one axis and
``ndim=cf.ge(2)`` selects items which span two or more
axes (see `cf.ge`).''',
# ----------------------------------------------------------------
'{+axes}': '''axes : *optional*
{+Fef,}Select items whose data array spans at least one of the
specified axes, taken in any order, and possibly others. The
axes are those that would be selected by this call of the
field's `~cf.Field.axes` method: ``f.axes(axes)`` or, if
*axes* is a dictionary of parameters to the `~cf.Field.axes`
method, ``f.axes(**axes)``. See `cf.Field.axes` for details.
*Example:*
To select items which span a time axis, and possibly
others, you could set: ``axes='T'``.
*Example:*
To select items which span a latitude and/or longitude
axes, and possibly others, you could set: ``axes=['X',
'Y']``.
*Example:*
To specify axes with size 19 you could set ``axes={'size':
19}``. To specify depth axes with size 40 or more, you
could set: ``axes={'axes': 'depth', 'size': cf.ge(40)}``
(see `cf.ge`).''',
# ----------------------------------------------------------------
'{+axes_subset}': '''axes_subset : *optional*
{+Fef,}Select items whose data array spans all of the
specified axes, taken in any order, and possibly others. The
axes are those that would be selected by this call of the
field's `~cf.Field.axes` method: ``f.axes(axes_subset)`` or,
if *axes_subset* is a dictionary of parameters ,
``f.axes(**axes_subset)``. See `cf.Field.axes` for details.
*Example:*
To select items which span a time axes, and possibly
others, you could set: ``axes_subset='T'``.
*Example:*
To select items which span latitude and longitude
axes, and possibly others, you could set:
``axes_subset=['X', 'Y']``.
*Example:*
To specify axes with size 19 you could set
``axes_subset={'size': 19}``. To specify depth axes with
size 40 or more, you could set: ``axes_subset={'axes':
'depth', 'size': cf.ge(40)}`` (see `cf.ge`).''',
# ----------------------------------------------------------------
'{+axes_superset}': '''axes_superset : *optional*
{+Fef,}Select items whose data arrays span a subset of the
specified axes, taken in any order, and no others. The axes
are those that would be selected by this call of the field's
`~cf.Field.axes` method: ``f.axes(axes_superset)`` or, if
*axes_superset* is a dictionary of parameters,
``f.axes(**axes_superset)``. See `cf.Field.axes` for details.
*Example:*
To select items which span a time axis, and no others, you
could set: ``axes_superset='T'``.
*Example:*
To select items which span latitude and/or longitude axes,
and no others, you could set: ``axes_superset=['X',
'Y']``.
*Example:*
To specify axes with size 19 you could set
``axes_superset={'size': 19}``. To specify depth axes with
size 40 or more, you could set: ``axes_superset={'axes':
'depth', 'size': cf.ge(40)}`` (see `cf.ge`).''',
# ----------------------------------------------------------------
'{+axes_all}': '''axes_all : *optional*
{+Fef,}Select items whose data arrays span all of the
specified axes, taken in any order, and no others. The axes
are those that would be selected by this call of the field's
`~cf.Field.axes` method: ``f.axes(axes_all)`` or, if
*axes_all* is a dictionary of parameters,
``f.axes(**axes_all)``. See `cf.Field.axes` for details.
*Example:*
To select items which span a time axis, and no others,
you could set: ``axes_all='T'``.
*Example:*
To select items which span latitude and longitude axes,
and no others, you could set: ``axes_all=['X', 'Y']``.
*Example:*
To specify axes with size 19 you could set
``axes_all={'size': 19}``. To specify depth axes with
size 40 or more, you could set: ``axes_all={'axes':
'depth', 'size': cf.ge(40)}`` (see `cf.ge`).''',
# ----------------------------------------------------------------
'{+role}': '''role : (sequence of) str, optional
Select items of the given roles. Valid roles are:
======= ============================
role Items selected
======= ============================
``'d'`` Dimension coordinate objects
``'a'`` Auxiliary coordinate objects
``'m'`` Cell measure objects
``'r'`` Coordinate reference objects
======= ============================
Multiple roles may be specified by a multi-character string or
a sequence.
*Example:*
Selecting auxiliary coordinate and cell measure objects
may be done with any of the following values of *role*:
``'am'``, ``'ma'``, ``('a', 'm')``, ``['m', 'a']``,
``set(['a', 'm'])``, etc.''',
# ----------------------------------------------------------------
'{+exact}': '''exact : bool, optional
{+Fef,}The *exact* parameter applies to the interpretation of
string-valued conditions given by the *items* parameter. By
default *exact* is False, which means that:
* A string-valued condition is treated as a regular
expression understood by the :py:obj:`re` module and an
item is selected if its corresponding value matches the
regular expression using the `re.match` method (i.e. if
zero or more characters at the beginning of item's value
match the regular expression pattern).
* Units and calendar strings are evaluated for equivalence
rather then equality (e.g. ``'metre'`` is equivalent to
``'m'`` and to ``'km'``).
..
*Example:*
To select items with with any units of pressure:
``f.{+name}('units:hPa')``. To select items with a
standard name which begins with "air" and with any units
of pressure: ``f.{+name}({'standard_name': 'air', 'units':
'hPa'})``.
If *exact* is True then:
* A string-valued condition is not treated as a regular
expression and an item is selected if its corresponding
value equals the string.
* Units and calendar strings are evaluated for exact equality
rather than equivalence (e.g. ``'metre'`` is equal to
``'m'``, but not to ``'km'``).
..
*Example:*
To select items with with units of hectopascals but not,
for example, Pascals: ``f.{+name}('units:hPa',
exact=True)``. To select items with a standard name of
exactly "air_pressure" and with units of exactly
hectopascals: ``f.{+name}({'standard_name':
'air_pressure', 'units': 'hPa'}, exact=True)``.
Note that `cf.Query` objects provide a mechanism for
overriding the *exact* parameter for individual values.
*Example:*
``f.{+name}({'standard_name': cf.eq('air', exact=False),
'units': 'hPa'}, exact=True)`` will select items with a
standard name which begins "air" but with units of exactly
hectopascals (see `cf.eq`).
*Example:*
``f.{+name}({'standard_name': cf.eq('air_pressure'),
'units': 'hPa'})`` will select items with a standard name
of exactly "air_pressure" but with any units of pressure
(see `cf.eq`).''',
# ----------------------------------------------------------------
'{+match_and}': '''match_and : bool, optional
By default *match_and* is True and{+,fef,} items are selected
if they satisfy the all of the specified conditions.
If *match_and* is False then items are selected if they
satisfy at least one of the specified conditions.
*Example:*
To select items with identity beginning with "ocean"
**and** two data array axes: ``f.{+name}('ocean',
ndim=2)``.
*Example:*
To select items with identity beginning with "ocean"
**or** two data array axes: ``f.{+name}('ocean', ndim=2,
match_and=False)``.''',
# ----------------------------------------------------------------
'{+inverse}': '''inverse : bool, optional
If True then{+,fef,} select items other than those selected by all
other criteria.''',
# ----------------------------------------------------------------
'{+copy}': '''copy : bool, optional
If True then a returned item is a copy. By default it is not
copied.''',
# ----------------------------------------------------------------
'{+key}': '''key : bool, optional
If True then{+,fef,} return the domain's identifier for the selected
item, rather than the item itself.''',
# ----------------------------------------------------------------
'{+items}': '''items : *optional*
{+Fef,}Select items whose attributes or CF properties satisfy
the given conditions. The *items* parameter may be one, or a
sequence, of:
* `None` or an empty dictionary. All items are
selected. This is the default.
..
* A string specifying one of the CF coordinate types:
``'T'``, ``'X'``, ``'Y'`` or ``'Z'``. An item has an
attribute for each coordinate type and is selected if the
attribute for the specified type is True.
*Example:*
To select CF time items: ``items='T'``.
..
* A string which identifies items based on their
string-valued metadata. The value may take one of the
following forms:
============== ========================================
Value Interpretation
============== ========================================
Contains ``:`` Selects on the CF property specified
before the first ``:``
Contains ``%`` Selects on the attribute specified
before the first ``%``
Anything else Selects on identity as returned by the
item's `!identity` method
============== ========================================
By default the part of the string to be compared with an
item is treated as a regular expression understood by the
:py:obj:`re` module and an item is selected if its
corresponding value matches the regular expression using
the :py:obj:`re.match` method (i.e. if zero or more
characters at the beginning of item's value match the
regular expression pattern). See the *exact* parameter for
details.
*Example:*
To select items with standard names which begin "lat":
``items='lat'``.
*Example:*
To select items with long names which begin "air":
``items='long_name:air'``.
*Example:*
To select items with netCDF variable names which begin
"lon": ``items='ncvar%lon'``.
*Example:*
To select items with identities which end with the
letter "z": ``items='.*z$'``.
*Example:*
To select items with long names which start with the
string ".*a": ``items='long_name%\.\*a'``.
..
* A dictionary which identifies properties of the items with
corresponding tests on their values. An item is selected
if **all** of the tests in the dictionary are passed.
In general, each dictionary key is a CF property name with
a corresponding value to be compared against the item's CF
property value.
If the dictionary value is a string then by default it is
treated as a regular expression understood by the
:py:obj:`re` module and an item is selected if its
corresponding value matches the regular expression using
the :py:obj:`re.match` method (i.e. if zero or more
characters at the beginning of item's value match the
regular expression pattern). See the *exact* parameter for
details.
*Example:*
To select items with standard name of exactly
"air_temperature" and long name beginning with the
letter "a": ``items={'standard_name':
cf.eq('air_temperature'), 'long_name': 'a'}`` (see
`cf.eq`).
Some key/value pairs have a special interpretation:
================== ====================================
Special key Value
================== ====================================
``'units'`` The value must be a string and by
default is evaluated for
equivalence, rather than equality,
with an item's `units` property,
for example a value of ``'Pa'``
will match units of Pascals or
hectopascals, etc. See the *exact*
parameter.
``'calendar'`` The value must be a string and by
default is evaluated for
equivalence, rather than equality,
with an item's `calendar`
property, for example a value of
``'noleap'`` will match a calendar
of noleap or 365_day. See the
*exact* parameter.
`None` The value is interpreted as for a
string value of the *items*
parameter. For example,
``items={None: 'air'}`` is
equivalent to ``items='air'``,
``items={None: 'ncvar%pressure'}``
is equivalent to
``items='ncvar%pressure'`` and
``items={None: 'Y'}`` is equivalent
to ``items='Y'``.
================== ====================================
*Example:*
To select items with standard name starting with
"air", units of temperature and a netCDF variable name
of "tas" you could set
``items={'standard_name': 'air', 'units': 'K', None:
'ncvar%tas$'}``.
..
* A domain item identifier (such as ``'dim1'``, ``'aux0'``,
``'msr2'``, ``'ref0'``, etc.). Selects the corresponding
item.
*Example:*
To select the item with domain identifier "dim1":
``items='dim1'``.
If *items* is a sequence of any combination of the above then
the selected items are the union of those selected by each
element of the sequence. If the sequence is empty then no
items are selected.''',
# ----------------------------------------------------------------
'{+axes, kwargs}': '''axes, kwargs : *optional*
Select axes. The *axes* parameter may be one, or a sequence,
of:
* `None`. If no *kwargs* arguments have been set
then all axes are selected. This is the default.
..
* An integer. Explicitly selects the axis corresponding to
the given position in the list of axes of the field's data
array.
*Example:*
To select the third data array axis: ``axes=2``. To
select the last axis: ``axes=-1``.
..
* A :py:obj:`slice` object. Explicitly selects the axes
corresponding to the given positions in the list of axes
of the field's data array.
*Example:*
To select the last three data array axes:
``axes=slice(-3, None)``
..
* A domain axis identifier. Explicitly selects this axis.
*Example:*
To select axis "dim1": ``axes='dim1'``.
..
* Any value accepted by the *items* parameter of the field's
`items` method. Used in conjunction with the *kwargs*
parameters to select the axes which span the items that
would be identified by this call of the field's `items`
method: ``f.items(items=axes, axes=None, **kwargs)``. See
`cf.Field.items` for details.
*Example:*
To select the axes spanned by one dimensionsal time
coordinates: ``f.{+name}('T', ndim=1)``.
If *axes* is a sequence of any combination of the above then
the selected axes are the union of those selected by each
element of the sequence. If the sequence is empty then no axes
are selected.''',
# ----------------------------------------------------------------
'{+size}': '''size : *optional*
Select axes whose sizes equal *size*. Axes whose sizes lie
within a range sizes may be selected if *size* is a `cf.Query`
object.
*Example:*
``size=1`` selects size 1 axes.
*Example:*
``size=cf.ge(2)`` selects axes with sizes greater than 1
(see `cf.ge`).''',
# ----------------------------------------------------------------
'{+i}': '''i : bool, optional
If True then update the {+variable} in place. By default a new
{+variable} is created. In either case, a {+variable} is
returned.''',
# ----------------------------------------------------------------
'def_aux': '''
Return {a} item of the domain, or its domain identifier.
{+item_selection}
If no unique item can be found then `None` is returned.
To find multiple items, use the `{+name}s` method.
Note that ``f.{+name}(inverse=False, **kwargs)`` is equivalent to
``f.item(role='a', inverse=False, **kwargs)``.
.. seealso:: `auxs`, `measure`, `coord`, `ref`, `dim`, `item`,
`remove_item`
:Examples 1:
A latitude item could potentially be selected with any of:
>>> a = f.{+name}('Y')
>>> a = f.{+name}('latitude')
>>> a = f.{+name}('long_name:latitude')
>>> a = f.{+name}('aux1')
>>> a = f.{+name}(axes_all='Y')
:Parameters:
{+items}
{+ndim}
{+axes}
{+axes_all}
{+axes_subset}
{+axes_superset}
{+match_and}
{+exact}
{+inverse}
{+key}
{+copy}''',
# ----------------------------------------------------------------
'def_axes_sizes': '''
Return the sizes of domain axes.
.. seealso:: `axis`, `axis_size`, `axis_identity`, `axis_name`
:Examples 1:
Find the sizes of all domain axes:
>>> a = f.axes_sizes()
Find the size of the X axis:
>>> a = f.axes_sizes('X')
:Parameters:
{+axes, kwargs}
{+size}
key : bool, optional
In the output dictionary, identify axes by domain identifier,
rather than by name.''',
# ----------------------------------------------------------------
'def_axis': '''
Return a domain axis identifier.
.. versionadded:: 1.0
.. seealso:: `axes`, `axis_name`, `axis_size`, `remove_axes`
:Examples 1:
:Parameters:
{+axes, kwargs}
{+size}''',
# ----------------------------------------------------------------
'def_dim': '''
Return {a} item of the domain, or its domain identifier.
{+item_selection}
If no unique item can be found then `None` is returned.
To find multiple items, use the `{+name}s` method.
Note that ``f.{+name}(inverse=False, **kwargs)`` is equivalent to
``f.item(role='d', inverse=False, **kwargs)``.
.. seealso:: `aux`, `measure`, `coord`, `ref`, `dims`, `item`,
`remove_item`
:Examples 1:
A latitude item could potentially be selected with any of:
>>> d = f.{+name}('Y')
>>> d = f.{+name}('latitude')
>>> d = f.{+name}('long_name:latitude')
>>> d = f.{+name}('dim1')
>>> d = f.{+name}(axes_all='Y')''',
# ----------------------------------------------------------------
'def_iscyclic': '''
Whether or not a particular axis is cyclic.
.. versionadded:: 1.0
.. seealso:: `axis`, `cyclic`, `period`''',
# ----------------------------------------------------------------
'def_measure': '''
Return {a} item of the domain, or its domain identifier.
{+item_selection}
If no unique item can be found then `None` is returned.
To find multiple items, use the `{+name}s` method.
Note that ``f.{+name}(inverse=False, **kwargs)`` is equivalent to
``f.item(role='m', inverse=False, **kwargs)``.
.. seealso:: `aux`, `coord`, `dim`, `item`, `measures`, `ref`,
`remove_item`
:Examples 1:
An area item could potentially be selected with any of:
>>> c = f.{+name}('area')
>>> c = f.{+name}('units:metre2')
>>> c = f.{+name}('msr1')''',
# ----------------------------------------------------------------
'def_ref': '''
Return {a} item of the domain, or its domain identifier.
{+item_selection}
If no unique item can be found then `None` is returned.
To find multiple items, use the `{+name}s` method.
Note that ``f.{+name}(inverse=False, **kwargs)`` is equivalent to
``f.item(role='r', inverse=False, **kwargs)``.
.. seealso:: `aux`, `measure`, `coord`, `refs`, `dim`, `item`,
`remove_item`
:Examples 1:
A rotated latitude longitude item could be selected with:
>>> c = f.{+name}('rotated_latitude_longitude')''',
# ----------------------------------------------------------------
}
p = re_compile('(?<=.)([A-Z])') # E.g. DimensionCoordinate or Variable
one = re_compile('(\[\+N\].*\n|\[\+1\])')
many = re_compile('(\[\+1\].*\n|\[\+N\])')
zero = re_compile('\[\+0\]')
#first_char = re.compile('^(\s|\n)*(.)')
fef = re_compile('{\+.*?[Ff]ef,?}(.)')
fefx = re_compile('{\+(.*?)([Ff])ef,?}(.)')
def _replacement(match):
'''Used in a `re.sub` for for replacing:
'{+Fef,}X' with 'For each field, x'
and
'{+,fef,}' with ', for each field,'
'''
comma = match.group(1)
if comma:
comma += ' '
return comma+match.group(2)+"or each field, "+match.group(3).lower()
def _replacement0(match):
'''Used in a `re.sub` for for replacing:
'{+Fef,}X' with 'For each field, X'
and
'{+,fef,}' with ', for each field,'
'''
return match.group(1)
def _update_docstring(name, f, attr_name):
'''
'''
doc = f.__doc__
if doc is None:
return
name_lc = p.sub(r' \1', name).lower()
doc = doc.replace('{+name}' , attr_name)
doc = doc.replace('{+Variable}', name)
doc = doc.replace('{+variable}', name_lc)
kwargs = {}
for arg in set(re_findall('{\+.*?}', doc)):
if arg in ('{+Fef,}', '{+,fef,}', '{+fef}'):
continue
ds = docstring[arg].replace('{+name}', attr_name)
ds = ds.replace('{+Variable}', name)
ds = ds.replace('{+variable}', name_lc)
doc = doc.replace(arg, ds)
if name == 'FieldList':
doc = many.sub('', doc)
doc = zero.sub('[0]', doc)
doc = fefx.sub(_replacement, doc)
else:
doc = one.sub('', doc)
doc = zero.sub('', doc)
doc = fef.sub(_replacement0, doc)
f.__doc__ = doc
#--- End: def
class RewriteDocstringMeta(type):
'''Modify docstrings.
To do this, we intercede before the class is created and modify the
docstrings of its attributes.
This will not affect inherited methods, however, so we also need to
loop through the parent classes. We cannot simply modify the
docstrings, because then the parent classes' methods will have the
wrong docstring. Instead, we must actually copy the functions, and
then modify the docstring.
'''
# http://www.jesshamrick.com/2013/04/17/rewriting-python-docstrings-with-a-metaclass/
def __new__(cls, name, parents, attrs):
for attr_name in attrs:
# skip special methods
if attr_name.startswith('__'):
continue
# skip non-functions
attr = attrs[attr_name]
if not hasattr(attr, '__call__'):
continue
if not hasattr(attr, 'func_doc'):
continue
# update docstring
_update_docstring(name, attr, attr_name)
for parent in parents:
for attr_name in dir(parent):
# we already have this method
if attr_name in attrs:
continue
# skip special methods
if attr_name.startswith('__'):
continue
# get the original function and copy it
a = getattr(parent, attr_name)
# skip non-functions
if not hasattr(a, '__call__'):
continue
f = getattr(a, '__func__', None)
if f is None:
continue
# copy function
attr = type(f)(
f.func_code, f.func_globals, f.func_name,
f.func_defaults, f.func_closure)
doc = f.__doc__
# update docstring and add attr
_update_docstring(name, attr, attr_name)
attrs[attr_name] = attr
# create the class
obj = super(RewriteDocstringMeta, cls).__new__(
cls, name, parents, attrs)
return obj
#--- End: class
# ====================================================================
#
# Variable object
#
# ====================================================================
[docs]class Variable(object):
'''
Base class for storing a data array with metadata.
A variable contains a data array and metadata comprising properties to
describe the physical nature of the data.
All components of a variable are optional.
'''
__metaclass__ = RewriteDocstringMeta
# Do not ever change this:
_list = False
# Define the reserved attributes. These are methods which can't be
# overwritten, as well as a few attributes.
_reserved_attrs = ('_reserved_attrs',
'_insert_data'
'_set_Data_attributes',
'binary_mask',
'chunk',
'clip',
'copy',
'cos',
'delprop',
'dump',
'equals',
'expand_dims',
'flip',
'getprop',
'hasprop',
'identity',
'match',
'name',
'override_units',
'select',
'setprop',
'sin',
'subspace',
'transpose',
'where',
)
_special_properties = set(('units', 'calendar',
'_FillValue', 'missing_value'))
def __init__(self, properties={}, attributes=None, data=None, copy=True):
'''
**Initialization**
:Parameters:
properties : dict, optional
Initialize a new instance with CF properties from the
dictionary's key/value pairs.
attributes : dict, optional
Provide the new instance with attributes from the dictionary's
key/value pairs.
data : cf.Data, optional
Provide the new instance with an N-dimensional data array.
copy : bool, optional
If False then do not deep copy arguments prior to
initialization. By default arguments are deep copied.
'''
self._fill_value = None
# _hasbounds is True if and only if there are cell bounds.
self._hasbounds = False
# True if and only if there is a data array stored in
# self.Data
self._hasData = False
# Initialize the _private dictionary with an empty Units
# object
self._private = {'special_attributes': {},
'simple_properties' : {},
}
setprop = self.setprop
if copy:
for prop, value in properties.iteritems():
setprop(prop, deepcopy(value))
if attributes:
for attr, value in attributes.iteritems():
setattr(self, attr, deepcopy(value))
# self.__dict__.update(deepcopy(attributes))
else:
for prop, value in properties.iteritems():
setprop(prop, value)
if attributes:
# self.__dict__.update(attributes)
for attr, value in attributes.iteritems():
setattr(self, attr, value)
#--- End: if
if data is not None:
self.insert_data(data, copy=copy)
else:
# _hasData is True if and only if there is a data array
# stored in self.Data
self._hasData = False
#--- End: def
def __array__(self, *dtype):
'''
'''
if self._hasData:
return self.data.__array__(*dtype)
raise ValueError("%s has no numpy.ndarray interface'" %
self.__class__.__name__)
#--- End: def
def __contains__(self, value):
'''
Called to implement membership test operators.
x.__contains__(y) <==> y in x
'''
return self.equals(value)
#--- End: def
def __data__(self):
'''
Returns a new reference to self.data.
'''
if self._hasData:
return self.data
raise ValueError(
"{0} has no Data interface".format(self.__class__.__name__))
#--- End: def
def __delattr__(self, attr):
'''
x.__delattr__(attr) <==> del x.attr
'''
if attr in self._reserved_attrs:
raise AttributeError("Can't delete reserved attribute %r" % attr)
super(Variable, self).__delattr__(attr)
#--- End: def
def __getitem__(self, index):
'''
Called to implement evaluation of x[index].
x.__getitem__(index) <==> x[index]
Not to be confused with `subspace`.
'''
# if ((isinstance(index, slice) and len((None,)[index]) != 1) or
# index not in (0, -1)):
# raise IndexError("%s index out of range: %s" %
# (self.__class__.__name__, index))
if index != 0 and index != -1:
raise IndexError("%s index out of range: %s" %
(self.__class__.__name__, index))
return self
#--- End: def
def __len__(self):
'''
Called by the :py:obj:`len` built-in function.
x.__len__() <==> len(x)
Always returns 1.
:Examples:
>>> len(f)
1
'''
return 1
#--- End: def
def __deepcopy__(self, memo):
'''
Called by the :py:obj:`copy.deepcopy` standard library function.
'''
return self.copy()
#--- End: def
def __add__(self, y):
'''
The binary arithmetic operation ``+``
x.__add__(y) <==> x+y
'''
return self._binary_operation(y, '__add__')
#--- End: def
def __iadd__(self, y):
'''
The augmented arithmetic assignment ``+=``
x.__iadd__(y) <==> x+=y
'''
return self._binary_operation(y, '__iadd__')
#--- End: def
def __radd__(self, y):
'''
The binary arithmetic operation ``+`` with reflected operands
x.__radd__(y) <==> y+x
'''
return self._binary_operation(y, '__radd__')
#--- End: def
def __sub__(self, y):
'''
The binary arithmetic operation ``-``
x.__sub__(y) <==> x-y
'''
return self._binary_operation(y, '__sub__')
#--- End: def
def __isub__(self, y):
'''
The augmented arithmetic assignment ``-=``
x.__isub__(y) <==> x-=y
'''
return self._binary_operation(y, '__isub__')
#--- End: def
def __rsub__(self, y):
'''
The binary arithmetic operation ``-`` with reflected operands
x.__rsub__(y) <==> y-x
'''
return self._binary_operation(y, '__rsub__')
#--- End: def
def __mul__(self, y):
'''
The binary arithmetic operation ``*``
x.__mul__(y) <==> x*y
'''
return self._binary_operation(y, '__mul__')
#--- End: def
def __imul__(self, y):
'''
The augmented arithmetic assignment ``*=``
x.__imul__(y) <==> x*=y
'''
return self._binary_operation(y, '__imul__')
#--- End: def
def __rmul__(self, y):
'''
The binary arithmetic operation ``*`` with reflected operands
x.__rmul__(y) <==> y*x
'''
return self._binary_operation(y, '__rmul__')
#--- End: def
def __div__(self, y):
'''
The binary arithmetic operation ``/``
x.__div__(y) <==> x/y
'''
return self._binary_operation(y, '__div__')
#--- End: def
def __idiv__(self, y):
'''
The augmented arithmetic assignment ``/=``
x.__idiv__(y) <==> x/=y
'''
return self._binary_operation(y, '__idiv__')
#--- End: def
def __rdiv__(self, y):
'''
The binary arithmetic operation ``/`` with reflected operands
x.__rdiv__(y) <==> y/x
'''
return self._binary_operation(y, '__rdiv__')
#--- End: def
def __floordiv__(self, y):
'''
The binary arithmetic operation ``//``
x.__floordiv__(y) <==> x//y
'''
return self._binary_operation(y, '__floordiv__')
#--- End: def
def __ifloordiv__(self, y):
'''
The augmented arithmetic assignment ``//=``
x.__ifloordiv__(y) <==> x//=y
'''
return self._binary_operation(y, '__ifloordiv__')
#--- End: def
def __rfloordiv__(self, y):
'''
The binary arithmetic operation ``//`` with reflected operands
x.__rfloordiv__(y) <==> y//x
'''
return self._binary_operation(y, '__rfloordiv__')
#--- End: def
def __truediv__(self, y):
'''
The binary arithmetic operation ``/`` (true division)
x.__truediv__(y) <==> x/y
'''
return self._binary_operation(y, '__truediv__')
#--- End: def
def __itruediv__(self, y):
'''
The augmented arithmetic assignment ``/=`` (true division)
x.__itruediv__(y) <==> x/=y
'''
return self._binary_operation(y, '__itruediv__')
#--- End: def
def __rtruediv__(self, y):
'''
The binary arithmetic operation ``/`` (true division) with reflected
operands
x.__rtruediv__(y) <==> y/x
'''
return self._binary_operation(y, '__rtruediv__')
#--- End: def
def __pow__(self, y, modulo=None):
'''
The binary arithmetic operations ``**`` and ``pow``
x.__pow__(y) <==> x**y
'''
if modulo is not None:
raise NotImplementedError("3-argument power not supported for %r" %
self.__class__.__name__)
return self._binary_operation(y, '__pow__')
#--- End: def
def __ipow__(self, y, modulo=None):
'''
The augmented arithmetic assignment ``**=``
x.__ipow__(y) <==> x**=y
'''
if modulo is not None:
raise NotImplementedError("3-argument power not supported for %r" %
self.__class__.__name__)
return self._binary_operation(y, '__ipow__')
#--- End: def
def __rpow__(self, y, modulo=None):
'''
The binary arithmetic operations ``**`` and ``pow`` with reflected
operands
x.__rpow__(y) <==> y**x
'''
if modulo is not None:
raise NotImplementedError("3-argument power not supported for %r" %
self.__class__.__name__)
return self._binary_operation(y, '__rpow__')
#--- End: def
def __mod__(self, y):
'''
The binary arithmetic operation ``%``
x.__mod__(y) <==> x % y
.. versionadded:: 1.0
'''
return self._binary_operation(y, '__mod__')
#--- End: def
def __imod__(self, y):
'''
The binary arithmetic operation ``%=``
x.__imod__(y) <==> x %= y
.. versionadded:: 1.0
'''
return self._binary_operation(y, '__imod__')
#--- End: def
def __rmod__(self, y):
'''
The binary arithmetic operation ``%`` with reflected operands
x.__rmod__(y) <==> y % x
.. versionadded:: 1.0
'''
return self._binary_operation(y, '__rmod__')
#--- End: def
def __eq__(self, y):
'''
The rich comparison operator ``==``
x.__eq__(y) <==> x==y
'''
return self._binary_operation(y, '__eq__')
#--- End: def
def __ne__(self, y):
'''
The rich comparison operator ``!=``
x.__ne__(y) <==> x!=y
'''
return self._binary_operation(y, '__ne__')
#--- End: def
def __ge__(self, y):
'''
The rich comparison operator ``>=``
x.__ge__(y) <==> x>=y
'''
return self._binary_operation(y, '__ge__')
#--- End: def
def __gt__(self, y):
'''
The rich comparison operator ``>``
x.__gt__(y) <==> x>y
'''
return self._binary_operation(y, '__gt__')
#--- End: def
def __le__(self, y):
'''
The rich comparison operator ``<=``
x.__le__(y) <==> x<=y
'''
return self._binary_operation(y, '__le__')
#--- End: def
def __lt__(self, y):
'''
The rich comparison operator ``<``
x.__lt__(y) <==> x<y
'''
return self._binary_operation(y, '__lt__')
#--- End: def
def __and__(self, y):
'''
The binary bitwise operation ``&``
x.__and__(y) <==> x&y
'''
return self._binary_operation(y, '__and__')
#--- End: def
def __iand__(self, y):
'''
The augmented bitwise assignment ``&=``
x.__iand__(y) <==> x&=y
'''
return self._binary_operation(y, '__iand__')
#--- End: def
def __rand__(self, y):
'''
The binary bitwise operation ``&`` with reflected operands
x.__rand__(y) <==> y&x
'''
return self._binary_operation(y, '__rand__')
#--- End: def
def __or__(self, y):
'''
The binary bitwise operation ``|``
x.__or__(y) <==> x|y
'''
return self._binary_operation(y, '__or__')
#--- End: def
def __ior__(self, y):
'''
The augmented bitwise assignment ``|=``
x.__ior__(y) <==> x|=y
'''
return self._binary_operation(y, '__ior__')
#--- End: def
def __ror__(self, y):
'''
The binary bitwise operation ``|`` with reflected operands
x.__ror__(y) <==> y|x
'''
return self._binary_operation(y, '__ror__')
#--- End: def
def __xor__(self, y):
'''
The binary bitwise operation ``^``
x.__xor__(y) <==> x^y
'''
return self._binary_operation(y, '__xor__')
#--- End: def
def __ixor__(self, y):
'''
The augmented bitwise assignment ``^=``
x.__ixor__(y) <==> x^=y
'''
return self._binary_operation(y, '__ixor__')
#--- End: def
def __rxor__(self, y):
'''
The binary bitwise operation ``^`` with reflected operands
x.__rxor__(y) <==> y^x
'''
return self._binary_operation(y, '__rxor__')
#--- End: def
def __lshift__(self, y):
'''
The binary bitwise operation ``<<``
x.__lshift__(y) <==> x<<y
'''
return self._binary_operation(y, '__lshift__')
#--- End: def
def __ilshift__(self, y):
'''
The augmented bitwise assignment ``<<=``
x.__ilshift__(y) <==> x<<=y
'''
return self._binary_operation(y, '__ilshift__')
#--- End: def
def __rlshift__(self, y):
'''
The binary bitwise operation ``<<`` with reflected operands
x.__rlshift__(y) <==> y<<x
'''
return self._binary_operation(y, '__rlshift__')
#--- End: def
def __rshift__(self, y):
'''
The binary bitwise operation ``>>``
x.__lshift__(y) <==> x>>y
'''
return self._binary_operation(y, '__rshift__')
#--- End: def
def __irshift__(self, y):
'''
The augmented bitwise assignment ``>>=``
x.__irshift__(y) <==> x>>=y
'''
return self._binary_operation(y, '__irshift__')
#--- End: def
def __rrshift__(self, y):
'''
The binary bitwise operation ``>>`` with reflected operands
x.__rrshift__(y) <==> y>>x
'''
return self._binary_operation(y, '__rrshift__')
#--- End: def
def __abs__(self):
'''
The unary arithmetic operation ``abs``
x.__abs__() <==> abs(x)
'''
return self._unary_operation('__abs__')
#--- End: def
def __neg__(self):
'''
The unary arithmetic operation ``-``
x.__neg__() <==> -x
'''
return self._unary_operation('__neg__')
#--- End: def
def __invert__(self):
'''
The unary bitwise operation ``~``
x.__invert__() <==> ~x
'''
return self._unary_operation('__invert__')
#--- End: def
def __pos__(self):
'''
The unary arithmetic operation ``+``
x.__pos__() <==> +x
'''
return self._unary_operation('__pos__')
#--- End: def
def __repr__(self):
'''
Called by the :py:obj:`repr` built-in function.
x.__repr__() <==> repr(x)
'''
name = self.name('')
if self._hasData:
dims = ', '.join([str(x) for x in self.shape])
else:
dims = []
dims = '(%s)' % dims
# Units
if self.Units._calendar:
units = self.Units._calendar
else:
units = getattr(self, 'units', '')
return '<CF %s: %s%s %s>' % (self.__class__.__name__,
self.name(''), dims, units)
#--- End: def
def __str__(self):
'''
Called by the :py:obj:`str` built-in function.
x.__str__() <==> str(x)
'''
return self.__repr__()
#--- End: def
# ================================================================
# Private methods
# ================================================================
def _binary_operation(self, y, method):
'''
Implement binary arithmetic and comparison operations on the
{+variable}'s Data with the numpy broadcasting rules. It is intended
to be called by the binary arithmetic and comparison methods, such as
`!__sub__` and `!__lt__`.
:Parameters:
operation : str
The binary arithmetic or comparison method name (such as
``'__imul__'`` or ``'__ge__'``).
:Returns:
new : cf.+{Variable}
A new {+variable}, or the same {+variable} if the operation
was in-place.
:Examples:
>>> u = cf.{+Variable}(data=cf.Data([0, 1, 2, 3]))
>>> v = cf.{+Variable}(data=cf.Data([1, 1, 3, 4]))
>>> w = u._binary_operation(u, '__add__')
>>> print w.array
[1 2 5 7]
>>> w = u._binary_operation(v, '__lt__')
>>> print w.array
[ True False True True]
>>> u._binary_operation(2, '__imul__')
>>> print u.array
[0 2 4 6]
'''
if not self._hasData:
raise ValueError(
"Can't apply operator %s to a %s with no Data" %
(method, self.__class__.__name__))
inplace = method[2] == 'i'
xsn = self.getprop('standard_name', None)
ysn = getattr(y, 'standard_name', None)
x_Units = self.Units
y_Units = getattr(y, 'Units', _units_None)
if isinstance(y, self.__class__):
y = y.Data
if not inplace:
new = self.copy(_omit_Data=True)
new.Data = self.Data._binary_operation(y, method)
#if not new.Data.Units.equivalent(original_units):
# # this is coarse!
# new.delprop('standard_name')
#
# if hasattr(new, 'history'):
# history = [new.getprop('history')]
# else:
# history = []
#
# history.append(new.getprop('standard_name'))
# history.append(method)
#
# new.setprop('history', ' '.join(history))
##--- End: if
# return new
else:
new = self
new.Data._binary_operation(y, method)
#--- End: def
new_Units = new.Data.Units
if (not (new_Units.equivalent(x_Units) or
new_Units.equivalent(y_Units)) or
xsn is not None and ysn is not None and xsn != ysn):
try:
new.delprop('standard_name')
except AttributeError:
pass
try:
new.delprop('long_name')
except AttributeError:
pass
try:
del new.ncvar
except AttributeError:
pass
try:
del new.id
except AttributeError:
pass
#--- End: if
return new
#--- End: def
def _change_axis_names(self, dim_name_map):
'''Change the axis names of the Data object.
:Parameters:
dim_name_map : dict
:Returns:
None
:Examples:
>>> f._change_axis_names({'0': 'dim1', '1': 'dim2'})
'''
if self._hasData:
self.Data.change_axis_names(dim_name_map)
#--- End: def
def _conform_for_assignment(self, other):
return other
def _del_special_attr(self, attr):
'''
'''
d = self._private['special_attributes']
if attr in d:
del d[attr]
return
raise AttributeError("Can't delete non-existent %s attribute %r" %
(self.__class__.__name__, attr))
#--- End: def
def _dump_simple_properties(self, omit=(), _level=0):
'''
:Parameters:
omit : sequence of strs, optional
Omit the given CF properties from the description.
_level : int, optional
:Returns:
out : str
:Examples:
'''
indent1 = ' ' * _level
string = []
# Simple properties
simple = self._simple_properties()
attrs = sorted(set(simple) - set(omit))
for attr in attrs:
name = '%s%s = ' % (indent1, attr)
value = repr(simple[attr])
indent = ' ' * (len(name))
if value.startswith("'") or value.startswith('"'):
indent = '%(indent)s ' % locals()
string.append(textwrap_fill(name+value, 79,
subsequent_indent=indent))
#--- End: for
return '\n'.join(string)
#--- End: def
def _equivalent_data(self, other, atol=None, rtol=None, copy=True):
'''
:Parameters:
transpose : dict, optional
{+atol}
{+rtol}
copy : bool, optional
If False then the *other* coordinate construct might get
change in place.
:Returns:
out : bool
Whether or not the two variables have equivalent data arrays.
'''
if self._hasData != other._hasData:
# add traceback
return False
if not self._hasData:
return True
data0 = self.data
data1 = other.data
units = data0.Units
if not units.equivalent(data1.Units):
# add traceback
return
if data0._shape != data1.shape:
# add traceback
return False
if not units.equals(data1.Units):
if copy:
data1 = data1.copy()
copy = False
data1.Units = units
#--- End: if
if not data0.equals(data1, rtol=rtol, atol=atol, ignore_fill_value=True):
# add traceback
return False
return True
#--- End: def
def _get_special_attr(self, attr):
'''
'''
d = self._private['special_attributes']
if attr in d:
return d[attr]
raise AttributeError("%s doesn't have attribute %r" %
(self.__class__.__name__, attr))
#--- End: def
def _list_attribute(self, attr):
return type(self)([getattr(f, attr) for f in self._list])
#--- End: def
def _list_method(self, method, kwargs={}):
if 'i' in kwargs and kwargs['i']:
# In-place
for f in self:
getattr(f, method)(**kwargs)
return self
else:
# New instance
return type(self)([getattr(f, method)(**kwargs) for f in self])
#--- End: def
def _parameters(self, d):
del d['self']
if 'kwargs' in d:
d.update(d.pop('kwargs'))
return d
#--- End: def
def _parse_match(self, match):
'''Called by `match`
:Parameters:
match :
As for the *match* parameter of `match` method.
:Returns:
out : list
'''
if not match:
return ()
if isinstance(match, (basestring, dict, Query)):
match = (match,)
matches = []
for m in match:
if isinstance(m, basestring):
if ':' in m:
# CF property (string-valued)
m = m.split(':')
matches.append({m[0]: ':'.join(m[1:])})
else:
# Identity (string-valued) or python attribute
# (string-valued) or axis type
matches.append({None: m})
elif isinstance(m, dict):
# Dictionary
matches.append(m)
else:
# Identity (not string-valued, e.g. cf.Query).
matches.append({None: m})
#--- End: for
return matches
#--- End: def
def _query_set(self, values, exact=True):
'''
'''
kwargs2 = self._parameters(locals())
if self._list:
return self._list_method('_query_set', kwargs2)
new = self.copy(_omit_Data=True)
new.Data = self.Data._query_set(**kwargs2)
return new
#--- End: def
def _query_contain(self, value):
'''
'''
kwargs2 = self._parameters(locals())
if self._list:
return self._list_method('_query_contain', kwargs2)
new = self.copy(_omit_Data=True)
new.Data = self.Data._query_contain(**kwargs2)
return new
#--- End: def
def _query_wi(self, value0, value1):
'''
'''
kwargs2 = self._parameters(locals())
if self._list:
return self._list_method('_query_wi', kwargs2)
new = self.copy(_omit_Data=True)
new.Data = self.Data._query_wi(value0, value1)
return new
#--- End: def
def _query_wo(self, value0, value1):
'''
'''
kwargs2 = self._parameters(locals())
if self._list:
return self._list_method('_query_wo', kwargs2)
new = self.copy(_omit_Data=True)
new.Data = self.Data._query_wo(value0, value1)
return new
#--- End: def
def _simple_properties(self):
return self._private['simple_properties']
#--- End: def
def _set_special_attr(self, attr, value):
self._private['special_attributes'][attr] = value
#--- End: def
def _unary_operation(self, method):
'''
Implement unary arithmetic operations on the data array.
:Parameters:
method : str
The unary arithmetic method name (such as "__abs__").
:Returns:
new : Variable
A new Variable.
:Examples:
>>> print v.array
[1 2 -3 -4 -5]
>>> w = v._unary_operation('__abs__')
>>> print w.array
[1 2 3 4 5]
>>> w = v.__abs__()
>>> print w.array
[1 2 3 4 5]
>>> w = abs(v)
>>> print w.array
[1 2 3 4 5]
'''
if not self._hasData:
raise ValueError(
"Can't apply operator %s to a %s with no Data" %
(method, self.__class__.__name__))
new = self.copy(_omit_Data=True)
new.Data = self.Data._unary_operation(method)
return new
#--- End: def
def _YMDhms(self, attr):
'''
'''
if self._hasData:
out = self.copy(_omit_Data=True)
out.insert_data(getattr(self.data, attr), copy=False)
try:
del out.standard_name
except AttributeError:
pass
out.long_name = attr
return out
#--- End: if
raise ValueError(
"ERROR: Can't get {0}s when there is no data array".format(attr))
#--- End: def
def _hmmm(self, method):
if self._hasData:
out = self.copy(_omit_Data=True)
out.insert_data(getattr(self.data, method)(), copy=False)
try:
del out.standard_name
except AttributeError:
pass
out.long_name = method
return out
#--- End: if
raise ValueError(
"ERROR: Can't get {0} when there is no data array".format(method))
#--- End: def
def _forbidden(self, x, name):
raise AttributeError(
"{0} has no {1} {2!r}.".format(self.__class__.__name__, x, name))
#--- End: def
# ================================================================
# Forbidden methods
# ================================================================
def append(self, *args, **kwargs): self._forbidden('method', 'append')
# def count(self, *args, **kwargs): self._forbidden('method', 'count')
def extend(self, *args, **kwargs): self._forbidden('method', 'extend')
def index(self, *args, **kwargs): self._forbidden('method', 'index')
def insert(self, *args, **kwargs): self._forbidden('method', 'insert')
def pop(self, *args, **kwargs): self._forbidden('method', 'pop')
def remove(self, *args, **kwargs): self._forbidden('method', 'remove')
# ================================================================
# Attributes
# ================================================================
@property
def Data(self):
'''
The `cf.Data` object containing the data array.
The use of this attribute does not guarantee that any missing data
value that has been set is passed on to the `cf.Data` object. Use the
`data` attribute to ensure that this is the case.
:Examples:
>>> f.Data
<CF Data: >
'''
if self._hasData:
return self._private['Data']
raise AttributeError("%s doesn't have attribute 'Data'" %
self.__class__.__name__)
#--- End: def
@Data.setter
def Data(self, value):
private = self._private
private['Data'] = value
# Delete Units from the variable
private['special_attributes'].pop('Units', None)
self._hasData = True
#--- End: def
@Data.deleter
def Data(self):
private = self._private
data = private.pop('Data', None)
if data is None:
raise AttributeError("Can't delete non-existent %s attribute 'Data'" %
self.__class__.__name__)
# Save the Units to the variable
private['special_attributes']['Units'] = data.Units
self._hasData = False
#--- End: def
# ----------------------------------------------------------------
# Attribute
# ----------------------------------------------------------------
@property
def data(self):
'''
The `cf.Data` object containing the data array.
.. seealso:: `array`, `cf.Data`, `hasdata`, `varray`
:Examples:
>>> f.hasdata
True
>>> f.data
<CF Data: [[267.3, ..., 234.5]] K>
'''
if self._hasData:
data = self.Data
data.fill_value = self._fill_value
return data
raise AttributeError("%s object doesn't have attribute 'data'" %
self.__class__.__name__)
#--- End: def
@data.setter
def data(self, value):
self.Data = value
@data.deleter
def data(self):
del self.Data
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def hasbounds(self):
'''
True if there are cell bounds.
If present, cell bounds are stored in the `!bounds` attribute.
:Examples:
>>> if c.hasbounds:
... b = c.bounds
'''
return self._hasbounds
#--- End: def
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def hasdata(self):
'''
True if there is a data array.
If present, the data array is stored in the `data` attribute.
.. seealso:: `data`, `hasbounds`
:Examples:
>>> if f.hasdata:
... print f.data
'''
return self._hasData
#--- End: def
# ----------------------------------------------------------------
# Attribute
# ----------------------------------------------------------------
@property
def Units(self):
'''The `cf.Units` object containing the units of the data array.
Stores the units and calendar CF properties in an internally
consistent manner. These are mirrored by the `units` and `calendar` CF
properties respectively.
:Examples:
>>> f.Units
<CF Units: K>
>>> f.Units
<CF Units: days since 2014-1-1 calendar=noleap>
'''
if self._hasData:
return self.Data.Units
try:
return self._get_special_attr('Units')
except AttributeError:
self._set_special_attr('Units', _units_None)
return _units_None
#--- End: def
@Units.setter
def Units(self, value):
if self._hasData:
self.Data.Units = value
else:
self._set_special_attr('Units', value)
#--- End: def
@Units.deleter
def Units(self):
raise AttributeError(
"Can't delete %s attribute 'Units'. Use the override_units method." %
self.__class__.__name__)
@property
def year(self):
'''
The year of each date-time data array element.
Only applicable to data arrays with reference time units.
.. seealso:: `month`, `day`, `hour`, `minunte`, second`
:Examples:
>>> f.dtarray
[ 450-11-15 00:00:00 450-12-16 12:30:00 451-01-16 12:00:45]
>>> f.year.array
[450 450 451]
'''
return self._YMDhms('year')
#--- End: def
@property
def month(self):
'''
The month of each date-time data array element.
Only applicable to data arrays with reference time units.
.. seealso:: `year`, `day`, `hour`, `minunte`, second`
:Examples:
>>> f.dtarray
[ 450-11-15 00:00:00 450-12-16 12:30:00 451-01-16 12:00:45]
>>> f.month.array
[11 12 1]
'''
return self._YMDhms('month')
#--- End: def
@property
def day(self):
'''
The day of each date-time data array element.
Only applicable to data arrays with reference time units.
.. seealso:: `year`, `month`, `hour`, `minute`, second`
:Examples:
>>> f.dtarray
[ 450-11-15 00:00:00 450-12-16 12:30:00 451-01-16 12:00:45]
>>> f.day.array
[15 16 16]
'''
return self._YMDhms('day')
#--- End: def
@property
def hour(self):
'''
The hour of each date-time data array element.
Only applicable to data arrays with reference time units.
.. seealso:: `year`, `month`, `day`, `minute`, second`
:Examples:
>>> f.dtarray
[ 450-11-15 00:00:00 450-12-16 12:30:00 451-01-16 12:00:45]
>>> f.hour.array
[ 0 12 12]
'''
return self._YMDhms('hour')
#--- End: def
@property
def minute(self):
'''
The minute of each date-time data array element.
Only applicable to data arrays with reference time units.
.. seealso:: `year`, `month`, `day`, `hour`, second`
:Examples:
>>> f.dtarray
[ 450-11-15 00:00:00 450-12-16 12:30:00 451-01-16 12:00:45]
>>> f.minute.array
[ 0 30 0]
'''
return self._YMDhms('minute')
#--- End: def
@property
def second(self):
'''
The second of each date-time data array element.
Only applicable to data arrays with reference time units.
.. seealso:: `year`, `month`, `day`, `hour`, `minute`
:Examples:
>>> f.dtarray
[ 450-11-15 00:00:00 450-12-16 12:30:00 451-01-16 12:00:45]
>>> f.second.array
[ 0 0 45]
'''
return self._YMDhms('second')
#--- End: def
[docs] def mask_invalid(self, i=False):
'''{+Fef,}Mask the array where invalid values occur (NaN or inf).
Note that:
* Invalid values in the results of arithmetic operations only occur if
the raising of `FloatingPointError` exceptions has been suppressed
by `cf.Data.seterr`.
* If the raising of `FloatingPointError` exceptions has been allowed
then invalid values in the results of arithmetic operations it is
possible for them to be automatically converted to masked values,
depending on the setting of `cf.Data.mask_fpe`. In this case, such
automatic conversion might be faster than calling `mask_invalid`.
.. seealso:: `cf.Data.mask_fpe`, `cf.Data.seterr`
:Examples 1:
>>> g = f.mask_invalid()
:Parameters:
{+i}
:Returns:
out : cf.{+Variable}
:Examples:
>>> print f[+0].array
[ 0. 1.]
>>> print g[+0].array
[ 1. 2.]
>>> old = cf.Data.seterr('ignore')
>>> h = g/f
>>> print h[+0].array
[ inf 2.]
>>> h.mask_invalid(i=True)
>>> print h[+0].array
[-- 2.]
>>> h = g**12345
>>> print h[+0].array
[ 1. inf]
>>> h = h.mask_invalid()
>>> print h[+0].array
[1. --]
>>> old = cf.Data.seterr('raise')
>>> old = cf.Data.mask_fpe(True)
>>> print (g/f)[+0].array
[ -- 2]
>>> print (g**12345)[+0].array
[1. -- ]
'''
# List functionality
if self._list:
kwargs2 = self._parameters(locals())
return self._list_method('mask_invalid', kwargs2)
if i:
v = self
else:
v = self.copy()
if self._hasData:
v.Data = v.Data.mask_invalid(i=True)
return v
#--- End: def
def max(self):
'''The maximum of the data array.
.. seealso:: `collapse`, `mean`, `mid_range`, `min`, `range`,
`sample_size`, `sd`, `sum`, `var`
:Examples 1:
>>> d = f.{+name}()
:Returns:
out : cf.Data
The maximum of the data array.
:Examples 2:
>>> f.data
<CF Data: [[[236.512756348, ..., 256.93371582]]] K>
>>> f.max().data
<CF Data: 311.343780518 K>
'''
if self._hasData:
return self.data.max(squeeze=True)
raise ValueError(
"ERROR: Can't get the maximum when there is no data array")
#--- End: def
def mean(self):
'''The unweighted mean the data array.
.. seealso:: `collapse`, `max`, `mid_range`, `min`, `range`,
`sample_size`, `sd`, `sum`, `var`
:Examples 1:
>>> d = f.{+name}()
:Returns:
out : cf.Data
The unweighted mean the data array.
:Examples 2:
>>> f.data
<CF Data: [[[236.512756348, ..., 256.93371582]]] K>
>>> f.mean()
<CF Data: 280.192227593 K>
'''
if self._hasData:
return self.data.mean(squeeze=True)
raise ValueError(
"ERROR: Can't get the mean when there is no data array")
#--- End: def
def mid_range(self):
'''The unweighted average of the maximum and minimum of
the data array.
.. seealso:: `collapse`, `max`, `mean`, `min`, `range`,
`sample_size`, `sd`, `sum`, `var`
:Examples 1:
>>> d = f.{+name}()
:Returns:
out : cf.Data
The unweighted average of the maximum and minimum of the
data array.
:Examples 2:
>>> f.data
<CF Data: [[[236.512756348, ..., 256.93371582]]] K>
>>> f.mid_range()
<CF Data: 255.08618927 K>
'''
if self._hasData:
return self.data.mid_range(squeeze=True)
raise ValueError(
"ERROR: Can't get the mid-range when there is no data array")
#--- End: def
def min(self):
'''The minimum of the data array.
.. seealso:: `collapse`, `max`, `mean`, `mid_range`, `range`,
`sample_size`, `sd`, `sum`, `var`
:Examples 1:
>>> d = f.{+name}()
:Returns:
out : cf.Data
The minimum of the data array.
:Examples 2:
>>> f.data
<CF Data: [[[236.512756348, ..., 256.93371582]]] K>
>>> f.min()
<CF Data: 198.828598022 K>
'''
if self._hasData:
return self.data.min(squeeze=True)
raise ValueError(
"ERROR: Can't get the minimum when there is no data array")
#--- End: def
def range(self):
'''
The absolute difference between the maximum and minimum of the data
array.
.. seealso:: `collapse`, `max`, `mean`, `mid_range`, `min`,
`sample_size`, `sd`, `sum`, `var`
:Examples 1:
>>> d = f.{+name}()
:Returns:
out : cf.Data
The absolute difference between the maximum and minimum of the
data array.
:Examples 2:
>>> f.data
<CF Data: [[[236.512756348, ..., 256.93371582]]] K>
>>> f.range()
<CF Data: 112.515182495 K>
'''
if self._hasData:
return self.data.range(squeeze=True)
raise ValueError(
"ERROR: Can't get the range when there is no data array")
#--- End:
def remove_data(self):
'''
Remove and return the data array.
:Returns:
out : cf.Data or None
The removed data array, or `None` if there isn't one.
:Examples:
>>> f._hasData
True
>>> f.data
<CF Data: [0, ..., 9] m>
>>> f.remove_data()
<CF Data: [0, ..., 9] m>
>>> f._hasData
False
>>> print f.remove_data()
None
'''
if not self._hasData:
return
data = self.data
del self.Data
return data
#--- End: def
def sample_size(self):
'''The number of non-missing data elements in the data array.
.. seealso:: `collapse`, `max`, `mean`, `mid_range`, `min`, `range`,
`sd`, `sum`, `var`
:Examples 1:
>>> d = f.{+name}()
:Returns:
out : cf.Data
The number of non-missing data elements in the data array.
:Examples 2:
>>> f.data
<CF Data: [[[236.512756348, ..., 256.93371582]]] K>
>>> f.sample_size()
<CF Data: 98304.0>
'''
if self._hasData:
return self.data.sample_size(squeeze=True)
raise ValueError(
"ERROR: Can't get the sample size when there is no data array")
#--- End: def
def sd(self):
'''The unweighted sample standard deviation of the data array.
.. seealso:: `collapse`, `max`, `mean`, `mid_range`, `min`, `range`,
`sample_size`, `sum`, `var`
:Examples 1:
>>> d = f.{+name}()
:Returns:
out : cf.Data
The unweighted sample standard deviation of the data array.
:Examples 2:
>>> f.data
<CF Data: [[[236.512756348, ..., 256.93371582]]] K>
>>> f.sd()
<CF Data: 22.685052535 K>
'''
if self._hasData:
return self.data.sd(squeeze=True)
raise ValueError(
"ERROR: Can't get the standard deviation when there is no data array")
#--- End: def
def sum(self):
'''The sum of the data array.
.. seealso:: `collapse`, `max`, `mean`, `mid_range`, `min`, `range`,
`sample_size`, `sd`, `var`
:Examples 1:
>>> d = f.{+name}()
:Returns:
out : cf.Data
The sum of the data array.
:Examples 2:
>>> f.data
<CF Data: [[[236.512756348, ..., 256.93371582]]] K>
>>> f.sum()
<CF Data: 27544016.7413 K>
'''
if self._hasData:
return self.data.sum(squeeze=True)
raise ValueError(
"ERROR: Can't get the sum when there is no data array")
#--- End: def
def var(self):
'''The unweighted sample variance of the data array.
.. seealso:: `collapse`, `max`, `mean`, `mid_range`, `min`, `range`,
`sample_size`, `sd`, `sum`
:Examples 1:
>>> d = f.{+name}()
:Returns:
out : cf.Data
The unweighted sample variance of the data array.
:Examples 2:
>>> f.data
<CF Data: [[[236.512756348, ..., 256.93371582]]] K>
>>> f.var()
<CF Data: 514.611608515 K2>
'''
if self._hasData:
return self.data.var(squeeze=True)
raise ValueError(
"ERROR: Can't get the variance when there is no data array")
#--- End: def
# ----------------------------------------------------------------
# Attribute: ancillary_variables
# ----------------------------------------------------------------
@property
def ancillary_variables(self):
'''An object containing one or more CF ancillary variables.
:Examples:
>>> f.ancillary_variables
[<CF Field: specific_humidity standard_error(time(12)) g/g>]
'''
return self._get_special_attr('ancillary_variables')
#--- End: def
@ancillary_variables.setter
def ancillary_variables(self, value):
self._set_special_attr('ancillary_variables', value)
@ancillary_variables.deleter
def ancillary_variables(self):
self._del_special_attr('ancillary_variables')
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def T(self):
'''
Always False.
.. seealso:: `X`, `Y`, `Z`
:Examples:
>>> print f.T
False
'''
return False
#--- End: def
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def X(self):
'''
Always False.
.. seealso:: `T`, `Y`, `Z`
:Examples:
>>> print f.X
False
'''
return False
#--- End: def
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def Y(self):
'''
Always False.
.. seealso:: `T`, `X`, `Z`
:Examples:
>>> print f.Y
False
'''
return False
#--- End: def
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def Z(self):
'''
Always False.
.. seealso:: `T`, `X`, `Y`
:Examples:
>>> print f.Z
False
'''
return False
#--- End: def
# ----------------------------------------------------------------
# CF property
# ----------------------------------------------------------------
@property
def add_offset(self):
'''
The add_offset CF property.
This property is only used when writing to a file on disk.
:Examples:
>>> f.add_offset = -4.0
>>> f.add_offset
-4.0
>>> del f.add_offset
>>> f.setprop('add_offset', 10.5)
>>> f.getprop('add_offset')
10.5
>>> f.delprop('add_offset')
'''
return self.getprop('add_offset')
#--- End: def
@add_offset.setter
def add_offset(self, value):
self.setprop('add_offset', value)
self.dtype = numpy_result_type(self.dtype, numpy_array(value).dtype)
#--- End: def
@add_offset.deleter
def add_offset(self):
self.delprop('add_offset')
if not self.hasprop('scale_factor'):
del self.dtype
#--- End: def
# ----------------------------------------------------------------
# CF property: calendar
# ----------------------------------------------------------------
@property
def calendar(self):
'''
The calendar CF property.
:Examples:
>>> f.calendar = 'noleap'
>>> f.calendar
'noleap'
>>> del f.calendar
>>> f.setprop('calendar', 'proleptic_gregorian')
>>> f.getprop('calendar')
'proleptic_gregorian'
>>> f.delprop('calendar')
'''
value = getattr(self.Units, 'calendar', None)
if value is None:
raise AttributeError("%s doesn't have CF property 'calendar'" %
self.__class__.__name__)
return value
#--- End: def
@calendar.setter
def calendar(self, value):
self.Units = Units(getattr(self, 'units', None), value)
#--- End: def
@calendar.deleter
def calendar(self):
if getattr(self, 'calendar', None) is None:
raise AttributeError("Can't delete non-existent %s CF property 'calendar'" %
self.__class__.__name__)
self.Units = Units(getattr(self, 'units', None))
#--- End: def
# ----------------------------------------------------------------
# CF property
# ----------------------------------------------------------------
@property
def comment(self):
'''
The comment CF property.
:Examples:
>>> f.comment = 'This simulation was done on an HP-35 calculator'
>>> f.comment
'This simulation was done on an HP-35 calculator'
>>> del f.comment
>>> f.setprop('comment', 'a comment')
>>> f.getprop('comment')
'a comment'
>>> f.delprop('comment')
'''
return self.getprop('comment')
#--- End: def
@comment.setter
def comment(self, value): self.setprop('comment', value)
@comment.deleter
def comment(self): self.delprop('comment')
# ----------------------------------------------------------------
# CF property
# ----------------------------------------------------------------
@property
def _FillValue(self):
'''
The _FillValue CF property.
Note that this attribute is primarily for writing data to disk and is
independent of the missing data mask. It may, however, get used when
unmasking data array elements.
The recommended way of retrieving the missing data value is with the
`fill_value` method.
.. seealso:: `fill_value`, `missing_value`
:Examples:
>>> f._FillValue = -1.0e30
>>> f._FillValue
-1e+30
>>> del f._FillValue
Mask the data array where it equals a missing data value:
>>> f.setitem(cf.masked, condition=f.fill_value()) DCH
'''
d = self._private['simple_properties']
if '_FillValue' in d:
return d['_FillValue']
raise AttributeError("%s doesn't have CF property '_FillValue'" %
self.__class__.__name__)
#--- End: def
@_FillValue.setter
def _FillValue(self, value):
# self.setprop('_FillValue', value)
self._private['simple_properties']['_FillValue'] = value
self._fill_value = self.getprop('missing_value', value)
#--- End: def
@_FillValue.deleter
def _FillValue(self):
self._private['simple_properties'].pop('_FillValue', None)
self._fill_value = getattr(self, 'missing_value', None)
#--- End: def
# ----------------------------------------------------------------
# CF property
# ----------------------------------------------------------------
@property
def history(self):
'''
The history CF property.
:Examples:
>>> f.history = 'created on 2012/10/01'
>>> f.history
'created on 2012/10/01'
>>> del f.history
>>> f.setprop('history', 'created on 2012/10/01')
>>> f.getprop('history')
'created on 2012/10/01'
>>> f.delprop('history')
'''
return self.getprop('history')
#--- End: def
@history.setter
def history(self, value): self.setprop('history', value)
@history.deleter
def history(self): self.delprop('history')
# ----------------------------------------------------------------
# CF property
# ----------------------------------------------------------------
@property
def leap_month(self):
'''
The leap_month CF property.
:Examples:
>>> f.leap_month = 2
>>> f.leap_month
2
>>> del f.leap_month
>>> f.setprop('leap_month', 11)
>>> f.getprop('leap_month')
11
>>> f.delprop('leap_month')
'''
return self.getprop('leap_month')
#--- End: def
@leap_month.setter
def leap_month(self, value): self.setprop('leap_month', value)
@leap_month.deleter
def leap_month(self): self.delprop('leap_month')
# ----------------------------------------------------------------
# CF property
# ----------------------------------------------------------------
@property
def leap_year(self):
'''
The leap_year CF property.
:Examples:
>>> f.leap_year = 1984
>>> f.leap_year
1984
>>> del f.leap_year
>>> f.setprop('leap_year', 1984)
>>> f.getprop('leap_year')
1984
>>> f.delprop('leap_year')
'''
return self.getprop('leap_year')
#--- End: def
@leap_year.setter
def leap_year(self, value): self.setprop('leap_year', value)
@leap_year.deleter
def leap_year(self): self.delprop('leap_year')
# ----------------------------------------------------------------
# CF property
# ----------------------------------------------------------------
@property
def long_name(self):
'''
The long_name CF property.
:Examples:
>>> f.long_name = 'zonal_wind'
>>> f.long_name
'zonal_wind'
>>> del f.long_name
>>> f.setprop('long_name', 'surface air temperature')
>>> f.getprop('long_name')
'surface air temperature'
>>> f.delprop('long_name')
'''
return self.getprop('long_name')
#--- End: def
@long_name.setter
def long_name(self, value): self.setprop('long_name', value)
@long_name.deleter
def long_name(self): self.delprop('long_name')
# ----------------------------------------------------------------
# CF property
# ----------------------------------------------------------------
@property
def missing_value(self):
'''
The missing_value CF property.
Note that this attribute is used primarily for writing data to disk
and is independent of the missing data mask. It may, however, be used
when unmasking data array elements.
The recommended way of retrieving the missing data value is with the
`fill_value` method.
.. seealso:: `_FillValue`, `fill_value`
:Examples:
>>> f.missing_value = 1.0e30
>>> f.missing_value
1e+30
>>> del f.missing_value
Mask the data array where it equals a missing data value:
>>> f.setitem(cf.masked, condition=f.fill_value()) DCH
'''
d = self._private['simple_properties']
if 'missing_value' in d:
return d['missing_value']
raise AttributeError("%s doesn't have CF property 'missing_value'" %
self.__class__.__name__)
#--- End: def
@missing_value.setter
def missing_value(self, value):
self._private['simple_properties']['missing_value'] = value
self._fill_value = value
#--- End: def
@missing_value.deleter
def missing_value(self):
self._private['simple_properties'].pop('missing_value', None)
self._fill_value = getattr(self, '_FillValue', None)
#--- End: def
# ----------------------------------------------------------------
# CF property
# ----------------------------------------------------------------
@property
def month_lengths(self):
'''
The month_lengths CF property.
Stored as a tuple but may be set as any array-like object.
:Examples:
>>> f.month_lengths = numpy.array([34, 31, 32, 30, 29, 27, 28, 28, 28, 32, 32, 34])
>>> f.month_lengths
(34, 31, 32, 30, 29, 27, 28, 28, 28, 32, 32, 34)
>>> del f.month_lengths
>>> f.setprop('month_lengths', [34, 31, 32, 30, 29, 27, 28, 28, 28, 32, 32, 34])
>>> f.getprop('month_lengths')
(34, 31, 32, 30, 29, 27, 28, 28, 28, 32, 32, 34)
>>> f.delprop('month_lengths')
'''
return self.getprop('month_lengths')
#--- End: def
@month_lengths.setter
def month_lengths(self, value): self.setprop('month_lengths', tuple(value))
@month_lengths.deleter
def month_lengths(self): self.delprop('month_lengths')
# ----------------------------------------------------------------
# CF property
# ----------------------------------------------------------------
@property
def scale_factor(self):
'''
The scale_factor CF property.
This property is only used when writing to a file on disk.
:Examples:
>>> f.scale_factor = 10.0
>>> f.scale_factor
10.0
>>> del f.scale_factor
>>> f.setprop('scale_factor', 10.0)
>>> f.getprop('scale_factor')
10.0
>>> f.delprop('scale_factor')
'''
return self.getprop('scale_factor')
#--- End: def
@scale_factor.setter
def scale_factor(self, value): self.setprop('scale_factor', value)
@scale_factor.deleter
def scale_factor(self): self.delprop('scale_factor')
# ----------------------------------------------------------------
# CF property
# ----------------------------------------------------------------
@property
def standard_name(self):
'''
The standard_name CF property.
:Examples:
>>> f.standard_name = 'time'
>>> f.standard_name
'time'
>>> del f.standard_name
>>> f.setprop('standard_name', 'time')
>>> f.getprop('standard_name')
'time'
>>> f.delprop('standard_name')
'''
return self.getprop('standard_name')
#--- End: def
@standard_name.setter
def standard_name(self, value): self.setprop('standard_name', value)
@standard_name.deleter
def standard_name(self): self.delprop('standard_name')
# ----------------------------------------------------------------
# CF property
# ----------------------------------------------------------------
@property
def units(self):
'''
The units CF property.
:Examples:
>>> f.units = 'K'
>>> f.units
'K'
>>> del f.units
>>> f.setprop('units', 'm.s-1')
>>> f.getprop('units')
'm.s-1'
>>> f.delprop('units')
'''
value = getattr(self.Units, 'units', None)
if value is None:
raise AttributeError("%s doesn't have CF property 'units'" %
self.__class__.__name__)
return value
#--- End: def
@units.setter
def units(self, value):
self.Units = Units(value, getattr(self, 'calendar', None))
#--- End: def
@units.deleter
def units(self):
if getattr(self, 'units', None) is None:
self.Units = Units(None, getattr(self, 'calendar', None))
#--- End: def
# ----------------------------------------------------------------
# CF property
# ----------------------------------------------------------------
@property
def valid_max(self):
'''
The valid_max CF property.
:Examples:
>>> f.valid_max = 100.0
>>> f.valid_max
100.0
>>> del f.valid_max
>>> f.setprop('valid_max', 100.0)
>>> f.getprop('valid_max')
100.0
>>> f.delprop('valid_max')
'''
return self.getprop('valid_max')
#--- End: def
@valid_max.setter
def valid_max(self, value): self.setprop('valid_max', value)
@valid_max.deleter
def valid_max(self): self.delprop('valid_max')
# ----------------------------------------------------------------
# CF property
# ----------------------------------------------------------------
@property
def valid_min(self):
'''
The valid_min CF property.
:Examples:
>>> f.valid_min = 8.0
>>> f.valid_min
8.0
>>> del f.valid_min
>>> f.setprop('valid_min', 8.0)
>>> f.getprop('valid_min')
8.0
>>> f.delprop('valid_min')
'''
return self.getprop('valid_min')
#--- End: def
@valid_min.setter
def valid_min(self, value): self.setprop('valid_min', value)
@valid_min.deleter
def valid_min(self): self.delprop('valid_min')
# ----------------------------------------------------------------
# CF property
# ----------------------------------------------------------------
@property
def valid_range(self):
'''
The valid_range CF property.
Stored as a tuple but may be set as any array-like object.
:Examples:
>>> f.valid_range = numpy.array([100., 400.])
>>> f.valid_range
(100.0, 400.0)
>>> del f.valid_range
>>> f.setprop('valid_range', [100.0, 400.0])
>>> f.getprop('valid_range')
(100.0, 400.0)
>>> f.delprop('valid_range')
'''
return tuple(self.getprop('valid_range'))
#--- End: def
@valid_range.setter
def valid_range(self, value): self.setprop('valid_range', tuple(value))
@valid_range.deleter
def valid_range(self): self.delprop('valid_range')
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def subspace(self):
'''
Return a new variable whose data is subspaced.
This attribute may be indexed to select a subspace from dimension
index values.
**Subspacing by indexing**
Subspacing by dimension indices uses an extended Python slicing
syntax, which is similar numpy array indexing. There are two
extensions to the numpy indexing functionality:
* Size 1 dimensions are never removed.
An integer index i takes the i-th element but does not reduce the
rank of the output array by one.
* When advanced indexing is used on more than one dimension, the
advanced indices work independently.
When more than one dimension's slice is a 1-d boolean array or 1-d
sequence of integers, then these indices work independently along
each dimension (similar to the way vector subscripts work in
Fortran), rather than by their elements.
:Examples:
'''
return SubspaceVariable(self)
#--- End: def
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def shape(self):
'''
A tuple of the data array's dimension sizes.
.. seealso:: `data`, `hasdata`, `ndim`, `size`
:Examples:
>>> f.shape
(73, 96)
>>> f.ndim
2
>>> f.ndim
0
>>> f.shape
()
>>> f.hasdata
True
>>> len(f.shape) == f.dnim
True
>>> reduce(lambda x, y: x*y, f.shape, 1) == f.size
True
'''
if self._hasData:
return tuple(self.Data.shape)
raise AttributeError("%s doesn't have attribute 'shape'" %
self.__class__.__name__)
#--- End: def
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def ndim(self):
'''
The number of dimensions in the data array.
.. seealso:: `data`, `hasdata`, `isscalar`, `shape`
:Examples:
>>> f.hasdata
True
>>> f.shape
(73, 96)
>>> f.ndim
2
>>> f.shape
()
>>> f.ndim
0
'''
if self._hasData:
return self.Data.ndim
raise AttributeError("%s doesn't have attribute 'ndim'" %
self.__class__.__name__)
#--- End: def
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def size(self):
'''
The number of elements in the data array.
.. seealso:: `data`, `hasdata`, `ndim`, `shape`
:Examples:
>>> f.shape
(73, 96)
>>> f.size
7008
>>> f.shape
()
>>> f.ndim
0
>>> f.size
1
>>> f.shape
(1, 1, 1)
>>> f.ndim
3
>>> f.size
1
>>> f.hasdata
True
>>> f.size == reduce(lambda x, y: x*y, f.shape, 1)
True
'''
if self._hasData:
return self.Data.size
raise AttributeError("%s doesn't have attribute 'size'" %
self.__class__.__name__)
#--- End: def
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def dtarray(self):
'''
An independent numpy array of date-time objects.
Only applicable for reference time units.
If the calendar has not been set then the CF default calendar will be
used and the units will be updated accordingly.
The data type of the data array is unchanged.
.. seealso:: `array`, `asdatetime`, `asreftime`, `dtvarray`, `varray`
:Examples:
'''
if self._hasData:
return self.data.dtarray
raise AttributeError("%s has no data array" % self.__class__.__name__)
#--- End: def
# ----------------------------------------------------------------
# Attribute
# ----------------------------------------------------------------
@property
def dtype(self):
'''
The `numpy` data type of the data array.
By default this is the data type with the smallest size and smallest
scalar kind to which all sub-arrays of the master data array may be
safely cast without loss of information. For example, if the
sub-arrays have data types 'int64' and 'float32' then the master data
array's data type will be 'float64'; or if the sub-arrays have data
types 'int64' and 'int32' then the master data array's data type will
be 'int64'.
Setting the data type to a `numpy.dtype` object, or any object
convertible to a `numpy.dtype` object, will cause the master data
array elements to be recast to the specified type at the time that
they are next accessed, and not before. This does not immediately
change the master data array elements, so, for example, reinstating
the original data type prior to data access results in no loss of
information.
Deleting the data type forces the default behaviour. Note that if the
data type of any sub-arrays has changed after `dtype` has been set
(which could occur if the data array is accessed) then the reinstated
default data type may be different to the data type prior to `dtype`
being set.
:Examples:
>>> f.dtype
dtype('float64')
>>> type(f.dtype)
<type 'numpy.dtype'>
>>> print f.array
[0.5 1.5 2.5]
>>> import numpy
>>> f.dtype = numpy.dtype(int)
>>> print f.array
[0 1 2]
>>> f.dtype = bool
>>> print f.array
[False True True]
>>> f.dtype = 'float64'
>>> print f.array
[ 0. 1. 1.]
>>> print f.array
[0.5 1.5 2.5]
>>> f.dtype = int
>>> f.dtype = bool
>>> f.dtype = float
>>> print f.array
[ 0.5 1.5 2.5]
'''
if self._hasData:
return self.Data.dtype
raise AttributeError("%s doesn't have attribute 'dtype'" %
self.__class__.__name__)
#--- End: def
@dtype.setter
def dtype(self, value):
# DCH - allow dtype to be set before data c.f. Units
if self._hasData:
self.Data.dtype = value
#--- End: def
@dtype.deleter
def dtype(self):
if self._hasData:
del self.Data.dtype
#--- End: def
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def dtvarray(self):
'''
A numpy array view the data array converted to date-time objects.
Only applicable for reference time units.
If the calendar has not been set then the CF default calendar will be
used and the units will be updated accordingly.
.. seealso:: `array`, `asdatetime`, `asreftime`, `dtarray`, `varray`
:Examples:
'''
if self._hasData:
return self.data.dtvarray
raise AttributeError("%s has no data array" % self.__class__.__name__)
#--- End: def
# ----------------------------------------------------------------
# Attribute (read/write only)
# ----------------------------------------------------------------
@property
def hardmask(self):
'''
Whether the mask is hard (True) or soft (False).
When the mask is hard, masked elements of the data array can not be
unmasked by assignment, but unmasked elements may be still be masked.
When the mask is soft, masked entries of the data array may be
unmasked by assignment and unmasked entries may be masked.
By default, the mask is hard.
.. seealso:: `where`, `subspace`
:Examples:
>>> f.hardmask = False
>>> f.hardmask
False
'''
if self._hasData:
return self.Data.hardmask
raise AttributeError("%s doesn't have attribute 'hardmask'" %
self.__class__.__name__)
#--- End: def
@hardmask.setter
def hardmask(self, value):
if self._hasData:
self.Data.hardmask = value
else:
raise AttributeError("%s doesn't have attribute 'hardmask'" %
self.__class__.__name__)
#--- End: def
@hardmask.deleter
def hardmask(self):
raise AttributeError("Won't delete %s attribute 'hardmask'" %
self.__class__.__name__)
#--- End: def
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def array(self):
'''
A numpy array deep copy of the data array.
Changing the returned numpy array does not change the data array.
.. seealso:: `data`, `dtarray`, `dtvarray`, `varray`
:Examples 1:
>>> f.data
<CF Data: [0, ... 4] kg m-1 s-2>
>>> a = f.array
>>> type(a)
<type 'numpy.ndarray'>
>>> print a
[0 1 2 3 4]
>>> a[0] = 999
>>> print a
[999 1 2 3 4]
>>> print f.array
[0 1 2 3 4]
>>> f.data
<CF Data: [0, ... 4] kg m-1 s-2>
'''
if self._hasData:
return self.data.array
raise AttributeError("%s has no data array" % self.__class__.__name__)
#--- End: def
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def unsafe_array(self):
'''
'''
if self._hasData:
return self.data.unsafe_array
raise AttributeError("%s has no data array" % self.__class__.__name__)
#--- End: def
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def varray(self):
'''
A numpy array view of the data array.
Changing the elements of the returned view changes the data array.
.. seealso:: `array`, `data`, `dtarray`, `dtvarray`
:Examples 1:
>>> f.data
<CF Data: [0, ... 4] kg m-1 s-2>
>>> a = f.array
>>> type(a)
<type 'numpy.ndarray'>
>>> print a
[0 1 2 3 4]
>>> a[0] = 999
>>> print a
[999 1 2 3 4]
>>> print f.array
[999 1 2 3 4]
>>> f.data
<CF Data: [999, ... 4] kg m-1 s-2>
'''
if self._hasData:
return self.data.varray
raise AttributeError("%s has no data array" % self.__class__.__name__)
#--- End: def
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def isauxiliary(self):
'''
True if the variable is an auxiliary coordinate object.
.. seealso:: `ismeasure`, `isdimension`
:Examples:
>>> f.isauxiliary
False
'''
return False
#--- End: def
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def ismeasure(self):
'''True if the variable is a cell measure object.
.. seealso:: `isauxiliary`, `isdimension`
:Examples:
>>> f.ismeasure
False
'''
return False
#--- End: def
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def isdimension(self):
'''True if the variable is a dimension coordinate object.
.. seealso:: `isauxiliary`, `isdimension`
:Examples:
>>> f.isdimension
False
'''
return False
#--- End: def
@property
def isscalar(self):
'''True if the data array is scalar.
.. seealso:: `hasdata`, `ndim`
:Examples:
>>> f.ndim
0
>>> f.isscalar
True
>>> f.ndim >= 1
True
>>> f.isscalar
False
>>> f.hasdata
False
>>> f.isscalar
False
'''
if not self._hasData:
return False
return self.Data.isscalar
#--- End: if
[docs] def ceil(self, i=False):
'''{+Fef,}The ceiling of the data array.
The ceiling of the scalar ``x`` is the smallest integer ``i``, such
that ``i >= x``.
.. versionadded:: 1.0
.. seealso:: `floor`, `rint`, `trunc`
:Examples 1:
Create a new {+variable} with the ceiling of the data:
>>> g = f.ceil()
:Parameters:
{+i}
:Returns:
out : cf.{+Variable}
{+Fef,}The {+variable} with the ceiling of data array values.
:Examples 2:
>>> print f.array
[-1.9 -1.5 -1.1 -1. 0. 1. 1.1 1.5 1.9]
>>> print f.ceil().array
[-1. -1. -1. -1. 0. 1. 2. 2. 2.]
>>> print f.array
[-1.9 -1.5 -1.1 -1. 0. 1. 1.1 1.5 1.9]
>>> print f.ceil(i=True).array
[-1. -1. -1. -1. 0. 1. 2. 2. 2.]
>>> print f.array
[-1. -1. -1. -1. 0. 1. 2. 2. 2.]
'''
# List functionality
if self._list:
kwargs2 = self._parameters(locals())
return self._list_method('ceil', kwargs2)
if not self._hasData:
raise ValueError("Can't ceil %r" % self.__class__.__name__)
if i:
v = self
else:
v = self.copy()
v.Data.ceil(i=True)
return v
#--- End: def
[docs] def chunk(self, chunksize=None):
'''
Partition the data array.
:Parameters:
chunksize : int
:Returns:
None
'''
if self._hasData:
self.Data.chunk(chunksize)
#--- End: def
[docs] def clip(self, a_min, a_max, units=None, i=False):
'''
Clip (limit) the values in the data array in place.
Given an interval, values outside the interval are clipped to the
interval edges.
Parameters :
a_min : scalar
a_max : scalar
units : str or Units
{+i}
:Returns:
None
:Examples:
'''
if i:
v = self
else:
v = self.copy()
if self._hasData:
v.Data.clip( a_min, a_max, units=units, i=True)
return v
#--- End: def
[docs] def close(self):
'''
Close all files referenced by the variable.
Note that a closed file will be automatically reopened if its contents
are subsequently required.
:Returns:
None
:Examples:
>>> v.close()
'''
# List functionality
if self._list:
for f in self:
f.close()
return
if self._hasData:
self.Data.close()
#--- End: def
@classmethod
def concatenate(cls, variables, axis=0, _preserve=True):
'''
Join a sequence of variables together.
:Parameters:
variables : sequence of cf.{+Variable}
axis : int, optional
_preserve : bool, optional
:Returns:
out : cf.{+Variable}
'''
variable0 = variables[0]
if len(variables) == 1:
return variable0.copy()
out = variable0.copy(_omit_Data=True)
out.Data = Data.concatenate([v.Data for v in variables], axis=axis,
_preserve=_preserve)
return out
#--- End: def
[docs] def copy(self, _omit_Data=False, _only_Data=False,
_omit_special=None, _omit_properties=False,
_omit_attributes=False):
'''
Return a deep copy.
``f.copy()`` is equivalent to ``copy.deepcopy(f)``.
:Examples 1:
>>> g = f.copy()
:Returns:
out :
The deep copy.
:Examples 2:
>>> g = f.copy()
>>> g is f
False
>>> f.equals(g)
True
>>> import copy
>>> h = copy.deepcopy(f)
>>> h is f
False
>>> f.equals(g)
True
'''
if self._list:
kwargs2 = self._parameters(locals())
return self._list_method('copy', kwargs2)
new = type(self)()
# ts = type(self)
# new = ts.__new__(ts)
if _only_Data:
if self._hasData:
new.Data = self.Data.copy()
return new
#--- End: if
self_dict = self.__dict__.copy()
self_private = self_dict.pop('_private')
del self_dict['_hasData']
new.__dict__['_fill_value'] = self_dict.pop('_fill_value')
new.__dict__['_hasbounds'] = self_dict.pop('_hasbounds')
if self_dict and not _omit_attributes:
try:
new.__dict__.update(loads(dumps(self_dict, -1)))
except PicklingError:
new.__dict__.update(deepcopy(self_dict))
private = {}
if not _omit_Data and self._hasData:
private['Data'] = self_private['Data'].copy()
new._hasData = True
special = self_private['special_attributes'].copy()
if _omit_special:
for prop in _omit_special:
special.pop(prop, None)
#--- End: if
for prop, value in special.iteritems():
special[prop] = value.copy()
private['special_attributes'] = special
if not _omit_properties:
try:
private['simple_properties'] = loads(dumps(self_private['simple_properties'], -1))
except PicklingError:
private['simple_properties'] = deepcopy(self_private['simple_properties'])
else:
private['simple_properties'] = {}
new._private = private
return new
#--- End: def
[docs] def cos(self, i=False):
'''
{+Fef,}Take the trigonometric cosine of the data array.
Units are accounted for in the calculation, so that the the cosine of
90 degrees_east is 0.0, as is the cosine of 1.57079632 radians. If the
units are not equivalent to radians (such as Kelvin) then they are
treated as if they were radians.
The output units are '1' (nondimensionsal).
.. seealso:: `sin`, `tan`
:Examples 1:
>>> g = f.cos()
:Parameters:
{+i}
:Returns:
out : cf.{+Variable}
{+Fef,}The {+variable} with the cosine of data array values.
:Examples 2:
>>> f.Units
<CF Units: degrees_east>
>>> print f.array
[[-90 0 90 --]]
>>> f.cos()
>>> f.Units
<CF Units: 1>
>>> print f.array
[[0.0 1.0 0.0 --]]
>>> f.Units
<CF Units: m s-1>
>>> print f.array
[[1 2 3 --]]
>>> f.cos()
>>> f.Units
<CF Units: 1>
>>> print f.array
[[0.540302305868 -0.416146836547 -0.9899924966 --]]
'''
if i:
v = self
else:
v = self.copy()
if v._hasData:
v.Data.cos(i=True)
return v
#--- End: def
def count(self, x):
'''
Emulate
f.count(x)
Equality is tested with the {+variable}'s `cf.{+Variable}.equals`
method (as opposed to the ``==`` operator).
Note that ``f.count(x)`` is equivalent to ``1 if f.equals(x) else 0``.
'''
return int(self.equals(x))
#--- End: def
def cyclic(self, axes=None, iscyclic=True):
'''
Set the cyclicity of axes of the data array.
.. seealso:: `iscyclic`
:Parameters:
axes : (sequence of) int
The axes to be set. Each axis is identified by its integer
position. By default no axes are set.
iscyclic: bool, optional
If False then the axis is set to be non-cyclic. By default the
axis is set to be cyclic.
:Returns:
out : set
:Examples:
>>> f.cyclic()
{}
>>> f.cyclic(1)
{}
>>> f.cyclic()
{1}
'''
if self._hasData:
return self.Data.cyclic(axes, iscyclic)
else:
return {}
#--- End: def
[docs] def datum(self, *index):
'''
Return an element of the data array as a standard Python scalar.
The first and last elements are always returned with ``f.datum(0)``
and ``f.datum(-1)`` respectively, even if the data array is a scalar
array or has two or more dimensions.
:Parameters:
index : *optional*
Specify which element to return. When no positional arguments
are provided, the method only works for data arrays with one
element (but any number of dimensions), and the single element
is returned. If positional arguments are given then they must
be one of the following:
* An integer. This argument is interpreted as a flat index
into the array, specifying which element to copy and
return.
Example: If the data aray shape is ``(2, 3, 6)`` then:
* ``f.datum(0)`` is equivalent to ``f.datum(0, 0, 0)``.
* ``f.datum(-1)`` is equivalent to ``f.datum(1, 2, 5)``.
* ``f.datum(16)`` is equivalent to ``f.datum(0, 2, 4)``.
If *index* is ``0`` or ``-1`` then the first or last data
array element respecitively will be returned, even if the
data array is a scalar array or has two or more
dimensions.
..
* Two or more integers. These arguments are interpreted as a
multidimensionsal index to the array. There must be the
same number of integers as data array dimensions.
..
* A tuple of integers. This argument is interpreted as a
multidimensionsal index to the array. There must be the
same number of integers as data array dimensions.
Example: ``f.datum((0, 2, 4))`` is equivalent to
``f.datum(0, 2, 4)``; and ``f.datum(())`` is equivalent
to ``f.datum()``.
:Returns:
out :
A copy of the specified element of the array as a suitable
Python scalar.
:Examples:
>>> print f.array
2
>>> f.datum()
2
>>> 2 == f.datum(0) == f.datum(-1) == f.datum(())
True
>>> print f.array
[[2]]
>>> 2 == f.datum() == f.datum(0) == f.datum(-1)
True
>>> 2 == f.datum(0, 0) == f.datum((-1, -1)) == f.datum(-1, 0)
True
>>> print f.array
[[4 -- 6]
[1 2 3]]
>>> f.datum(0)
4
>>> f.datum(-1)
3
>>> f.datum(1)
masked
>>> f.datum(4)
2
>>> f.datum(-2)
2
>>> f.datum(0, 0)
4
>>> f.datum(-2, -1)
6
>>> f.datum(1, 2)
3
>>> f.datum((0, 2))
6
'''
if not self._hasData:
raise ValueError(
"ERROR: Can't return an element when there is no data array")
return self.Data.datum(*index)
#--- End: def
[docs] def dump(self, display=True, prefix=None, omit=()):
'''
Return a string containing a full description of the instance.
:Parameters:
display : bool, optional
If False then return the description as a string. By default
the description is printed, i.e. ``f.dump()`` is equivalent to
``print f.dump(display=False)``.
omit : sequence of strs, optional
Omit the given CF properties from the description.
prefix : *optional*
Ignored.
:Returns:
out : None or str
A string containing the description.
:Examples:
>>> f.dump()
Data(1, 2) = [[2999-12-01 00:00:00, 3000-12-01 00:00:00]] 360_day
axis = 'T'
standard_name = 'time'
>>> f.dump(omit=('axis',))
Data(1, 2) = [[2999-12-01 00:00:00, 3000-12-01 00:00:00]] 360_day
standard_name = 'time'
'''
string = []
if self._hasData:
data = self.Data
string.append('Data%s = %s' % (data.shape, data))
string.append(self._dump_simple_properties(omit=omit))
string = '\n'.join(string)
if display:
print string
else:
return string
#--- End: def
[docs] def equals(self, other, rtol=None, atol=None, ignore_fill_value=False,
traceback=False, ignore=(), _set=False):
'''
True if two {+variable}s are equal, False otherwise.
:Parameters:
other :
The object to compare for equality.
{+atol}
{+rtol}
ignore_fill_value : bool, optional
If True then data arrays with different fill values are
considered equal. By default they are considered unequal.
traceback : bool, optional
If True then print a traceback highlighting where the two
{+variable}s differ.
ignore : tuple, optional
The names of CF properties to omit from the comparison.
:Returns:
out : bool
Whether or not the two {+variable}s are equal.
:Examples:
>>> f.equals(f)
True
>>> g = f + 1
>>> f.equals(g)
False
>>> g -= 1
>>> f.equals(g)
True
>>> f.setprop('name', 'name0')
>>> g.setprop('name', 'name1')
>>> f.equals(g)
False
>>> f.equals(g, ignore=['name'])
True
'''
# Check for object identity
if self is other:
return True
# Check that each instance is of the same type
if not isinstance(other, self.__class__):
if traceback:
print("{0}: Incompatible types: {0}, {1}".format(
self.__class__.__name__,
other.__class__.__name__))
return False
#--- End: if
# Check that there are equal numbers of variables
len_self = len(self)
if len_self != len(other):
if traceback:
print("{0}: Different numbers of ppp: {1}, {2}".format(
self.__class__.__name__,
len_self,
len(other)))
return False
#--- End: if
if len_self != 1:
if not _set:
# ----------------------------------------------------
# Two lists, each with more than one element: Check
# the lists pair-wise.
# ----------------------------------------------------
for i, (f, g) in enumerate(zip(self, other)):
if not f.equals(g, rtol=rtol, atol=atol,
ignore_fill_value=ignore_fill_value,
traceback=traceback, ignore=ignore):
if traceback:
print("{0}: Different element {1}: {2!r}, {3!r}".format(
self.__class__.__name__, i, f, g))
return False
else:
# ----------------------------------------------------
# Two lists, each with more than one element: Check
# the lists set-wise.
# ----------------------------------------------------
# Group the variables by identity
self_identity = {}
for f in self:
self_identity.setdefault(f.identity(), []).append(f)
other_identity = {}
for f in other:
other_identity.setdefault(f.identity(), []).append(f)
# Check that there are the same identities
if set(self_identity) != set(other_identity):
if traceback:
print("{0}: Different sets of identities: {1}, {2}".format(
self.__class__.__name__,
set(self_identity),
set(other_identity)))
return False
#--- End: if
# Check that there are the same number of variables
# for each identity
for identity, fl in self_identity.iteritems():
gl = other_identity[identity]
if len(fl) != len(gl):
if traceback:
print("{0}: Different numbers of {1!r} {2}s: {3}, {4}".format(
self.__class__.__name__,
identity,
fl[0].__class__.__name__,
len(fl),
len(gl)))
return False
#--- End: fo
# For each identity, check that there are matching
# pairs of equal fields.
for identity, fl in self_identity.iteritems():
gl = other_identity[identity]
for f in fl:
found_match = False
for i, g in enumerate(gl):
if f.equals(g, rtol=rtol, atol=atol,
ignore_fill_value=ignore_fill_value,
ignore=ignore, traceback=False):
found_match = True
del gl[i]
break
#--- End: for
if not found_match:
if traceback:
print("{0}: No {1} equal to: {2!r}".format(
self.__class__.__name__,
g.__class__.__name__,
f))
return False
#--- End: for
#--- End: for
#--- End: if
# --------------------------------------------------------
# Lists are equal
# --------------------------------------------------------
return True
#--- End: if
# Still here?
if self._list:
self = self[0]
if other._list:
other = other[0]
# ------------------------------------------------------------
# Check the simple properties
# ------------------------------------------------------------
if ignore_fill_value:
ignore += ('_FillValue', 'missing_value')
self_simple = self._private['simple_properties']
other_simple = other._private['simple_properties']
if (set(self_simple).difference(ignore) !=
set(other_simple).difference(ignore)):
if traceback:
print("%s: Different properties: %s, %s" %
(self.__class__.__name__,
self_simple, other_simple))
return False
#--- End: if
if rtol is None:
rtol = RTOL()
if atol is None:
atol = ATOL()
for attr, x in self_simple.iteritems():
if attr in ignore:
continue
y = other_simple[attr]
if not equals(x, y, rtol=rtol, atol=atol,
ignore_fill_value=ignore_fill_value,
traceback=traceback):
if traceback:
print("%s: Different %s properties: %r, %r" %
(self.__class__.__name__, attr, x, y))
return False
#--- End: for
# ------------------------------------------------------------
# Check the special attributes
# ------------------------------------------------------------
self_special = self._private['special_attributes']
other_special = other._private['special_attributes']
if set(self_special) != set(other_special):
if traceback:
print("%s: Different attributes: %s" %
(self.__class__.__name__,
set(self_special).symmetric_difference(other_special)))
return False
#--- End: if
for attr, x in self_special.iteritems():
y = other_special[attr]
if attr == 'ancillary_variables':
result = set_equals(x, y, rtol=rtol, atol=atol,
ignore_fill_value=ignore_fill_value,
traceback=traceback)
else:
result = equals(x, y, rtol=rtol, atol=atol,
ignore_fill_value=ignore_fill_value,
traceback=traceback)
if not result:
if traceback:
print("%s: Different %s properties: %r, %r" %
(self.__class__.__name__, attr, x, y))
return False
#--- End: for
# ------------------------------------------------------------
# Check the data
# ------------------------------------------------------------
self_hasData = self._hasData
if self_hasData != other._hasData:
if traceback:
print("%s: Different data" % self.__class__.__name__)
return False
if self_hasData:
if not self.data.equals(other.data, rtol=rtol, atol=atol,
ignore_fill_value=ignore_fill_value,
traceback=traceback):
if traceback:
print("%s: Different data" % self.__class__.__name__)
return False
#--- End: if
return True
#--- End: def
[docs] def floor(self, i=False):
'''{+Fef,}Floor the data array.
The floor of the scalar ``x`` is the largest integer ``i``, such that
``i <= x``.
.. versionadded:: 1.0
.. seealso:: `ceil`, `rint`, `trunc`
:Examples 1:
Create a new {+variable} with the floor of the data:
>>> g = f.floor()
:Parameters:
{+i}
:Returns:
out : cf.{+Variable}
{+Fef,}The {+variable} with the floor of data array values.
:Examples 2:
>>> print f.array
[-1.9 -1.5 -1.1 -1. 0. 1. 1.1 1.5 1.9]
>>> print f.floor().array
[-2. -2. -2. -1. 0. 1. 1. 1. 1.]
>>> print f.array
[-1.9 -1.5 -1.1 -1. 0. 1. 1.1 1.5 1.9]
>>> print f.floor(i=True).array
[-2. -2. -2. -1. 0. 1. 1. 1. 1.]
>>> print f.array
[-2. -2. -2. -1. 0. 1. 1. 1. 1.]
'''
# List functionality
if self._list:
kwargs2 = self._parameters(locals())
return self._list_method('floor', kwargs2)
if not self._hasData:
raise ValueError("Can't floor %r" % self.__class__.__name__)
if i:
v = self
else:
v = self.copy()
v.Data.floor(i=True)
return v
#--- End: def
[docs] def match(self, match=None, ndim=None, exact=False, match_and=True,
inverse=False, _Flags=False, _CellMethods=False):
'''
Determine whether or not a variable satisfies conditions.
Conditions may be specified on the variable's attributes and CF
properties.
:Parameters:
:Returns:
out : bool
Whether or not the variable matches the given criteria.
:Examples:
'''
conditions_have_been_set = False
something_has_matched = False
if ndim is not None:
conditions_have_been_set = True
try:
found_match = self.ndim == ndim
except AttributeError:
found_match = False
if match_and and not found_match:
return bool(inverse)
something_has_matched = True
#--- End: if
matches = self._parse_match(match)
if matches:
conditions_have_been_set = True
found_match = True
for match in matches:
found_match = True
# ----------------------------------------------------
# Try to match cf.Units
# ----------------------------------------------------
if 'units' in match or 'calendar' in match:
match = match.copy()
units = Units(match.pop('units', None), match.pop('calendar', None))
if not exact:
found_match = self.Units.equivalent(units)
else:
found_match = self.Units.equals(units)
if not found_match:
continue
#--- End: if
# ----------------------------------------------------
# Try to match cell methods
# ----------------------------------------------------
if _CellMethods and 'cell_methods' in match:
f_cell_methods = self.getprop('cell_methods', None)
if not f_cell_methods:
found_match = False
continue
match = match.copy()
cell_methods = match.pop('cell_methods')
if not exact:
n = len(cell_methods)
if n > len(f_cell_methods):
found_match = False
else:
found_match = f_cell_methods[-n:].equivalent(cell_methods)
else:
found_match = f_cell_methods.equals(cell_methods)
if not found_match:
continue
#--- End: if
# ---------------------------------------------------
# Try to match cf.Flags
# ---------------------------------------------------
if _Flags and ('flag_masks' in match or
'flag_meanings' in match or
'flag_values' in match):
f_flags = getattr(self, Flags, None)
if not f_flags:
found_match = False
continue
match = match.copy()
found_match = f_flags.equals(
Flags(flag_masks=match.pop('flag_masks', None),
flag_meanings=match.pop('flag_meanings', None),
flag_values=match.pop('flag_values', None)))
if not found_match:
continue
#--- End: if
for prop, value in match.iteritems():
if prop is None:
if value is None:
continue
if isinstance(value, basestring):
if value in ('T', 'X', 'Y', 'Z'):
# Axis type
x = getattr(self, value)
value = True
else:
value = value.split('%')
if len(value) == 1:
value = value[0].split(':')
if len(value) == 1:
# Identity (string-valued)
x = self.identity(None)
value = value[0]
else:
# CF property (string-valued)
x = self.getprop(value[0], None)
value = ':'.join(value[1:])
else:
# Python attribute (string-valued)
x = getattr(self, value[0], None)
value = '%'.join(value[1:])
else:
# Identity (not string-valued, e.g. cf.Query)
x = self.identity(None)
else:
# CF property
x = self.getprop(prop, None)
if x is None:
found_match = False
elif isinstance(x, basestring) and isinstance(value, basestring):
if exact:
found_match = (value == x)
else:
found_match = re_match(value, x)
else:
found_match = (value == x)
try:
found_match == True
except ValueError:
found_match = False
#--- End: if
if not found_match:
break
#--- End: for
if found_match:
something_has_matched = True
break
#--- End: for
if match_and and not found_match:
return bool(inverse)
if conditions_have_been_set:
if something_has_matched:
return not bool(inverse)
else:
return bool(inverse)
else:
return not bool(inverse)
#--- End: def
@property
def mask(self):
'''The mask of the data array.
Values of True indicate masked elements.
.. seealso:: `binary_mask`
:Examples:
>>> f.shape
(12, 73, 96)
>>> m = f.mask
>>> m.long_name
'mask'
>>> m.shape
(12, 73, 96)
>>> m.dtype
dtype('bool')
>>> m.data
<CF Data: [[[True, ..., False]]] >
'''
if not self._hasData:
raise ValueError(
"ERROR: Can't get mask when there is no data array")
out = self.copy(_omit_Data=True, _omit_properties=True,
_omit_attributes=True)
out.insert_data(self.data.mask, copy=False)
out.long_name = 'mask'
return out
#--- End: def
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def attributes(self):
'''
A dictionary of the attributes which are not CF properties.
:Examples:
>>> f.attributes
{}
>>> f.foo = 'bar'
>>> f.attributes
{'foo': 'bar'}
>>> f.attributes.pop('foo')
'bar'
>>> f.attributes
{'foo': 'bar'}
'''
attributes = self.__dict__.copy()
del attributes['_hasbounds']
del attributes['_hasData']
del attributes['_private']
return attributes
#--- End: def
# ----------------------------------------------------------------
# Attribute (read only)
# ----------------------------------------------------------------
@property
def properties(self):
'''
A dictionary of the CF properties.
:Examples:
>>> f.properties
{'_FillValue': 1e+20,
'foo': 'bar',
'long_name': 'Surface Air Temperature',
'standard_name': 'air_temperature',
'units': 'K'}
'''
return self._simple_properties().copy()
#--- End: def
# ================================================================
# Methods
# ================================================================
def all(self):
'''Test whether all data array elements evaluate to True.
Performs a logical and over the data array and returns the
result. Masked values are considered as True during computation.
.. seealso:: `any`
:Examples 1:
>>> b = f.all()
:Returns:
out : bool
Whether ot not all data array elements evaluate to True.
:Examples:
>>> print f.array
[[0 3 0]]
>>> f.all()
False
>>> print f.array
[[1 3 --]]
>>> f.all()
True
'''
if self._hasData:
return self.Data.all()
return False
#--- End: def
def allclose(self, y, atol=None, rtol=None):
'''Returns True if two broadcastable {+variable}s have equal array
values to within numerical tolerance.
For numeric data arrays ``f.allclose(y, atol, rtol)`` is equivalent to
``(abs(f - y) <= atol + rtol*abs(y)).all()``; for other data types
it is equivalent to ``(f == y).all()``.
.. seealso:: `~cf.{+Variable}.equals`
:Examples 1:
>>> b = f.allclose(y)
:Parameters:
y : data-like object
The object to be compared with the data array. *y* must be
broadcastable to the data array and if *y* has units then they
must be compatible.
{+data-like}
{+atol}
{+rtol}
:Returns:
out : bool
Whether or not the two data arrays are equivalent.
:Examples 2:
'''
if not self._hasData:
return False
if isinstance(y, self.__class__):
if not y._hasData:
return False
y = self._conform_for_assignment(y)
#-- End: if
y = getattr(y, 'Data', y)
return self.Data.allclose(y, rtol=rtol, atol=atol)
#--- End: def
def any(self):
'''Return True if any data array elements evaluate to True.
Performs a logical or over the data array and returns the
result. Masked values are considered as False during computation.
.. seealso:: `all`
:Examples 1:
>>> b = f.any()
:Returns:
out : bool
Whether ot not any data array elements evaluate to True.
:Examples 2:
>>> print f.array
[[0 0 0]]
>>> f.any()
False
>>> print f.array
[[-- 0 0]]
>>> d.any()
False
>>> print f.array
[[-- 3 0]]
>>> f.any()
True
'''
if self._hasData:
return self.Data.any()
return False
#--- End: def
[docs] def asdatetime(self, i=False):
'''{+Fef,}Convert the internal representation of data array elements
to date-time objects.
Only applicable to {+variable}s with reference time units.
If the calendar has not been set then the CF default calendar will be
used and the units will be updated accordingly.
.. seealso:: `asreftime`
:Examples 1:
>>> g = f.asdatetime()
:Parameters:
{+i}
:Returns:
out : cf.{+Variable}
:Examples 2:
>>> t.asreftime().dtype
dtype('float64')
>>> t.asdatetime().dtype
dtype('O')
'''
# List functionality
if self._list:
kwargs2 = self._parameters(locals())
return self._list_method('asdatetime', kwargs2)
if not self._hasData:
raise AttributeError(
"{0} has no data array".format(self.__class__.__name__))
if i:
v = self
else:
v = self.copy()
v.data.asdatetime(i=True)
return v
#--- End: def
[docs] def asreftime(self, i=False):
'''{+Fef,}Convert the internal representation of data array elements
to numeric reference times.
Only applicable to {+variable}s with reference time units.
If the calendar has not been set then the CF default calendar will be
used and the units will be updated accordingly.
.. seealso:: `asdatetime`
:Examples 1:
>>> g = f.asreftime()
:Parameters:
{+i}
:Returns:
out : cf.{+Variable}
:Examples 2:
>>> t.asdatetime().dtype
dtype('O')
>>> t.asreftime().dtype
dtype('float64')
'''
# List functionality
if self._list:
kwargs2 = self._parameters(locals())
return self._list_method('asreftime', kwargs2)
if not self._hasData:
raise AttributeError(
"{0} has no data array".format(self.__class__.__name__))
if i:
v = self
else:
v = self.copy()
v.data.asreftime(i=True)
return v
#--- End: def
[docs] def fill_value(self, default=None):
'''
Return the data array missing data value.
This is the value of the `missing_value` CF property, or if that is
not set, the value of the `_FillValue` CF property, else if that is
not set, ``None``. In the last case the default `numpy` missing data
value for the array's data type is assumed if a missing data value is
required.
:Parameters:
default : *optional*
If the missing value is unset then return this value. By
default, *default* is `None`. If *default* is the special
value ``'netCDF'`` then return the netCDF default value
appropriate to the data array's data type is used. These may
be found as follows:
>>> import netCDF4
>>> print netCDF4.default_fillvals
:Returns:
out :
The missing data value, or the value specified by *default* if
one has not been set.
:Examples:
>>> f.fill_value()
None
>>> f._FillValue = -1e30
>>> f.fill_value()
-1e30
>>> f.missing_value = 1073741824
>>> f.fill_value()
1073741824
>>> del f.missing_value
>>> f.fill_value()
-1e30
>>> del f._FillValue
>>> f.fill_value()
None
>>> f,dtype
dtype('float64')
>>> f.fill_value(default='netCDF')
9.969209968386869e+36
>>> f._FillValue = -999
>>> f.fill_value(default='netCDF')
-999
'''
fillval = self._fill_value
if fillval is None:
if default == 'netCDF':
d = self.dtype
fillval = _netCDF4_default_fillvals[d.kind + str(d.itemsize)]
else:
fillval = default
#--- End: if
return fillval
# return self._fill_value
#--- End: def
[docs] def flip(self, axes=None, i=False):
'''
{+Fef,}Flip dimensions of the data array in place.
.. seealso:: `expand_dims`, `squeeze`, `transpose`
:Parameters:
axes : (sequence of) int
Flip the dimensions whose positions are given. By default all
dimensions are flipped.
:Returns:
out : cf.{+Variable}
:Examples:
>>> f.flip()
>>> f.flip(1)
>>> f.flip([0, 1])
>>> g = f[::-1, :, ::-1]
>>> f.flip([2, 0]).equals(g)
True
'''
kwargs2 = self._parameters(locals())
# List functionality
if self._list:
return self._list_method('flip', kwargs2)
if not self._hasData and (axes or axes == 0):
raise ValueError("Can't flip %r" % self.__class__.__name__)
if i:
v = self
else:
v = self.copy()
if v._hasData:
kwargs2['i'] = True
v.Data.flip(**kwargs2)
return v
#--- End: def
[docs] def select(self, *args, **kwargs):
'''
Return the instance if it matches the given conditions.
``v.select(*args, **kwargs)`` is equivalent to ``v if v.match(*args,
**kwargs) else cf.List()``.
See `cf.Variable.match` for details.
:Parameters:
args, kwargs : *optional*
See `cf.Variable.match`.
:Returns:
out : cf.{+Variable} or list
If the variable matches the given conditions then it is
returned as an object identity. Otherwise an empty list is
returned.
'''
if self.match(*args, **kwargs):
return self
else:
return []
#--- End: def
@property
def binary_mask(self):
'''
A binary (0 and 1) missing data mask of the data array.
The binary mask's data array comprises dimensionless 32-bit integers
and has 0 where the data array has missing data and 1 otherwise.
:Examples:
>>> print f.mask.array
[[ True False True False]]
>>> b = f.binary_mask()
>>> print b.array
[[0 1 0 1]]
'''
return type(self)(properties={'long_name': 'binary_mask'},
data=self.Data.binary_mask(),
copy=False)
#--- End: def
def exp(self, i=False):
'''{+Fef,}The exponential of the data array.
.. seealso:: `log`
:Examples 1:
>>> g = f.exp()
:Parameters:
{+i}
:Returns:
out : cf.{+Variable}
{+Fef,}The {+variable} with the exponential of data array
values.
:Examples 2:
>>> f[+0].data
<CF Data: [[1, 2]]>
>>> f.exp()[+0].data
<CF Data: [[2.71828182846, 7.38905609893]]>
>>> f[+0].data
<CF Data: [[1, 2]] 2>
>>> f.exp()[+0].data
<CF Data: [[7.38905609893, 54.5981500331]]>
>>> g = f.exp(i=True)
>>> g is f
True
>>> f[+0].data
<CF Data: [[1, 2]] kg m-1 s-2>
>>> f.exp()
ValueError: Can't take exponential of dimensional quantities: <CF Units: kg m-1 s-2>
'''
# List functionality
if self._list:
kwargs2 = self._parameters(locals())
return self._list_method('exp', kwargs2)
if i:
v = self
else:
v = self.copy()
if v._hasData:
v.Data.exp(i=True)
return v
#--- End: def
[docs] def expand_dims(self, position=0, i=False):
'''
Insert a size 1 axis into the data array.
.. seealso:: `flip`, `squeeze`, `transpose`
:Parameters:
position : int, optional
Specify the position amongst the data array axes where the new
axis is to be inserted. By default the new axis is inserted at
position 0, the slowest varying position.
{+i}
:Returns:
None
:Examples:
>>> v.expand_dims(2)
>>> v.expand_dims(-1)
'''
# List functionality
if self._list:
kwargs2 = self._parameters(locals())
return self._list_method('expand_dims', kwargs2)
if not self._hasData:
raise ValueError(
"Can't insert axis into %r" % self.__class__.__name__)
if i:
v = self
else:
v = self.copy()
v.Data.expand_dims(position, i=True)
return v
#--- End: def
def set_equals(self, other, rtol=None, atol=None, ignore_fill_value=False,
traceback=False, ignore=()):
'''
'''
kwargs2 = self._parameters(locals())
return self.equals(_set=True, **kwargs2)
#--- End: def
[docs] def sin(self, i=False):
'''
{+Fef,}Take the trigonometric sine of the data array.
Units are accounted for in the calculation. For example, the the sine
of 90 degrees_east is 1.0, as is the sine of 1.57079632 radians. If
the units are not equivalent to radians (such as Kelvin) then they are
treated as if they were radians.
The Units are changed to '1' (nondimensionsal).
.. seealso:: `cos`, `tan`
:Examples 1:
>>> g = f.sin()
:Parameters:
{+i}
:Returns:
out : cf.{+Variable}
{+Fef,}The {+variable} with the sine of data array values.
:Examples 2:
>>> f.Units
<CF Units: degrees_north>
>>> print f.array
[[-90 0 90 --]]
>>> f.sin()
>>> f.Units
<CF Units: 1>
>>> print f.array
[[-1.0 0.0 1.0 --]]
>>> f.Units
<CF Units: m s-1>
>>> print f.array
[[1 2 3 --]]
>>> f.sin()
>>> f.Units
<CF Units: 1>
>>> print f.array
[[0.841470984808 0.909297426826 0.14112000806 --]]
'''
# List functionality
if self._list:
kwargs2 = self._parameters(locals())
return self._list_method('sin', kwargs2)
if i:
v = self
else:
v = self.copy()
if v._hasData:
v.Data.sin(i=True)
return v
#--- End: def
def tan(self, i=False):
'''
Take the trigonometric tangent of the data array.
Units are accounted for in the calculation, so that the the tangent of
180 degrees_east is 0.0, as is the sine of 3.141592653589793
radians. If the units are not equivalent to radians (such as Kelvin)
then they are treated as if they were radians.
The Units are changed to '1' (nondimensionsal).
:Parameters:
{+i}
:Returns:
out : cf.{+Variable}
{+Fef,}The {+variable} with the tangent of data array values.
:Examples:
'''
# List functionality
if self._list:
kwargs2 = self._parameters(locals())
return self._list_method('tan', kwargs2)
if i:
v = self
else:
v = self.copy()
if v._hasData:
v.Data.tan(i=True)
return v
#--- End: def
def log(self, base=10, i=False):
'''{+Fef,}The logarithm of the data array.
By default the natural logarithm is taken, but any base may be
specified.
.. seealso:: `exp`
:Examples 1:
>>> g = f.log()
:Parameters:
base : number, optional
The base of the logiarthm. By default a natural logiarithm is
taken.
{+i}
:Returns:
out : cf.{+Variable}
{+Fef,}The {+variable} with the logarithm of data array
values.
:Examples:
:Examples 2:
>>> f[+0].data
<CF Data: [[1, 2]]>
>>> f.log()[+0].data
<CF Data: [[0.0, 0.69314718056]] ln(re 1)>
>>> f[+0].data
<CF Data: [[1, 2]] 2>
>>> f.log()[+0].data
<CF Data: [[0.0, 0.69314718056]] ln(re 2 1)>
>>> f[+0].data
<CF Data: [[1, 2]] kg s-1 m-2>
>>> f.log()[+0].data
<CF Data: [[0.0, 0.69314718056]] ln(re 1 m-2.kg.s-1)>
>>> g = f.log(i=True)
>>> g is f
True
>>> f[+0].Units
<CF Units: >
>>> f.log()
ValueError: Can't take the logarithm to the base 2.718281828459045 of <CF Units: >
'''
# List functionality
if self._list:
kwargs2 = self._parameters(locals())
return self._list_method('log', kwargs2)
if i:
v = self
else:
v = self.copy()
if v._hasData:
v.Data.log(base, i=True)
return v
#--- End: def
[docs] def squeeze(self, axes=None, i=False):
'''
Remove size 1 dimensions from the data array in place.
.. seealso:: `expand_dims`, `flip`, `transpose`
:Parameters:
axes : (sequence of) int, optional
The size 1 axes to remove. By default, all size 1 axes are
removed. Size 1 axes for removal are identified by their
integer positions in the data array.
{+i}
:Returns:
out : cf.Variable
:Examples:
>>> v.squeeze()
>>> v.squeeze(1)
>>> v.squeeze([1, 2])
'''
# List functionality
if self._list:
kwargs2 = self._parameters(locals())
return self._list_method('squeeze', kwargs2)
if not self._hasData and (axes or axes == 0):
raise ValueError("Can't squeeze %r" % self.__class__.__name__)
if i:
v = self
else:
v = self.copy()
if v._hasData:
v.Data.squeeze(axes, i=True)
return v
#--- End: def
[docs] def transpose(self, axes=None, i=False):
'''
{+Fef,}Permute the dimensions of the data array.
.. seealso:: `expand_dims`, `flip`, `squeeze`
:Parameters:
axes : (sequence of) int
The new axis order of the data array. By default the order is
reversed. Each axis of the new order is identified by its
original integer position.
{+i}
:Returns:
out : cf.{+Variable}
:Examples:
>>> f.shape
(2, 3, 4)
>>> f.transpose()
>>> f.shape
(4, 3, 2)
>>> f.transpose([1, 2, 0])
>>> f.shape
(3, 2, 4)
>>> f.transpose((1, 0, 2))
>>> f.shape
(2, 3, 4)
'''
# List functionality
if self._list:
kwargs2 = self._parameters(locals())
return self._list_method('transpose', kwargs2)
if not self._hasData and (axes or axes == 0):
raise ValueError(
"Can't transpose %r with no data" % self.__class__.__name__)
if i:
v = self
else:
v = self.copy()
if v._hasData:
v.Data.transpose(axes, i=True)
return v
#--- End: def
[docs] def trunc(self, i=False):
'''{+Fef,}Truncate the data array.
The truncated value of the scalar ``x``, is the nearest integer ``i``
which is closer to zero than ``x`` is. I.e. the fractional part of the
signed number ``x`` is discarded.
.. versionadded:: 1.0
.. seealso:: `ceil`, `floor`, `rint`
:Examples 1:
>>> g = f.trunc()
:Parameters:
{+i}
:Returns:
out : cf.{+Variable}
{+Fef,}The {+variable} with truncated data array values.
:Examples 2:
>>> print f.array
[-1.9 -1.5 -1.1 -1. 0. 1. 1.1 1.5 1.9]
>>> print f.trunc().array
[-1. -1. -1. -1. 0. 1. 1. 1. 1.]
>>> print f.array
[-1.9 -1.5 -1.1 -1. 0. 1. 1.1 1.5 1.9]
>>> print f.trunc(i=True).array
[-1. -1. -1. -1. 0. 1. 1. 1. 1.]
>>> print f.array
[-1. -1. -1. -1. 0. 1. 1. 1. 1.]
'''
# List functionality
if self._list:
kwargs2 = self._parameters(locals())
return self._list_method('trunc', kwargs2)
if not self._hasData:
raise ValueError("Can't trunc %r" % self.__class__.__name__)
if i:
v = self
else:
v = self.copy()
v.Data.trunc(i=True)
return v
#--- End: def
[docs] def unique(self):
'''The unique elements of the data array.
:Examples 1:
>>> f.unique()
:Returns:
out : cf.Data
Returns the unique data array values in a one dimensional
`cf.Data` object.
:Examples 2:
>>> print f.array
[[4 2 1]
[1 2 3]]
>>> print f.unique().array
[1 2 3 4]
>>> f.subspace[1, -1] = cf.masked
>>> print f.array
[[4 2 1]
[1 2 --]]
>>> print f.unique().array
[1 2 4]
'''
if self._hasData:
return self.data.unique()
raise ValueError(
"ERROR: Can't get unique values when there is no data array")
#--- End: def
[docs] def setprop(self, prop, value):
'''
{+Fef,}Set a CF property.
.. seealso:: `delprop`, `getprop`, `hasprop`
:Examples 1:
>>> f.setprop('standard_name', 'time')
>>> f.setprop('foo', 12.5)
:Parameters:
prop : str
The name of the CF property.
value :
The value for the property.
:Returns:
None
'''
# List functionality
if self._list:
for f in self:
f.setprop(prop, value)
return
# Set a special attribute
if prop in self._special_properties:
setattr(self, prop, value)
return
# Still here? Then set a simple property
self._private['simple_properties'][prop] = value
#--- End: def
[docs] def hasprop(self, prop):
'''
Return True if a CF property exists, otherise False.
.. seealso:: `delprop`, `getprop`, `setprop`
:Examples 1:
>>> x = f.hasprop('standard_name')
:Parameters:
prop : str
The name of the property.
:Returns:
out : bool
True if the CF property exists, otherwise False.
'''
# Has a special property? # DCH
if prop in self._special_properties:
return hasattr(self, prop)
# Still here? Then has a simple property?
return prop in self._private['simple_properties']
#--- End: def
[docs] def identity(self, default=None):
'''Return the identity.
The identity is the first found of the following:
* The `standard_name` CF property.
* The `!id` attribute.
* The value of the *default* parameter.
.. seealso:: `name`
:Parameters:
default : optional
The identity if one could not otherwise be found. By default,
*default* is `None`.
:Returns:
out :
The identity.
:Examples:
>>> f.standard_name = 'Kelvin'
>>> f.id = 'foo'
>>> f.identity()
'Kelvin'
>>> del f.standard_name
>>> f.identity()
'foo'
>>> del f.id
>>> f.identity()
None
>>> f.identity('bar')
'bar'
>>> print f.identity()
None
'''
return self.name(default, identity=True)
#--- End: def
def index(self, x, start=0, stop=None):
'''
L.index(value, [start, [stop]]) -- return first index of value.
Each field in the {+variable} is compared with the field's
`~cf.Field.equals` method (as aopposed to the ``==`` operator).
It is an error if there is no such field.
Note that f.index(x) is equivalent to ``0 if f.equals(x) else 0``.
if self.equals(x):
return 1
return 0
#--- End: def
.. seealso:: :py:obj:`list.index`
:Examples:
>>>
'''
try:
if stop is None:
(None,).index(x, start)
else:
(None,).index(x, start, stop)
except ValueError:
raise ValueError("{0} is not equal to {1!r}".format(
self.__class__.__name__, x))
if not self.equals(x):
raise ValueError("{0} is not equal to {1!r}".format(
self.__class__.__name__, x))
return 0
#--- End: def
[docs] def insert_data(self, data, copy=True):
'''
Insert a new data array into the variable in place.
:Parameters:
data : cf.Data
copy : bool, optional
:Returns:
None
'''
# List functionality
if self._list:
for f in self:
f.insert_data(data, copy=copy)
return
if not copy:
self.Data = data
else:
self.Data = data.copy()
#--- End: def
def inspect(self):
'''
Inspect the object for debugging.
.. seealso:: `cf.inspect`
:Returns:
None
:Examples:
>>> f.inspect()
'''
print cf_inspect(self)
#--- End: def
# def getattr(self, attr, *default):
# '''
#
#Get a named attribute.
#
#``f.getattr(attr, *default)`` is equivalent to ``getattr(f, attr,
#*default)``.
#
#.. seealso:: `delattr`, `hasattr`, `setattr`
#
#:Parameters:
#
# attr : str
# The attribute's name.
#
# default : *optional*
# When a default argument is given, it is returned when the
# attribute doesn't exist; without it, an exception is raised in
# that case.
#
#:Returns:
#
# out :
# The attribute's value.
#
#:Examples:
#
#>>> f.foo = -99
#>>> fl.getattr('foo')
#-99
#>>> del f.foo
#>>> fl.getattr('foo', 'bar')
#'bar'
#
#'''
# return getattr(self, attr, *default)
# #--- End: def
#
# def hasattr(self, attr):
# '''
#
#Return whether an attribute exists.
#
#``f.hasattr(attr)`` is equivalent to ``hasattr(f, attr)``.
#
#.. seealso:: `delattr`, `getattr`, `setattr`
#
#:Parameters:
#
# attr : str
# The attribute's name.
#
#:Returns:
#
# out : bool
# Whether the object has the attribute.
#
#:Examples:
#
#>>> f.foo = -99
#>>> fl.hasattr('foo')
#True
#>>> del f.foo
#>>> fl.hasattr('foo')
#False
#
#'''
# return hasattr(self, attr)
# #--- End: def
[docs] def getprop(self, prop, *default):
'''
Get a CF property.
When a default argument is given, it is returned when the attribute
doesn't exist; without it, an exception is raised in that case.
.. seealso:: `delprop`, `hasprop`, `setprop`
:Parameters:
prop : str
The name of the CF property.
default : optional
Return *default* if and only if the variable does not have the
named property.
:Returns:
out :
The value of the named property, or the default value.
:Raises:
AttributeError :
If the variable does not have the named property and a default
value has not been set.
:Examples:
>>> f.getprop('standard_name')
>>> f.getprop('standard_name', None)
>>> f.getprop('foo')
AttributeError: Field doesn't have CF property 'foo'
>>> f.getprop('foo', 'bar')
'bar'
'''
# Get a special attribute
if prop in self._special_properties:
return getattr(self, prop, *default)
# Still here? Then get a simple attribute
d = self._private['simple_properties']
if default:
return d.get(prop, default[0])
elif prop in d:
return d[prop]
raise AttributeError("%s doesn't have CF property %r" %
(self.__class__.__name__, prop))
#--- End: def
# def delattr(self, attr):
# '''
#
#Delete an attribute.
#
#[+1]Note that ``f.delattr(attr)`` is equivalent to ``delattr(f, attr)``.
#
#.. seealso:: `getattr`, `hasattr`, `setattr`
#
#:Examples 1:
#
#>>> f.delattr('foo')
#
#:Parameters:
#
# attr : str
# The attribute's name.
#
#:Returns:
#
# None
#
#:Examples 2:
#
#>>> f.getattr('foo')
#'bar'
#
#'''
# # List functionality
# if self._list:
# for f in self:
# f.delattr(attr)
# return
#
# delattr(self, attr)
# #--- End: def
[docs] def delprop(self, prop):
'''
Delete a CF property.
.. seealso:: `getprop`, `hasprop`, `setprop`
:Examples 1:
>>> f.delprop('standard_name')
:Parameters:
prop : str
The name of the CF property.
:Returns:
`None`
:Examples 2:
>>> f.foo = 'bar'
>>> f.delprop('foo')
>>> f.delprop('foo')
AttributeError: Can't delete non-existent Field CF property 'foo'
'''
# List functionality
if self._list:
for f in self:
f.delprop(prop)
return
# Delete a special attribute
if prop in self._special_properties:
delattr(self, prop)
return
# Still here? Then delete a simple attribute
d = self._private['simple_properties']
if prop in d:
del d[prop]
else:
raise AttributeError("Can't delete non-existent %s CF property %r" %
(self.__class__.__name__, prop))
#--- End: def
[docs] def name(self, default=None, identity=False, ncvar=False):
'''Return a name.
By default the name is the first found of the following:
1. The `standard_name` CF property.
2. The `!id` attribute.
3. The `long_name` CF property, preceeded by the string
``'long_name:'``.
4. The `!ncvar` attribute, preceeded by the string ``'ncvar%'``.
5. The value of the *default* parameter.
Note that ``f.name(identity=True)`` is equivalent to ``f.identity()``.
.. seealso:: `identity`
:Examples 1:
>>> n = f.name()
>>> n = f.name(default='NO NAME'))
:Parameters:
default : *optional*
If no name can be found then return the value of the *default*
parameter. By default the default is `None`.
identity : bool, optional
If True then 3. and 4. are not considered as possible names.
ncvar : bool, optional
If True then 1., 2. and 3. are not considered as possible
names.
:Returns:
out :
The name.
:Examples 2:
>>> f.standard_name = 'air_temperature'
>>> f.long_name = 'temperature of the air'
>>> f.ncvar = 'tas'
>>> f.name()
'air_temperature'
>>> del f.standard_name
>>> f.name()
'long_name:temperature of the air'
>>> del f.long_name
>>> f.name()
'ncvar:tas'
>>> del f.ncvar
>>> f.name()
None
>>> f.name('no_name')
'no_name'
>>> f.standard_name = 'air_temperature'
>>> f.name('no_name')
'air_temperature'
'''
if ncvar:
if identity:
raise ValueError(
"Can't find identity/name: ncvar and identity parameters can't both be True")
n = getattr(self, 'ncvar', None)
if n is not None:
return 'ncvar%%%s' % n
return default
#--- End: if
n = self.getprop('standard_name', None)
if n is not None:
return n
n = getattr(self, 'id', None)
if n is not None:
return n
if identity:
return default
n = self.getprop('long_name', None)
if n is not None:
return 'long_name:%s' % n
n = getattr(self, 'ncvar', None)
if n is not None:
return 'ncvar%%%s' % n
return default
#--- End: def
def override_calendar(self, calendar, i=False):
'''{+Fef,}Override the calendar of date-time units.
The new calendar **need not** be equivalent to the original one and
the data array elements will not be changed to reflect the new
units. Therefore, this method should only be used when it is known
that the data array values are correct but the calendar has been
incorrectly encoded.
Not to be confused with setting `cf.Field.calendar` or
`cf.Field.Units` attributes to a calendar which is equivalent to the
original calendar
.. seealso:: `cf.Field.calendar`, `cf.Field.override_calendar`,
`cf.Field.units`, `cf.Field.Units`
:Examples 1:
>>> g = f.override_calendar('noleap')
:Parameters:
calendar : str
The new calendar.
{+i}
:Returns:
out : cf.{+Variable}
:Examples 2:
'''
# List functionality
if self._list:
kwargs2 = self._parameters(locals())
return self._list_method('override_calendar', kwargs2)
if i:
v = self
else:
v = self.copy()
if v._hasData:
v.Data.override_calendar(calendar, i=True)
else:
if not v.Units.isreftime:
raise ValueError(
"Can't override the calender of non-reference-time units: {0!r}".format(
self.Units))
v.Units = Units(getattr(v.Units, 'units', None), calendar=calendar)
#--- End: if
return v
#--- End: def
[docs] def override_units(self, units, i=False):
'''{+Fef,}Override the units.
The new units **need not** be equivalent to the original ones and the
data array elements will not be changed to reflect the new
units. Therefore, this method should only be used when it is known
that the data array values are correct but the units have incorrectly
encoded.
Not to be confused with setting `cf.Field.units` or `cf.Field.Units`
attributes to units which are equivalent to the original units.
.. seealso:: `cf.Field.calendar`, `cf.Field.override_units`,
`cf.Field.units`, `cf.Field.Units`
:Examples 1:
>>> g = f.override_units('m')
:Parameters:
units : str or cf.Units
The new units for the data array.
{+i}
:Returns:
out : cf.{+Variable}
:Examples 2:
>>> f[+0].Units
<CF Units: hPa>
>>> f[+0].datum(0)
100000.0
>>> f.override_units('km')
>>> f[+0].Units
<CF Units: km>
>>> f[+0].datum(0)
100000.0
>>> f.override_units(cf.Units('watts'))
>>> f[+0].Units
<CF Units: watts>
>>> f[+0].datum(0)
100000.0
'''
# List functionality
if self._list:
kwargs2 = self._parameters(locals())
return self._list_method('override_units', kwargs2)
if i:
v = self
else:
v = self.copy()
if v._hasData:
v.Data.override_units(units, i=True)
else:
v.Units = Units(units)
return v
#--- End: def
[docs] def rint(self, i=False):
'''
{+Fef,} round data array.
The scalar ``x`` is rounded to the nearest integer ``i``.
.. versionadded:: 1.0
.. seealso:: `ceil`, `floor`, `trunc`
:Examples 1:
Create a new {+variable} with rounded data:
>>> g = f.rint()
:Parameters:
{+i}
:Returns:
out : cf.{+Variable}
{+Fef,}The {+variable} with rounded data array values.
:Examples 2:
>>> print f.array
[-1.9 -1.5 -1.1 -1. 0. 1. 1.1 1.5 1.9]
>>> print f.rint().array
[-2. -2. -1. -1. 0. 1. 1. 2. 2.]
>>> print f.array
[-1.9 -1.5 -1.1 -1. 0. 1. 1.1 1.5 1.9]
>>> print f.rint(i=True).array
[-2. -2. -1. -1. 0. 1. 1. 2. 2.]
>>> print f.array
[-2. -2. -1. -1. 0. 1. 1. 2. 2.]
'''
# List functionality
if self._list:
kwargs2 = self._parameters(locals())
return self._list_method('rint', kwargs2)
if not self._hasData:
raise ValueError("Can't rint %r" % self.__class__.__name__)
if i:
v = self
else:
v = self.copy()
v.Data.rint(i=True)
return v
#--- End: def
def round(self, decimals=0, i=False):
'''{+Fef,} evenly round elements of the data array to the given
number of decimals.
.. versionadded:: 1.1.4
.. seealso:: `ceil`, `floor`, `rint`, `trunc`
:Parameters:
decimals : int, optional
Number of decimal places to round to (default: 0). If decimals
is negative, it specifies the number of positions to the left
of the decimal point.
{+i}
:Returns:
out : cf.{+Variable}
{+Fef,}The {+variable} with rounded data array values.
:Examples:
>>> print f.array
[-1.81, -1.41, -1.01, -0.91, 0.09, 1.09, 1.19, 1.59, 1.99])
>>> print f.round().array
[-2., -1., -1., -1., 0., 1., 1., 2., 2.]
>>> print f.round(1).array
[-1.8, -1.4, -1. , -0.9, 0.1, 1.1, 1.2, 1.6, 2. ]
>>> print f.round(-1).array
[-0., -0., -0., -0., 0., 0., 0., 0., 0.]
'''
# List functionality
if self._list:
kwargs2 = self._parameters(locals())
return self._list_method('round', kwargs2)
if not self._hasData:
raise ValueError("Can't round %r" % self.__class__.__name__)
if i:
v = self
else:
v = self.copy()
v.Data.round(decimals=decimals, i=True)
return v
#--- End: def
def roll(self, iaxis, shift, i=False):
'''
.. seealso:: `expand_dims`, `flip`, `squeeze`, `transpose`
:Parameters:
iaxis : int
{+i}
:Returns:
out : cf.{+Variable}
:Examples:
'''
if not self._hasData:
raise ValueError("Can't roll %r" % self.__class__.__name__)
if i:
v = self
else:
v = self.copy()
v.Data.roll(iaxis, shift, i=True)
return v
#--- End: def
# def setattr(self, attr, value):
# '''
#
#Set a named attribute.
#
#``f.setattr(attr, value)`` is equivalent to ``setattr(f, attr,
#value)``.
#
#.. seealso:: `delattr`, `hasattr`, `getattr`
#
#:Parameters:
#
# attr : str
# The attribute's name.
#
# value :
# The value to set the attribute.
#
#:Returns:
#
# None
#
#:Examples:
#
#>>> f.setattr('foo', -99)
#>>> f.foo
#-99
#
#'''
#
# setattr(self, attr, value)
# #--- End: def
[docs] def where(self, condition, x=None, y=None, i=False):
'''
Set data array elements depending on a condition.
.. seealso:: `cf.masked`, `hardmask`, `subspace`
:Returns:
out : cf.{+Variable}
'''
if not self._hasData:
raise ValueError(
"ERROR: Can't set data in nonexistent data array")
if isinstance(condition, Variable):
if not condition._hasData:
raise ValueError(
"ERROR: Can't set data when %r condition has no data array" %
condition.__class__.__name__)
condition = condition.Data
#--- End: if
if x is not None and isinstance(x, Variable):
if not x._hasData:
raise ValueError(
"ERROR: Can't set data from %r with no data array" %
x.__class__.__name__)
x = x.Data
#--- End: if
if y is not None and isinstance(y, Variable):
if not y._hasData:
raise ValueError(
"ERROR: Can't set data from %r with no data array" %
y.__class__.__name__)
y = y.Data
#--- End: if
if i:
v = self
else:
v = self.copy()
v.Data.where(condition, x, y, i=True)
return v
#--- End: def
#--- End: class
# ====================================================================
#
# SubspaceVariable object
#
# ====================================================================
class SubspaceVariable(object):
__slots__ = ('variable',)
def __init__(self, variable):
'''
Set the contained variable.
'''
self.variable = variable
#--- End: def
def __getitem__(self, indices):
'''
Called to implement evaluation of x[indices].
x.__getitem__(indices) <==> x[indices]
'''
variable = self.variable
new = variable.copy(_omit_Data=True)
if variable._hasData:
new.Data = variable.Data[indices]
return new
#--- End: def
def __setitem__(self, indices, value):
'''
Called to implement assignment to x[indices]
x.__setitem__(indices, value) <==> x[indices]
'''
if isinstance(value, Variable):
value = value.Data
self.variable.Data[indices] = value
#--- End: def
#--- End: class