Implement BI and editorial numeric notation families
Problem
Dataface currently exposes compact numeric formatting mostly through the Vega-Lite
/ D3 shorthand path, which is good for BI-style K / M / B output but does not
capture the broader product decision now recorded in the docs: Dataface should
support both BI notation (K / M / B / T) and editorial notation
(k / mn / bn / tr) as coherent surface-fit display families. Without that
work, narrative surfaces will either inherit BI formatting that is not the best
typographic fit or drift into ad hoc suffix rules that break consistency across
charts, KPIs, tooltips, tables, and annotations.
Context
- Decision and background:
ai_notes/considerations/NUMERIC_NOTATION_AND_ABBREVIATION.mddocs/docs/guides/chart-design-notes.md(Note 52)tasks/workstreams/graph-library/initiatives/m2-semantic-type-behaviors/decisions.md- Existing implementation entry points:
dataface/core/render/format_utils.py—_format_si(),format_d3(),format_value(),format_kpi_parts()dataface/core/compile/types.py—FormatConfigmodeldataface/core/render/chart/decisions.pydataface/core/render/chart/presentation.py- Callers of the formatting pipeline:
kpi.py,table.py,vega_lite_types.py,pipeline.py - Existing behavior already assumes BI-style compact notation in multiple places
through D3/Vega-Lite format strings such as
,.2sand~s. - The implementation should keep raw numeric values unformatted in the data layer and apply notation at render time.
- Scientific / SI notation is out of scope for this task except to preserve the architecture boundary that keeps it separate from BI and editorial notation.
Possible Solutions
A. Add notation field to FormatConfig + suffix lookup table (Recommended)
Add a notation: Literal["bi", "editorial"] | None field to FormatConfig. Default to "bi" when using SI format (s type). Pass the notation through format_d3() → _format_si() to select the suffix table.
- BI suffixes:
K,M,B,T(current behavior, default) - Editorial suffixes:
k,mn,bn,tr
Trade-offs: Minimal change, backwards-compatible, notation choice is explicit in YAML config.
B. Separate preset families (compact-bi, compact-editorial)
Add new preset names like compact-editorial. Simpler but doesn't compose — users who use raw D3 ,.2s specs can't opt into editorial notation.
C. Global config-level notation default
Add chart.notation_family: bi to default_config.yml. Would require threading config through all format calls. Over-engineered for now.
Plan
Use approach A. Steps:
- Add
notationfield toFormatConfigintypes.py - Define suffix lookup tables in
format_utils.py(BI and Editorial) - Thread
notationparameter throughformat_d3()→_format_si() - Update
format_value()andformat_kpi_parts()to extract and pass notation - Write tests first (TDD), then implement
- QA: N/A (no UI — pure formatting logic)
Implementation Progress
- Added
notation: Literal["bi", "editorial"] | Nonefield toFormatConfigintypes.py - Added
NOTATION_SUFFIXESlookup table informat_utils.pymapping family → {threshold: suffix} - Refactored
_format_si()to use the table-driven lookup instead of hardcoded if/elif - Threaded
notationthroughformat_d3()→_format_si(),format_value(),format_kpi_parts() - Added
_get_notation()helper to extract notation from FormatConfig/dict inputs - 12 new tests covering BI default, explicit BI, editorial suffixes, negatives, below-threshold, FormatConfig/dict threading, KPI parts, backwards compatibility, and non-SI passthrough
- All 145 existing + new tests pass, ruff + mypy clean
QA Exploration
- [x] QA exploration completed — N/A, pure formatting logic with no UI surface
Review Feedback
- [ ] Review cleared