Skip to content

Io utils

results_to_dataset

results_to_dataset(results, burst_times, z=None, freq=None, attrs=None)

Concatenate a list of per-burst result dictionaries into an xarray Dataset.

Dimensions are inferred from the shapes of the values. The first axis of each output variable is always burst_time (length len(results)). Inner axes are matched against z and freq when provided:

=================================  ============================
First non-None value shape         Output dims
=================================  ============================
scalar                             (burst_time,)
(len(z),)                          (burst_time, z)
(len(z) - 1,)                      (burst_time, z_mid)
(len(freq),)                       (burst_time, freq)
(len(z), len(freq))                (burst_time, z, freq)
(len(z) - 1, len(freq))            (burst_time, z_mid, freq)
=================================  ============================

Keys "time", "z", and "freq" inside the per-burst dicts are treated as coordinates and are not written as data variables.

Parameters:

Name Type Description Default
results list of dict

Per-burst result dictionaries. Keys missing from a given burst are filled with NaN in the output.

required
burst_times ndarray

1D array of length len(results) giving a representative timestamp for each burst. Used as the burst_time coordinate.

required
z ndarray

Height coordinate, shape (n_heights,). Used to match inner axes of shape (n_heights,) or (n_heights - 1,).

None
freq ndarray

Frequency coordinate, shape (n_freq,). Used to match inner axes of shape (n_freq,).

None
attrs dict

Global attributes to attach to the returned Dataset.

None

Returns:

Type Description
Dataset

Dataset with variables keyed by result-dict keys and burst_time as the leading dimension.

Source code in src/pytoast/utils/io_utils.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def results_to_dataset(
    results: list[dict[str, Any]],
    burst_times: np.ndarray,
    z: np.ndarray | None = None,
    freq: np.ndarray | None = None,
    attrs: dict | None = None,
) -> xr.Dataset:
    """Concatenate a list of per-burst result dictionaries into an xarray
    Dataset.

    Dimensions are inferred from the shapes of the values. The first axis of
    each output variable is always ``burst_time`` (length ``len(results)``).
    Inner axes are matched against ``z`` and ``freq`` when provided:

    ```
    =================================  ============================
    First non-None value shape         Output dims
    =================================  ============================
    scalar                             (burst_time,)
    (len(z),)                          (burst_time, z)
    (len(z) - 1,)                      (burst_time, z_mid)
    (len(freq),)                       (burst_time, freq)
    (len(z), len(freq))                (burst_time, z, freq)
    (len(z) - 1, len(freq))            (burst_time, z_mid, freq)
    =================================  ============================
    ```

    Keys ``"time"``, ``"z"``, and ``"freq"`` inside the per-burst dicts are
    treated as coordinates and are not written as data variables.

    Parameters
    ----------
    results : list of dict
        Per-burst result dictionaries. Keys missing from a given burst are
        filled with NaN in the output.
    burst_times : np.ndarray
        1D array of length ``len(results)`` giving a representative timestamp
        for each burst. Used as the ``burst_time`` coordinate.
    z : np.ndarray, optional
        Height coordinate, shape (n_heights,). Used to match inner axes of
        shape (n_heights,) or (n_heights - 1,).
    freq : np.ndarray, optional
        Frequency coordinate, shape (n_freq,). Used to match inner axes of
        shape (n_freq,).
    attrs : dict, optional
        Global attributes to attach to the returned Dataset.

    Returns
    -------
    xr.Dataset
        Dataset with variables keyed by result-dict keys and ``burst_time`` as
        the leading dimension.
    """
    if not isinstance(results, list) or len(results) == 0:
        raise ValueError("`results` must be a non-empty list of dicts")

    burst_times = np.asarray(burst_times)
    if burst_times.ndim != 1 or len(burst_times) != len(results):
        raise ValueError(f"`burst_times` must be 1D with length {len(results)}; got shape {burst_times.shape}")

    n_bursts = len(results)
    n_heights = int(len(z)) if z is not None else None
    n_freq = int(len(freq)) if freq is not None else None

    # Union of variable keys across all bursts, excluding coord-reserved names.
    var_keys: list[str] = []
    seen = set()
    for r in results:
        for k in r.keys():
            if k in COORD_KEYS or k in seen:
                continue
            seen.add(k)
            var_keys.append(k)

    coords: dict[str, Any] = {"burst_time": ("burst_time", burst_times)}
    if z is not None:
        z_arr = np.asarray(z)
        coords["z"] = ("z", z_arr)
        if n_heights is not None and n_heights > 1:
            coords["z_mid"] = ("z_mid", 0.5 * (z_arr[:-1] + z_arr[1:]))
    if freq is not None:
        coords["freq"] = ("freq", np.asarray(freq))

    data_vars: dict[str, tuple[tuple[str, ...], np.ndarray]] = {}
    for key in var_keys:
        canonical = _first_non_none_value(results, key)
        if canonical is None:
            continue  # all-None column; skip
        dims_inner, shape_inner = _infer_dims(canonical, key, n_heights=n_heights, n_freq=n_freq)
        full_shape = (n_bursts, *shape_inner)
        arr = np.full(full_shape, np.nan, dtype=float)
        for i, r in enumerate(results):
            v = r.get(key, None)
            if v is None:
                continue
            v_arr = np.asarray(v, dtype=float)
            if v_arr.shape != shape_inner:
                raise ValueError(
                    f"Shape mismatch for key {key!r} at burst {i}: expected {shape_inner}, got {v_arr.shape}"
                )
            arr[i] = v_arr
        dims = ("burst_time", *dims_inner)
        data_vars[key] = (dims, arr)

    ds = xr.Dataset(data_vars=data_vars, coords=coords)
    if attrs:
        ds.attrs.update(attrs)
    return ds