Problem / use case
With grouped CSV/JSON, every numeric column becomes its own stat type, and the UI creates one chart per stat.
For wide “competitor” tables (e.g. framework benchmarks), columns are series of the same metric, not separate charts. Example (concurrency.csv):
load,default,chi,echo,gin,goframe,httpz
100,3103.62,3103.73,...
1000,32423.67,32421.28,...
vizb bar concurrency.csv -g load -p y -o out.html
Today this yields 6 independent charts (one per framework). The desired shape is one chart where frameworks share an axis — same idea as merge’s --tag-axis / -A placing tags on n/x/y/z instead of splitting views.
Proposed solution
Flag
| Flag |
Short |
Values |
Default |
--col-axis |
-A |
n, x, y, z |
unset (current multi-chart behavior) |
Target UX:
vizb bar concurrency.csv -o out.html -g load -p y -A x
# or long form:
vizb bar concurrency.csv -o out.html -g load -p y --col-axis x
load → group dimension y (series / variants)
- numeric column names →
x categories
- all values share a single stat → one chart
Naming: --col-axis (“put column names on this axis”), parallel to merge’s --tag-axis. Avoids --stat-axis because --stat already means the analytics panel.
Data transform
After parse + aggregate, when --col-axis is set, expand multi-stat points into long form:
Before (-g load -p y):
{ "yAxis": "100", "stats": [
{ "type": "default", "value": 3103.62 },
{ "type": "chi", "value": 3103.73 }
]}
After (-A x / --col-axis x):
{ "yAxis": "100", "xAxis": "default", "stats": [{ "value": 3103.62 }] }
{ "yAxis": "100", "xAxis": "chi", "stats": [{ "value": 3103.73 }] }
Rules:
- One output point per
(input point × stat).
- Write the column label onto the chosen dimension (
Name / XAxis / YAxis / ZAxis).
- Replace
Stats with a single stat: Type left empty so JSON omits type (json:"type,omitempty" on shared.Stat). Wire format has no type key → UI sees undefined. Same Value.
- Ensure
Dataset.Axes includes the inject axis (reuse merge’s ensureInjectAxis pattern).
Chart title (UI)
Empty/omitted stats[].type still yields one chart signature. The card header must not stay blank:
- When type is empty/undefined, show
Dataset.Name (from -n / default "Comparisons") as the chart title.
- Accept that the dataset name may also appear elsewhere in the UI.
Validation
| Condition |
Behavior |
--col-axis without group |
warn + skip — flag ignored; multi-chart default unchanged |
--col-axis with non-csv/json |
warn + skip |
Solo --select / value / mixed (no grouped multi-column stats) |
warn + skip |
Same axis as group (e.g. -g load -p x -A x) |
fatal error + exit |
-A z without both x and y present after grouping |
fatal error + exit |
| Only one numeric column |
allowed (expand still produces one chart) |
Same-axis conflict must fail hard (do not overwrite or append like merge tag injection):
vizb bar concurrency.csv -g load -p x -A x -o out.html
# Error: --col-axis "x" conflicts with group dimension (already used by --group-pattern)
Implementation sketch
| Layer |
Change |
cmd/cli/dataflags.go |
Add col-axis / -A to DataFlags |
pkg/parser/registry.go |
Config.ColAxis string |
cmd/cli/flagbag.go |
Map flag → cfg.ColAxis in ParseConfig |
shared/expand.go |
ExpandStatsOntoAxis(points, dim Dimension) []DataPoint + tests |
shared/dataset.go |
Stat.Type → json:"type,omitempty" |
shared/merge.go |
Export axis-ensure helper for dataset assembly |
cmd/cli/pipeline.go |
After aggregate: validate + expand when ColAxis set; ensure inject axis in buildDataset |
| UI |
Empty type → chart title falls back to Dataset.Name |
| Docs |
Group guide: competitor / wide-table example |
Aggregation stays before expand so duplicate rows still sum per column first.
Acceptance / verification
# One chart: frameworks on X, load on Y series
vizb bar concurrency.csv -g load -p y -A x -n "HTTP frameworks" -o /tmp/comp.json
# assert: stats have no type key, xAxis in {default,chi,...}, yAxis in {100,1000,5000}
# HTML: chart card title shows "HTTP frameworks"
# Classic grouped bar: load on X, frameworks as Y series
vizb bar concurrency.csv -g load -p x -A y -o /tmp/comp2.html
# Same axis as group → fatal exit
vizb bar concurrency.csv -g load -p x -A x -o /tmp/bad.html
# No group → warn + skip
vizb bar concurrency.csv -A x -o /tmp/skip.json
Out of scope
- Changing default multi-chart behavior without the flag
- UI-side “merge stats into series” without reshape
- Auto-detecting competitor tables (all-numeric wide files still need explicit
-g + -A)
- Renaming merge’s
-A or sharing one flag across merge and chart commands
Problem / use case
With grouped CSV/JSON, every numeric column becomes its own stat type, and the UI creates one chart per stat.
For wide “competitor” tables (e.g. framework benchmarks), columns are series of the same metric, not separate charts. Example (
concurrency.csv):Today this yields 6 independent charts (one per framework). The desired shape is one chart where frameworks share an axis — same idea as merge’s
--tag-axis/-Aplacing tags onn/x/y/zinstead of splitting views.Proposed solution
Flag
--col-axis-An,x,y,zTarget UX:
vizb bar concurrency.csv -o out.html -g load -p y -A x # or long form: vizb bar concurrency.csv -o out.html -g load -p y --col-axis xload→ group dimensiony(series / variants)xcategoriesNaming:
--col-axis(“put column names on this axis”), parallel to merge’s--tag-axis. Avoids--stat-axisbecause--statalready means the analytics panel.Data transform
After parse + aggregate, when
--col-axisis set, expand multi-stat points into long form:Before (
-g load -p y):{ "yAxis": "100", "stats": [ { "type": "default", "value": 3103.62 }, { "type": "chi", "value": 3103.73 } ]}After (
-A x/--col-axis x):{ "yAxis": "100", "xAxis": "default", "stats": [{ "value": 3103.62 }] } { "yAxis": "100", "xAxis": "chi", "stats": [{ "value": 3103.73 }] }Rules:
(input point × stat).Name/XAxis/YAxis/ZAxis).Statswith a single stat:Typeleft empty so JSON omitstype(json:"type,omitempty"onshared.Stat). Wire format has notypekey → UI seesundefined. SameValue.Dataset.Axesincludes the inject axis (reuse merge’sensureInjectAxispattern).Chart title (UI)
Empty/omitted
stats[].typestill yields one chart signature. The card header must not stay blank:Dataset.Name(from-n/ default"Comparisons") as the chart title.Validation
--col-axiswithout group--col-axiswith non-csv/json--select/ value / mixed (no grouped multi-column stats)-g load -p x -A x)-A zwithout bothxandypresent after groupingSame-axis conflict must fail hard (do not overwrite or append like merge tag injection):
vizb bar concurrency.csv -g load -p x -A x -o out.html # Error: --col-axis "x" conflicts with group dimension (already used by --group-pattern)Implementation sketch
cmd/cli/dataflags.gocol-axis/-AtoDataFlagspkg/parser/registry.goConfig.ColAxis stringcmd/cli/flagbag.gocfg.ColAxisinParseConfigshared/expand.goExpandStatsOntoAxis(points, dim Dimension) []DataPoint+ testsshared/dataset.goStat.Type→json:"type,omitempty"shared/merge.gocmd/cli/pipeline.goColAxisset; ensure inject axis inbuildDatasetDataset.NameAggregation stays before expand so duplicate rows still sum per column first.
Acceptance / verification
Out of scope
-g+-A)-Aor sharing one flag across merge and chart commands