mescla.types.ChemTable

class mescla.types.ChemTable(data, species=None, index=None, units='mg/L', sigma=None, censored=None)[source]

Bases: object

A matrix of concentrations with row and column labels.

Parameters:
  • data (array_like, shape (n_rows, n_species)) – Concentrations. Rows are waters, columns are chemical species.

  • species (sequence of str, optional) – Column labels. Generated as s1, s2, ... when omitted.

  • index (sequence of str, optional) – Row labels. Generated using _row_prefix when omitted.

  • units (str or mapping, default "mg/L") –

    Units, per species. A single string applies to every species; a mapping names the exceptions, with anything unlisted taking DEFAULT_UNIT.

    Per-species units are not pedantry. A real analysis sheet routinely mixes them – concentrations in mg/L beside electrical conductivity in uS/cm and a delta value in permil – and a single table-level label can only describe such a table by lying about part of it. The conversions in mescla.prep.units read and write these per species, so a table that is partly converted says so accurately.

    The mixing algebra itself is linear within each species, so mixed units across columns are mathematically harmless. It is the conversions that depend on the label being right.

  • sigma (array_like, shape (n_rows, n_species), optional) – Standard deviations of the analyses. None means “unweighted”; a scalar is broadcast to every entry.

  • censored (array_like of bool, shape (n_rows, n_species), optional) – True where the value is a non-detect reported at its detection limit, so the true concentration is somewhere in [0, value]. The estimators treat these as one-sided information rather than as measurements – see mescla.mixing.lsq.mixing_ratios().

Notes

Missing analyses are represented as nan in data. They are allowed in the container – real datasets are ragged – and each function states what it does with them. See mescla.prep.missing.

data: ndarray
species: tuple[str, ...] = None
index: tuple[str, ...] = None
units: Any = 'mg/L'
sigma: ndarray | None = None
censored: ndarray | None = None
property n_rows: int
property n_species: int
property shape: tuple[int, int]
classmethod from_frame(frame, units='mg/L', sigma=None)[source]

Build from a DataFrame whose columns are species and whose index labels rows.

Parameters:
  • frame (pd.DataFrame)

  • units (Any)

  • sigma (Any)

Return type:

ChemTable

to_frame()[source]

Return the concentrations as a DataFrame (species as columns).

Return type:

pd.DataFrame

sigma_frame()[source]

Return the standard deviations as a DataFrame, or None if unset.

Return type:

pd.DataFrame | None

select_species(species)[source]

Return a copy restricted to species, in the order given.

This is the workhorse of iterative EMMA species elimination (Tubau et al., 2014, analyses A-E).

Parameters:

species (list[str] | tuple[str, ...])

Return type:

ChemTable

drop_species(species)[source]

Return a copy without species.

Parameters:

species (str | list[str] | tuple[str, ...])

Return type:

ChemTable

select_rows(rows)[source]

Return a copy restricted to the named rows, in the order given.

Parameters:

rows (list[str] | tuple[str, ...])

Return type:

ChemTable

unit_of(species)[source]

Unit of one species.

Parameters:

species (str)

Return type:

str

units_dict()[source]

Units as a {species: unit} mapping.

Return type:

dict[str, str]

property is_homogeneous: bool

True when every species shares one unit.

property common_unit: str | None

The shared unit, or None when the table mixes units.

with_units(units)[source]

Return a copy relabelled. Values are untouched – this states what the numbers already are; it does not convert them.

Use it when a loader guessed wrong, e.g. a table read as mg/L whose conductivity column is really uS/cm:

table = table.with_units({"EC": "uS/cm"})
Parameters:

units (Any)

Return type:

ChemTable

property n_missing: int

Number of missing analyses (nan entries).

property n_censored: int

Number of non-detects.

property is_complete: bool

True when every analysis is present.