Source code for smif.metadata.coordinates
"""Each dimension of a multi-dimensional dataset can be indexed by a set of coordinates.
Dimensions might be represented as Euclidean coordinates over a grid::
>>> x = Coordinates('x', range(0, 100))
>>> y = Coordinates('y', range(0, 100))
A subset of Local Authority Districts in England would be a region-based spatial dimension::
>>> local_authority_districts = Coordinates('LADs', ['E07000128', 'E07000180'])
The hours of an average annual day would be a temporal dimension::
>>> hours = Coordinates('daily_hours', [
... {'name': 0, 'interval': [['PT0H', 'PT1H']]}
... {'name': 1, 'interval': [['PT1H', 'PT2H']]}
... {'name': 2, 'interval': [['PT2H', 'PT3H']]}
... ])
The economic sectors from the International Standard Industrial Classification of All Economic
Activities (ISIC), revision 4 would be a categorical dimension::
>>> economic_sector = Coordinates('ISICrev4', [
... {'name': 'A', 'desc': 'Agriculture, forestry and fishing'},
... {'name': 'B', 'desc': 'Mining and quarrying'},
... {'name': 'C', 'desc': 'Manufacturing'},
... {'name': 'D', 'desc': 'Electricity, gas, steam and air conditioning supply'}
... ])
"""
[docs]class Coordinates(object):
"""Coordinates provide the labels to index a dimension, along with metadata that may be
useful to describe, visualise or convert between dimensions.
Coordinate element names are used to label each position along a finite dimension.
A dict mapping dimension name to list of coordinate elements can be passed to a
:class:`~smif.metadata.spec.Spec` (or :class:`xarray.DataArray`) as `coords`.
Attributes
----------
name : str
Dimension name
dim : str
Alias for dimension name
ids : list
List of labels
elements : list[dict]
List of labels with metadata
Parameters
----------
name : str
Name to identify the dimension that these coordinates index
elements : list
List of simple data types (used to identify elements), or a list of dicts with 'name'
key and other metadata
Raises
------
ValueError
If the list of elements is empty, or infinite
KeyError
If the elements are not a list of simple data types
or a list of dicts with a 'name' key
"""
def __init__(self, name, elements):
self.name = name
self._ids = None
self._elements = None
self._set_elements(elements)
def __eq__(self, other):
return self.name == other.name and self.elements == other.elements
def __hash__(self):
return hash(tuple(frozenset(e.items()) for e in self._elements))
def __repr__(self):
return "<Coordinates name='{}' elements={}>".format(self.name, self.ids)
@property
def elements(self):
"""Elements are a list of dicts with at least a 'name' key
Coordinate elements should not be changed.
"""
return self._elements
@property
def ids(self):
"""Element ids is a list of coordinate identifiers"""
return self._ids
@property
def names(self):
"""Names is an alias for Coordinates.ids"""
return self._ids
def _set_elements(self, elements):
"""Set elements with a list of ids (string or numeric) or dicts (including key 'id')"""
if not elements:
raise ValueError("Coordinates.elements must not be empty")
try:
len(elements)
except TypeError:
raise ValueError("Coordinate.elements must be finite in length")
try:
self._ids = [e["name"] for e in elements]
self._elements = elements
except KeyError:
# elements must have name
msg = (
"Elements in dimension '{}' must have a name field, "
"or be a simple list of identifiers"
)
raise KeyError(msg.format(self.name))
except (TypeError, IndexError):
# elements might not be dict-like - in which case, treat them as names
self._ids = elements
self._elements = [{"name": e} for e in elements]
@property
def dim(self):
"""Dim (dimension) is an alias for Coordinates.name"""
return self.name
@dim.setter
def dim(self, dim):
"""Set name as dim"""
self.name = dim