Interoperability with xarray and NetCDF

This example converts a SpectroChemPy dataset to xarray, uses it as a normal xarray object, saves it as NetCDF, and restores it in SpectroChemPy.

Create a small dataset

A synthetic dataset keeps this example fast and self-contained. It represents three infrared spectra measured on a short wavenumber axis.

from datetime import UTC
from datetime import datetime
from pathlib import Path
from tempfile import TemporaryDirectory

import numpy as np

import spectrochempy as scp


wavenumbers = scp.Coord(
    [1800.0, 1760.0, 1720.0, 1680.0, 1640.0, 1600.0],
    name="x",
    title="wavenumber",
    units="cm^-1",
)
samples = scp.Coord(
    [0.0, 1.0, 2.0],
    labels=["reference", "sample A", "sample B"],
    name="y",
    title="sample index",
)

data = np.array(
    [
        [0.10, 0.14, 0.22, 0.18, 0.12, 0.08],
        [0.12, 0.18, 0.31, 0.24, 0.15, 0.10],
        [0.09, 0.16, 0.28, 0.21, 0.13, 0.07],
    ]
)
mask = np.zeros_like(data, dtype=bool)
mask[1, 2] = True

dataset = scp.NDDataset(
    data,
    dims=["y", "x"],
    coordset=[samples, wavenumbers],
    mask=mask,
    units="absorbance",
    name="demo_spectra",
    title="Infrared spectra",
    description="Synthetic spectra for the xarray and NetCDF tutorial.",
    author="SpectroChemPy documentation team",
    origin="synthetic example",
    meta={
        "sample_set": "tutorial",
        "conditions": {"temperature_K": 298.15, "atmosphere": "nitrogen"},
        "replicates": 3,
    },
)
dataset.acquisition_date = datetime(2025, 2, 3, 9, 30, tzinfo=UTC)
dataset.history = "Created for the xarray and NetCDF interoperability tutorial"

Inspect the SpectroChemPy object

Coordinates, units, scientific identity, and provenance are all attached to the dataset before conversion.

NDDataset [demo_spectra] — float64, shape: (y:3, x:6), a.u.
name
:
demo_spectra
author
:
SpectroChemPy documentation team
created
:
2026-08-18 21:26:05+00:00
description
:
Synthetic spectra for the xarray and NetCDF tutorial.
history
:
2026-08-18 21:26:05+00:00> Created for the xarray and NetCDF interoperability tutorial
Data
title
:
Infrared spectra
values
:
[[ 0.1 0.14 ... 0.12 0.08]
[ 0.12 0.18 ... 0.15 0.1]
[ 0.09 0.16 ... 0.13 0.07]] a.u.
shape
:
(y:3, x:6)
Dimension `x`
size
:
6
title
:
wavenumber
coordinates
:
[ 1800 1760 1720 1680 1640 1600] cm⁻¹
Dimension `y`
size
:
3
title
:
sample index
coordinates
:
[ 0 1 2]
labels
:
[ reference sample A sample B]


print("Dimensions:", tuple(dataset.dims))
print("Coordinates:", dataset.coordtitles)
print("Data units:", dataset.units)
print(
    "Selected metadata:",
    {
        "name": dataset.name,
        "title": dataset.title,
        "author": dataset.author,
        "origin": dataset.origin,
        "acquisition_date": dataset.acquisition_date,
    },
)
Dimensions: ('y', 'x')
Coordinates: ['wavenumber', 'sample index']
Data units: a.u.
Selected metadata: {'name': 'demo_spectra', 'title': 'Infrared spectra', 'author': 'SpectroChemPy documentation team', 'origin': 'synthetic example', 'acquisition_date': '2025-02-03 09:30:00+00:00'}

Convert to xarray

to_xarray returns a regular xarray.Dataset. This makes the data available to tools throughout the scientific Python ecosystem while retaining the portable SpectroChemPy metadata needed for restoration.

xds = dataset.to_xarray()

print("xarray type:", type(xds))
print("xarray dimensions:", dict(xds.sizes))
print("xarray coordinates:", tuple(xds.coords))
print(
    "Selected portable attrs:",
    {
        key: xds.attrs[key]
        for key in ("scpy_name", "scpy_title", "scpy_author", "scpy_origin")
    },
)
xarray type: <class 'xarray.core.dataset.Dataset'>
xarray dimensions: {'y': 3, 'x': 6}
xarray coordinates: ('y', 'x', 'y_labels')
Selected portable attrs: {'scpy_name': 'demo_spectra', 'scpy_title': 'Infrared spectra', 'scpy_author': 'SpectroChemPy documentation team', 'scpy_origin': 'synthetic example'}

Use xarray normally

For example, xarray can compute a mean spectrum along the sample dimension. We keep this derived object separate and use the original exported dataset for the round-trip below.

xds_mean = xds.mean(dim="y")
print("Mean spectrum shape:", xds_mean["demo_spectra"].shape)
Mean spectrum shape: (6,)

Export to NetCDF and restore

The SpectroChemPy NetCDF method uses the same xarray-backed portable mapping. A temporary directory avoids leaving generated files in the source tree.

Check the portable round-trip

The numerical data, support information, and selected scientific metadata all survive. created and modified are generated automatically when an NDDataset is created; they do not need to be set manually.

Restored successfully: {'dimensions': ('y', 'x'), 'units': 'a.u.', 'mask': True, 'identity': 'demo_spectra', 'history entries': 1, 'metadata sample set': 'tutorial'}

What is preserved?

Portable persistence keeps the scientific subset that is useful across tools and languages. It is intentionally narrower than SpectroChemPy’s native persistence.

Portable round-trip

Examples

Preserved

Dimensions and coordinates, units, masks, name/title/description, author/origin, timestamps, history, JSON-compatible metadata, and supported textual coordinate labels

Not full native fidelity

Arbitrary Python metadata objects, full CoordSet topology, result containers, and richer non-portable label structures

Use native SpectroChemPy persistence when exact runtime fidelity is required; use xarray and NetCDF when interoperability is the goal.

Total running time of the script: (0 minutes 0.539 seconds)