Source code for piblin_jax.data.collections.measurement_set

"""
MeasurementSet base class for piblin-jax.

Container for multiple Measurement objects representing a series of related measurements.
"""

from collections.abc import Iterator
from typing import Any

from .measurement import Measurement


[docs] class MeasurementSet: """ Base class for collections of Measurement objects. A MeasurementSet represents a series of related measurements, such as: - Time series measurements - Replicate measurements - Parameter sweep measurements - Multi-sample measurements This is the base class. Specialized variants include: - ConsistentMeasurementSet: All measurements have same structure - TidyMeasurementSet: All measurements share comparable conditions - TabularMeasurementSet: Measurements arranged in tabular format Parameters ---------- measurements : list[Measurement] List of Measurement objects in this set. conditions : dict[str, Any] | None, optional Experimental conditions for the entire measurement series (e.g., sample, experimental setup, date). details : dict[str, Any] | None, optional Additional context for this measurement series (e.g., series description, experimental notes). Attributes ---------- measurements : tuple[Measurement, ...] Immutable tuple of measurements in this set. conditions : dict[str, Any] Experimental conditions for this measurement series. details : dict[str, Any] Additional metadata for this measurement series. Notes ----- The measurements are stored as a tuple to ensure immutability, which is required for JAX transformations. Individual measurements can be accessed by indexing or iteration. Examples -------- >>> import numpy as np >>> from piblin_jax.data.datasets import OneDimensionalDataset >>> from piblin_jax.data.collections import Measurement, MeasurementSet >>> >>> # Create replicate measurements >>> x = np.linspace(0, 10, 100) >>> measurements = [] >>> for i in range(3): ... y = np.sin(x) + np.random.normal(0, 0.1, len(x)) ... ds = OneDimensionalDataset(x, y) ... m = Measurement( ... datasets=[ds], ... conditions={"replicate": i+1} ... ) ... measurements.append(m) >>> >>> # Create measurement set >>> ms = MeasurementSet( ... measurements=measurements, ... conditions={"sample": "S1", "date": "2025-10-18"}, ... details={"notes": "Replicate measurements with noise"} ... ) >>> >>> # Access measurements >>> len(ms) 3 >>> first_measurement = ms[0] >>> for m in ms: ... print(m.conditions["replicate"]) 1 2 3 """
[docs] def __init__( self, measurements: list[Measurement], conditions: dict[str, Any] | None = None, details: dict[str, Any] | None = None, ): """ Initialize MeasurementSet with measurements and metadata. Parameters ---------- measurements : list[Measurement] List of Measurement objects in this set. conditions : dict[str, Any] | None, optional Experimental conditions for this measurement series. details : dict[str, Any] | None, optional Additional context for this measurement series. """ self._measurements = tuple(measurements) # Immutable for JAX compatibility self._conditions = conditions if conditions is not None else {} self._details = details if details is not None else {}
@property def measurements(self) -> tuple[Measurement, ...]: """ Get all measurements in this set. :no-index: Returns ------- tuple[Measurement, ...] Immutable tuple of Measurement objects. Examples -------- >>> ms.measurements (<Measurement at 0x...>, <Measurement at 0x...>, <Measurement at 0x...>) """ return self._measurements @property def conditions(self) -> dict[str, Any]: """ Get experimental conditions for this measurement series. :no-index: Returns ------- dict[str, Any] Dictionary of experimental conditions (sample, date, setup, etc.). Examples -------- >>> ms.conditions {'sample': 'S1', 'date': '2025-10-18', 'instrument': 'Spec-X'} """ return self._conditions @property def details(self) -> dict[str, Any]: """ Get additional details for this measurement series. :no-index: Returns ------- dict[str, Any] Dictionary of additional context (notes, quality, etc.). Examples -------- >>> ms.details {'notes': 'Time series', 'quality': 'good'} """ return self._details
[docs] def __len__(self) -> int: """ Get number of measurements in this set. Returns ------- int Number of measurements. Examples -------- >>> len(ms) 3 """ return len(self._measurements)
[docs] def __iter__(self) -> Iterator[Measurement]: """ Iterate over measurements in this set. Yields ------ Measurement Each measurement in order. Examples -------- >>> for measurement in ms: ... print(len(measurement)) 1 1 1 """ return iter(self._measurements)
[docs] def __getitem__(self, index: int | slice) -> Measurement | tuple[Measurement, ...]: """ Get measurement by index. Parameters ---------- index : int or slice Index or slice to access measurements. Returns ------- Measurement or tuple[Measurement, ...] Measurement at the given index, or tuple of measurements for slice. Examples -------- >>> ms[0] <Measurement at 0x...> >>> ms[0:2] (<Measurement at 0x...>, <Measurement at 0x...>) """ return self._measurements[index]