Module sprime.response_pipeline
Response preprocessing for raw qHTS curves before linear-x 4PL fitting.
Implements the test/control ratio, max normalization (scale so the largest value is 1,
then x100), and the conventional x100 scale so that intermediate values align with the committed
test fixture tests/fixtures/SPrime_variation_reference.csv (columns test/control ratio,
normalise to 1, x 100 scale, and Nonnormalized, x100 scale).
For typical inhibition-style curves (response falls with dose), the largest control-relative
ratio is usually at the lowest concentration; then :func:normalize_to_max_value() matches the
spreadsheet normalise to 1 step (first point -> 1). If a later point is numerically larger, max
normalization scales to that peak instead of forcing index 0 to 1.
DMSO in docstrings means the vehicle control readout (typical solvent: DMSO). The
recommended CSV column for the per-profile control value at import time is
Control_Response (see project documentation).
This module is the library source of truth for the x100 convention; callers should use
these functions rather than re-implementing factors ad hoc. For strict CSV import
(skip_control_response_normalization=False), :meth:~sprime.sprime.RawDataset.to_screening_dataset
applies :func:pipeline_asymptote_normalized() automatically before fitting raw curves.
Use these helpers directly when building in-memory arrays or custom pipelines (e.g. response-scale only).
The variation reference fixture is maintained so that Nonnormalized, x100 scale equals 100 x the
second test/control ratio column row-by-row, and raw percent nucleus values are consistent
with those ratios given the DMSO control row (35.3 in that file). The library
definition pipeline_response_scale() = 100 x (raw / Control_Response) matches those
columns within normal floating-point tolerance when the same inputs are used.
Global variables
var S_PRIME_RESPONSE_SCALE_FACTOR : float-
Fixed multiplier (100) for both major raw pipelines (reference CSV x 100 scale). Treat as immutable in practice for production -
scale_factor/factorarguments that default to this value should stay at 100 unless there is a rare, documented exception.
Functions
def normalize_to_max_value(values: Sequence[float]) ‑> List[float]-
Expand source code
def normalize_to_max_value(values: Sequence[float]) -> List[float]: """ Scale a series so the **maximum** value becomes **1.0**; all other values are <= 1. After **test/control** ratios, this is the asymptote-normalized path's "normalise to 1" step before x100: the **largest** ratio (often the low-dose / vehicle-region point on descending curves) maps to 1; remaining points are proportional and lower. Args: values: Non-empty sequence (e.g. control-relative ratios). Returns: List where ``max(result) == 1.0`` (within float tolerance) and each entry is ``values[i] / max(values)``. Raises: ValueError: If ``values`` is empty, or if ``max(values) == 0``. """ if not values: raise ValueError("normalize_to_max_value requires a non-empty sequence") v = [float(x) for x in values] m = max(v) if m == 0: raise ValueError("normalize_to_max_value: maximum value is zero (cannot scale to unity).") return [x / m for x in v]Scale a series so the maximum value becomes 1.0; all other values are <= 1.
After test/control ratios, this is the asymptote-normalized path's "normalise to 1" step before x100: the largest ratio (often the low-dose / vehicle-region point on descending curves) maps to 1; remaining points are proportional and lower.
Args
values- Non-empty sequence (e.g. control-relative ratios).
Returns
List where
max(result) == 1.0(within float tolerance) and each entry isvalues[i] / max(values).Raises
ValueError- If
valuesis empty, or ifmax(values) == 0.
def pipeline_asymptote_normalized(raw_responses: Sequence[float],
control_response: float,
*,
scale_factor: float = 100.0) ‑> List[float]-
Expand source code
def pipeline_asymptote_normalized( raw_responses: Sequence[float], control_response: float, *, scale_factor: float = S_PRIME_RESPONSE_SCALE_FACTOR, ) -> List[float]: """ **Asymptote-normalized** path: test/control ratio -> :func:`normalize_to_max_value` -> multiply by ``scale_factor``. Matches the reference CSV columns *test/control ratio*, *normalise to 1*, *x 100 scale* when the max ratio coincides with the sheet's intended reference (typically the low-dose peak). ``scale_factor`` defaults to **100**; it should **rarely** differ from that for real data. """ ratios = ratios_to_control(raw_responses, control_response) unity = normalize_to_max_value(ratios) return scale_responses(unity, scale_factor)Asymptote-normalized path: test/control ratio -> :func:
normalize_to_max_value()-> multiply byscale_factor.Matches the reference CSV columns test/control ratio, normalise to 1, x 100 scale when the max ratio coincides with the sheet's intended reference (typically the low-dose peak).
scale_factordefaults to 100; it should rarely differ from that for real data. def pipeline_response_scale(raw_responses: Sequence[float],
control_response: float,
*,
scale_factor: float = 100.0) ‑> List[float]-
Expand source code
def pipeline_response_scale( raw_responses: Sequence[float], control_response: float, *, scale_factor: float = S_PRIME_RESPONSE_SCALE_FACTOR, ) -> List[float]: """ **Response-scale** path (no max normalization): test/control ratio, then multiply by ``scale_factor``. Matches the reference CSV path: *test/control ratio* -> *Nonnormalized, x100 scale* when ``scale_factor`` is ``100`` (the default - keep it at **100** unless you have a rare, documented reason to override). """ ratios = ratios_to_control(raw_responses, control_response) return scale_responses(ratios, scale_factor)Response-scale path (no max normalization): test/control ratio, then multiply by
scale_factor.Matches the reference CSV path: test/control ratio -> Nonnormalized, x100 scale when
scale_factoris100(the default - keep it at 100 unless you have a rare, documented reason to override). def ratios_to_control(responses: Sequence[float], control_response: float) ‑> List[float]-
Expand source code
def ratios_to_control(responses: Sequence[float], control_response: float) -> List[float]: """ First preprocessing step: **test/control ratio** - divide each raw response by the vehicle (**DMSO**) control for that experimental context (e.g. same plate). Args: responses: Raw readouts at each concentration (same order as concentrations). control_response: Single control (**DMSO**) response; must be non-zero. Returns: List of ratios ``r / control_response``. Raises: ValueError: If ``control_response`` is zero. """ if control_response == 0: raise ValueError( "control_response must be non-zero for control-relative ratios (DMSO/vehicle readout)." ) cr = float(control_response) return [float(r) / cr for r in responses]First preprocessing step: test/control ratio - divide each raw response by the vehicle (DMSO) control for that experimental context (e.g. same plate).
Args
responses- Raw readouts at each concentration (same order as concentrations).
control_response- Single control (DMSO) response; must be non-zero.
Returns
List of ratios
r / control_response.Raises
ValueError- If
control_responseis zero.
def scale_responses(values: Sequence[float], factor: float = 100.0) ‑> List[float]-
Expand source code
def scale_responses( values: Sequence[float], factor: float = S_PRIME_RESPONSE_SCALE_FACTOR, ) -> List[float]: """ Apply the conventional **x100** scale. ``factor`` defaults to **100** and should **almost never** be changed for production workflows (matches the variation reference and S' conventions). Non-default values are for rare cases such as unit tests or hand-built pipelines. The factor is a **reporting convention** only; it does not add biological meaning by itself. Both *asymptote-normalized* and *response-scale* pipelines use this after the prior steps defined in the reference spreadsheet. """ f = float(factor) return [float(x) * f for x in values]Apply the conventional x100 scale.
factordefaults to 100 and should almost never be changed for production workflows (matches the variation reference and S' conventions). Non-default values are for rare cases such as unit tests or hand-built pipelines.The factor is a reporting convention only; it does not add biological meaning by itself. Both asymptote-normalized and response-scale pipelines use this after the prior steps defined in the reference spreadsheet.