Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Creating DataArrays

Creating DataArrays in Coordax: A Pedagogical Introduction

This guide demonstrates how to create Field objects (coordax’s equivalent to xarray DataArrays) for different types of scientific data.

import coordax as cx
import numpy as np

# NOTE: cx.field() is preferred over cx.wrap() in coordax >= 0.2

1. RGB Image (Height × Width × Channels)

An RGB image has spatial dimensions (height, width) and a channel dimension (R, G, B). Because LabeledAxis ticks must be numeric, we use SizedAxis for the channel dimension.

height, width, channels = 8, 8, 3
rgb_data = np.random.randint(0, 256, size=(height, width, channels), dtype=np.uint8)
# shape: (8, 8, 3) | dtype: uint8 | units: digital number [0,255]

h_axis = cx.LabeledAxis('height', np.arange(height, dtype=float))
w_axis = cx.LabeledAxis('width', np.arange(width, dtype=float))
# NOTE: LabeledAxis ticks must be numeric; use SizedAxis for categorical dims
c_axis = cx.SizedAxis('channel', channels)

rgb_image = cx.field(rgb_data, h_axis, w_axis, c_axis)
# shape: (8, 8, 3) | dims: ('height','width','channel') | units: DN
print(f"RGB Image: {rgb_image.dims}, shape: {rgb_image.shape}")
RGB Image: ('height', 'width', 'channel'), shape: (8, 8, 3)

2. Hyperspectral/Radiance Image (Height × Width × Wavelength)

Hyperspectral images have spatial dimensions plus a wavelength/spectral dimension.

height, width, n_bands = 8, 8, 16
hsi_data = np.random.randn(height, width, n_bands).astype(np.float32)

h_axis = cx.LabeledAxis('y', np.arange(height, dtype=float))
w_axis = cx.LabeledAxis('x', np.arange(width, dtype=float))
wavelength_axis = cx.LabeledAxis('wavelength', np.linspace(400, 900, n_bands))

hsi_image = cx.field(hsi_data, h_axis, w_axis, wavelength_axis)
# shape: (8, 8, 16) | dims: ('y','x','wavelength') | units: reflectance [-]
print(f"HSI Image: {hsi_image.dims}, shape: {hsi_image.shape}")

# Access wavelength coordinate values via coord_fields
wavelengths = hsi_image.coord_fields['wavelength']
# shape: (16,) | dims: ('wavelength',) | units: nm  [400 … 900 nm]
print(f"Wavelength range: {float(wavelengths.data.min()):.1f} - {float(wavelengths.data.max()):.1f} nm")
HSI Image: ('y', 'x', 'wavelength'), shape: (8, 8, 16)
Wavelength range: 400.0 - 900.0 nm

3. Time Series (Time)

A simple 1D time series with temporal coordinates.

n_timesteps = 100
time_series_data = (
    np.sin(np.linspace(0, 10 * np.pi, n_timesteps))
    + np.random.randn(n_timesteps) * 0.1
)

time_axis = cx.LabeledAxis('time', np.arange(n_timesteps, dtype=float))
time_series = cx.field(time_series_data, time_axis)
# shape: (100,) | dims: ('time',) | units: dimensionless [sin+noise]
print(f"Time Series: {time_series.dims}, shape: {time_series.shape}")
Time Series: ('time',), shape: (100,)

4. 2D Spatial + Time (e.g., Temperature field over time)

n_time, ny, nx = 10, 16, 16
spatial_temporal_data = np.random.randn(n_time, ny, nx).astype(np.float32)

time_axis = cx.LabeledAxis('time', np.arange(n_time) * 3600.0)  # seconds
y_axis = cx.LabeledAxis('y', np.linspace(0, 100, ny))            # km
x_axis = cx.LabeledAxis('x', np.linspace(0, 100, nx))            # km

temperature_field = cx.field(spatial_temporal_data, time_axis, y_axis, x_axis)
# shape: (10, 16, 16) | dims: ('time','y','x') | units: K (synthetic random data)
print(f"2D+Time Field: {temperature_field.dims}, shape: {temperature_field.shape}")
2D+Time Field: ('time', 'y', 'x'), shape: (10, 16, 16)

5. 3D Spatial + Time (e.g., Atmospheric/Ocean model)

n_time, nz, ny, nx = 5, 4, 8, 8
volume_temporal_data = np.random.randn(n_time, nz, ny, nx).astype(np.float32)

time_axis = cx.LabeledAxis('time', np.arange(n_time, dtype=float))
z_axis = cx.LabeledAxis('z', np.linspace(0, 10, nz))        # km
y_axis = cx.LabeledAxis('y', np.linspace(-50, 50, ny))
x_axis = cx.LabeledAxis('x', np.linspace(-50, 50, nx))

atmospheric_field = cx.field(volume_temporal_data, time_axis, z_axis, y_axis, x_axis)
# shape: (5, 4, 8, 8) | dims: ('time','z','y','x') | units: K
print(f"3D+Time Field: {atmospheric_field.dims}, shape: {atmospheric_field.shape}")
3D+Time Field: ('time', 'z', 'y', 'x'), shape: (5, 4, 8, 8)

6. 2D Spatial + Wavelength + Time (Satellite Imagery)

n_time, ny, nx, n_wl = 4, 8, 8, 16
spectral_temporal_data = np.random.randn(n_time, ny, nx, n_wl).astype(np.float32)

time_axis = cx.LabeledAxis('time', np.arange(n_time, dtype=float) * 86400.0)
y_axis = cx.LabeledAxis('latitude', np.linspace(-10, 10, ny))
x_axis = cx.LabeledAxis('longitude', np.linspace(-10, 10, nx))
wavelength_axis = cx.LabeledAxis('wavelength', np.linspace(400, 2500, n_wl))

satellite_timeseries = cx.field(
    spectral_temporal_data, time_axis, y_axis, x_axis, wavelength_axis
)
# shape: (4, 8, 8, 16) | dims: ('time','latitude','longitude','wavelength') | units: reflectance
print(f"2D+Wavelength+Time: {satellite_timeseries.dims}, shape: {satellite_timeseries.shape}")
2D+Wavelength+Time: ('time', 'latitude', 'longitude', 'wavelength'), shape: (4, 8, 8, 16)

7. Key Patterns Summary

  • cx.LabeledAxis('name', numeric_array) — axis with physical tick values (ticks must be numeric)
  • cx.SizedAxis('name', size) — axis with only a name and size (no tick values; use for categorical dims)
  • cx.field(data, axis1, axis2, ...) — create a coordinate-aware Field (preferred in coordax >= 0.2)
  • field.coord_fields['dim'] — access coordinate value Field for a dimension
  • field.axes['dim'].ticks — access raw tick values from a LabeledAxis
# Demonstrate key access patterns
print("=== Key access patterns ===")

# LabeledAxis ticks
wl_axis = cx.LabeledAxis('wavelength', np.linspace(400, 900, 8))
field_1d = cx.field(np.random.randn(8), wl_axis)
# shape: (8,) | dims: ('wavelength',) | units: arbitrary

print(f"field.dims: {field_1d.dims}")
print(f"field.shape: {field_1d.shape}")
print(f"field.axes['wavelength'].ticks[:3]: {field_1d.axes['wavelength'].ticks[:3]}")
print(f"field.coord_fields['wavelength'].data[:3]: {field_1d.coord_fields['wavelength'].data[:3]}")

# SizedAxis (no ticks)
sz_axis = cx.SizedAxis('channel', 3)
field_sized = cx.field(np.random.randn(3), sz_axis)
print(f"\nSizedAxis dims: {field_sized.dims}, shape: {field_sized.shape}")
=== Key access patterns ===
field.dims: ('wavelength',)
field.shape: (8,)
field.axes['wavelength'].ticks[:3]: [400.         471.42857143 542.85714286]
field.coord_fields['wavelength'].data[:3]: [400.         471.42857143 542.85714286]

SizedAxis dims: ('channel',), shape: (3,)