clophfit.prtecan.titration#

Prtecan/prtecan.py.

Classes#

TitrationConfig

Parameters defining the fitting data with callback support.

BufferFit

Store (robust) linear fit result.

Buffer

Buffer handling for a titration.

TitrationResults

Manage titration results with optional lazy computation.

Titration

Build titrations from grouped Tecanfiles and concentrations or pH values.

TecanConfig

Group tecan cli options.

McmcSpec

A per-well MCMC request, decided by the caller rather than parsed downstream.

Module Contents#

class clophfit.prtecan.titration.TitrationConfig#

Parameters defining the fitting data with callback support.

noise_alpha: tuple[float, ...] = ()#

Proportional noise coefficients per label.

When provided, adds a proportional term to the error estimate so that high-signal wells are appropriately down-weighted: y_err^2 = gain * signal + bg_err^2 + (alpha * signal)^2

Values typically from MCMC multi-noise shared_noise_params.csv. Empty tuple disables the correction (legacy behaviour).

noise_gain: tuple[float, ...] = ()#

Poisson gain coefficients per label.

Replaces the hardcoded gain=1 in the shot-noise Poisson term: y_err^2 = gain * signal + bg_err^2 + (alpha * signal)^2

Values typically from MCMC multi-noise shared_noise_params.csv. Empty tuple keeps gain=1 (legacy behaviour).

mask_outliers: bool = False#

Mask geometric outliers in each well’s curve before fitting. Default is False.

outlier_threshold: float = 0.2#

Threshold for geometric outlier scoring (0-1). Default is 0.2.

set_callback(callback)#

Set the callback to be triggered on parameter change.

Parameters:

callback (collections.abc.Callable[[], None])

Return type:

None

class clophfit.prtecan.titration.BufferFit#

Store (robust) linear fit result.

property empty: bool#

True when all attributes are NaN, emulating DataFrame’s empty behavior.

Return type:

bool

class clophfit.prtecan.titration.Buffer#

Buffer handling for a titration.

Manages background correction and fitting for buffer wells.

property dataframes: dict[str, pandas.DataFrame]#

Buffer dataframes with fit.

Return type:

dict[str, pandas.DataFrame]

property dataframes_nrm: dict[str, pandas.DataFrame]#

Buffer normalized dataframes with fit.

Return type:

dict[str, pandas.DataFrame]

property wells: list[str]#

List of buffer wells.

Return type:

list[str]

property bg: dict[str, clophfit.clophfit_types.ArrayF]#

List of buffer values.

Return type:

dict[str, clophfit.clophfit_types.ArrayF]

property bg_err: dict[str, clophfit.clophfit_types.ArrayF]#

List of buffer SEM values.

Return type:

dict[str, clophfit.clophfit_types.ArrayF]

property bg_noise: dict[str, float]#

Intrinsic well noise (RMSE/pooled SD) values.

Return type:

dict[str, float]

plot(*, nrm=False, title=None)#

Plot buffers of all labelblocksgroups.

Parameters:
  • nrm (bool)

  • title (str | None)

Return type:

seaborn.FacetGrid

class clophfit.prtecan.titration.TitrationResults#

Bases: clophfit.fitting.data_structures.ResidualsMixin

Manage titration results with optional lazy computation.

Provide either the small scheme + fit_keys directly, or a titration keyword to snapshot both from a Titration:

TitrationResults(scheme=tit.scheme, fit_keys=tit.fit_keys, results=res)
TitrationResults(results=res, titration=tit)  # equivalent, more concise

titration is an InitVar: only scheme and fit_keys are copied off it, so the (potentially large) raw plate data is never retained.

noise_model carries the calibrated per-label noise model when the fit produced one (fgls_fit_plate); it is None for plain fit_plate.

residual_table(*, binding_function=None, robust=None, student_t_nu=None, outlier_threshold=3.0)#

Compute the canonical plate-wide residual table.

Delegates to each well’s own FitResult.residual_table(), so robustness is auto-detected per well from that well’s own mini (its lmfit Minimizer, ODR output, or PyMC trace). A plate fitted with method="mcmc" therefore standardizes each well against its own trace, instead of being forced to Normal standardization.

Parameters:
  • binding_function (Callable[..., object] | None) – Model evaluated for yhat; defaults to binding_1site. Forwarded unchanged to each well.

  • robust (bool | None) – Force the Student-t standardization of std_res. None auto-detects per well from that well’s trace.

  • student_t_nu (float | None) – Student-t degrees of freedom (None uses detected/default).

  • outlier_threshold (float) – Threshold for the is_residual_outlier flag.

Returns:

The canonical residual table (see residuals), built by concatenating each well’s own table. Wells whose fit failed carry no dataset or result and are skipped, so the table may cover fewer wells than fit_keys.

Return type:

pd.DataFrame

property dataframe: pandas.DataFrame#

Convert FitResult dictionary to a DataFrame.

Return type:

pandas.DataFrame

compute_all()#

Compute results for all keys.

Return type:

None

n_sd(par='K', expected_sd=0.15)#

Compute median of K.

Parameters:
  • par (str)

  • expected_sd (float)

Return type:

float

static all_computed()#

Check if all keys have been computed.

Return type:

bool

export_pngs(folder)#

Export all fit result plots as PNG files.

Parameters:

folder (str | pathlib.Path)

Return type:

None

export_data(folder)#

Export all datasets as CSV files.

Parameters:

folder (str | pathlib.Path)

Return type:

None

plot_k(xlim=None, title='')#

Plot K values as stripplot.

Wells fitted on fewer labels than the rest are marked with a trailing *, and the title says how many there are. Their K rests on 3 parameters over 7 points rather than 6 over 14 and loses the ratiometric cancellation, so it is systematically less certain than its neighbours - which a bare stripplot would otherwise hide.

Parameters:
  • xlim (tuple[float, float] | None, optional) – Range.

  • title (str, optional) – To name the plot.

Returns:

The figure.

Return type:

figure.Figure

class clophfit.prtecan.titration.Titration#

Bases: clophfit.prtecan.models.TecanfilesGroup

Build titrations from grouped Tecanfiles and concentrations or pH values.

Parameters:
  • tecanfiles (list[Tecanfile]) – List of Tecanfiles.

  • x (ArrayF) – Concentration or pH values.

  • is_ph

  • x_err

Raises:

ValueError – For unexpected file format, e.g. header names.

is_ph: bool = False#

Indicate if x values represent pH.

x_err: clophfit.clophfit_types.ArrayF#

Uncertainties for x values (default is empty array).

buffer: Buffer#

Buffer wells data and fit results. Set during initialization.

property fit_keys: set[str]#

Set of wells to be fitted.

Return type:

set[str]

detect_and_discard_bad_wells(*, outlier_threshold=0.2, bg_multiplier=3.0, max_k_stderr=None)#

Detect and discard bad wells from masked per-label signal quality.

By default, each well is converted to a per-label dataset with _create_ds(), masked with apply_outlier_mask(), and then discarded when the mean masked signal of any label falls below a background-derived floor. The floor uses bg_err when available and falls back to bg_noise.

A signal test alone cannot catch a well that is bright but uninformative. On L2, C05 has ample signal and a meaningless titration - K = 7.138 +- 254 pH - and survives every bg_multiplier from 2.0 to 4.0, all of which discard the same single well. So a fit-quality test runs alongside it: fit the well and ask how well K is pinned.

The smoothness, roughness and trendline criteria that used to sit here were removed. They were disabled by default, no caller ever passed them, and at plausible thresholds (0.5/0.5/3.0) they discarded 90 of 90 wells on L2 - not a criterion, a bug in waiting.

Parameters:
  • outlier_threshold (float | None) – Threshold passed to apply_outlier_mask() before computing per-label summary statistics. If None, no masking is applied.

  • bg_multiplier (float | None) – Discard a well when any masked per-label mean signal is below bg_multiplier * mean(background_floor). If None, this check is disabled.

  • max_k_stderr (float | None) – Discard a well whose fitted K has a standard error above this, or a non-finite one. None uses the titration’s own x span, which is the scale-free form of the rule: a well whose midpoint cannot be located inside the window actually titrated carries no information about K, however bright it is. Pass math.inf to disable.

Returns:

Newly discarded well keys.

Return type:

list[str]

clear_all_data_results()#

Clear fit keys, data, results and bg when buffer or scheme properties change.

Return type:

None

property params: TitrationConfig#

The datafit parameters.

Return type:

TitrationConfig

property bg: dict[str, clophfit.clophfit_types.ArrayF]#

List of buffer values.

Return type:

dict[str, clophfit.clophfit_types.ArrayF]

property bg_err: dict[str, clophfit.clophfit_types.ArrayF]#

List of buffer SEM values.

Return type:

dict[str, clophfit.clophfit_types.ArrayF]

property bg_noise: dict[str, float]#

Intrinsic well noise (RMSE/pooled SD) values.

Return type:

dict[str, float]

classmethod fromlistfile(list_file, *, is_ph, base_dir=None)#

Build Titration from a list[.pH|.Cl] file.

Parameters:
  • list_file (Path | str) – Path to the list file containing [filenames x x_err].

  • is_ph (bool) – Whether x values represent pH (True) or concentrations (False).

  • base_dir (Path | str | None) – Directory holding the Tecan files. Relative filenames in the list file are resolved against it; defaults to the list file’s own directory. Use it when list files and .xls files are kept in separate trees (e.g. data/processed/ and data/raw/).

Returns:

The constructed Titration object.

Return type:

Titration

property additions: list[float] | None#

List of initial volume followed by additions.

Return type:

list[float] | None

load_additions(additions_file)#

Load additions from file.

Reads a CSV file with a single column ‘add’ containing addition volumes, and updates the Titration’s additions property.

Parameters:

additions_file (Path) – Path to the additions CSV file.

Return type:

None

property data: dict[str, dict[str, clophfit.clophfit_types.ArrayF]]#

Buffer subtracted and corrected for dilution data.

Return type:

dict[str, dict[str, clophfit.clophfit_types.ArrayF]]

property scheme: clophfit.prtecan.models.PlateScheme#

Scheme for known samples like {‘buffer’, [‘H12’, ‘H01’], ‘ctrl’…}.

Return type:

clophfit.prtecan.models.PlateScheme

load_scheme(schemefile)#

Load scheme from file and set buffer wells.

Reads a scheme file to define buffer wells, known samples, and control wells, then updates the Titration’s scheme and buffer wells.

Parameters:

schemefile (Path) – Path to the scheme CSV file.

Return type:

None

create_ds(key, label)#

Create a dataset for the given key.

Parameters:
  • key (str)

  • label (str)

Return type:

clophfit.fitting.data_structures.Dataset

property excluded_labels: dict[str, set[str]]#

Labels dropped per well, keyed by well.

Returns:

Well to the set of its excluded labels. Wells with every label excluded are discarded outright instead of appearing here.

Return type:

dict[str, set[str]]

exclude_label(key, label)#

Drop one label of one well from the global fit.

The 400 nm channel is dim by construction, so a well can fail a background test on one label while the other is perfectly usable. Discarding the well would throw away a good titration; dropping the label keeps it, at the cost of a less certain K - which is why the results table records how many labels each well was fitted on.

Parameters:
  • key (str) – Well identifier.

  • label (str) – Label to drop for that well. Dropping every label discards the well.

Return type:

None

create_global_ds(key)#

Create a global dataset for the given key.

Parameters:

key (str)

Return type:

clophfit.fitting.data_structures.Dataset

create_dataset_dict(label=None)#

Create a dictionary of datasets for all fit_keys, optionally masking outliers.

Parameters:

label (str | None, optional) – Specific label to extract. If None, creates global datasets containing all labels. Default is None.

Returns:

A dictionary mapping well keys to their corresponding Datasets.

Return type:

dict[str, Dataset]

fit_plate(datasets=None, method='', *, label=None, **kwargs)#

Run a single-pass fit on an entire plate of datasets.

Parameters:
  • datasets (dict[str, Dataset] | None) – Mapping of well keys (e.g. ‘A01’) to Dataset objects. When None, datasets are built with create_dataset_dict(), which also applies outlier masking when params.mask_outliers is set.

  • method (str) – The fitting method: ‘lm’ (default), ‘huber’, ‘odr’, or ‘mcmc’. Other methods supported by clophfit.fitting.core.fit_binding_glob() may also be used.

  • label (str | None) – Build per-label datasets for this label instead of global ones. Only valid when datasets is None.

  • **kwargs (Any) – Additional keyword arguments passed to the fitting function.

Returns:

Plate results carrying this titration’s scheme and fit_keys.

Return type:

TitrationResults

Raises:

ValueError – If both datasets and label are given.

fgls_fit_plate(datasets=None, *, label=None, sigma_floor=None, first_pass_method='huber', second_pass_method='lm', max_iter=3, tol=0.001)#

Run iterative Feasible Generalized Least Squares (FGLS) on the plate.

Fits every well with first_pass_method using the existing y_errc, calibrates a per-label noise model from the plate-wide residuals with the floor anchored to sigma_floor, re-applies the calibrated weights and re-fits with second_pass_method, iterating until gain and alpha converge or max_iter is reached.

Parameters:
  • datasets (dict[str, Dataset] | None) – Mapping of well keys to Dataset objects. When None, datasets are built with create_dataset_dict().

  • label (str | None) – Build per-label datasets for this label instead of global ones. Only valid when datasets is None.

  • sigma_floor (dict[str, float] | None) – Known read-noise floor per label. Defaults to bg_noise.

  • first_pass_method (str) – Method for the first-pass fit.

  • second_pass_method (str) – Method for subsequent passes.

  • max_iter (int) – Maximum FGLS iterations.

  • tol (float) – Relative tolerance for gain/alpha convergence.

Returns:

Plate results carrying this titration’s scheme and fit_keys, with noise_model set to the converged (or last) calibration.

Return type:

TitrationResults

Raises:

ValueError – If both datasets and label are given.

plot_temperature(title='')#

Plot temperatures of all labelblocksgroups.

Creates a line plot showing measured temperatures versus concentration/pH values, with statistics overlays.

Parameters:

title (str, optional) – Additional title text to append to the plot.

Returns:

The matplotlib Figure object containing the plot.

Return type:

figure.Figure

class clophfit.prtecan.titration.TecanConfig#

Group tecan cli options.

detect_bad: bool = True#

Run bad-well detection before fitting (pre-fit) and after (post-fit).

class clophfit.prtecan.titration.McmcSpec#

A per-well MCMC request, decided by the caller rather than parsed downstream.

Parameters:
  • model (Literal["single", "single-refit", "multi"]) – Which fit to run. "single" samples each well once; "single-refit" runs the robust screening pass then refits; "multi" fits every well jointly with control K shared across each control group.

  • sampler (SamplerConfig) – NUTS controls forwarded to pm.sample.

  • structured_noise (bool) – Build the physical floor + gain * y + (alpha * y) ** 2 observation noise instead of scaling y_err by a learned ye_mag multiplier.

  • noise_mode (Literal["centered", "fixed"]) – How a supplied gain/alpha hint is treated when structured_noise is set: centred on (a hint the posterior may leave) or pinned to it. A parameter with no supplied value is always free.

  • per_well_ye_mags (bool | None) – Whether the ye_mag multiplier is per well rather than per label. None leaves the library to resolve it from the noise family, which couples the two; pass a bool to keep them independent. Only meaningful for model="multi", since a single-well fit has one well.

  • ye_mag_parameterization (Literal["centered", "hierarchical", "separable", "separable_step"]) – How per-well ye_mags are structured: independent per label ("centered"), a shared well factor with per-label deviations ("hierarchical"), or a per-label level plus one shared well factor ("separable").

  • robust (RobustConfig) – Likelihood family. The default is a plain Normal; a Student-t with nu=3 is the arm that scored best on this campaign’s plates.

  • ctr_free_k (bool) – Give every well its own K instead of pooling each control group onto a shared one. Only meaningful for model="multi". Pooling buys no accuracy at the construct level and makes the stated interval too narrow, and the library wells have no group to pool with, so free K is the setting that matches what a plate is fitted for.