named-arrays#
named_arrays is an implementation of a
named tensor
that includes first-class support for astropy.units.
Every axis of an array carries a name, and axes are referenced by that name
instead of by position, which allows for more readable code and better
modularity.
With a bare numpy.ndarray, the meaning of each axis lives in the
programmer’s head, and combining two arrays usually means inserting singleton
dimensions until their shapes line up.
Naming the axes removes both problems: arrays broadcast against each other by
matching names, so a singleton dimension is never needed, and an operation such
as a mean along the wavelength axis says exactly that.
named_arrays does not extend the numpy API like xarray.
Instead, it generalizes the numpy API to only use axis names instead of position.
This means that terms such as shape,
which referred to a tuple of integers in the numpy API,
is now a dict, where the keys are the axis names and the values
are the number of elements along that axis.
This forces consumers of this library to stick to the named axes,
and to not “cheat” by using positional indexing.
Many functions in the numpy API have been overridden if possible.
Other functions which are not expressible using the numpy API have
been redefined in the named_arrays namespace.
Installation#
named_arrays is published on PyPI and can be installed using:
pip install named-arrays
Features#
The array types form a hierarchy, from a plain named tensor up to a discrete function of several variables.
named_arrays.ScalarArray, a named tensor withastropy.unitssupport, the analogue ofxarray.Variable. Implicit variants such asnamed_arrays.ScalarLinearSpacedescribe an array without materializing it, and remember the arguments used to define it.named_arrays.UncertainScalarArray, which carries a distribution alongside the nominal value and propagates uncertainty through every operation.named_arrays.NormalUncertainScalarArraybuilds one from a width.named_arrays.Cartesian2dVectorArrayandnamed_arrays.Cartesian3dVectorArray, along with named-component variants such asnamed_arrays.SpectralPositionalVectorArray, where each component is itself any of the array types above.named_arrays.Cartesian2dMatrixArrayand its relatives, which are vectors of vectors, and support the usual matrix operations.named_arrays.FunctionArray, a discrete function pairing inputs with outputs, the analogue ofxarray.DataArray.
Several modules extend the array types to other libraries:
named_arrays.plt for matplotlib,
named_arrays.random and named_arrays.stats for sampling and
statistics,
named_arrays.regridding for resampling curvilinear grids,
named_arrays.optimize for root finding and minimization,
named_arrays.transformations for rotations and translations,
and named_arrays.ndfilters, named_arrays.colorsynth,
named_arrays.geometry, named_arrays.pdf, and
named_arrays.numexpr.
Key concepts#
The shape is a dictionary.
named_arrays.AbstractArray.shape maps each axis name to its length,
and there is no positional equivalent.
Anywhere the numpy API takes an axis=0, this library takes an
axis="detector_x".
Arrays broadcast by matching names.
Two arrays combine along the axes whose names they share, and the axes unique
to either one are added to the result.
An array of shape {"x": 3} plus an array of shape {"y": 2} therefore
has shape {"x": 3, "y": 2}, with no reshaping and no singleton dimensions.
Adding a new dimension to a calculation is a matter of giving an input an extra
named axis.
Arrays are explicit or implicit.
An explicit array such as named_arrays.ScalarArray stores its values.
An implicit array such as named_arrays.ScalarLinearSpace stores the
arguments that define it, so start, stop, and num remain available
long after the array is created.
Implicit arrays work in every operation an explicit array does, and
named_arrays.AbstractArray.explicit materializes one on demand.
Units and uncertainties come along for the ride.
The values inside an array can be an astropy.units.Quantity, so a
dimensional error surfaces as an exception rather than a wrong number.
An named_arrays.UncertainScalarArray carries a distribution which is
propagated through arithmetic, so an error bar at the end of a calculation
needs no separate bookkeeping.
Most of the numpy API already works.
These arrays implement the __array_function__ and __array_ufunc__
protocols, so numpy.mean(), numpy.sqrt(), and most of their
siblings accept them directly, using axis names.
Operations that numpy cannot express are defined in the
named_arrays namespace instead.
Examples#
Arrays with different axis names broadcast against each other automatically, and reductions take the name of the axis to remove.
import numpy as np
import astropy.units as u
import matplotlib.pyplot as plt
import named_arrays as na
a = na.ScalarArray(np.array([1, 2, 3]), axes=("x",))
b = na.ScalarArray(np.array([4, 5]), axes=("y",))
c = a + b
c
ScalarArray(
ndarray=[[5, 6],
[6, 7],
[7, 8]],
axes=('x', 'y'),
)
c.mean("x")
ScalarArray(
ndarray=[6., 7.],
axes=('y',),
)
Indexing uses a dictionary of axis names, so the meaning of an index does not depend on the order of the axes.
c[dict(x=0)]
ScalarArray(
ndarray=[5, 6],
axes=('y',),
)
Since an extra named axis costs nothing, a family of curves is one array, and one plotting call draws all of them.
# Define the independent variable
x = na.linspace(0, 2 * np.pi, axis="x", num=101) * u.rad
# Add an axis representing three different amplitudes
amplitude = na.ScalarArray(np.array([1, 2, 3]), axes=("amplitude",))
# The result has both axes, without any reshaping
y = amplitude * np.sin(x)
fig, ax = plt.subplots(constrained_layout=True);
na.plt.plot(x, y, axis="x", ax=ax);
ax.set_xlabel(f"angle ({x.unit:latex_inline})");
ax.set_ylabel("amplitude");
Uncertainty is propagated through every operation, so the error bar at the end of a calculation needs no separate bookkeeping.
# Define a radius known to about 5%
radius = na.NormalUncertainScalarArray(
nominal=10 * u.cm,
width=0.5 * u.cm,
num_distribution=11,
)
# Compute the area of the corresponding circle
area = np.pi * np.square(radius)
# The uncertainty in the radius is carried into the area
area.nominal, np.std(area.distribution, axis="_distribution")
(<Quantity 314.15926536 cm2>,
ScalarArray(
ndarray=23.63049567 cm2,
axes=(),
))
API Reference#
An in-depth description of the classes and functions defined in the this library.
A named tensor implementation with |
Tutorials#
Jupyter notebook examples on how to use named_arrays.
References#
Folke Eriksson. On the measure of solid angles. Mathematics Magazine, 63(3):184–187, 1990. URL: https://doi.org/10.1080/0025570X.1990.11977515, arXiv:https://doi.org/10.1080/0025570X.1990.11977515, doi:10.1080/0025570X.1990.11977515.
Gabriel Goh. Why momentum really works. Distill, 2017. URL: http://distill.pub/2017/momentum, doi:10.23915/distill.00006.
Pieter G. van Dokkum. Cosmic-Ray Rejection by Laplacian Edge Detection. \pasp , 113(789):1420–1427, November 2001. arXiv:astro-ph/0108003, doi:10.1086/323894.