Source code for xdas.coordinates.scalar
"""
:class:`ScalarCoordinate`: non-dimensional (scalar) coordinate.
Carries a single value without being tied to an array axis.
"""
import numpy as np
from typing_extensions import override
from .core import Coordinate, parse
[docs]
class ScalarCoordinate(Coordinate, ctype="scalar"):
"""
Non-dimensional coordinate that carries a single scalar value.
Unlike dimensional coordinates, a :class:`ScalarCoordinate` is not tied
to an array axis and has no length. Typical use: metadata attached to a
:class:`DataArray` (e.g. an instrument identifier or a shot time).
Parameters
----------
data : scalar-like
The scalar value. Cannot be ``None``.
dim : must be ``None``
Passing a non-``None`` value raises :exc:`ValueError`.
dtype : dtype-like, optional
Cast *data* to this dtype.
"""
[docs]
@override
def __init__(self, data=None, dim=None, dtype=None):
if data is None:
raise TypeError("scalar coordinate cannot be empty, please provide a value")
data, dim = parse(data, dim)
if dim is not None:
raise ValueError("a scalar coordinate cannot be a dim")
if not self._isvalid(data):
raise TypeError("`data` must be scalar-like")
self.data = np.asarray(data, dtype=dtype)
@classmethod
@override
def from_block(cls, start, size, step, dim=None, dtype=None):
raise TypeError("cannot build a scalar coordinate from a block")
@override
def __len__(self):
return 1
@override
def __getitem__(self, item):
raise TypeError("scalar coordinate is not subscriptable")
@override
def __array__(self, dtype=None, copy=None):
# TODO: drop this workaround once Python 3.10 is no longer supported
# (EOL Oct 2026). numpy < 2.3 raises when copy=False on a 0-d array;
# numpy 2.3+ (requires Python 3.11+) handles it correctly.
if copy:
return np.array(self.data, dtype=dtype)
return np.asarray(self.data, dtype=dtype)
@override
def __repr__(self):
return np.array2string(self.data, threshold=0, edgeitems=1)
@property
def dim(self):
"""Always ``None`` — scalar coordinates have no associated dimension."""
return None
@dim.setter
def dim(self, value):
"""Not supported — raises :exc:`ValueError` if *value* is not ``None``."""
if value is not None:
raise ValueError("A scalar coordinate cannot have a `dim` other that None")
@property
@override
def dtype(self):
return self.data.dtype
@property
@override
def ndim(self):
return 0
@property
@override
def shape(self):
return ()
@property
@override
def indices(self):
raise TypeError("scalar coordinate has no indices")
@property
@override
def start(self):
raise TypeError("scalar coordinate has no start")
@property
@override
def end(self):
raise TypeError("scalar coordinate has no end")
@staticmethod
@override
def _isvalid(data):
data = np.asarray(data)
return (data.dtype != np.dtype(object)) and (data.ndim == 0)
@override
def _is_monotonic_increasing(self):
raise TypeError("scalar coordinate has no axis")
@override
def _get_value(self, index):
raise TypeError("scalar coordinate has no elements to index")
@override
def _get_indexer(self, value, method=None):
raise TypeError("cannot get index of scalar coordinate")
@override
def _slice(self, slc):
raise TypeError("scalar coordinate is not sliceable")
@override
def _concat(self, other):
raise TypeError("cannot concatenate scalar coordinate")
@override
def _to_dataset(self, dataset, attrs):
if self.name is None:
raise ValueError("cannot serialize a coordinate with no name")
dataset = dataset.assign_coords(
{self.name: (self.dim, self.values) if self.dim else self.values}
)
return dataset, attrs
@classmethod
@override
def _collect_from_dataset(cls, dataset, name):
return {}
@override
def get_sampling_interval(self, cast=True):
return None
@override
def isscalar(self):
return True
@override
def to_index(self, item, method=None, endpoint=True):
raise NotImplementedError("cannot get index of scalar coordinate")