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.21. 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 dimensionfield.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,)