Differences from xarray
fiery.xtensor is an xarray-like library, and it
follows xarray's conventions wherever it can: named dimensions, coordinate
labels keyed by dimension name, selection with .sel / .isel,
broadcasting and alignment by name, and a set_options context manager.
If you know xarray, XTensor should feel familiar.
The one structural difference colours everything else: an XTensor is a
torch.Tensor (a subclass), not a container that wraps an array. Autograd,
device, dtype, and __torch_function__ all keep working, and every torch.*
op accepts an XTensor. xarray, by contrast, wraps a NumPy/Dask array and
exposes a curated API over it.
This page lists where the two diverge, in both directions.
Added — things xarray does not have
- A real tensor. Gradients flow through named ops;
.cuda(),.double(),torch.stack,@, and friends all work, because anXTensoris a tensor. - Numeric coordinates with an affine. A dimension coordinate can be stored
compactly as
spacing/origin(materialised on demand), so a regular grid costs O(1) and its spacing can be a learnable tensor that gradients flow back to (Proposal 0001). - Two kinds of unit. Beyond coordinate position units, a tensor's
values carry a physical data unit with dimensional algebra and
conversion, via an optional pint backend
(Proposal 0003). xarray leaves units to
the separate
pint-xarrayproject. - Axis descriptors. A name can be enriched with OME-NGFF-style
type,unit, andorientationfields, and ops can resolve adimby type (Axis descriptors). - Unnamed dimensions. A torch tensor often has anonymous axes (a batch dim,
say);
XTensorallows a dim to be unnamed (None). xarray requires every dimension to be named. - Attribute access to a label.
x.redreaches the position labelled"red"when that label is unambiguous — sugar over.sel. - Ergonomic constructors.
xvector/xmatrixand thex*factories (xzeros,xones,xfill, …) name and label axes at creation time.
Missing — xarray features not (yet) here
- No
Dataset.XTensoris theDataArrayanalogue only; there is no multi-variable container. - No split-apply-combine.
groupby,resample,rolling,weighted, and the rest of that suite are not implemented. - No IO / plotting / pandas bridge. No NetCDF/Zarr readers, no
.plot, no.to_pandas()/MultiIndex. - Curvilinear
.selis single-point and brute force;.interpisn't there yet. A dim can carry a generallat(y, x)-style array of arbitrary values over multiple dims — a curvilinear coordinate, alongside the compact affine form (Proposal 0005, issue #82)..sel(lat=.., lon=..)finds the single nearest grid point by squared Euclidean distance over the queried coordinates' raw magnitudes (no scipy/sklearn dependency, so it stays GPU-capable) — but brute force, with no tree index behind it, and unit-blind (mixing e.g. degrees and metres weights the nearer-magnitude one more heavily). Fine for a one-off lookup against even a large grid; it does not scale to bulk regridding (querying a whole new grid's worth of points at once), where the distance-matrix cost grows with the product of the source grid size and the query count — the reason every tree-based library in this space (xarray'sNDPointIndex,xoak,pyresample) exists in the first place. Curvilinear.interpisn't implemented at all yet. - Alignment is inner-join only. There is no
join="outer"/"left"/"right"(which would need NaN fill); operands align to the intersection of their labels. (xarray's defaultarithmetic_joinis also"inner", so the common case matches.)
Behaviour & vocabulary differences
names, notdims. The dimension names live on.names(xarray:.dims); the values are atorch.Tensor, not a NumPy array.- Unnamed axes are allowed. xarray has no unnamed dims; xtensor does (a
plain tensor's anonymous batch axis). A pointwise op still aligns by name
when the unnamed axes are all leading — the named suffix aligns by name,
the anonymous prefix broadcasts positionally — and when both operands share
the same
names. An all-unnamed operand, a plain tensor, or a scalar broadcasts positionally; aNoneafter a named axis on differently-named operands raises (ambiguous). See broadcasting. - Selection vs interpolation are separate verbs. Like xarray,
.selpicks an existing position (exact, or snapped bymodewith atolerance) and.interpcomputes values off the grid, on both regular and irregular numeric coordinates (Proposal 0004). - A range
.selis half-open, not inclusive.x.sel(t=slice(lo, hi))selectslo <= value < hi, like ordinary Python/__getitem__slicing. xarray's label-based.sel(x=slice(a, b))is inclusive on both ends (a <= value <= b) — a deliberate divergence here, to keep one slicing rule across the whole library rather than a special inclusive-both-ends case for coordinate ranges (issue #109). - Name-as-
dimis method-only.x.sum(dim="height")works; the functional formtorch.sum(x, dim="height")does not — a PyTorch limitation (its C-level argument parser rejects a stringdimbefore our hook runs), not a choice of ours (see Names & dimensions).