accessors module#

Custom xarray accessors for human-readable labels.

This module provides two complementary accessors:

  1. .labels accessor - Generic mechanism to attach and retrieve human-readable labels for any

    DataArray that uses integer codes internally.

    • Requires that the DataArray has an attribute .attrs["mapping"] containing an integer-to-string dictionary.

    • Example:
      >>> import xarray as xr
      >>> da = xr.DataArray([0], dims="realization", name="realization")
      >>> da.attrs["mapping"] = {0: "manual profile"}
      >>> da.labels.get().item()
      'manual profile'
      
  2. .icssg accessor - Specialized accessor for snow grain type codes according to the

    International Classification for Seasonal Snow on the Ground (ICSSG).

    • Operates on DataArrays that store three-digit SLF-style codes (“Swiss Code F1F2F3”) in their .attrs["units"] metadata.

    • Provides convenience methods:
      • .icssg.primary() → returns primary ICSSG label

      • .icssg.secondary() → returns secondary ICSSG label

Together, these accessors allow xsnow datasets to remain efficient by storing compact integer codes internally, while exposing user-friendly string labels on demand for analysis, visualization, or export.

class xsnow.accessors.LabelsAccessor(xarray_obj)#

Bases: object

A custom xarray accessor to retrieve the human-readable names for variables that use integer codes for efficiency.

This accessor will work on any coordinate or data variable that owns an attribute called “mapping”, which contains the integer-to-string dictionary.

get(as_dict=False)#

Return a labeled version of the DataArray using the integer-to-label mapping stored in .attrs[“mapping”].

Return type:

DataArray | dict

Parameters:#

as_dict (bool): If True, return the raw mapping dict (or {} if none exists).

Returns:#

:

DataArray of string labels with dataset dimensions preserved or dict with the integer-to-labels mapping (empty dict if no mapping exists).

Examples:#

>>> from xsnow.sample_data import single_profile
>>> ds = single_profile()
>>> ds["realization"]
>>> ds["realization"].attrs["mapping"] = {0: "manual profile"}
>>> ds["realization"].labels.get().item()
'manual profile'
>>> ds["location"].labels.get().item()
'Kasererwinkl C6'

Raises:#

AttributeError

If no ‘mapping’ attribute is present and as_dict=False.

class xsnow.accessors.ICSSGAccessor(xarray_obj)#

Bases: object

An accessor to convert integer graincodes to human-readable grain type labels following the convention by the International Classification for Seasonal Snow on the Ground (ICSSG).

Expected: DataArray of int/float codes; NaN allowed.

primary()#

Convert an integer grain code to the ICSSG label of the primary grain type.

Return type:

DataArray

Returns:#

:

DataArray of string labels broadcast over input dimensions.

Examples:#

>>> from xsnow.sample_data import single_profile
>>> ds = single_profile()
>>> ds["grain_type"].squeeze().values
array([772.,   3.,   3., 772.,   4., 772.,   4.,   4.,   6.,   2.],
    dtype=float32)
>>> ds["primary"] = ds["grain_type"].icssg.primary()
>>> ds["primary"].squeeze().values
array(['MFcr', 'RG', 'RG', 'MFcr', 'FC', 'MFcr', 'FC', 'FC', 'SH', 'DF'],
    dtype=object)

Raises:#

AttributeError

If the attribute in ‘units’ is not a known or implemented grain code

secondary()#

Convert an integer grain code to the ICSSG label of the secondary grain type.

Return type:

DataArray

Returns:#

:

DataArray of string labels broadcast over input dimensions.

See Also:#

primary : Convert to primary grain types (see Example section there).

Raises:#

AttributeError

If the attribute in ‘units’ is not a known or implemented grain code

class xsnow.accessors.TimezoneAccessor(xarray_obj)#

Bases: object

Timezone utilities for a timezone-naive time coordinate.

Expected target: a 1D datetime64[ns] DataArray (usually the ‘time’ coord). The intended timezone is stored as a string in the DataArray’s .attrs[‘timezone’] (e.g. ‘UTC’).

Core idea:
  • Keep on-disk times tz-naive for portability.

  • Record the associated tz as attribute.

  • Provide helpers to get a tz-aware index for selection/plotting.

get_attr(default=None)#

Return timezone string from attrs (or default if not present).

Return type:

Optional[str]

set_attr(tz)#

Set/overwrite the timezone attribute (validation included).

Return type:

None

localize(tz=None, *, ambiguous='raise', nonexistent='raise', as_series=False)#

Convert the tz-naive datetime64 array to a tz-aware DatetimeIndex (or Series), using the tz provided or the value in .attrs[‘timezone’].

Returns a pandas object because xarray does not store tz-aware dtypes in coords/data yet in a portable way.

Parameters:
  • tz (str, optional) – Timezone like ‘UTC’ or ‘Europe/Vienna’. Defaults to the stored ‘timezone’ attribute.

  • ambiguous (handling for DST edge cases (forwarded to pandas).)

  • nonexistent (handling for DST edge cases (forwarded to pandas).)

  • as_series (bool) – If True, return a pandas.Series aligned to the same, unchanged index labels as the input DataArray.

Return type:

DatetimeIndex | Series

Examples

>>> time_tz = ds['time'].tz.localize()  # use stored attr
>>> time_tz_utc = time_tz.tz_convert("UTC")
to_datetimeindex_in(target_tz, *, as_series=False, ambiguous='raise', nonexistent='raise')#

Localize to the stored timezone (see .get_attr()), then convert to target_tz and return a pandas DatetimeIndex (or Series).

Parameters:
  • target_tz (str, default "UTC") – Timezone to convert the localized index into.

  • as_series (bool, default False) – If True, return a pandas.Series aligned to the input coordinate.

  • ambiguous (Literal['raise', 'infer', 'NaT']) – Passed to pandas tz_localize when localizing the naive values.

  • nonexistent (Literal['raise', 'shift_forward', 'shift_backward', 'NaT']) – Passed to pandas tz_localize when localizing the naive values.

Return type:

DatetimeIndex | Series

Examples

>>> idx_vienna = ds['time'].tz.to_datetimeindex_in("Europe/Vienna")
>>> idx_utc_series = ds['time'].tz.to_datetimeindex_in("UTC", as_series=True)
to_naive_in(target_tz, *, as_series=False, ambiguous='raise', nonexistent='raise')#

Localize to the stored timezone, then convert to target_tz, drop tz info (make naive), and return as a DataArray (default) or Series.

This is useful when you want to store or compare times in a single canonical tz-naive representation.

Parameters:
  • target_tz (str, default "UTC") – Timezone to convert into before dropping tz info.

  • as_series (bool, default False) – Instead of an xarray.DataArray, return a pandas.Series?

  • ambiguous (Literal['raise', 'infer', 'NaT']) – Passed to pandas tz_localize when localizing the naive values.

  • nonexistent (Literal['raise', 'shift_forward', 'shift_backward', 'NaT']) – Passed to pandas tz_localize when localizing the naive values.

Return type:

DataArray | Series | ndarray

Examples

>>> # UTC-naive view for storage/comparison
>>> t_utc_naive = ds['time'].tz.to_naive_in("UTC")
>>> # Vienna-naive view
>>> t_vie_naive_series = ds['time'].tz.to_naive_in("Europe/Vienna", return_type="series")
between(start=None, end=None, tz=None, *, as_series=False)#

Boolean mask selecting a time range expressed in tz, without mutating the stored (timezone-naive) coordinate.

Parameters:
  • start (str | pandas.Timestamp, optional) – Start datetime in tz. If tz-naive, it will be localized to the target tz. Defaults to the earliest timestamp.

  • end (str | pandas.Timestamp, optional) – End datetime in tz. If tz-naive, it will be localized to the target tz. Defaults to the latest timestamp.

  • tz (str, optional) – Timezone name (e.g. “UTC”, “Europe/Vienna”). If start/end are tz-aware, their timezone takes priority. If omitted, uses .attrs[“timezone”].

  • as_series (bool, default False) – If True, return a pandas.Series mask. Otherwise return an xarray.DataArray aligned to the ‘time’ dimension.

Returns:

Boolean mask aligned to the time coordinate.

Return type:

xarray.DataArray | pandas.Series

Examples

>>> mask = ds['time'].tz.between("2025-01-01 06:00", "2025-01-01 12:00", tz="Europe/Vienna")
>>> ds_sub = ds.sel(time=mask)