Source code for ampworks.utils._rich_table

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:  # pragma: no cover
    from pathlib import Path
    from typing import Any, Self, Sequence
    from pandas import Series, DataFrame


[docs] class RichTable: """DataFrame-like results container.""" _required_cols: Sequence[str] = [] # override in subclasses def __init__(self, df: DataFrame) -> None: """ Provides a structured way to store data using a `pd.DataFrame` with additional validation and formatting features. Use this class directly by passing in a `pd.DataFrame`, or subclass it to define custom containers with required columns. Inheriting classes should define the class attribute `_required_cols` which is a list of column names that must be present in the input. If any are missing, initialization will raise a `ValueError`. Parameters ---------- df : pd.DataFrame The input dataframe to store. Columns are validated against the `_required_cols` attribute and then stored. Notes ----- While this container is meant to act as a simplified dataframe, access to the full `pd.DataFrame` is provided via the `df` property. While the entire dataframe cannot be replaced, it can be manipulated in place through this property. Examples -------- A minimal example using `RichTable` directly: .. code-block:: python import pandas as pd from ampworks.utils import RichTable df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]}) table = RichTable(df) print(table) Subclassing to enforce required columns: .. code-block:: python import pandas as pd from ampworks.utils import RichTable class CustomTable(RichTable): _required_cols = ['Seconds', 'Volts'] df = pd.DataFrame({'Seconds': [0, 1], 'Volts': [3.8, 3.7]}) table = CustomTable(df) # valid, no errors raised """ self._validate_columns(df) self._df = df.copy() def __getitem__(self, key: str | list[str]) -> Series | DataFrame: """Retrieve one of more columns by key.""" return self._df[key] def __setattr__(self, name: str, value: Any) -> None: if name.startswith('_'): object.__setattr__(self, name, value) else: raise AttributeError( f"Cannot set attribute {name}. Use 'df' to modify columns." ) def __getattr__(self, name: str) -> Series: """Provide attribute-style access to columns.""" df = object.__getattribute__(self, '_df') if name in df.columns: return df[name] raise AttributeError(name) def __repr__(self) -> str: """Return the string representation of the underlying DataFrame.""" return repr(self._df) @classmethod def _validate_columns(cls, df: DataFrame) -> None: """ Ensure that all required columns are present. Parameters ---------- df : pd.DataFrame The input DataFrame to validate during initialization. Raises ------ ValueError If any columns listed in `_required_cols` are missing. """ from ampworks._checks import _check_columns _check_columns(df, set(cls._required_cols))
[docs] @classmethod def from_csv(cls, path: str | Path) -> Self: """ Create a new instance from a CSV file. Parameters ---------- path : str or Path Path to the CSV file. Returns ------- table : Self A new instance initialized with data from the file. """ from pandas import read_csv df = read_csv(path) return cls(df)
@property def df(self) -> DataFrame: """The underlying DataFrame stored in the container.""" return self._df
[docs] def to_csv(self, path: str | Path) -> None: """ Write the table to a CSV file. Parameters ---------- path : str or Path Path to the output file. """ self._df.to_csv(path, index=False)
[docs] def copy(self) -> Self: """ Returns a copy of the instance. Returns ------- table : Self A deep copy of the instance. Does not share memory with original. """ from copy import deepcopy return deepcopy(self)