Skip to content

Add transient detection explainer#332

Open
h-mayorquin wants to merge 6 commits into
mainfrom
explainer_transients
Open

Add transient detection explainer#332
h-mayorquin wants to merge 6 commits into
mainfrom
explainer_transients

Conversation

@h-mayorquin
Copy link
Copy Markdown
Collaborator

@h-mayorquin h-mayorquin commented May 6, 2026

@codecov
Copy link
Copy Markdown

codecov Bot commented May 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@h-mayorquin h-mayorquin mentioned this pull request May 6, 2026
12 tasks
@h-mayorquin h-mayorquin requested a review from pauladkisson May 14, 2026 03:25
Copy link
Copy Markdown
Collaborator

@pauladkisson pauladkisson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This explainer needs some substantive improvements, I think, but overall looks pretty good.

I also highlighted a variety of British spellings that should be switched to American.


The indicator brightness rises and falls with whichever underlying biological signal the indicator is engineered to report (intracellular calcium for GCaMP, extracellular dopamine for sensors like dLight, and so on for serotonin, acetylcholine, voltage, and the rest of the indicator catalogue). A brief, large upward deflection in the trace therefore corresponds to a brief, large excursion in that local signal: a bout of population activity, a transmitter release event, or a shift in release-uptake balance, depending on which indicator is in use.

Transient detection (also called peak detection) is the analysis that extracts the times of those deflections. The resulting event list is useful for relating activity to other observables (behavioural timestamps, drug administration, sensory stimuli, signals recorded simultaneously from other regions), for aggregating into a session-level event rate or mean amplitude that compares across conditions, and for cross-correlating event streams between regions to extract timing relationships independent of slow shared drift.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*behavioral


## Detecting transients

The basic detector is conceptually simple. To flag samples that are anomalous relative to the rest of the trace, we characterise that trace by two summary numbers: a typical level and a noise scale (how far a typical sample sits from the typical level). A sample more than K noise scales above the typical level is counted as a transient, where K is a sensitivity multiplier the user chooses (larger K means fewer detections of any kind, real or noise; typical photometry defaults sit around 3). The output is a discrete list of *(time, amplitude)* events extracted from a continuous recording.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*characterize


![A 60 s synthetic z-scored trace with eight identical-amplitude calcium events, against a slowly wandering baseline. Dotted light-gray vertical lines mark the chunk boundaries; the dashed gray line is the detection threshold. Each event is marked with a dot at its peak: red = detected, navy = missed. Top: a fixed session-wide threshold computed from session_median + 3 × session_MAD. The drift inflates the session-wide noise scale enough that the threshold lands above every event, and all eight are missed (navy dots). Bottom: the same trace with a chunk-local threshold drawn as a step function that follows chunk_median + 3 × chunk_MAD. The threshold now moves with the baseline, and most of the events the fixed threshold missed are recovered (red dots), with a few still missed (navy dots) where within-chunk drift contamination still inflates the local noise scale. The fix is partial: chunking improves on the session-wide failure but does not eliminate the underlying tension between drift and noise estimation.](../_static/images/transient_detection_explainer/fig2_drift_failure_and_fix.svg)

The size of the chunk (the window size) is itself a parameter, and the algorithm behaves very differently across its range. When chunks are too small (panels A and B below, at 1 s), the noise scale is computed from too few samples and is itself noisy: it has high statistical variance just from random sampling, and the threshold derived from it jumps erratically chunk to chunk. Real events are still caught, since they sit well above any plausible noise level, but in chunks where the threshold happens to land low, noise wiggles cross it and register as false positives. When chunks are too large (panels E and F, at 60 s), the slow drift that motivated chunking in the first place lives entirely inside the chunk and inflates its noise scale; the threshold sits at a flat, high value that real events fail to cross. In between sits the working regime (panels C and D, at 15 s), where the threshold adapts smoothly to the wandering baseline. In each row the right column zooms in on the same 5 s slice.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section is a little confusing:

  • I can't really see the threshold crossings because they get obscured by the dots.
  • The legend isn't really explained in the main text.
  • The size of the chunks isn't super clear when it's just in parentheses: (panels A and B below, at 1s).
  • It's also a little confusing to say that the threshold adapts in the optimal chunk size, since it's not really doing anything different. It's just that the chunk size is more appropriately fit to the time scale variation in the data, right?


![A three-row by two-column figure with panels labelled A-F. Each row uses a different chunk window size on the same drifting trace; each row has a wide overview panel (left) and a zoom panel (right) covering the same 5 s slice (t = 9-14 s, marked by a pale lavender band on the wide panel). The wide panel shows the chunked detection threshold (dashed gray step function), dotted chunk-boundary lines, a labelled bracket marking one chunk's width at the bottom, and ground-truth events as red dots (detected) or navy dots (missed). The zoom panel shows the same data plus all false positives as filled orange circles. A row of legend items at the bottom of the figure names every glyph used. Panel A (window = 1 s, wide): the threshold steps up and down erratically chunk to chunk. Panel B (zoom): multiple threshold steps inside the slice, orange FP markers from noise crossings, and the real event at t ≈ 11.4 caught (variance failure). Panel C (window = 15 s, wide): the threshold adapts smoothly to the baseline. Panel D (zoom): the same event caught alongside few or no FPs (working regime). Panel E (window = 60 s, wide): the threshold is essentially flat at a high value because drift inflates the session-wide noise scale; many events miss the threshold (navy dots). Panel F (zoom): the same event sits below the threshold and goes undetected (bias failure).](../_static/images/transient_detection_explainer/fig3_threshold_three_regimes.svg)

Summarising the above, two failure modes are worth avoiding: (1) chunks so short that the per-chunk noise scale is itself noisy, with high statistical variance from chunk to chunk just from random sampling, and (2) chunks so long that the long-term trends the chunking was meant to localise (residual photobleaching, hemodynamic effects, slow drift) reappear inside each chunk and contaminate the noise scale. Both push the per-chunk threshold away from where the local noise alone would place it. The physical characteristics of the signal suggest a goldilocks range for the window: drift sources span tens of seconds (e.g. hemodynamic effects) to many minutes (e.g. photobleaching), so the window must sit well below that timescale, and at the same time large enough that the per-chunk noise scale is statistically stable across chunks. A chunk in the 10 to 30 second range satisfies both bounds and is a sensible default to start from.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*Summarizing
*localize

Also, a chunk size in the range of 10 to 30 seconds. Is that a legitimate recommendation for fiber photometry data, or is that just an artifact of the mocks used in this explainer?


Summarising the above, two failure modes are worth avoiding: (1) chunks so short that the per-chunk noise scale is itself noisy, with high statistical variance from chunk to chunk just from random sampling, and (2) chunks so long that the long-term trends the chunking was meant to localise (residual photobleaching, hemodynamic effects, slow drift) reappear inside each chunk and contaminate the noise scale. Both push the per-chunk threshold away from where the local noise alone would place it. The physical characteristics of the signal suggest a goldilocks range for the window: drift sources span tens of seconds (e.g. hemodynamic effects) to many minutes (e.g. photobleaching), so the window must sit well below that timescale, and at the same time large enough that the per-chunk noise scale is statistically stable across chunks. A chunk in the 10 to 30 second range satisfies both bounds and is a sensible default to start from.

This is the standard bias-variance trade-off applied to a noise estimator parametrised by window size: short windows produce a high-variance estimate (jittery threshold, false positives), long windows produce a biased estimate (inflated by drift, false negatives).
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*parameterized

Invoking bias-variance trade-off here seems a little strange. I don't really see how it applies.


A subtler effect: events that span a chunk boundary slightly bias both adjacent chunks' noise scales upward. With 15 s windows and ~1 s events the bias is small, but it grows for shorter windows or longer events.

### Addressing outlier-induced bias
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the plot, the k1 cutoff and the naive threshold look like they're the same in panels 2 and 3. If they are in fact the same, it would be better to just have one legend and line type for both of them for consistency.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I find it's a bit confusing to consider two different types of large transients that are considered outliers, since we already do artifact removal in a separate step. I prefer to focus this example on the simple concept of large transient events masking small transient events. I think that makes the purpose of this step in the pipeline more clear. I get that it could potentially help with artifacts that slip through as well, but I think it's distracting to consider that possibility.


## Limitations

The threshold + local-maximum approach is one design choice among several. It assumes event shapes are heterogeneous enough that no fixed template applies and that an event-list output is what downstream analyses want. When those assumptions are wrong, other approaches exist: template matching when event shape is well-defined, deconvolution (the calcium-imaging standard for single-cell traces), or working on the continuous trace directly.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this section could be a bit better. Referencing other approaches doesn't really convey the limitations. Throughout the text earlier, some of the limitations were surfaced, like the baseline noise rate and the inherent difficulty of detecting large events along with small events and drifting recordings. I think those would be much better limitations to mention than just saying other approaches exist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants