Skip to content

Attributes

Each DatasetView carries typed attributes at two levels: dataset-level (global) attributes and per-variable attributes on individual arrays. Attribute values are stored in the .af files — global values in a reserved _global file, per-variable values on the variable's own file. Only the attribute key names are recorded in atlas.json (as part of the schema), so listing which attributes exist is cheap and doesn't load any array bytes.

Dataset-level (global) attributes

ds = atlas.create_dataset("jan_2024")

ds.set_attribute("month", 1)            # inferred int
ds.set_attribute("station", "KNMI")     # inferred str
ds.set_attribute("calibrated", True)    # inferred bool

ds.get_attribute("month")               # -> 1
ds.get_attribute("missing")             # -> None
ds.attributes()                         # -> {"month": 1, "station": "KNMI", "calibrated": True}

Per-variable attributes

Attributes can also attach to a specific array (e.g. units on temperature):

ds.define_array("temperature", dtype="float32", dims=["lat", "lon"], shape=[4, 8])
ds.set_array_attribute("temperature", "units", "degC")
ds.set_array_attribute("temperature", "valid_range", [-40.0, 60.0], dtype="f64")

ds.get_array_attribute("temperature", "units")   # -> "degC"
ds.array_attributes("temperature")               # -> {"units": "degC", "valid_range": [...]}

set_array_attribute raises KeyError if the array isn't defined in the dataset.

Durability

Attribute writes are buffered in memory and only reach disk on atlas.flush() (or leaving a with atlas: block), together with the array data. The reserved _global/data.af file is created lazily — only once a dataset actually sets a global attribute.

The on-disk type system

Atlas's Attr type mirrors the underlying array-format AttributeValue:

On-disk type Python type returned on read
bool bool
int8 / int16 / int32 / int64 int
uint8 / uint16 / uint32 / uint64 int
float32 / float64 float
string str
binary bytes
timestamp_nanoseconds int (nanoseconds; stored as an RFC 3339 string)
any of the above as a list list

Type is inferred from the Python value by default — intint64, floatfloat64, strstring, bytesbinary, boolbool.

Overriding inferred types

Pass dtype= to narrow or force a specific type (works on both set_attribute and set_array_attribute):

ds.set_attribute("sensor_id", 7, dtype="int8")          # stored as int8, range-checked
ds.set_attribute("ratio", 0.5, dtype="float32")         # stored as float32
ds.set_attribute("observed_at",
                 np.datetime64("2024-01-15T10:00", "ns").astype("int64").item(),
                 dtype="timestamp_nanoseconds")

Unlike earlier versions, width hints now preserve the storage type: dtype="int8" stores an 8-bit integer, not a widened int64. A timestamp_nanoseconds attribute is stored as an RFC 3339 string and restored to a timestamp on read.

Per-variable xarray attributes

When you write an xr.Dataset via atlas.add_xarray_dataset(ds, name), each variable's attrs are stored as real per-variable attributes on that variable's array, and the dataset's own attrs become dataset-level attributes:

ds = xr.Dataset(
    data_vars={"temperature": xr.DataArray(arr, dims=["lat", "lon"],
                                            attrs={"units": "C"})},
    attrs={"station": "KNMI"},
)
atlas.add_xarray_dataset(ds, "jan_2024")

view = atlas.open_dataset("jan_2024")
view.attributes()                        # {"station": "KNMI"}
view.array_attributes("temperature")     # {"units": "C"}

On read, atlas.open_as_xarray_dataset("jan_2024") puts each variable's attrs back on the right DataArray and the global attrs back on the Dataset.

See xarray integration for the full storage convention.

JSON-encoded "complex" attributes

xarray attribute values are sometimes nested dicts or other structures that don't map to atlas's on-disk types. The xarray bridge JSON-encodes those and prefixes the string with json:, decoding transparently on read. You generally don't need to think about this, but it's why some values come back as strings starting with json: if you read them through the raw attribute API. Simple lists of scalars are stored natively as typed-list attributes.