class Pipeline(Node): """ Node defined by a JSON definition. Attributes ---------- json : string pipeline JSON definition definition : OrderedDict pipeline definition output : Output pipeline output node : Node pipeline output node do_write_output : Bool True to call output.write() on execute, false otherwise. """ definition = OrderedDictTrait(readonly=True, help="pipeline definition") json = tl.Unicode(readonly=True, help="JSON definition") output = tl.Instance(Output, readonly=True, help="pipeline output") do_write_output = tl.Bool(True) def _first_init(self, path=None, **kwargs): if (path is not None) + ('definition' in kwargs) + ('json' in kwargs) != 1: raise TypeError( "Pipeline requires exactly one 'path', 'json', or 'definition' argument" ) if path is not None: with open(path) as f: kwargs['definition'] = json.load(f, object_pairs_hook=OrderedDict) return kwargs @tl.validate('json') def _json_validate(self, proposal): s = proposal['value'] definition = json.loads(s) parse_pipeline_definition(definition) return json.dumps(json.loads(s), cls=JSONEncoder) # standardize @tl.validate('definition') def _validate_definition(self, proposal): definition = proposal['value'] parse_pipeline_definition(definition) return definition @tl.default('json') def _json_from_definition(self): return json.dumps(self.definition, cls=JSONEncoder) @tl.default('definition') def _definition_from_json(self): print("definition from json") return json.loads(self.json, object_pairs_hook=OrderedDict) @tl.default('output') def _parse_definition(self): return parse_pipeline_definition(self.definition) def eval(self, coordinates, output=None): """Evaluate the pipeline, writing the output if one is defined. Parameters ---------- coordinates : TYPE Description """ self._requested_coordinates = coordinates output = self.output.node.eval(coordinates, output) if self.do_write_output: self.output.write(output, coordinates) self._output = output return output # ----------------------------------------------------------------------------------------------------------------- # properties, forwards output node # ----------------------------------------------------------------------------------------------------------------- @property def node(self): return self.output.node @property def units(self): return self.node.units @property def dtype(self): return self.node.dtype @property def cache_type(self): return self.node.cache_type @property def style(self): return self.node.style
class MyClass(tl.HasTraits): d = OrderedDictTrait()
class Coordinates(tl.HasTraits): """ Multidimensional Coordinates. Coordinates are used to evaluate Nodes and to define the coordinates of a DataSource nodes. The API is modeled after coords in `xarray <http://xarray.pydata.org/en/stable/data-structures.html>`_: * Coordinates are created from a list of coordinate values and dimension names. * Coordinate values are always either ``float`` or ``np.datetime64``. For convenience, podpac automatically converts datetime strings such as ``'2018-01-01'`` to ``np.datetime64``. * The allowed dimensions are ``'lat'``, ``'lon'``, ``'time'``, and ``'alt'``. * Coordinates from multiple dimensions can be stacked together to represent a *list* of coordinates instead of a *grid* of coordinates. The name of the stacked coordinates uses an underscore to combine the underlying dimensions, e.g. ``'lat_lon'``. Coordinates are dict-like, for example: * get coordinates by dimension name: ``coords['lat']`` * get iterable dimension keys and coordinates values: ``coords.keys()``, ``coords.values()`` * loop through dimensions: ``for dim in coords: ...`` Parameters ---------- dims Tuple of dimension names, potentially stacked. udims Tuple of individual dimension names, always unstacked. """ _coords = OrderedDictTrait(trait=tl.Instance(BaseCoordinates), default_value=OrderedDict()) def __init__(self, coords, dims=None, coord_ref_sys=None, ctype=None, distance_units=None): """ Create multidimensional coordinates. Arguments --------- coords : list List of coordinate values for each dimension. Valid coordinate values: * single coordinate value (number, datetime64, or str) * array of coordinate values * list of stacked coordinate values * :class:`Coordinates1d` or :class:`StackedCoordinates` object dims : list of str, optional List of dimension names. Optional if all items in ``coords`` are named. Valid names are * 'lat', 'lon', 'alt', or 'time' for unstacked coordinates * dimension names joined by an underscore for stacked coordinates coord_ref_sys : str, optional Default coordinates reference system ctype : str, optional Default coordinates type. One of 'point', 'midpoint', 'left', 'right'. distance_units : Units Default distance units. """ if not isinstance(coords, (list, tuple, np.ndarray, xr.DataArray)): raise TypeError( "Invalid coords, expected list or array, not '%s'" % type(coords)) if dims is not None and not isinstance(dims, (tuple, list)): raise TypeError("Invalid dims type '%s'" % type(dims)) if dims is None: for i, c in enumerate(coords): if not isinstance(c, (BaseCoordinates, xr.DataArray)): raise TypeError( "Cannot get dim for coordinates at position %d with type '%s'" "(expected 'Coordinates1d' or 'DataArray')" % (i, type(c))) dims = [c.name for c in coords] if len(dims) != len(coords): raise ValueError("coords and dims size mismatch, %d != %d" % (len(dims), len(coords))) # get/create coordinates dcoords = OrderedDict() for i, dim in enumerate(dims): if dim in dcoords: raise ValueError( "Duplicate dimension name '%s' at position %d" % (dim, i)) if isinstance(coords[i], BaseCoordinates): c = coords[i] elif '_' in dim: cs = [ val if isinstance(val, Coordinates1d) else ArrayCoordinates1d(val) for val in coords[i] ] c = StackedCoordinates(cs) else: c = ArrayCoordinates1d(coords[i]) dcoords[dim] = c self._set_properties(c, dim, ctype, distance_units, coord_ref_sys, i) self.set_trait('_coords', dcoords) super(Coordinates, self).__init__() @tl.validate('_coords') def _validate_coords(self, d): val = d['value'] if len(val) == 0: return val for dim, c in val.items(): if dim != c.name: raise ValueError("Dimension name mismatch, '%s' != '%s'" % (dim, c.name)) dims = [dim for c in val.values() for dim in c.dims] for dim in dims: if dims.count(dim) != 1: raise ValueError("Duplicate dimension name '%s' in dims %s" % (dim, tuple(val.keys()))) crs = list(val.values())[0].coord_ref_sys for i, c in enumerate(val.values()): if c.coord_ref_sys != crs: raise ValueError( "coord_ref_sys mismatch '%s' != '%s' at pos %d" % (c.coord_ref_sys, crs, i)) return val def _set_properties(self, c, name, ctype, distance_units, coord_ref_sys, pos): if isinstance(c, Coordinates1d): cs = [c] names = [name] else: cs = list(c) names = name.split('_') for c, name in zip(cs, names): # set or check the coord_ref_sys if coord_ref_sys is not None: if 'coord_ref_sys' not in c.properties: c.set_trait('coord_ref_sys', coord_ref_sys) elif coord_ref_sys != c.coord_ref_sys: raise ValueError( "coord_ref_sys mismatch %s != %s at pos %d" % (coord_ref_sys, c.coord_ref_sys, pos)) # only set name, ctype, and units if they aren't already set if name is not None and 'name' not in c.properties: c.name = name if ctype is not None and 'ctype' not in c.properties: c.set_trait('ctype', ctype) if distance_units is not None and c.name in [ 'lat', 'lon', 'alt' ] and 'units' not in c.properties: c.set_trait('units', distance_units) # ------------------------------------------------------------------------------------------------------------------ # Alternate constructors # ------------------------------------------------------------------------------------------------------------------ @staticmethod def _coords_from_dict(d, order=None): if sys.version < '3.6': if order is None and len(d) > 1: raise TypeError('order required') if order is not None: if set(order) != set(d): raise ValueError("order %s does not match dims %s" % (order, d)) else: order = d.keys() coords = [] for dim in order: if isinstance(d[dim], Coordinates1d): c = d[dim].copy(name=dim) elif isinstance(d[dim], tuple): c = UniformCoordinates1d.from_tuple(d[dim], name=dim) else: c = ArrayCoordinates1d(d[dim], name=dim) coords.append(c) return coords @classmethod def grid(cls, dims=None, coord_ref_sys=None, ctype=None, distance_units=None, **kwargs): """ Create a grid of coordinates. Valid coordinate values: * single coordinate value (number, datetime64, or str) * array of coordinate values * ``(start, stop, step)`` tuple for uniformly-spaced coordinates * Coordinates1d object This is equivalent to creating unstacked coordinates with a list of coordinate values:: podpac.Coordinates.grid(lat=[0, 1, 2], lon=[10, 20], dims=['lat', 'lon']) podpac.Coordinates([[0, 1, 2], [10, 20]], dims=['lan', 'lon']) Arguments --------- lat : optional coordinates for the latitude dimension lon : optional coordinates for the longitude dimension alt : optional coordinates for the altitude dimension time : optional coordinates for the time dimension dims : list of str, optional in Python>=3.6 List of dimension names, must match the provided keyword arguments. In Python 3.6 and above, the ``dims`` argument is optional, and the dims will match the order of the provided keyword arguments. coord_ref_sys : str, optional Default coordinates reference system ctype : str, optional Default coordinates type. One of 'point', 'midpoint', 'left', 'right'. distance_units : Units Default distance units. Returns ------- :class:`Coordinates` podpac Coordinates See Also -------- points """ coords = cls._coords_from_dict(kwargs, order=dims) return cls(coords, coord_ref_sys=coord_ref_sys, ctype=ctype, distance_units=distance_units) @classmethod def points(cls, coord_ref_sys=None, ctype=None, distance_units=None, dims=None, **kwargs): """ Create a list of multidimensional coordinates. Valid coordinate values: * single coordinate value (number, datetime64, or str) * array of coordinate values * ``(start, stop, step)`` tuple for uniformly-spaced coordinates * Coordinates1d object Note that the coordinates for each dimension must be the same size. This is equivalent to creating stacked coordinates with a list of coordinate values and a stacked dimension name:: podpac.Coordinates.points(lat=[0, 1, 2], lon=[10, 20, 30], dims=['lat', 'lon']) podpac.Coordinates([[[0, 1, 2], [10, 20, 30]]], dims=['lan_lon']) Arguments --------- lat : optional coordinates for the latitude dimension lon : optional coordinates for the longitude dimension alt : optional coordinates for the altitude dimension time : optional coordinates for the time dimension dims : list of str, optional in Python>=3.6 List of dimension names, must match the provided keyword arguments. In Python 3.6 and above, the ``dims`` argument is optional, and the dims will match the order of the provided keyword arguments. coord_ref_sys : str, optional Default coordinates reference system ctype : str, optional Default coordinates type. One of 'point', 'midpoint', 'left', 'right'. distance_units : Units Default distance units. Returns ------- :class:`Coordinates` podpac Coordinates See Also -------- grid """ coords = cls._coords_from_dict(kwargs, order=dims) stacked = StackedCoordinates(coords) return cls([stacked], coord_ref_sys=coord_ref_sys, ctype=ctype, distance_units=distance_units) @classmethod def from_xarray(cls, xcoord, coord_ref_sys=None, ctype=None, distance_units=None): """ Create podpac Coordinates from xarray coords. Arguments --------- xcoord : xarray.core.coordinates.DataArrayCoordinates xarray coords coord_ref_sys : str, optional Default coordinates reference system ctype : str, optional Default coordinates type. One of 'point', 'midpoint', 'left', 'right'. distance_units : Units Default distance units. Returns ------- :class:`Coordinates` podpac Coordinates """ if not isinstance(xcoord, xarray.core.coordinates.DataArrayCoordinates): raise TypeError( "Coordinates.from_xarray expects xarray DataArrayCoordinates, not '%s'" % type(xcoord)) coords = [] for dim in xcoord.dims: if isinstance(xcoord.indexes[dim], (pd.DatetimeIndex, pd.Float64Index, pd.Int64Index)): c = ArrayCoordinates1d.from_xarray(xcoord[dim]) elif isinstance(xcoord.indexes[dim], pd.MultiIndex): c = StackedCoordinates.from_xarray(xcoord[dim]) coords.append(c) return cls(coords, coord_ref_sys=coord_ref_sys, ctype=ctype, distance_units=distance_units) @classmethod def from_definition(cls, d): """ Create podpac Coordinates from a coordinates definition. Arguments --------- d : list coordinates definition Returns ------- :class:`Coordinates` podpac Coordinates See Also -------- from_json, definition """ if not isinstance(d, list): raise TypeError( "Could not parse coordinates definition of type '%s'" % type(d)) coords = [] for elem in d: if isinstance(elem, list): c = StackedCoordinates.from_definition(elem) elif 'start' in elem and 'stop' in elem and ('step' in elem or 'size' in elem): c = UniformCoordinates1d.from_definition(elem) elif 'values' in elem: c = ArrayCoordinates1d.from_definition(elem) else: raise ValueError( "Could not parse coordinates definition item with keys %s" % elem.keys()) coords.append(c) return cls(coords) @classmethod def from_json(cls, s): """ Create podpac Coordinates from a coordinates JSON definition. Example JSON definition:: [ { "name": "lat", "start": 1, "stop": 10, "step": 0.5, }, { "name": "lon", "start": 1, "stop": 2, "size": 100 }, { "name": "time", "ctype": "left" "values": [ "2018-01-01", "2018-01-03", "2018-01-10" ] } ] Arguments --------- s : str coordinates JSON definition Returns ------- :class:`Coordinates` podpac Coordinates See Also -------- json """ d = json.loads(s) return cls.from_definition(d) # ------------------------------------------------------------------------------------------------------------------ # standard dict-like methods # ------------------------------------------------------------------------------------------------------------------ def keys(self): """ dict-like keys: dims """ return self._coords.keys() def values(self): """ dict-like values: coordinates for each key/dimension """ return self._coords.values() def items(self): """ dict-like items: (dim, coordinates) pairs """ return self._coords.items() def get(self, dim, default=None): """ dict-like get: get coordinates by dimension name with an optional """ try: return self[dim] except KeyError: return default def __iter__(self): return iter(self._coords) def __getitem__(self, dim): if dim in self._coords: return self._coords[dim] # extracts individual coords from stacked coords for c in self._coords.values(): if isinstance(c, StackedCoordinates) and dim in c.dims: return c[dim] raise KeyError("Dimension '%s' not found in Coordinates %s" % (dim, self.dims)) def __setitem__(self, dim, c): if not dim in self.dims: raise KeyError("Cannot set dimension '%s' in Coordinates %s" % (dim, self.dims)) # try to cast to ArrayCoordinates1d if not isinstance(c, BaseCoordinates): c = ArrayCoordinates1d(c) if c.name is None: c.name = dim d = self._coords.copy() d[dim] = c self._coords = d def __delitem__(self, dim): if not dim in self.dims: raise KeyError("Cannot delete dimension '%s' in Coordinates %s" % (dim, self.dims)) del self._coords[dim] def __len__(self): return len(self._coords) def update(self, other): """ dict-like update: add/replace coordinates using another Coordinates object """ if not isinstance(other, Coordinates): raise TypeError( "Cannot update Coordinates with object of type '%s'" % type(other)) d = self._coords.copy() d.update(other._coords) self._coords = d def __eq__(self, other): if not isinstance(other, Coordinates): return False # shortcuts if self.dims != other.dims: return False if self.shape != other.shape: return False # full check of underlying coordinates if self._coords != other._coords: return False return True # ------------------------------------------------------------------------------------------------------------------ # Properties # ------------------------------------------------------------------------------------------------------------------ @property def dims(self): """:tuple: Tuple of dimension names, potentially stacked. See Also -------- udims """ return tuple(c.name for c in self._coords.values()) @property def shape(self): """:tuple: Tuple of the number of coordinates in each dimension.""" return tuple(c.size for c in self._coords.values()) @property def ndim(self): """:int: Number of dimensions. """ return len(self.dims) @property def size(self): """:int: Total number of coordinates.""" if len(self.shape) == 0: return 0 return np.prod(self.shape) @property def udims(self): """:tuple: Tuple of unstacked dimension names. If there are no stacked dimensions, then ``dims`` and ``udims`` will be the same:: In [1]: lat = [0, 1] In [2]: lon = [10, 20] In [3]: time = '2018-01-01' In [4]: c = podpac.Coordinates([lat, lon, time], dims=['lat', 'lon', 'time']) In [5]: c.dims Out[5]: ('lat', 'lon', 'time') In [6]: c.udims Out[6]: ('lat', 'lon', 'time') If there are stacked dimensions, then ``udims`` contains the individual dimension names:: In [7]: c = podpac.Coordinates([[lat, lon], time], dims=['lat_lon', 'time']) In [8]: c.dims Out[8]: ('lat_lon', 'time') In [9]: c.udims Out[9]: ('lat', 'lon', 'time') See Also -------- dims """ return tuple(dim for c in self._coords.values() for dim in c.dims) @property def coords(self): """ :xarray.core.coordinates.DataArrayCoordinates: xarray coords, a dictionary-like container of coordinate arrays. """ x = xr.DataArray(np.empty(self.shape), coords=[c.coordinates for c in self._coords.values()], dims=self.dims) return x.coords @property def definition(self): """ Serializable coordinates definition. The ``definition`` can be used to create new Coordinates:: c = podpac.Coordinates(...) c2 = podpac.Coordinates.from_definition(c.definition) See Also -------- from_definition, json """ return [c.definition for c in self._coords.values()] @property def json(self): """:str: JSON-serialized coordinates definition. The ``json`` can be used to create new Coordinates:: c = podapc.Coordinates(...) c2 = podpac.Coordinates.from_json(c.definition) The serialized definition is used to define coordinates in pipelines and to transport coordinates, e.g. over HTTP and in AWS lambda functions. It also provides a consistent hashable value. See Also -------- from_json """ return json.dumps(self.definition, cls=podpac.core.utils.JSONEncoder) @property def hash(self): """ Coordinates hash. *Note: To be replaced with the __hash__ method.* """ return hash_alg(self.json.encode('utf-8')).hexdigest() # #@property # #def gdal_transform(self): # if self['lon'].regularity == 'regular' and self['lat'].regularity == 'regular': # lon_bounds = self['lon'].area_bounds # lat_bounds = self['lat'].area_bounds # transform = [lon_bounds[0], self['lon'].delta, 0, lat_bounds[0], 0, -self['lat'].delta] # else: # raise NotImplementedError # return transform @property def coord_ref_sys(self): """:str: coordinate reference system.""" if not self._coords: return None # the coord_ref_sys is the same for all coords return list(self._coords.values())[0].coord_ref_sys @property def gdal_crs(self): """:str: GDAL coordinate reference system.""" if not self._coords: return None return GDAL_CRS[self.coord_ref_sys] # ------------------------------------------------------------------------------------------------------------------ # Methods # ------------------------------------------------------------------------------------------------------------------ def drop(self, dims, ignore_missing=False): """ Remove the given dimensions from the Coordinates `dims`. Parameters ---------- dims : str, list Dimension(s) to drop. ignore_missing : bool, optional If True, do not raise an exception if a given dimension is not in ``dims``. Default ``False``. Returns ------- :class:`Coordinates` Coordinates object with the given dimensions removed Raises ------ KeyError If a given dimension is missing in the Coordinates (and ignore_missing is ``False``). See Also -------- udrop """ if not isinstance(dims, (tuple, list)): dims = (dims, ) for dim in dims: if not isinstance(dim, string_types): raise TypeError("Invalid drop dimension type '%s'" % type(dim)) if dim not in self.dims and not ignore_missing: raise KeyError( "Dimension '%s' not found in Coordinates with dims %s" % (dim, self.dims)) return Coordinates( [c for c in self._coords.values() if c.name not in dims]) # do we ever need this? def udrop(self, dims, ignore_missing=False): """ Remove the given individual dimensions from the Coordinates `udims`. Unlike `drop`, ``udrop`` will remove parts of stacked coordinates:: In [1]: c = podpac.Coordinates([[[0, 1], [10, 20]], '2018-01-01'], dims=['lat_lon', 'time']) In [2]: c Out[2]: Coordinates lat_lon[lat]: ArrayCoordinates1d(lat): Bounds[0.0, 1.0], N[2], ctype['midpoint'] lat_lon[lon]: ArrayCoordinates1d(lon): Bounds[10.0, 20.0], N[2], ctype['midpoint'] time: ArrayCoordinates1d(time): Bounds[2018-01-01, 2018-01-01], N[1], ctype['midpoint'] In [3]: c.udrop('lat') Out[3]: Coordinates lon: ArrayCoordinates1d(lon): Bounds[10.0, 20.0], N[2], ctype['midpoint'] time: ArrayCoordinates1d(time): Bounds[2018-01-01, 2018-01-01], N[1], ctype['midpoint'] Parameters ---------- dims : str, list Individual dimension(s) to drop. ignore_missing : bool, optional If True, do not raise an exception if a given dimension is not in ``udims``. Default ``False``. Returns ------- :class:`Coordinates` Coordinates object with the given dimensions removed. Raises ------ KeyError If a given dimension is missing in the Coordinates (and ignore_missing is ``False``). See Also -------- drop """ if not isinstance(dims, (tuple, list)): dims = (dims, ) for dim in dims: if not isinstance(dim, string_types): raise TypeError("Invalid drop dimension type '%s'" % type(dim)) if dim not in self.udims and not ignore_missing: raise KeyError( "Dimension '%s' not found in Coordinates with udims %s" % (dim, self.udims)) cs = [] for c in self._coords.values(): if isinstance(c, Coordinates1d): if c.name not in dims: cs.append(c) elif isinstance(c, StackedCoordinates): stacked = [s for s in c if s.name not in dims] if len(stacked) > 1: cs.append(StackedCoordinates(stacked)) elif len(stacked) == 1: cs.append(stacked[0]) return Coordinates(cs) def intersect(self, other, outer=False, return_indices=False): """ Get the coordinate values that are within the bounds of a given coordinates object. The intersection is calculated in each dimension separately. The default intersection selects coordinates that are within the other coordinates bounds:: In [1]: coords = Coordinates([[0, 1, 2, 3]], dims=['lat']) In [2]: other = Coordinates([[1.5, 2.5]], dims=['lat']) In [3]: coords.intersect(other).coords Out[3]: Coordinates: * lat (lat) float64 2.0 The *outer* intersection selects the minimal set of coordinates that contain the other coordinates:: In [4]: coords.intersect(other, outer=True).coords Out[4]: Coordinates: * lat (lat) float64 1.0 2.0 3.0 The *outer* intersection also selects a boundary coordinate if the other coordinates are outside this coordinates bounds but *inside* its area bounds:: In [5]: other_near = Coordinates([[3.25]], dims=['lat']) In [6]: other_far = Coordinates([[10.0]], dims=['lat']) In [7]: coords.intersect(other_near, outer=True).coords Coordinates: * lat (lat) float64 3.0 In [8]: coords.intersect(other_far, outer=True).coords Coordinates: * lat (lat) float64 Parameters ---------- other : :class:`Coordinates1d`, :class:`StackedCoordinates`, :class:`Coordinates` Coordinates to intersect with. outer : bool, optional If True, do an *outer* intersection. Default False. return_indices : bool, optional If True, return slice or indices for the selection in addition to coordinates. Default False. Returns ------- intersection : :class:`Coordinates` Coordinates object consisting of the intersection in each dimension. idx : list List of indices for each dimension that produces the intersection, only if ``return_indices`` is True. """ intersections = [ c.intersect(other, outer=outer, return_indices=return_indices) for c in self.values() ] if return_indices: coords = Coordinates([c for c, I in intersections]) idx = [I for c, I in intersections] return coords, tuple(idx) else: return Coordinates(intersections) def unique(self): """ Remove duplicate coordinate values from each dimension. Returns ------- coords : Coordinates New Coordinates object with unique, sorted coordinate values in each dimension. """ return Coordinates([ c[np.unique(c.coordinates, return_index=True)[1]] for c in self.values() ]) def unstack(self): """ Unstack the coordinates of all of the dimensions. Returns ------- unstacked : :class:`Coordinates` A new Coordinates object with unstacked coordinates. See Also -------- xr.DataArray.unstack """ return Coordinates([self[dim] for dim in self.udims]) def iterchunks(self, shape, return_slices=False): """ Get a generator that yields Coordinates no larger than the given shape until the entire Coordinates is covered. Parameters ---------- shape : tuple The maximum shape of the chunk, with sizes corresponding to the `dims`. return_slice : boolean, optional Return slice in addition to Coordinates chunk. Yields ------ coords : :class:`Coordinates` A Coordinates object with one chunk of the coordinates. slices : list slices for this Coordinates chunk, only if ``return_slices`` is True """ l = [[slice(i, i + n) for i in range(0, m, n)] for m, n in zip(self.shape, shape)] for slices in itertools.product(*l): coords = Coordinates([ self._coords[dim][slc] for dim, slc in zip(self.dims, slices) ]) if return_slices: yield coords, slices else: yield coords def transpose(self, *dims, **kwargs): """ Transpose (re-order) the dimensions of the Coordinates. Parameters ---------- dim_1, dim_2, ... : str, optional Reorder dims to this order. By default, reverse the dims. in_place : boolean, optional If True, transpose the dimensions in-place. Otherwise (default), return a new, transposed Coordinates object. Returns ------- transposed : :class:`Coordinates` The transposed Coordinates object. See Also -------- xarray.DataArray.transpose : return a transposed DataArray """ in_place = kwargs.get('in_place', False) if len(dims) == 0: dims = list(self._coords.keys())[::-1] if len(dims) != self.ndim: raise ValueError( "Invalid transpose dimensions, input %s does not match dims %s" % (dims, self.dims)) if in_place: self._coords = OrderedDict([(dim, self._coords[dim]) for dim in dims]) return self else: return Coordinates([self._coords[dim] for dim in dims]) # ------------------------------------------------------------------------------------------------------------------ # Operators/Magic Methods # ------------------------------------------------------------------------------------------------------------------ def __repr__(self): # TODO JXM rep = str(self.__class__.__name__) for c in self._coords.values(): if isinstance(c, Coordinates1d): rep += '\n\t%s: %s' % (c.name, c) elif isinstance(c, StackedCoordinates): for _c in c: rep += '\n\t%s[%s]: %s' % (c.name, _c.name, _c) return rep