Skip to content

Print spectral processing provenance on the figure #78

Description

@AlexisJanin

Deferred out of #71 / #73. No demand yet — filed so the design is not re-derived from scratch when it is wanted. Sibling of #77 (which covers making parameters configurable; this one covers disclosing them).

Problem

A spectrogram or PSD figure does not say what was done to the data to produce it. Two different jobs hide in that gap:

  1. Citation — the processing chain a reader would paste into a methods section. Constant, boring, always the same for a given config.
  2. Exception report — something happened to the data that the user did not ask for: timestamps regularised, gaps masked, windows dropped. Rare, and the thing that actually changes how a figure should be read.

Mixed into one string, the exception hides inside the boilerplate. Kept separate, the absence of the second line is itself information.

Sketch

Citation (always):  Hann 4.00 s · 50 % · demeaned · Δf 0.25 Hz · 256 Hz · dB re µV²/Hz
Caveat (only when): resampled to 256.0 Hz (jitter 8 %) · 3 gaps masked, 41 s ·
                    12 of 47 windows dropped

Subplot titles already exist (signal_container.py) with 80–90 px of inter-subplot gap budgeted, so a second <sub> line is close to free.

Design notes worth keeping

Effective values, never requested ones. n_window = max(round(window_s/dt), 2) and n_step = max(round(n_window*(1-overlap)), 1) both round. A requested window_s: 4.0 at 249 Hz really runs 3.996 s; a requested overlap: 0.5 on a 7-sample window really runs 0.571. Printing the request would be provenance theatre carrying our authority.

Hence a carrier object, not recomputation at the plot layer. That same rounding means the caller cannot re-derive the facts — a caller recomputing from requested values would print a plausible-looking lie. A frozen SpectralProvenance dataclass threaded build_uniform_grid → stft → _framed_power → the public functions, emitting numbers rather than sentences, with formatting near the plot layer and literals in constants.py. Two optional PlotOptions fields carry the finished strings.

Facts and where each becomes available:

  • build_uniform_grid — effective dt, whether the resample branch was taken, measured jitter, gap count, total gap duration
  • stft — n_window, n_step, effective window length, Δf, frame count
  • psd — frames dropped into gaps

Report magnitude, not a binary alarm (jitter 8 %, not "⚠ jitter"). JITTER_TOLERANCE = 0.05 was tuned against a recording with zero jitter, so it has never been tested against a case that exceeds it. If real ICU timestamps wobble 6–10 %, every plot from that device carries a warning, users habituate within a week, and silence stops meaning anything. Resampling at 6 % is also harmless (0.24 ms at 250 Hz). Reporting the measured number avoids inventing a second unvalidated constant to patch the first.

Intersection/difference split for overlaid PSD traces. The subtitle carries facts identical across every trace in the subplot; facts that differ between traces move to that trace's legend name and hover. Caveats stay subplot-level and unioned.

Trigger to pick this up

Someone asks "what exactly did you do to my data", or tries to describe a ClinicalScope figure in a publication. Until then the tutorial's Known limitations says the figure does not carry this and to keep the database_options file alongside it.

Related

An ADR may be worth writing alongside this, recording the durable rule — defaults track scipy.signal.welch; a divergence needs a stated reason, and every effective parameter is printed on the figure. Held off for now: with a single instance it would be an ADR about one decision. Worth writing when a second spectral default comes up for debate. Distinct from ADR-0006, which answers "what may we compute?" rather than "how do we choose and disclose what we don't expose?".

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions