Problem / motivation
BarChartWidget plots every value in the resolved data vector as its own bar, with no way to cap the number shown. Verified in libs/Dashboard/BarChartWidget.m:60–86: refresh resolves data/cats then hands the whole vector to bar/barh. The only public knobs are DataFcn, Orientation, and Stacked (:2–6).
For a high-cardinality categorical view — per-sensor peak values, fault counts by channel, "biggest contributors" — this renders dozens of unreadable hair-thin bars, and there is no way to focus on the worst offenders. The canonical "top-N" / focused-Pareto-head analysis is impossible today.
This is distinct from #268 (Sort, which only reorders the bars already shown) — Sort changes order, MaxBars changes cardinality — and from the annotation items #234 (value labels), #253 (axis labels), #211 (threshold overlays). None of those reduces the bar count.
Proposed feature
Add an opt-in MaxBars limit. When the data has more categories than MaxBars, keep the MaxBars-1 largest values and fold the remainder into a single aggregate bar labelled OtherLabel (default 'Other'). Selection is by magnitude, independent of #268's display Sort.
b = BarChartWidget('DataFcn', @perSensorPeaks, ...
'MaxBars', 8, ... % keep 7 tallest + one "Other"
'OtherLabel', 'Other'); % '' => drop the remainder instead
Rough sketch
- Lib/class:
libs/Dashboard/BarChartWidget.m (single file).
- Public API:
MaxBars = Inf (default → byte-identical to today, no limit) and OtherLabel = 'Other'.
- Render: after
data/cats are resolved (:60) and before the bar plot, a ~6-line helper: get top-N indices via sort(data,'descend'), rebuild data as [data(idx(1:k)); sum(data(idx(k+1:end)))] and cats likewise with the Other label (k = MaxBars-1). When the limit is active, take the cla+replot path rather than the in-place YData fast-path (:62–67) so the reduced set and its tick labels stay in sync each tick. Works for both bar/barh (orientation branch untouched). No-op when data already ≤ MaxBars. If OtherLabel = '', drop the remainder instead of aggregating.
- Serialize: emit
s.maxBars / s.otherLabel in toStruct (:124–132) only when non-default; read with isfield guards in fromStruct (:136–158) defaulting to Inf / 'Other' — same round-trip pattern as orientation/stacked (:126–127, :144–145). Old saves load unbounded and render unchanged.
Value
Medium-High. "Top-N worst offenders / biggest contributors" is a standard exploratory move for a sensor-analysis engineer facing many channels or categories. Today a 40-channel DataFcn produces 40 hair-thin bars and the eye cannot pick the worst few; MaxBars turns the tile into a focused ranking. Natural pair with #268 (Sort) — sorted + capped = the head of a Pareto — but independently useful.
Constraints check
- Toolbox-free: yes — only base
sort / sum.
- Backward-compatible: yes — default
MaxBars = Inf is byte-identical to current behavior; old serialized dashboards load unbounded via isfield guards.
- Pure MATLAB/Octave: yes.
- Widget contract: unchanged — additive props + existing
toStruct/fromStruct/refresh, no base-class or layout change.
Effort estimate
S — single file: two public props + a ~6-line top-N/Other reduction helper + a fast-path guard + a toStruct/fromStruct round-trip. Plus one test: MaxBars=3 on [5 1 4 2 3] (cats {a..e}) → heights [5 4 6] labels {a,c,Other} (Other = 1+2+3); MaxBars=Inf preserves input unchanged; values round-trip through serialization.
AI-proposed via /feature-scout — needs a human product decision before implementation.
Problem / motivation
BarChartWidgetplots every value in the resolved data vector as its own bar, with no way to cap the number shown. Verified inlibs/Dashboard/BarChartWidget.m:60–86:refreshresolvesdata/catsthen hands the whole vector tobar/barh. The only public knobs areDataFcn,Orientation, andStacked(:2–6).For a high-cardinality categorical view — per-sensor peak values, fault counts by channel, "biggest contributors" — this renders dozens of unreadable hair-thin bars, and there is no way to focus on the worst offenders. The canonical "top-N" / focused-Pareto-head analysis is impossible today.
This is distinct from #268 (
Sort, which only reorders the bars already shown) —Sortchanges order,MaxBarschanges cardinality — and from the annotation items #234 (value labels), #253 (axis labels), #211 (threshold overlays). None of those reduces the bar count.Proposed feature
Add an opt-in
MaxBarslimit. When the data has more categories thanMaxBars, keep theMaxBars-1largest values and fold the remainder into a single aggregate bar labelledOtherLabel(default'Other'). Selection is by magnitude, independent of #268's displaySort.Rough sketch
libs/Dashboard/BarChartWidget.m(single file).MaxBars = Inf(default → byte-identical to today, no limit) andOtherLabel = 'Other'.data/catsare resolved (:60) and before the bar plot, a ~6-line helper: get top-N indices viasort(data,'descend'), rebuilddataas[data(idx(1:k)); sum(data(idx(k+1:end)))]andcatslikewise with the Other label (k = MaxBars-1). When the limit is active, take thecla+replot path rather than the in-placeYDatafast-path (:62–67) so the reduced set and its tick labels stay in sync each tick. Works for bothbar/barh(orientation branch untouched). No-op when data already ≤MaxBars. IfOtherLabel = '', drop the remainder instead of aggregating.s.maxBars/s.otherLabelintoStruct(:124–132) only when non-default; read withisfieldguards infromStruct(:136–158) defaulting toInf/'Other'— same round-trip pattern asorientation/stacked(:126–127,:144–145). Old saves load unbounded and render unchanged.Value
Medium-High. "Top-N worst offenders / biggest contributors" is a standard exploratory move for a sensor-analysis engineer facing many channels or categories. Today a 40-channel
DataFcnproduces 40 hair-thin bars and the eye cannot pick the worst few;MaxBarsturns the tile into a focused ranking. Natural pair with #268 (Sort) — sorted + capped = the head of a Pareto — but independently useful.Constraints check
sort/sum.MaxBars = Infis byte-identical to current behavior; old serialized dashboards load unbounded viaisfieldguards.toStruct/fromStruct/refresh, no base-class or layout change.Effort estimate
S — single file: two public props + a ~6-line top-N/Other reduction helper + a fast-path guard + a
toStruct/fromStructround-trip. Plus one test:MaxBars=3on[5 1 4 2 3](cats{a..e}) → heights[5 4 6]labels{a,c,Other}(Other = 1+2+3);MaxBars=Infpreserves input unchanged; values round-trip through serialization.AI-proposed via /feature-scout — needs a human product decision before implementation.