Skip to content

Tag: add histogram(edges) — programmatic value-distribution / bin-count primitive (the missing distribution sibling of getStats) #380

Description

@HanSur94

Problem / motivation

The Tag analysis family is broad — statistics (getStats, #223), order-statistics (percentile/quantile, #339), relational (correlate #340, lagCorrelation #341), data-cleaning (removeOutliers #343), comparison (compareWindows #358), plus the earlier derivative / integral / movingStat / exceedance / findGaps / resampleUniform / findPeaks / crossings / spectrum batch. But there is no value-distribution / bin-count primitive — no way to ask a Tag "how many samples fall in each value band?" programmatically.

The distribution is computed in the codebase, but only inside the render layer: HistogramWidget bins its bound series with histcounts (libs/Dashboard/HistogramWidget.m:63, auto bin count at :58-60). So the histogram exists as a widget but not as a Tag method. To get a distribution off a Tag today for a report table, a headless QA gate, or a script, you must pull [~, Y] = tag.getXY() (libs/SensorThreshold/Tag.m:123) and hand-roll histcounts(Y, ...) yourself — re-implementing the exact binning the widget already does. It is the missing order-stat/distribution sibling of getStats.

Proposed feature

A public, read-only histogram method on the Tag base class (concrete for every subclass via the abstract getXY) returning the binned distribution of the tag's values:

[counts, edges, centers] = tag.histogram()            % auto bins
[counts, edges, centers] = tag.histogram(20)          % 20 bins
[counts, edges, centers] = tag.histogram(edgesVector) % explicit edges
  • edges optional: default auto bin count max(10, round(sqrt(numel(Y)))) (matching HistogramWidget.m:58-60); an integer → bin count; a numeric vector → explicit bin edges.
  • Drops NaNs from the value vector before binning.
  • Returns raw counts, the edges used, and convenience bin centers.

Rough sketch

  • Lib/class: libs/SensorThreshold/Tag.m — one new base method (all SensorTag/StateTag/DerivedTag/CompositeTag/MonitorTag inherit it, since each implements getXY).
  • Public-API shape: [counts, edges, centers] = histogram(obj, edges).
  • Impl: [~, Y] = obj.getXY(); Y = Y(~isnan(Y)); [counts, edges] = histcounts(Y, edges|nbins); centers = (edges(1:end-1)+edges(2:end))/2; — the same histcounts call the dashboard already uses, so no new dependency.
  • Test: known series + explicit edges → expected counts; auto-edges shape; empty series → empty counts; NaN-drop verified.

Value

Distribution / bin-count is bread-and-butter for a sensor engineer producing a report ("samples per pressure band"), a headless QA gate, or any script that needs counts without opening a dashboard. It completes the numeric-summary surface alongside getStats (#223) and percentile (#339). Value is real but widget-adjacent — the visual need is already met by HistogramWidget; this is the programmatic/report axis of the same computation.

Constraints check

  • Toolbox-free:histcounts is base MATLAB and already used in-repo (HistogramWidget.m:63); Octave-safe; a manual-accumulate fallback is trivial if ever needed.
  • Backward-compatible: ✅ pure read-only addition — no serialization touched, no existing method changed, existing scripts and serialized dashboards/tags unaffected.
  • Pure MATLAB/Octave: ✅ no MEX, no new files.
  • Contract: ✅ works through the existing Tag/getXY contract; no DashboardWidget/Tag base-contract change.

Effort estimate

S — one base method on Tag.m + one test. (Optional follow-ons, out of this slice: a getXYRange-scoped time-window overload, and a 'Normalization' mode count/probability/pdf.)

Open product decisions (flag for human)

  • Return shape: counts + edges + centers (proposed) vs a single struct.
  • Whether a 'Normalization' option is in v1 or deferred — proposal keeps v1 to raw counts.

AI-proposed via /feature-scout — needs a human product decision before implementation.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions