acia.analysis.doubling_time#
Per-cell doubling time from lineage topology, and its temporal evolution.
This module walks a TrackastraTracker-style tracklet_graph (one node
per cell cycle, with start_frame/end_frame attributes; edges encode
divisions) to compute, for every cleanly resolved division, the real-time
duration between a mother cell’s birth and its daughters’ birth –
compute_doubling_times(). plot_doubling_time_hose() then turns the
resulting per-cell table into a “temporal hose”: a mean doubling-time curve
with percentile-bootstrap confidence bands, showing how division timing
evolves over the course of an experiment.
This is unrelated to acia.analysis.growth_rate.estimate_growth_rate(),
which fits a whole-population exponential curve and derives an aggregate
doubling time from its slope – nothing here is per-cell or lineage-aware.
- acia.analysis.doubling_time.DEFAULT_CI_LEVELS = (0.95,)#
default confidence levels for the temporal hose plot’s bootstrap bands
- acia.analysis.doubling_time.compute_doubling_times(tracklet_graph, source)[source]#
Compute the real-time doubling duration of every cleanly resolved division.
A tracklet
nqualifies as a “clean division” only if it has exactly one identified mother and exactly two daughters:tracklet_graph.in_degree(n) == 1 and tracklet_graph.out_degree(n) == 2. Tracklets within_degree(n) == 0(a root, present at the movie start – birth time unknown/left-censored),out_degree(n) == 0(alive at the movie end, exited the field of view, or lost tracking – division time unknown/right-censored), orout_degree(n) > 2(a tracking/merge artifact, not a clean split) are excluded.Time is always read via
source.timepoints[frame_idx]– a per-frame pintQuantityarray resolved once at the source level – never computed by hand asframe_delta * frame_interval. This is what makes the result correct for both a nominal fixed frame interval and genuinely irregular real timestamps.- Parameters:
tracklet_graph (nx.DiGraph) – one node per tracklet, keyed by an arbitrary hashable label, with
start_frame/end_frameint attributes; edgesparent -> childencode a division (seeacia.tracking.formats.read_ctc_tracklet_graph()).source (ImageSequenceSource) – the time-calibrated image sequence the tracklets were derived from (see
acia.base.ImageSequenceSource.timepoints).
- Returns:
A DataFrame indexed by the qualifying tracklet’s label (index name
"tracklet"), with columnsstart_time,end_timeanddoubling_timeaspint[<unit>]columns (unit-safe, matchingacia.analysis.attach_units()’s pint representation), where<unit>issource.timepoints’s unit. The unit is also recorded indf.attrs["units"]for consistency with the rest ofacia.analysis. Empty (no qualifying division) if none is found.- Raises:
ValueError – if
source.timepoints is None(noframe_intervaland no explicittimepointswere set onsource) – raised immediately, beforetracklet_graphis touched at all.- Return type:
pd.DataFrame
- acia.analysis.doubling_time.plot_doubling_time_hose(doubling_times_df, source, *, time_grid=None, ci_levels=(0.95,), min_n=5, n_bootstrap=1000, random_state=0, ax=None)[source]#
Plot mean doubling time evolving over the experiment as a “temporal hose”.
At every time
tintime_grid, a cell contributes itsdoubling_timeto the cross-section attfor as long as it was alive – i.e. wheneverstart_time <= t <= end_time– not just at the single instant it divided. Grid points with fewer thanmin_nalive-and-qualifying cells are left asNaN(a gap in the plotted line and bands, rather than a value computed from too few observations).Why a percentile bootstrap instead of a parametric mean +/- SEM band: a symmetric normal-approximation interval can produce a negative lower bound whenever the sample is small or noisy relative to its mean – which happens easily near the
min_nthreshold – and a negative doubling time is nonsensical for a duration. A percentile-of-observed-values bootstrap is built entirely from resampled means of actually-observed, strictly positive values, so it structurally cannot produce a negative bound. It also yields every requestedci_levelspercentile pair from the same bootstrap sample at no extra resampling cost, unlike computing each parametric band separately.- Parameters:
doubling_times_df (pd.DataFrame) – the output of
compute_doubling_times()(start_time/end_time/doubling_timepint columns).source (ImageSequenceSource) – the time-calibrated image sequence the tracklets were derived from; used to default
time_gridtosource.timepoints.time_grid – pint
Quantityarray of times to evaluate the hose at. Defaults tosource.timepointswhenNone.ci_levels – confidence levels for the bootstrap bands, e.g.
(0.95,)(default) or(0.5, 0.8, 0.95)for a multi-level fan chart. Each level is read from the same bootstrap sample at no extra cost.min_n (int) – minimum number of alive-and-qualifying cells required at a grid point to compute a mean/CI there; below this, the point is recorded as
NaN(default5). Must be>= 1. Note:min_n=1produces a mathematically-correct-but-zero-width band at any point with exactly one alive-and-qualifying cell, since the bootstrap then resamples that same single value every time – this is expected behavior reflecting genuinely low sample confidence at that point, not a bug.n_bootstrap (int) – number of bootstrap resamples per grid point (default
1000). Must be>= 1(same zero-width-band caveat asmin_n=1applies atn_bootstrap=1).random_state – seed (or
numpy.random.Generator-compatible value) fornumpy.random.default_rng(), fixed by default (0) so repeated calls are reproducible.ax (Axes | None) – optional matplotlib
Axesto draw into. WhenNonea new figure and axes are created.
- Returns:
A matplotlib
Figurewith the mean doubling-time curve and one shadedfill_betweenband perci_levelsentry (multiple levels nest as a fan chart around the same mean line).NaNregions render as natural gaps. The figure is not shown (noplt.show()).- Raises:
ValueError – if
time_gridisNoneandsource.timepointsis alsoNone(no time calibration to default to).- Return type:
Figure