| |
| |
| |
| |
| |
| |
| |
| |
| |
| key: charting |
| label: Charts, tables, and analytics transforms |
|
|
| kinds: [line, bar, area, scatter, map, pie, donut, stacked_bar, grouped_bar, ranked_bar, |
| stacked_pct, combo, yoy_bars, waterfall, pareto, histogram, heatmap, treemap, funnel, |
| bullet, bubble, sparkline] |
| transforms: "the full 60-op analytics library (table calcs, stats, business templates, re-query |
| windows) lives in analytics.skill.yml β this file covers the viz forms they feed" |
|
|
| recipes: |
| - ask: "top [N] [customers|products|states] as a chart" |
| plan: > |
| run_semantic_query(group_by=[dim], sort=-measure, limit=high) -> |
| transform_result([{op: top_n, by: measure, n: N, other: false}]) -> |
| make_chart(kind=ranked_bar, x=dim, y=measure) |
| guard: > |
| for a RANKING use other:false (a giant 'Other' bar crushes the scale β the platform |
| captions what was cut); use other:true when the chart claims a share of the whole |
| (donut, stacked). Never truncate without the transform. |
| |
| - ask: "how concentrated is [revenue|margin]? / do a few customers carry the book?" |
| plan: > |
| run_semantic_query(group_by=[dim], sort=-measure, limit=high) -> |
| make_chart(kind=pareto, x=dim, y=measure) # platform draws bars + cumulative-% line |
| guard: "pareto reads concentration; for a plain ranking use ranked_bar instead" |
|
|
| - ask: "share/mix of [revenue] by [category|BU] β part-to-whole" |
| plan: > |
| run_semantic_query(group_by=[dim]) -> if >6 groups: transform_result top_n -> |
| make_chart(kind=donut|pie, x=dim, y=measure). Mix OVER TIME: group_by dim + grain -> |
| make_chart(kind=stacked_bar, x=period, y=measure, series=dim); if the question is about |
| SHARES not levels -> stacked_pct. Hierarchical share -> treemap. |
| guard: "pie/donut only <=6 slices; percentages must come from the rows, never estimated" |
|
|
| - ask: "this year vs last year [by month] β YoY comparison" |
| plan: > |
| run_semantic_query(grain=month, date_from/date_to = this year) -> |
| transform_result([{op: yoy}]) -> make_chart(kind=yoy_bars, x=period, y=revenue) |
| guard: > |
| yoy re-runs the SAME governed query shifted -1 year; needs explicit date_from/date_to. |
| The platform prints the YoY % on the chart β never hand-compute deltas. |
| |
| - ask: "what drove the change / bridge [revenue] from X to Y" |
| plan: > |
| get a result whose rows are SIGNED contributions (e.g. yoy transform then compute per-group |
| delta is NOT available β query each component) -> make_chart(kind=waterfall, x=label, |
| y=amount). The platform appends the Total bar. |
| guard: "steps must sum to the change being explained; if they don't, say what's missing" |
|
|
| - ask: "distribution β how are [order values|customer sizes] spread?" |
| plan: > |
| run_semantic_query(group_by=[entity dim], limit=high) -> |
| make_chart(kind=histogram, x=measure) | or transform_result([{op: bin, of: measure, |
| bins: 12}]) -> make_chart(kind=bar, x=bucket, y=count) when you want the buckets as rows |
| guard: "histogram bins per-entity values β group by the entity first, never bin a trend" |
|
|
| - ask: "is [X] related to [Y]? (two measures per entity)" |
| plan: > |
| run_semantic_query(group_by=[entity], measures=[X, Y]) -> make_chart(kind=scatter, x=X, |
| y=Y); a third measure sizes the dots -> kind=bubble, size=Z |
| guard: "state that correlation is visual, not causal; outliers get named in the answer" |
|
|
| - ask: "level AND rate together (GM$ + GM%, revenue + orders)" |
| plan: > |
| run_semantic_query(grain=month, measures=[level, rate]) -> |
| make_chart(kind=combo, x=period, y=level, y2=rate) |
| guard: "y = the dollar level (bars, left); y2 = the rate/count (line, right)" |
|
|
| - ask: "smoothed trend / running total / share columns" |
| plan: > |
| transform_result ops: moving_average{of, window} (adds <of>_maN) Β· running_total{of} Β· |
| share_of_total{of} Β· rank{by} β then chart the derived result (e.g. line of the _ma3 |
| column, or make_table with the share column) |
| guard: "the first window-1 moving-average points are None by design β say so if asked" |
|
|
| - ask: "pipeline/stage conversion; actual vs target" |
| plan: > |
| funnel: rows in stage order -> make_chart(kind=funnel, x=stage, y=value). |
| target: make_chart(kind=bullet, x=label, y=actual, y2=target) |
| guard: "funnels need a real stage sequence; never reorder stages by size" |
|
|
| - ask: "same mini-chart per [BU|category] β compare shapes side by side" |
| plan: > |
| run_semantic_query(grain=month, group_by=[dim]) -> |
| make_chart(kind=line, x=period, y=measure, facet=dim) |
| guard: "facet shares scales across panels so shapes compare honestly" |
|
|
| - ask: "show me the numbers / a list / exact figures" |
| plan: > |
| run_semantic_query(...) -> make_table(result_id, columns=[...], title=?) |
| guard: > |
| make_table renders house-formatted with a totals row β prefer it over pasting rows into |
| the answer text whenever there are more than ~5 rows or 3 columns |
| |
| context: > |
| Every chart/table keeps its result_id and semantic query β the platform renders a |
| "Data behind" drill for each one, and saved views re-run query + transforms live. If a form |
| needs a column the result lacks, transform first (the error message names valid columns). |
| When two forms could work, pick the simpler; never decorate. |
| |