Skip to content

barracuda.donors

UI-neutral donor simulation, posterior extraction, variance decomposition, sensitivity, and independent-condition contrasts.

Leave-one-donor-out moments do not refit

leave_one_donor_out_moments reweights fitted posterior mixtures. It is a sensitivity summary, not predictive cross-validation.

Donor-aware simulation and posterior analysis.

This module contains the numerical parts of the donor-aware notebook and web reporting workflows without Plotly or Dash. It deliberately distinguishes two kinds of pairing:

  • parameters belonging to the same posterior particle retain their chain and draw labels; and
  • independently fitted conditions are compared with Cartesian posterior pairs (or reproducible independent Monte Carlo pairs when the Cartesian product is too large).

The inference backend historically calls the zero-inflated fraction phi_0. Public tables from this module use the package-wide p_zero spelling while accepting either variable name in an :class:arviz.InferenceData object.

Attributes

DONOR_MODEL_KEYS module-attribute

DONOR_MODEL_KEYS: Final[tuple[str, ...]] = ('homo', 'z2p', 'dis2p', 'hetero3')

_MODEL_ALIASES module-attribute

_MODEL_ALIASES: Final[dict[str, str]] = {'homo': 'homo', 'homogeneous': 'homo', 'homogeneouspoisson': 'homo', 'poisson': 'homo', 'z2p': 'z2p', 'zi': 'z2p', 'zeroinflated': 'z2p', 'zeroinflatedpoisson': 'z2p', 'dis2p': 'dis2p', 'distributed': 'dis2p', 'gamma': 'dis2p', 'gammapoisson': 'dis2p', 'hetero3': 'hetero3', 'full': 'hetero3', 'zir': 'hetero3', 'zigamma': 'hetero3', 'zeroinflatedgammapoisson': 'hetero3'}

_POPULATION_VARIABLES module-attribute

_POPULATION_VARIABLES: Final[tuple[tuple[str, tuple[str, ...]], ...]] = (('mu_lambda_population', ('mu_lambda_population',)), ('sigma_lambda_population', ('sigma_lambda_population',)), ('p_zero_population', ('p_zero_population', 'phi_0_population')))

_DONOR_VARIABLES module-attribute

_DONOR_VARIABLES: Final[tuple[tuple[str, tuple[str, ...]], ...]] = (('mu_lambda_donor', ('mu_lambda_donor',)), ('sigma_lambda_donor', ('sigma_lambda_donor',)), ('p_zero_donor', ('p_zero_donor', 'phi_0_donor')))

ContrastScale module-attribute

ContrastScale = Literal['absolute', 'percent_of_control_mean']

__all__ module-attribute

__all__ = ['DONOR_MODEL_KEYS', 'ContrastScale', 'DonorSimulationSpec', 'canonical_donor_model_key', 'simulate_donor_event_counts', 'population_posterior_frame', 'donor_posterior_frame', 'population_variance_decomposition', 'leave_one_donor_out_moments', 'cartesian_contrast_draws', 'condition_contrast_frame', 'summarize_contrast_draws']

Classes

DonorSimulationSpec dataclass

DonorSimulationSpec(donor_sizes: Mapping[object, int] | Sequence[tuple[object, int]], mu_lambda: Real | Mapping[object, Real] | Sequence[Real], model_key: str = 'hetero3', sigma_lambda: Real | Mapping[object, Real] | Sequence[Real] = 0.0, p_zero: Real | Mapping[object, Real] | Sequence[Real] = 0.0, observation_time: float = 1.0, seed: int | None = 2026)

Validated settings for one donor-aware event-count simulation.

donor_sizes preserves insertion order and may contain unequal cell counts. Each biological parameter may be a scalar shared by all donors, a donor-labelled mapping, or a sequence aligned to donor_sizes.

Parameters are moments among engaging cells: mu_lambda and sigma_lambda describe the donor's rate distribution, while p_zero is its structural-zero probability. Model-incompatible nonzero values are rejected rather than silently discarded.

Attributes

donor_ids property
donor_ids: tuple[str, ...]

Donor labels in the order used for simulation and parameter arrays.

cells_per_donor property
cells_per_donor: tuple[int, ...]

Validated cell counts aligned to :attr:donor_ids.

n_cells property
n_cells: int

Total number of simulated cells.

Functions:

canonical_donor_model_key

canonical_donor_model_key(model: object) -> str

Return one lowercase key for a donor-aware event-count model.

Spaces, underscores and hyphens are ignored, so both backend spellings such as "Dis2P" and descriptive names such as "gamma-poisson" resolve to the stable public key "dis2p".

_finite_float

_finite_float(value: Any, name: str) -> float

_validated_seed

_validated_seed(seed: int | None, name: str = 'seed') -> int | None

_donor_size_items

_donor_size_items(values: Mapping[object, int] | Sequence[tuple[object, int]]) -> tuple[tuple[str, int], ...]

_donor_parameter_values

_donor_parameter_values(values: Real | Mapping[object, Real] | Sequence[Real], *, donor_ids: Sequence[str], name: str) -> tuple[float, ...]

_mixture_truth

_mixture_truth(spec: DonorSimulationSpec) -> dict[str, object]

simulate_donor_event_counts

simulate_donor_event_counts(spec: DonorSimulationSpec) -> tuple[DataFrame, dict[str, object]]

Simulate donor-labelled cell counts and return exact generating truths.

Engaging rates are fixed for homo/z2p and Gamma-distributed with the requested mean and standard deviation for dis2p/hetero3. Structural zeros are sampled independently before Poisson event counts. Population truths use cell-count weights and, for rate moments, the active donor weights used by the donor-aware inference backend.

_posterior

_posterior(source: Any) -> tuple[Dataset, Any]

Return a posterior and the object carrying optional result metadata.

_first_variable

_first_variable(posterior: Dataset, aliases: Sequence[str]) -> str | None

_paired_draw_index

_paired_draw_index(posterior: Dataset) -> DataFrame

_subsample_draw_pairs

_subsample_draw_pairs(frame: DataFrame, max_draws: int | None, *, random_seed: int) -> DataFrame

_model_metadata

_model_metadata(metadata_source: Any, model_key: str | None) -> str | None

population_posterior_frame

population_posterior_frame(source: Any, *, condition: str | None = None, model_key: str | None = None, max_draws: int | None = None, random_seed: int = 307) -> DataFrame

Extract paired population draws from InferenceData or InferenceResult.

The returned chain and draw columns identify the posterior particle shared by every parameter in a row. phi_0_population is exposed as p_zero_population. Optional subsampling selects whole particle rows.

_donor_dimension

_donor_dimension(variable: DataArray, name: str) -> str

_resolved_donor_labels

_resolved_donor_labels(coordinates: ndarray, donor_labels: Sequence[object] | Mapping[object, object] | None) -> list[str]

donor_posterior_frame

donor_posterior_frame(source: Any, *, condition: str | None = None, model_key: str | None = None, donor_labels: Sequence[object] | Mapping[object, object] | None = None, max_draws: int | None = None, random_seed: int = 307) -> DataFrame

Extract donor draws while retaining within-particle parameter pairing.

Donor labels default to posterior coordinates. For an InferenceResult, stored donor_labels are used automatically. Backend phi_0_donor values are exposed as p_zero_donor.

_normalised_donor_weights

_normalised_donor_weights(values: Sequence[float] | Mapping[object, float] | ndarray, *, coordinates: ndarray) -> ndarray

_p_zero_donor

_p_zero_donor(posterior: Dataset) -> DataArray | None

_mixture_moments

_mixture_moments(posterior: Dataset, donor_weights: ndarray, *, donor_dimension: str, require_sigma: bool) -> Dataset

population_variance_decomposition

population_variance_decomposition(source: Any, donor_weights: Sequence[float] | Mapping[object, float] | ndarray, *, tolerance: float = 1e-10) -> Dataset

Split population rate variance into within- and between-donor terms.

Raw donor weights are normally observed cell counts. In zero-inflated models the rate moments use active weights :math:w_d(1-p_{0,d}) / \sum_j w_j(1-p_{0,j}). Every draw satisfies variance_total == variance_within_donor + variance_between_donor and sigma_lambda_population**2 == variance_total up to floating-point precision. Saved backend population moments are checked within tolerance when available.

leave_one_donor_out_moments

leave_one_donor_out_moments(source: Any, donor_weights: Sequence[float] | Mapping[object, float] | ndarray, *, donor_labels: Sequence[object] | Mapping[object, object] | None = None) -> Dataset

Recompute population mixture moments after excluding each donor.

This is a posterior sensitivity calculation, not a model refit. It reuses every fitted donor parameter draw, removes one donor from the mixture, renormalizes the supplied cell weights, and recomputes population moments. It therefore answers how fitted mixture summaries depend on each donor; it does not include the posterior changes that refitting would induce.

_finite_sample_matrix

_finite_sample_matrix(samples: Sequence[float] | ndarray, name: str) -> ndarray

cartesian_contrast_draws

cartesian_contrast_draws(treatment_samples: Sequence[float] | ndarray, control_samples: Sequence[float] | ndarray, *, scale: ContrastScale = 'absolute', control_mean: Sequence[float] | ndarray | None = None, max_exact_pairs: int = 500000, approximate_pairs: int = 100000, random_seed: int = 307) -> tuple[ndarray, dict[str, object]]

Compare independent posteriors using all or sampled Cartesian pairs.

When len(treatment) * len(control) exceeds max_exact_pairs, indices are sampled independently and uniformly using random_seed. Percentage contrasts divide by the fixed control posterior mean, never by individual control particles.

_required_columns

_required_columns(frame: DataFrame, columns: Sequence[str]) -> None

condition_contrast_frame

condition_contrast_frame(posterior_draws: DataFrame, *, treatment: str, control: str, parameter_columns: Sequence[str] | None = None, group_columns: Sequence[str] | None = None, scale: ContrastScale = 'absolute', max_exact_pairs: int = 500000, approximate_pairs: int = 100000, random_seed: int = 307) -> DataFrame

Compare independently fitted conditions in extracted posterior tables.

Use :func:population_posterior_frame or :func:donor_posterior_frame with condition=..., concatenate the resulting frames, then pass them here. If group_columns is omitted, model keys and donor identifiers present in the table are retained as comparison groups. Pass an explicit empty sequence only when pooling those identities is scientifically intended. Chain/draw labels are intentionally not paired across independently fitted conditions.

summarize_contrast_draws

summarize_contrast_draws(contrasts: DataFrame, *, group_columns: Sequence[str] | None = None, hdi_prob: float = 0.95) -> DataFrame

Summarize contrast columns with HDIs and sign probabilities.

When group_columns is omitted, treatment, control, model, and donor identity columns present in the contrast table are retained. Supply an explicit empty sequence to pool all rows.