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.

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");
_images/index_3_0.png

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.

named_arrays

A named tensor implementation with astropy.units support.

Tutorials#

Jupyter notebook examples on how to use named_arrays.

References#

[1]

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.

[2]

Gabriel Goh. Why momentum really works. Distill, 2017. URL: http://distill.pub/2017/momentum, doi:10.23915/distill.00006.

[3]

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.


Indices and tables#