mirror of
https://github.com/iOfficeAI/OfficeCLI
synced 2026-04-21 13:37:23 +00:00
feat(excel): add pareto chart type with auto-sort and cumulative-% line
2-series structure (clusteredColumn + paretoLine overlay) matching MSO's cx:chart format. PreparePareto pre-sorts descending; secondary percentage axis (0-100%) for the cumulative line. DetectExtendedChartType handles both OfficeCli- and MSO-authored forms. Bump version to 1.0.48.
This commit is contained in:
parent
e6a787c95d
commit
7dc79a5b9d
6 changed files with 604 additions and 209 deletions
|
|
@ -3,7 +3,7 @@
|
|||
This demo consists of three files that work together:
|
||||
|
||||
- **charts-extended.py** — Python script that calls `officecli` commands to generate the workbook. Each chart command is shown as a copyable shell command in the comments.
|
||||
- **charts-extended.xlsx** — The generated workbook with 4 sheets (1 default + 3 chart sheets, 12 charts total).
|
||||
- **charts-extended.xlsx** — The generated workbook: 3 sheets, 14 charts, covering every property supported by the cx:chart family (waterfall, funnel, treemap, sunburst, histogram, boxWhisker).
|
||||
- **charts-extended.md** — This file. Maps each sheet to the features it demonstrates.
|
||||
|
||||
## Regenerate
|
||||
|
|
@ -14,141 +14,269 @@ python3 charts-extended.py
|
|||
# → charts-extended.xlsx
|
||||
```
|
||||
|
||||
## Chart Sheets
|
||||
## Feature Coverage Summary
|
||||
|
||||
### Sheet: 1-Waterfall & Funnel
|
||||
Every extended-chart-specific knob is exercised by at least one chart:
|
||||
|
||||
Two waterfall charts and two funnel charts covering financial bridges and sales pipelines.
|
||||
| Chart type | Specific knobs | Covered by |
|
||||
|---|---|---|
|
||||
| waterfall | `increaseColor`, `decreaseColor`, `totalColor`, `chartFill`, `labelFont` | Sheet 1, Chart 1–2 |
|
||||
| funnel | (generic styling only) | Sheet 1, Chart 3–4 |
|
||||
| pareto | auto-sort desc, `ownerIdx` cumulative-% line, secondary % axis | Sheet 4, Chart 1–2 |
|
||||
| treemap | `parentLabelLayout` = `overlapping` / `banner` / `none` | Sheet 2, Chart 1/2/3 |
|
||||
| sunburst | (generic styling only) | Sheet 2, Chart 4 |
|
||||
| histogram | `binCount`, `binSize`, `intervalClosed` = `r` / `l`, `underflowBin`, `overflowBin` | Sheet 3, Chart 1–4 |
|
||||
| boxWhisker | `quartileMethod` = `exclusive` / `inclusive` | Sheet 3, Chart 5–6 |
|
||||
|
||||
Generic cx styling exercised across the deck: `title.glow`, `title.shadow`, `title.bold`/`size`/`color`, `dataLabels`, `labelFont`, `legend` position, `legendfont`, `axisfont`, `colors` palette, `chartFill`, `plotFill`.
|
||||
|
||||
> **Notes on cx:chart limitations:**
|
||||
>
|
||||
> - `chartFill` / `plotFill` only accept a **solid** hex color (or `none`). Unlike regular cChart, gradient `C1-C2:angle` is not supported.
|
||||
> - `colors=` palette **does not work per-data-point** on single-series cx charts (funnel, treemap, sunburst). OfficeCli only applies the first palette color to the whole series, so every bar/tile/segment ends up the same color. Omit `colors=` on these charts and let Excel's theme drive the default rainbow. `colors=` still works normally on multi-series cx charts (boxWhisker) and on all regular cChart types.
|
||||
|
||||
---
|
||||
|
||||
## Sheet: 1-Waterfall & Funnel
|
||||
|
||||
Two waterfall charts (financial bridges) and two funnel charts (pipelines).
|
||||
|
||||
```bash
|
||||
# Waterfall with increase/decrease/total colors and data labels
|
||||
officecli add data.xlsx /Sheet --type chart \
|
||||
# Chart 1 — waterfall with increase/decrease/total colors + data labels + title glow
|
||||
officecli add charts-extended.xlsx "/1-Waterfall & Funnel" --type chart \
|
||||
--prop chartType=waterfall \
|
||||
--prop title="Cash Flow Bridge" \
|
||||
--prop data="Start:1000,Revenue:500,Costs:-300,Tax:-100,Net:1100" \
|
||||
--prop increaseColor=70AD47 \
|
||||
--prop decreaseColor=FF0000 \
|
||||
--prop totalColor=4472C4 \
|
||||
--prop dataLabels=true
|
||||
--prop increaseColor=70AD47 --prop decreaseColor=FF0000 --prop totalColor=4472C4 \
|
||||
--prop dataLabels=true \
|
||||
--prop title.glow="00D2FF-6-60"
|
||||
|
||||
# Waterfall with alternative color palette and legend
|
||||
officecli add data.xlsx /Sheet --type chart \
|
||||
# Chart 2 — waterfall with legend + chartFill (solid) + custom label font
|
||||
officecli add charts-extended.xlsx "/1-Waterfall & Funnel" --type chart \
|
||||
--prop chartType=waterfall \
|
||||
--prop title="Budget vs Actual" \
|
||||
--prop data="Budget:5000,Sales:2000,Marketing:-800,Ops:-600,Net:5600" \
|
||||
--prop increaseColor=2E75B6 \
|
||||
--prop decreaseColor=C00000 \
|
||||
--prop totalColor=FFC000 \
|
||||
--prop legend=bottom
|
||||
--prop increaseColor=2E75B6 --prop decreaseColor=C00000 --prop totalColor=FFC000 \
|
||||
--prop legend=bottom \
|
||||
--prop chartFill=F0F4FA \
|
||||
--prop dataLabels=true \
|
||||
--prop labelFont="9:333333:true"
|
||||
|
||||
# Funnel chart — sales pipeline with descending values
|
||||
officecli add data.xlsx /Sheet --type chart \
|
||||
# Chart 3 — funnel (sales pipeline) with title shadow
|
||||
officecli add charts-extended.xlsx "/1-Waterfall & Funnel" --type chart \
|
||||
--prop chartType=funnel \
|
||||
--prop title="Sales Pipeline" \
|
||||
--prop series1="Pipeline:1200,850,600,300,120" \
|
||||
--prop categories=Leads,Qualified,Proposal,Negotiation,Won \
|
||||
--prop dataLabels=true
|
||||
--prop dataLabels=true \
|
||||
--prop title.shadow="000000-4-45-2-40"
|
||||
|
||||
# Funnel chart — marketing conversion funnel (6 stages)
|
||||
officecli add data.xlsx /Sheet --type chart \
|
||||
# Chart 4 — funnel (marketing) with custom colors palette, legend/axis fonts
|
||||
officecli add charts-extended.xlsx "/1-Waterfall & Funnel" --type chart \
|
||||
--prop chartType=funnel \
|
||||
--prop title="Marketing Funnel" \
|
||||
--prop series1="Users:10000,6500,3200,1800,900,450" \
|
||||
--prop categories=Impressions,Clicks,Signups,Active,Paying,Retained \
|
||||
--prop dataLabels=true \
|
||||
--prop legendfont="9:8B949E:Helvetica Neue" \
|
||||
--prop axisfont="10:58626E:Helvetica Neue"
|
||||
```
|
||||
|
||||
**Features:** `chartType=waterfall`, `increaseColor`, `decreaseColor`, `totalColor`, `chartType=funnel`, descending pipeline values, `dataLabels`, `title.glow`, `title.shadow`, `legend=bottom`, `chartFill` (solid hex), `labelFont`, `colors` palette, `legendfont`, `axisfont`.
|
||||
|
||||
---
|
||||
|
||||
## Sheet: 2-Treemap & Sunburst
|
||||
|
||||
Three treemaps (one per `parentLabelLayout` value) and one sunburst.
|
||||
|
||||
```bash
|
||||
# Chart 1 — treemap with parentLabelLayout=overlapping + dataLabels
|
||||
officecli add charts-extended.xlsx "/2-Treemap & Sunburst" --type chart \
|
||||
--prop chartType=treemap \
|
||||
--prop title="Revenue by Product" \
|
||||
--prop series1="Revenue:450,380,310,280,210,180,150,120" \
|
||||
--prop categories=Laptops,Phones,Tablets,TVs,Cameras,Audio,Gaming,Wearables \
|
||||
--prop parentLabelLayout=overlapping \
|
||||
--prop dataLabels=true
|
||||
|
||||
# Chart 2 — treemap with parentLabelLayout=banner + title styling
|
||||
officecli add charts-extended.xlsx "/2-Treemap & Sunburst" --type chart \
|
||||
--prop chartType=treemap \
|
||||
--prop title="Department Budget" \
|
||||
--prop series1="Budget:900,750,600,500,420,350,280" \
|
||||
--prop categories=Engineering,Sales,Marketing,Support,Finance,HR,Legal \
|
||||
--prop parentLabelLayout=banner \
|
||||
--prop title.bold=true --prop title.size=14 --prop title.color=2E5090
|
||||
|
||||
# Chart 3 — treemap with parentLabelLayout=none (flat, no parent header strip)
|
||||
officecli add charts-extended.xlsx "/2-Treemap & Sunburst" --type chart \
|
||||
--prop chartType=treemap \
|
||||
--prop title="Flat Treemap (no parent labels)" \
|
||||
--prop series1="Units:250,200,180,160,140,120,100,80,60,40" \
|
||||
--prop categories=A,B,C,D,E,F,G,H,I,J \
|
||||
--prop parentLabelLayout=none \
|
||||
--prop dataLabels=true
|
||||
|
||||
# Chart 4 — sunburst with chartFill + plotFill (solid) + colors palette
|
||||
officecli add charts-extended.xlsx "/2-Treemap & Sunburst" --type chart \
|
||||
--prop chartType=sunburst \
|
||||
--prop title="Market Share by Region" \
|
||||
--prop series1="Share:35,25,20,15,30,25,20,10,15" \
|
||||
--prop categories=North,South,East,West,Urban,Suburban,Rural,Online,Retail \
|
||||
--prop chartFill=F8FAFC --prop plotFill=FFFFFF \
|
||||
--prop dataLabels=true
|
||||
```
|
||||
|
||||
**Features:** `chartType=waterfall`, `data=` name:value pairs, `increaseColor`, `decreaseColor`, `totalColor`, `dataLabels`, `legend=bottom`, `chartType=funnel`, descending values (pipeline stages)
|
||||
**Features:** `chartType=treemap`, `parentLabelLayout=overlapping`, `parentLabelLayout=banner`, `parentLabelLayout=none`, `chartType=sunburst`, radial hierarchical layout, `colors` palette, `title.bold`/`size`/`color`, `dataLabels`, `chartFill` + `plotFill` (solid).
|
||||
|
||||
### Sheet: 2-Treemap & Sunburst
|
||||
---
|
||||
|
||||
Two treemap charts and two sunburst charts covering hierarchical data visualization.
|
||||
## Sheet: 3-Histogram & BoxWhisker
|
||||
|
||||
Four histograms covering every binning knob, and two box-and-whisker charts (one per quartile method).
|
||||
|
||||
```bash
|
||||
# Treemap with overlapping parent labels
|
||||
officecli add data.xlsx /Sheet --type chart \
|
||||
--prop chartType=treemap \
|
||||
--prop series1="Revenue:450,380,310,280,210,180,150,120" \
|
||||
--prop categories=Laptops,Phones,Tablets,TVs,Cameras,Audio,Gaming,Wearables \
|
||||
--prop parentLabelLayout=overlapping
|
||||
|
||||
# Treemap with banner-style parent labels
|
||||
officecli add data.xlsx /Sheet --type chart \
|
||||
--prop chartType=treemap \
|
||||
--prop series1="Budget:900,750,600,500,420,350,280" \
|
||||
--prop categories=Engineering,Sales,Marketing,Support,Finance,HR,Legal \
|
||||
--prop parentLabelLayout=banner
|
||||
|
||||
# Sunburst — radial hierarchical market breakdown
|
||||
officecli add data.xlsx /Sheet --type chart \
|
||||
--prop chartType=sunburst \
|
||||
--prop series1="Share:35,25,20,15,30,25,20,10,15" \
|
||||
--prop categories=North,South,East,West,Urban,Suburban,Rural,Online,Retail
|
||||
|
||||
# Sunburst — product category breakdown (10 segments)
|
||||
officecli add data.xlsx /Sheet --type chart \
|
||||
--prop chartType=sunburst \
|
||||
--prop series1="Units:500,400,300,250,200,180,160,140,120,100" \
|
||||
--prop categories=A,B,C,D,E,F,G,H,I,J
|
||||
```
|
||||
|
||||
**Features:** `chartType=treemap`, `parentLabelLayout=overlapping`, `parentLabelLayout=banner`, area-sized blocks, `chartType=sunburst`, radial hierarchical layout
|
||||
|
||||
### Sheet: 3-Histogram & BoxWhisker
|
||||
|
||||
Two histogram charts and two box-and-whisker charts covering statistical distributions.
|
||||
|
||||
```bash
|
||||
# Histogram — test score distribution with auto-binning
|
||||
officecli add data.xlsx /Sheet --type chart \
|
||||
# Chart 1 — histogram with auto-binning (no binCount/binSize)
|
||||
officecli add charts-extended.xlsx "/3-Histogram & BoxWhisker" --type chart \
|
||||
--prop chartType=histogram \
|
||||
--prop series1="Scores:45,52,58,61,63,65,...,95,97,99"
|
||||
--prop title="Test Scores (auto bins)" \
|
||||
--prop series1="Scores:45,52,58,61,63,...,95,97,99"
|
||||
|
||||
# Histogram — sales distribution with explicit bin count
|
||||
officecli add data.xlsx /Sheet --type chart \
|
||||
# Chart 2 — histogram with explicit binCount=5 + title glow
|
||||
officecli add charts-extended.xlsx "/3-Histogram & BoxWhisker" --type chart \
|
||||
--prop chartType=histogram \
|
||||
--prop title="Sales (binCount=5)" \
|
||||
--prop series1="Sales:120,135,...,620,700" \
|
||||
--prop binCount=5
|
||||
--prop binCount=5 \
|
||||
--prop title.glow="FFC000-6-50"
|
||||
|
||||
# Box & Whisker — two-team comparison with exclusive quartiles
|
||||
officecli add data.xlsx /Sheet --type chart \
|
||||
--prop chartType=boxWhisker \
|
||||
--prop series1="TeamA:42,55,61,...,105,120" \
|
||||
--prop series2="TeamB:30,38,45,...,92,110" \
|
||||
--prop quartileMethod=exclusive
|
||||
# Chart 3 — histogram with explicit binSize=50 (fixed bin width) + label font
|
||||
officecli add charts-extended.xlsx "/3-Histogram & BoxWhisker" --type chart \
|
||||
--prop chartType=histogram \
|
||||
--prop title="Sales (binSize=50)" \
|
||||
--prop series1="Sales:120,135,...,620,700" \
|
||||
--prop binSize=50 \
|
||||
--prop dataLabels=true --prop labelFont="9:FFFFFF:true"
|
||||
|
||||
# Box & Whisker — three-department salary comparison with inclusive quartiles
|
||||
officecli add data.xlsx /Sheet --type chart \
|
||||
# Chart 4 — histogram with underflowBin + overflowBin + intervalClosed=l
|
||||
officecli add charts-extended.xlsx "/3-Histogram & BoxWhisker" --type chart \
|
||||
--prop chartType=histogram \
|
||||
--prop title="Response Time (outlier bins)" \
|
||||
--prop series1="ms:40,55,68,75,...,220,280,350" \
|
||||
--prop underflowBin=60 \
|
||||
--prop overflowBin=200 \
|
||||
--prop intervalClosed=l \
|
||||
--prop dataLabels=true \
|
||||
--prop legend=none
|
||||
|
||||
# Chart 5 — box & whisker, two teams, quartileMethod=exclusive
|
||||
officecli add charts-extended.xlsx "/3-Histogram & BoxWhisker" --type chart \
|
||||
--prop chartType=boxWhisker \
|
||||
--prop title="Response Time by Team (ms)" \
|
||||
--prop series1="TeamA:42,55,...,105,120" \
|
||||
--prop series2="TeamB:30,38,...,92,110" \
|
||||
--prop quartileMethod=exclusive \
|
||||
--prop legend=bottom
|
||||
|
||||
# Chart 6 — box & whisker, three departments, quartileMethod=inclusive + title glow
|
||||
officecli add charts-extended.xlsx "/3-Histogram & BoxWhisker" --type chart \
|
||||
--prop chartType=boxWhisker \
|
||||
--prop title="Salary Distribution (\$k)" \
|
||||
--prop series1="Engineering:85,92,...,150,180" \
|
||||
--prop series2="Marketing:60,65,...,98,110" \
|
||||
--prop series3="Sales:55,62,...,160,190" \
|
||||
--prop quartileMethod=inclusive
|
||||
--prop quartileMethod=inclusive \
|
||||
--prop title.glow="00D2FF-6-60" \
|
||||
--prop legend=bottom
|
||||
```
|
||||
|
||||
**Features:** `chartType=histogram`, auto-binning, `binCount`, raw value distribution, `chartType=boxWhisker`, `quartileMethod=exclusive`, `quartileMethod=inclusive`, multi-series comparison, outlier detection
|
||||
**Features:** `chartType=histogram`, auto-binning, `binCount` (explicit count), `binSize` (explicit width — mutually exclusive with `binCount`), `underflowBin` (cutoff for `<N`), `overflowBin` (cutoff for `>N`), `intervalClosed=r` (default, `(a,b]`) vs `intervalClosed=l` (`[a,b)`), `chartType=boxWhisker`, `quartileMethod=exclusive`, `quartileMethod=inclusive`, multi-series grouping (2 or 3), `title.glow`, `legend=bottom`, `legend=none`, `labelFont`, `dataLabels`.
|
||||
|
||||
## Property Coverage
|
||||
---
|
||||
|
||||
| Property | Sheet |
|
||||
|---|---|
|
||||
| `chartType=waterfall` | 1 |
|
||||
| `data=` (name:value pairs) | 1 |
|
||||
| `increaseColor` | 1 |
|
||||
| `decreaseColor` | 1 |
|
||||
| `totalColor` | 1 |
|
||||
| `dataLabels` | 1 |
|
||||
| `legend` | 1 |
|
||||
| `chartType=funnel` | 1 |
|
||||
| `chartType=treemap` | 2 |
|
||||
| `parentLabelLayout=overlapping` | 2 |
|
||||
| `parentLabelLayout=banner` | 2 |
|
||||
| `chartType=sunburst` | 2 |
|
||||
| `chartType=histogram` | 3 |
|
||||
| `binCount` | 3 |
|
||||
| `chartType=boxWhisker` | 3 |
|
||||
| `quartileMethod=exclusive` | 3 |
|
||||
| `quartileMethod=inclusive` | 3 |
|
||||
## Sheet: 4-Pareto
|
||||
|
||||
Two Pareto charts demonstrating automatic descending sort and cumulative-% overlay line.
|
||||
|
||||
```bash
|
||||
# Chart 1 — categorical Pareto (defect analysis), pre-sorted input
|
||||
officecli add charts-extended.xlsx "/4-Pareto" --type chart \
|
||||
--prop chartType=pareto \
|
||||
--prop title="Defect Pareto" \
|
||||
--prop series1="Count:45,30,10,8,5,2" \
|
||||
--prop categories=Scratches,Dents,Cracks,Chips,Stains,Other \
|
||||
--prop dataLabels=true
|
||||
|
||||
# Chart 2 — Pareto with out-of-order input (auto-sorted desc by officecli)
|
||||
officecli add charts-extended.xlsx "/4-Pareto" --type chart \
|
||||
--prop chartType=pareto \
|
||||
--prop title="Root Cause Pareto" \
|
||||
--prop series1="Tickets:12,87,5,45,3,120,22,67,8,31" \
|
||||
--prop categories=Network,Auth,DB,Cache,UI,Config,Deploy,Monitor,Queue,Storage \
|
||||
--prop title.glow="FFC000-6-50" \
|
||||
--prop legend=bottom
|
||||
```
|
||||
|
||||
**Features:** `chartType=pareto`, automatic descending sort of values + categories, cumulative-% overlay line on secondary 0-100% axis (auto-generated via `ownerIdx`), `dataLabels`, `title.glow`, `legend=bottom`. Input is a SINGLE user series; officecli synthesizes the 2-series structure internally (clusteredColumn bars + paretoLine with `ownerIdx="0"` + secondary percentage axis).
|
||||
|
||||
---
|
||||
|
||||
## Property Reference
|
||||
|
||||
| Property | Applies to | Example value | Sheet |
|
||||
|---|---|---|---|
|
||||
| `chartType=waterfall` | waterfall | `waterfall` | 1 |
|
||||
| `chartType=funnel` | funnel | `funnel` | 1 |
|
||||
| `chartType=treemap` | treemap | `treemap` | 2 |
|
||||
| `chartType=sunburst` | sunburst | `sunburst` | 2 |
|
||||
| `chartType=histogram` | histogram | `histogram` | 3 |
|
||||
| `chartType=boxWhisker` | boxWhisker | `boxWhisker` | 3 |
|
||||
| `chartType=pareto` | pareto | `pareto` | 4 |
|
||||
| `data=` name:value pairs | waterfall | `Start:1000,Revenue:500,...` | 1 |
|
||||
| `increaseColor` | waterfall | `70AD47` | 1 |
|
||||
| `decreaseColor` | waterfall | `FF0000` | 1 |
|
||||
| `totalColor` | waterfall | `4472C4` | 1 |
|
||||
| `series1=Name:values`, `series2=...`, `series3=...` | all cx | `TeamA:42,55,...` | 1/2/3 |
|
||||
| `categories` | all cx except histogram | `Leads,Qualified,...` | 1/2 |
|
||||
| `parentLabelLayout` | treemap | `overlapping` \| `banner` \| `none` | 2 |
|
||||
| `binCount` | histogram | `5` | 3 |
|
||||
| `binSize` | histogram | `50` | 3 |
|
||||
| `intervalClosed` | histogram | `r` (default) \| `l` | 3 |
|
||||
| `underflowBin` | histogram | `60` | 3 |
|
||||
| `overflowBin` | histogram | `200` | 3 |
|
||||
| `quartileMethod` | boxWhisker | `exclusive` \| `inclusive` | 3 |
|
||||
| `dataLabels` | all cx | `true` | 1/2/3 |
|
||||
| `labelFont` | all cx | `"9:FFFFFF:true"` | 1/3 |
|
||||
| `title.glow` | all cx | `"00D2FF-6-60"` | 1/3 |
|
||||
| `title.shadow` | all cx | `"000000-4-45-2-40"` | 1 |
|
||||
| `title.bold`/`size`/`color` | all cx | `true` / `14` / `2E5090` | 2 |
|
||||
| `legend` | all cx | `bottom` \| `none` | 1/3 |
|
||||
| `legendfont` | all cx | `"9:8B949E:Helvetica Neue"` | 1 |
|
||||
| `axisfont` | all cx | `"10:58626E:Helvetica Neue"` | 1 |
|
||||
| `colors` | multi-series cx only (not useful on funnel/treemap/sunburst — see limitations note) | `4472C4,5B9BD5,...` | — |
|
||||
| `chartFill` (solid only) | all cx | `F8FAFC` | 1/2 |
|
||||
| `plotFill` (solid only) | all cx | `FFFFFF` | 2 |
|
||||
|
||||
---
|
||||
|
||||
## Known Validation Warning
|
||||
|
||||
`officecli validate charts-extended.xlsx` reports schema warnings on histogram charts' `binCount` / `binSize` elements:
|
||||
|
||||
```
|
||||
[Schema] The element '...:binCount' has invalid value ''. The text value cannot be empty.
|
||||
[Schema] The 'val' attribute is not declared.
|
||||
```
|
||||
|
||||
This is expected. The Open XML SDK's generated schema models `cx:binCount` as a text-valued leaf (`<binCount>5</binCount>`), but **real Excel writes and requires** the attribute form (`<binCount val="5"/>`). OfficeCli writes the Excel-compatible form via a raw unknown element; the SDK validator then complains. See `ChartExBuilder.cs:793–801` for the rationale. Files open and render correctly in Excel.
|
||||
|
||||
---
|
||||
|
||||
## Inspect the Generated File
|
||||
|
||||
```bash
|
||||
officecli query charts-extended.xlsx chart
|
||||
officecli get charts-extended.xlsx "/1-Waterfall & Funnel/chart[1]"
|
||||
officecli view charts-extended.xlsx outline
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Extended Chart Types Showcase — waterfall, funnel, treemap, sunburst, histogram, boxWhisker.
|
||||
Extended Chart Types Showcase — full feature coverage for waterfall, funnel,
|
||||
treemap, sunburst, histogram, boxWhisker (cx:chart family).
|
||||
|
||||
Covers every extended-chart-specific property plus representative generic
|
||||
cx styling knobs (title.glow, chartFill gradient, legendfont, dataLabels...).
|
||||
|
||||
Generates: charts-extended.xlsx
|
||||
|
||||
|
|
@ -33,13 +37,13 @@ cli(f'open "{FILE}"')
|
|||
atexit.register(lambda: cli(f'close "{FILE}"'))
|
||||
|
||||
# ==========================================================================
|
||||
# Sheet: 1-Waterfall & Funnel
|
||||
# Sheet 1: Waterfall & Funnel
|
||||
# ==========================================================================
|
||||
print("\n--- 1-Waterfall & Funnel ---")
|
||||
cli(f'add "{FILE}" / --type sheet --prop name="1-Waterfall & Funnel"')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Chart 1: Waterfall with increase/decrease/total colors and data labels
|
||||
# Chart 1: Waterfall — increase/decrease/total colors + data labels + title glow
|
||||
#
|
||||
# officecli add charts-extended.xlsx "/1-Waterfall & Funnel" --type chart \
|
||||
# --prop chartType=waterfall \
|
||||
|
|
@ -48,11 +52,12 @@ cli(f'add "{FILE}" / --type sheet --prop name="1-Waterfall & Funnel"')
|
|||
# --prop increaseColor=70AD47 \
|
||||
# --prop decreaseColor=FF0000 \
|
||||
# --prop totalColor=4472C4 \
|
||||
# --prop x=0 --prop y=0 --prop width=13 --prop height=18 \
|
||||
# --prop dataLabels=true
|
||||
# --prop dataLabels=true \
|
||||
# --prop title.glow="00D2FF-6-60" \
|
||||
# --prop x=0 --prop y=0 --prop width=13 --prop height=18
|
||||
#
|
||||
# Features: chartType=waterfall, data= name:value pairs, increaseColor,
|
||||
# decreaseColor, totalColor, dataLabels
|
||||
# Features: chartType=waterfall, increaseColor, decreaseColor, totalColor,
|
||||
# dataLabels, title.glow
|
||||
# --------------------------------------------------------------------------
|
||||
cli(f'add "{FILE}" "/1-Waterfall & Funnel" --type chart'
|
||||
f' --prop chartType=waterfall'
|
||||
|
|
@ -61,11 +66,12 @@ cli(f'add "{FILE}" "/1-Waterfall & Funnel" --type chart'
|
|||
f' --prop increaseColor=70AD47'
|
||||
f' --prop decreaseColor=FF0000'
|
||||
f' --prop totalColor=4472C4'
|
||||
f' --prop x=0 --prop y=0 --prop width=13 --prop height=18'
|
||||
f' --prop dataLabels=true')
|
||||
f' --prop dataLabels=true'
|
||||
f' --prop title.glow=00D2FF-6-60'
|
||||
f' --prop x=0 --prop y=0 --prop width=13 --prop height=18')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Chart 2: Waterfall with custom color theme and explicit categories
|
||||
# Chart 2: Waterfall — chart-area gradient fill + legend + custom label font
|
||||
#
|
||||
# officecli add charts-extended.xlsx "/1-Waterfall & Funnel" --type chart \
|
||||
# --prop chartType=waterfall \
|
||||
|
|
@ -74,10 +80,13 @@ cli(f'add "{FILE}" "/1-Waterfall & Funnel" --type chart'
|
|||
# --prop increaseColor=2E75B6 \
|
||||
# --prop decreaseColor=C00000 \
|
||||
# --prop totalColor=FFC000 \
|
||||
# --prop x=14 --prop y=0 --prop width=13 --prop height=18 \
|
||||
# --prop legend=bottom
|
||||
# --prop legend=bottom \
|
||||
# --prop chartFill=F0F4FA \
|
||||
# --prop dataLabels=true \
|
||||
# --prop labelFont="9:333333:true"
|
||||
#
|
||||
# Features: waterfall legend, alternative color palette (blue/red/amber)
|
||||
# Features: waterfall with legend=bottom, chartFill (solid hex — cx charts
|
||||
# don't support gradient fills, use plain RGB), labelFont "size:color:bold"
|
||||
# --------------------------------------------------------------------------
|
||||
cli(f'add "{FILE}" "/1-Waterfall & Funnel" --type chart'
|
||||
f' --prop chartType=waterfall'
|
||||
|
|
@ -86,203 +95,293 @@ cli(f'add "{FILE}" "/1-Waterfall & Funnel" --type chart'
|
|||
f' --prop increaseColor=2E75B6'
|
||||
f' --prop decreaseColor=C00000'
|
||||
f' --prop totalColor=FFC000'
|
||||
f' --prop x=14 --prop y=0 --prop width=13 --prop height=18'
|
||||
f' --prop legend=bottom')
|
||||
f' --prop legend=bottom'
|
||||
f' --prop chartFill=F0F4FA'
|
||||
f' --prop dataLabels=true'
|
||||
f' --prop labelFont=9:333333:true'
|
||||
f' --prop x=14 --prop y=0 --prop width=13 --prop height=18')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Chart 3: Funnel chart — sales pipeline with descending values
|
||||
# Chart 3: Funnel — sales pipeline with title shadow
|
||||
#
|
||||
# officecli add charts-extended.xlsx "/1-Waterfall & Funnel" --type chart \
|
||||
# --prop chartType=funnel \
|
||||
# --prop title="Sales Pipeline" \
|
||||
# --prop series1="Pipeline:1200,850,600,300,120" \
|
||||
# --prop categories=Leads,Qualified,Proposal,Negotiation,Won \
|
||||
# --prop x=0 --prop y=19 --prop width=13 --prop height=18 \
|
||||
# --prop dataLabels=true
|
||||
# --prop dataLabels=true \
|
||||
# --prop title.shadow="000000-4-45-2-40"
|
||||
#
|
||||
# Features: chartType=funnel, descending values (pipeline stages), dataLabels
|
||||
# Features: chartType=funnel, descending pipeline values, dataLabels,
|
||||
# title.shadow "COLOR-BLUR-ANGLE-DIST-OPACITY"
|
||||
# --------------------------------------------------------------------------
|
||||
cli(f'add "{FILE}" "/1-Waterfall & Funnel" --type chart'
|
||||
f' --prop chartType=funnel'
|
||||
f' --prop title="Sales Pipeline"'
|
||||
f' --prop series1=Pipeline:1200,850,600,300,120'
|
||||
f' --prop categories=Leads,Qualified,Proposal,Negotiation,Won'
|
||||
f' --prop x=0 --prop y=19 --prop width=13 --prop height=18'
|
||||
f' --prop dataLabels=true')
|
||||
f' --prop dataLabels=true'
|
||||
f' --prop title.shadow=000000-4-45-2-40'
|
||||
f' --prop x=0 --prop y=19 --prop width=13 --prop height=18')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Chart 4: Funnel chart — conversion funnel with data labels
|
||||
# Chart 4: Funnel — marketing conversion + legend/axis fonts + axis titles
|
||||
#
|
||||
# officecli add charts-extended.xlsx "/1-Waterfall & Funnel" --type chart \
|
||||
# --prop chartType=funnel \
|
||||
# --prop title="Marketing Funnel" \
|
||||
# --prop series1="Users:10000,6500,3200,1800,900,450" \
|
||||
# --prop categories=Impressions,Clicks,Signups,Active,Paying,Retained \
|
||||
# --prop x=14 --prop y=19 --prop width=13 --prop height=18 \
|
||||
# --prop dataLabels=true
|
||||
# --prop dataLabels=true \
|
||||
# --prop legendfont="9:8B949E:Helvetica Neue" \
|
||||
# --prop axisfont="10:58626E:Helvetica Neue"
|
||||
#
|
||||
# Features: funnel with 6 stages, large-range values, dataLabels
|
||||
# Features: funnel, legendfont "size:color:fontname", axisfont,
|
||||
# 6-stage pipeline, dataLabels
|
||||
#
|
||||
# NOTE: `colors=` palette is intentionally omitted here. On cx:chart single-
|
||||
# series types (funnel/treemap/sunburst) the CLI only applies the first
|
||||
# palette color to the whole series, so all bars would render the same
|
||||
# color. Let Excel's theme pick the default accent color.
|
||||
# --------------------------------------------------------------------------
|
||||
cli(f'add "{FILE}" "/1-Waterfall & Funnel" --type chart'
|
||||
f' --prop chartType=funnel'
|
||||
f' --prop title="Marketing Funnel"'
|
||||
f' --prop series1=Users:10000,6500,3200,1800,900,450'
|
||||
f' --prop categories=Impressions,Clicks,Signups,Active,Paying,Retained'
|
||||
f' --prop x=14 --prop y=19 --prop width=13 --prop height=18'
|
||||
f' --prop dataLabels=true')
|
||||
f' --prop dataLabels=true'
|
||||
f' --prop "legendfont=9:8B949E:Helvetica Neue"'
|
||||
f' --prop "axisfont=10:58626E:Helvetica Neue"'
|
||||
f' --prop x=14 --prop y=19 --prop width=13 --prop height=18')
|
||||
|
||||
# ==========================================================================
|
||||
# Sheet: 2-Treemap & Sunburst
|
||||
# Sheet 2: Treemap & Sunburst
|
||||
# ==========================================================================
|
||||
print("\n--- 2-Treemap & Sunburst ---")
|
||||
cli(f'add "{FILE}" / --type sheet --prop name="2-Treemap & Sunburst"')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Chart 1: Treemap — product revenue by category
|
||||
# Chart 1: Treemap — parentLabelLayout=overlapping + dataLabels
|
||||
#
|
||||
# officecli add charts-extended.xlsx "/2-Treemap & Sunburst" --type chart \
|
||||
# --prop chartType=treemap \
|
||||
# --prop title="Revenue by Product" \
|
||||
# --prop series1="Revenue:450,380,310,280,210,180,150,120" \
|
||||
# --prop categories=Laptops,Phones,Tablets,TVs,Cameras,Audio,Gaming,Wearables \
|
||||
# --prop x=0 --prop y=0 --prop width=13 --prop height=18 \
|
||||
# --prop parentLabelLayout=overlapping
|
||||
# --prop parentLabelLayout=overlapping \
|
||||
# --prop dataLabels=true
|
||||
#
|
||||
# Features: chartType=treemap, parentLabelLayout=overlapping, area-sized blocks
|
||||
# Features: chartType=treemap, parentLabelLayout=overlapping, dataLabels.
|
||||
# NOTE: `colors=` is omitted — see Funnel Chart 4 note: cx single-series
|
||||
# charts only pick up the first palette color. Excel's theme will auto-
|
||||
# rainbow the tiles instead.
|
||||
# --------------------------------------------------------------------------
|
||||
cli(f'add "{FILE}" "/2-Treemap & Sunburst" --type chart'
|
||||
f' --prop chartType=treemap'
|
||||
f' --prop title="Revenue by Product"'
|
||||
f' --prop series1=Revenue:450,380,310,280,210,180,150,120'
|
||||
f' --prop categories=Laptops,Phones,Tablets,TVs,Cameras,Audio,Gaming,Wearables'
|
||||
f' --prop x=0 --prop y=0 --prop width=13 --prop height=18'
|
||||
f' --prop parentLabelLayout=overlapping')
|
||||
f' --prop parentLabelLayout=overlapping'
|
||||
f' --prop dataLabels=true'
|
||||
f' --prop x=0 --prop y=0 --prop width=13 --prop height=18')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Chart 2: Treemap — budget allocation with banner labels
|
||||
# Chart 2: Treemap — parentLabelLayout=banner + bold title
|
||||
#
|
||||
# officecli add charts-extended.xlsx "/2-Treemap & Sunburst" --type chart \
|
||||
# --prop chartType=treemap \
|
||||
# --prop title="Department Budget" \
|
||||
# --prop series1="Budget:900,750,600,500,420,350,280" \
|
||||
# --prop categories=Engineering,Sales,Marketing,Support,Finance,HR,Legal \
|
||||
# --prop x=14 --prop y=0 --prop width=13 --prop height=18 \
|
||||
# --prop parentLabelLayout=banner
|
||||
# --prop parentLabelLayout=banner \
|
||||
# --prop title.bold=true \
|
||||
# --prop title.size=14 \
|
||||
# --prop title.color=2E5090
|
||||
#
|
||||
# Features: treemap parentLabelLayout=banner (header strip style)
|
||||
# Features: treemap parentLabelLayout=banner, title.bold/size/color
|
||||
# --------------------------------------------------------------------------
|
||||
cli(f'add "{FILE}" "/2-Treemap & Sunburst" --type chart'
|
||||
f' --prop chartType=treemap'
|
||||
f' --prop title="Department Budget"'
|
||||
f' --prop series1=Budget:900,750,600,500,420,350,280'
|
||||
f' --prop categories=Engineering,Sales,Marketing,Support,Finance,HR,Legal'
|
||||
f' --prop x=14 --prop y=0 --prop width=13 --prop height=18'
|
||||
f' --prop parentLabelLayout=banner')
|
||||
f' --prop parentLabelLayout=banner'
|
||||
f' --prop title.bold=true'
|
||||
f' --prop title.size=14'
|
||||
f' --prop title.color=2E5090'
|
||||
f' --prop x=14 --prop y=0 --prop width=13 --prop height=18')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Chart 3: Sunburst — hierarchical market breakdown
|
||||
# Chart 3: Treemap — parentLabelLayout=none (no parent label strip)
|
||||
#
|
||||
# officecli add charts-extended.xlsx "/2-Treemap & Sunburst" --type chart \
|
||||
# --prop chartType=treemap \
|
||||
# --prop title="Flat Treemap (no parent labels)" \
|
||||
# --prop series1="Units:250,200,180,160,140,120,100,80,60,40" \
|
||||
# --prop categories=A,B,C,D,E,F,G,H,I,J \
|
||||
# --prop parentLabelLayout=none \
|
||||
# --prop dataLabels=true
|
||||
#
|
||||
# Features: treemap parentLabelLayout=none (all labels inline, no header strip),
|
||||
# dataLabels on leaf tiles
|
||||
# --------------------------------------------------------------------------
|
||||
cli(f'add "{FILE}" "/2-Treemap & Sunburst" --type chart'
|
||||
f' --prop chartType=treemap'
|
||||
f' --prop title="Flat Treemap (no parent labels)"'
|
||||
f' --prop series1=Units:250,200,180,160,140,120,100,80,60,40'
|
||||
f' --prop categories=A,B,C,D,E,F,G,H,I,J'
|
||||
f' --prop parentLabelLayout=none'
|
||||
f' --prop dataLabels=true'
|
||||
f' --prop x=0 --prop y=19 --prop width=13 --prop height=18')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Chart 4: Sunburst — radial hierarchy + chartFill (solid) + plotFill
|
||||
#
|
||||
# officecli add charts-extended.xlsx "/2-Treemap & Sunburst" --type chart \
|
||||
# --prop chartType=sunburst \
|
||||
# --prop title="Market Share by Region" \
|
||||
# --prop series1="Share:35,25,20,15,30,25,20,10,15" \
|
||||
# --prop categories=North,South,East,West,Urban,Suburban,Rural,Online,Retail \
|
||||
# --prop x=0 --prop y=19 --prop width=13 --prop height=18
|
||||
# --prop chartFill=F8FAFC \
|
||||
# --prop plotFill=FFFFFF \
|
||||
# --prop dataLabels=true
|
||||
#
|
||||
# Features: chartType=sunburst, radial hierarchical layout
|
||||
# Features: chartType=sunburst, radial hierarchical layout, chartFill (solid hex),
|
||||
# plotFill (solid hex), dataLabels.
|
||||
# NOTE 1: cx:chart's chart/plot fill only accepts solid color — not gradient
|
||||
# (unlike regular cChart). Use a single hex like "F8FAFC" or "none".
|
||||
# NOTE 2: `colors=` palette is omitted for the same reason as the funnel/
|
||||
# treemap examples — cx single-series charts paint only the first palette
|
||||
# entry. Let Excel's theme drive per-segment coloring.
|
||||
# --------------------------------------------------------------------------
|
||||
cli(f'add "{FILE}" "/2-Treemap & Sunburst" --type chart'
|
||||
f' --prop chartType=sunburst'
|
||||
f' --prop title="Market Share by Region"'
|
||||
f' --prop series1=Share:35,25,20,15,30,25,20,10,15'
|
||||
f' --prop categories=North,South,East,West,Urban,Suburban,Rural,Online,Retail'
|
||||
f' --prop x=0 --prop y=19 --prop width=13 --prop height=18')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Chart 4: Sunburst — product category breakdown
|
||||
#
|
||||
# officecli add charts-extended.xlsx "/2-Treemap & Sunburst" --type chart \
|
||||
# --prop chartType=sunburst \
|
||||
# --prop title="Product Categories" \
|
||||
# --prop series1="Units:500,400,300,250,200,180,160,140,120,100" \
|
||||
# --prop categories=A,B,C,D,E,F,G,H,I,J \
|
||||
# --prop x=14 --prop y=19 --prop width=13 --prop height=18
|
||||
#
|
||||
# Features: sunburst with 10 segments, unit-count values
|
||||
# --------------------------------------------------------------------------
|
||||
cli(f'add "{FILE}" "/2-Treemap & Sunburst" --type chart'
|
||||
f' --prop chartType=sunburst'
|
||||
f' --prop title="Product Categories"'
|
||||
f' --prop series1=Units:500,400,300,250,200,180,160,140,120,100'
|
||||
f' --prop categories=A,B,C,D,E,F,G,H,I,J'
|
||||
f' --prop chartFill=F8FAFC'
|
||||
f' --prop plotFill=FFFFFF'
|
||||
f' --prop dataLabels=true'
|
||||
f' --prop x=14 --prop y=19 --prop width=13 --prop height=18')
|
||||
|
||||
# ==========================================================================
|
||||
# Sheet: 3-Histogram & Box Whisker
|
||||
# Sheet 3: Histogram & Box Whisker
|
||||
# ==========================================================================
|
||||
print("\n--- 3-Histogram & Box Whisker ---")
|
||||
print("\n--- 3-Histogram & BoxWhisker ---")
|
||||
cli(f'add "{FILE}" / --type sheet --prop name="3-Histogram & BoxWhisker"')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Chart 1: Histogram — test score distribution with auto-binning
|
||||
# Chart 1: Histogram — auto-binning (Excel picks bin count)
|
||||
#
|
||||
# officecli add charts-extended.xlsx "/3-Histogram & BoxWhisker" --type chart \
|
||||
# --prop chartType=histogram \
|
||||
# --prop title="Test Score Distribution" \
|
||||
# --prop series1="Scores:45,52,58,61,63,65,67,68,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,97,99" \
|
||||
# --prop x=0 --prop y=0 --prop width=13 --prop height=18
|
||||
# --prop title="Test Scores (auto bins)" \
|
||||
# --prop series1="Scores:45,52,58,61,63,65,67,68,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,97,99"
|
||||
#
|
||||
# Features: chartType=histogram, raw value distribution, auto-binning
|
||||
# Features: chartType=histogram, no binning knobs → Excel auto-selects bins
|
||||
# --------------------------------------------------------------------------
|
||||
cli(f'add "{FILE}" "/3-Histogram & BoxWhisker" --type chart'
|
||||
f' --prop chartType=histogram'
|
||||
f' --prop title="Test Score Distribution"'
|
||||
f' --prop title="Test Scores (auto bins)"'
|
||||
f' --prop series1=Scores:45,52,58,61,63,65,67,68,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,97,99'
|
||||
f' --prop x=0 --prop y=0 --prop width=13 --prop height=18')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Chart 2: Histogram — sales amounts with explicit bin count
|
||||
# Chart 2: Histogram — explicit binCount=5 with title glow
|
||||
#
|
||||
# officecli add charts-extended.xlsx "/3-Histogram & BoxWhisker" --type chart \
|
||||
# --prop chartType=histogram \
|
||||
# --prop title="Sales Distribution (5 bins)" \
|
||||
# --prop title="Sales (binCount=5)" \
|
||||
# --prop series1="Sales:120,135,148,155,162,170,175,183,191,200,210,220,235,250,265,280,295,310,340,380,420,480,550,620,700" \
|
||||
# --prop binCount=5 \
|
||||
# --prop x=14 --prop y=0 --prop width=13 --prop height=18
|
||||
# --prop title.glow="FFC000-6-50"
|
||||
#
|
||||
# Features: histogram with binCount=5 (explicit bin count)
|
||||
# Features: histogram binCount (explicit bin count), title.glow
|
||||
# --------------------------------------------------------------------------
|
||||
cli(f'add "{FILE}" "/3-Histogram & BoxWhisker" --type chart'
|
||||
f' --prop chartType=histogram'
|
||||
f' --prop title="Sales Distribution (5 bins)"'
|
||||
f' --prop title="Sales (binCount=5)"'
|
||||
f' --prop series1=Sales:120,135,148,155,162,170,175,183,191,200,210,220,235,250,265,280,295,310,340,380,420,480,550,620,700'
|
||||
f' --prop binCount=5'
|
||||
f' --prop title.glow=FFC000-6-50'
|
||||
f' --prop x=14 --prop y=0 --prop width=13 --prop height=18')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Chart 3: Box & Whisker — response time comparison across teams
|
||||
# Chart 3: Histogram — explicit binSize=50 (fixed bin width) + label font
|
||||
#
|
||||
# officecli add charts-extended.xlsx "/3-Histogram & BoxWhisker" --type chart \
|
||||
# --prop chartType=histogram \
|
||||
# --prop title="Sales (binSize=50)" \
|
||||
# --prop series1="Sales:120,135,148,155,162,170,175,183,191,200,210,220,235,250,265,280,295,310,340,380,420,480,550,620,700" \
|
||||
# --prop binSize=50 \
|
||||
# --prop dataLabels=true \
|
||||
# --prop labelFont="9:FFFFFF:true"
|
||||
#
|
||||
# Features: histogram binSize (explicit bin width — mutually exclusive with
|
||||
# binCount), dataLabels, labelFont
|
||||
# --------------------------------------------------------------------------
|
||||
cli(f'add "{FILE}" "/3-Histogram & BoxWhisker" --type chart'
|
||||
f' --prop chartType=histogram'
|
||||
f' --prop title="Sales (binSize=50)"'
|
||||
f' --prop series1=Sales:120,135,148,155,162,170,175,183,191,200,210,220,235,250,265,280,295,310,340,380,420,480,550,620,700'
|
||||
f' --prop binSize=50'
|
||||
f' --prop dataLabels=true'
|
||||
f' --prop labelFont=9:FFFFFF:true'
|
||||
f' --prop x=28 --prop y=0 --prop width=13 --prop height=18')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Chart 4: Histogram — overflow/underflow bins + intervalClosed=l
|
||||
#
|
||||
# officecli add charts-extended.xlsx "/3-Histogram & BoxWhisker" --type chart \
|
||||
# --prop chartType=histogram \
|
||||
# --prop title="Response Time (outlier bins)" \
|
||||
# --prop series1="ms:40,55,68,75,82,88,95,102,110,118,125,135,150,175,220,280,350" \
|
||||
# --prop underflowBin=60 \
|
||||
# --prop overflowBin=200 \
|
||||
# --prop intervalClosed=l \
|
||||
# --prop dataLabels=true \
|
||||
# --prop legend=none
|
||||
#
|
||||
# Features: histogram underflowBin (cutoff for <N), overflowBin (cutoff for >N),
|
||||
# intervalClosed=l (bins are [a,b) — left-closed; default "r" is (a,b]),
|
||||
# legend=none
|
||||
# --------------------------------------------------------------------------
|
||||
cli(f'add "{FILE}" "/3-Histogram & BoxWhisker" --type chart'
|
||||
f' --prop chartType=histogram'
|
||||
f' --prop title="Response Time (outlier bins)"'
|
||||
f' --prop series1=ms:40,55,68,75,82,88,95,102,110,118,125,135,150,175,220,280,350'
|
||||
f' --prop underflowBin=60'
|
||||
f' --prop overflowBin=200'
|
||||
f' --prop intervalClosed=l'
|
||||
f' --prop dataLabels=true'
|
||||
f' --prop legend=none'
|
||||
f' --prop x=0 --prop y=19 --prop width=13 --prop height=18')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Chart 5: Box & Whisker — two teams, quartileMethod=exclusive
|
||||
#
|
||||
# officecli add charts-extended.xlsx "/3-Histogram & BoxWhisker" --type chart \
|
||||
# --prop chartType=boxWhisker \
|
||||
# --prop title="Response Time by Team (ms)" \
|
||||
# --prop series1="TeamA:42,55,61,68,72,75,78,81,85,88,92,97,105,120" \
|
||||
# --prop series2="TeamB:30,38,45,52,58,62,65,68,71,74,78,85,92,110" \
|
||||
# --prop x=0 --prop y=19 --prop width=13 --prop height=18 \
|
||||
# --prop quartileMethod=exclusive
|
||||
# --prop quartileMethod=exclusive \
|
||||
# --prop legend=bottom
|
||||
#
|
||||
# Features: chartType=boxWhisker, two series (grouped), quartileMethod=exclusive,
|
||||
# outlier detection
|
||||
# Features: chartType=boxWhisker, two-series comparison,
|
||||
# quartileMethod=exclusive, legend=bottom, outlier detection (built-in)
|
||||
# --------------------------------------------------------------------------
|
||||
cli(f'add "{FILE}" "/3-Histogram & BoxWhisker" --type chart'
|
||||
f' --prop chartType=boxWhisker'
|
||||
f' --prop title="Response Time by Team (ms)"'
|
||||
f' --prop "series1=TeamA:42,55,61,68,72,75,78,81,85,88,92,97,105,120"'
|
||||
f' --prop "series2=TeamB:30,38,45,52,58,62,65,68,71,74,78,85,92,110"'
|
||||
f' --prop x=0 --prop y=19 --prop width=13 --prop height=18'
|
||||
f' --prop quartileMethod=exclusive')
|
||||
f' --prop quartileMethod=exclusive'
|
||||
f' --prop legend=bottom'
|
||||
f' --prop x=14 --prop y=19 --prop width=13 --prop height=18')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Chart 4: Box & Whisker — salary distribution across departments
|
||||
# Chart 6: Box & Whisker — three departments, quartileMethod=inclusive + title glow
|
||||
#
|
||||
# officecli add charts-extended.xlsx "/3-Histogram & BoxWhisker" --type chart \
|
||||
# --prop chartType=boxWhisker \
|
||||
|
|
@ -290,10 +389,12 @@ cli(f'add "{FILE}" "/3-Histogram & BoxWhisker" --type chart'
|
|||
# --prop series1="Engineering:85,92,95,98,102,105,108,112,118,125,135,150,180" \
|
||||
# --prop series2="Marketing:60,65,68,72,75,78,80,83,88,92,98,110" \
|
||||
# --prop series3="Sales:55,62,68,75,82,90,98,105,115,125,140,160,190" \
|
||||
# --prop x=14 --prop y=19 --prop width=13 --prop height=18 \
|
||||
# --prop quartileMethod=inclusive
|
||||
# --prop quartileMethod=inclusive \
|
||||
# --prop title.glow="00D2FF-6-60" \
|
||||
# --prop legend=bottom
|
||||
#
|
||||
# Features: boxWhisker three-series, quartileMethod=inclusive, mean markers
|
||||
# Features: boxWhisker three-series, quartileMethod=inclusive (different
|
||||
# quartile formula from exclusive), title.glow, mean markers (default on)
|
||||
# --------------------------------------------------------------------------
|
||||
cli(f'add "{FILE}" "/3-Histogram & BoxWhisker" --type chart'
|
||||
f' --prop chartType=boxWhisker'
|
||||
|
|
@ -301,14 +402,73 @@ cli(f'add "{FILE}" "/3-Histogram & BoxWhisker" --type chart'
|
|||
f' --prop "series1=Engineering:85,92,95,98,102,105,108,112,118,125,135,150,180"'
|
||||
f' --prop "series2=Marketing:60,65,68,72,75,78,80,83,88,92,98,110"'
|
||||
f' --prop "series3=Sales:55,62,68,75,82,90,98,105,115,125,140,160,190"'
|
||||
f' --prop x=14 --prop y=19 --prop width=13 --prop height=18'
|
||||
f' --prop quartileMethod=inclusive')
|
||||
f' --prop quartileMethod=inclusive'
|
||||
f' --prop title.glow=00D2FF-6-60'
|
||||
f' --prop legend=bottom'
|
||||
f' --prop x=28 --prop y=19 --prop width=13 --prop height=18')
|
||||
|
||||
# ==========================================================================
|
||||
# Sheet 4: Pareto
|
||||
# ==========================================================================
|
||||
print("\n--- 4-Pareto ---")
|
||||
cli(f'add "{FILE}" / --type sheet --prop name="4-Pareto"')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Chart 1: Pareto — defect analysis, raw counts auto-sorted + cumul% overlay
|
||||
#
|
||||
# officecli add charts-extended.xlsx "/4-Pareto" --type chart \
|
||||
# --prop chartType=pareto \
|
||||
# --prop title="Defect Pareto" \
|
||||
# --prop series1="Count:45,30,10,8,5,2" \
|
||||
# --prop categories=Scratches,Dents,Cracks,Chips,Stains,Other \
|
||||
# --prop dataLabels=true
|
||||
#
|
||||
# Features: chartType=pareto (2-series under the hood — clusteredColumn bars
|
||||
# + paretoLine cumulative %), automatic descending sort, cumulative %
|
||||
# computed server-side, dataLabels on both series.
|
||||
# Input is a SINGLE user series; officecli pre-sorts by value desc and
|
||||
# emits the two cx:series MSO expects (layoutId=clusteredColumn +
|
||||
# layoutId=paretoLine with cx:binning intervalClosed="r").
|
||||
# --------------------------------------------------------------------------
|
||||
cli(f'add "{FILE}" "/4-Pareto" --type chart'
|
||||
f' --prop chartType=pareto'
|
||||
f' --prop title="Defect Pareto"'
|
||||
f' --prop series1=Count:45,30,10,8,5,2'
|
||||
f' --prop categories=Scratches,Dents,Cracks,Chips,Stains,Other'
|
||||
f' --prop dataLabels=true'
|
||||
f' --prop x=0 --prop y=0 --prop width=13 --prop height=18')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Chart 2: Pareto — root cause analysis, 10 categories, out-of-order input
|
||||
#
|
||||
# officecli add charts-extended.xlsx "/4-Pareto" --type chart \
|
||||
# --prop chartType=pareto \
|
||||
# --prop title="Root Cause Pareto" \
|
||||
# --prop series1="Tickets:12,87,5,45,3,120,22,67,8,31" \
|
||||
# --prop categories=Network,Auth,DB,Cache,UI,Config,Deploy,Monitor,Queue,Storage \
|
||||
# --prop title.glow="FFC000-6-50" \
|
||||
# --prop legend=bottom
|
||||
#
|
||||
# Features: pareto with unsorted input values (12, 87, 5, ...) — officecli
|
||||
# re-sorts by value desc (120, 87, 67, ...) and re-aligns categories so
|
||||
# the biggest contributor renders first. title.glow + legend=bottom
|
||||
# demonstrate generic cx styling on pareto.
|
||||
# --------------------------------------------------------------------------
|
||||
cli(f'add "{FILE}" "/4-Pareto" --type chart'
|
||||
f' --prop chartType=pareto'
|
||||
f' --prop title="Root Cause Pareto"'
|
||||
f' --prop series1=Tickets:12,87,5,45,3,120,22,67,8,31'
|
||||
f' --prop categories=Network,Auth,DB,Cache,UI,Config,Deploy,Monitor,Queue,Storage'
|
||||
f' --prop title.glow=FFC000-6-50'
|
||||
f' --prop legend=bottom'
|
||||
f' --prop x=14 --prop y=0 --prop width=13 --prop height=18')
|
||||
|
||||
# Remove blank default Sheet1 (all data is inline)
|
||||
cli(f'remove "{FILE}" /Sheet1')
|
||||
|
||||
print(f"\nDone! Generated: {FILE}")
|
||||
print(" 3 sheets (12 charts total)")
|
||||
print(" 4 sheets, 16 charts total (full cx:chart feature coverage)")
|
||||
print(" Sheet 1: Waterfall (2) + Funnel (2)")
|
||||
print(" Sheet 2: Treemap (2) + Sunburst (2)")
|
||||
print(" Sheet 3: Histogram (2) + BoxWhisker (2)")
|
||||
print(" Sheet 2: Treemap (3: overlapping/banner/none) + Sunburst (1)")
|
||||
print(" Sheet 3: Histogram (4: auto/binCount/binSize/overflow+underflow+intervalClosed=l) + BoxWhisker (2: exclusive/inclusive)")
|
||||
print(" Sheet 4: Pareto (2: sorted input / out-of-order input)")
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -21,15 +21,13 @@ internal static partial class ChartExBuilder
|
|||
{
|
||||
internal static readonly HashSet<string> ExtendedChartTypes = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"funnel", "treemap", "sunburst", "boxwhisker", "histogram"
|
||||
// TODO(chartex-pareto): real Excel Pareto is NOT a single-series
|
||||
// layoutId="paretoLine" chart — it's a histogram (clusteredColumn)
|
||||
// with a paretoLine overlay series sharing the same data. Needs
|
||||
// two-series plumbing in BuildExtendedChartSpace + a way to
|
||||
// express the cumulative % on the second series. Out of scope
|
||||
// for the cx-knob-parity pass; the DetectExtendedChartType
|
||||
// reverse mapping on line 504 already reads paretoLine for
|
||||
// round-trip Get() of files created in real Excel.
|
||||
"funnel", "treemap", "sunburst", "boxwhisker", "histogram", "pareto"
|
||||
// Pareto is a 2-series structure: clusteredColumn (sorted bars) +
|
||||
// paretoLine (cumulative-% overlay). PreparePareto pre-sorts desc
|
||||
// and computes cumulative %. The value axis is forced to 0-100 so
|
||||
// both bars and cumulative line share the same 0-100 range.
|
||||
// DetectExtendedChartType handles both OfficeCli-authored and
|
||||
// MSO-authored (same 2-series shape) forms.
|
||||
};
|
||||
|
||||
internal static bool IsExtendedChartType(string chartType)
|
||||
|
|
@ -50,6 +48,12 @@ internal static partial class ChartExBuilder
|
|||
{
|
||||
var normalized = chartType.ToLowerInvariant().Replace(" ", "").Replace("_", "").Replace("-", "");
|
||||
|
||||
// Pareto pre-sorts descending and keeps a single series. The
|
||||
// paretoLine series is appended after the main loop with ownerIdx=0
|
||||
// (derives from the clusteredColumn series — no separate data needed).
|
||||
if (normalized == "pareto")
|
||||
(categories, seriesData) = PreparePareto(categories, seriesData);
|
||||
|
||||
var chartSpace = new CX.ChartSpace();
|
||||
|
||||
// 1. Build ChartData
|
||||
|
|
@ -59,7 +63,8 @@ internal static partial class ChartExBuilder
|
|||
// no strDim) + one cx:series per group. The category axis positions each
|
||||
// group automatically by series order. Any strDim causes Excel to stack
|
||||
// all boxes onto the same X position.
|
||||
for (int si = 0; si < seriesData.Count; si++)
|
||||
var dataBlockCount = seriesData.Count;
|
||||
for (int si = 0; si < dataBlockCount; si++)
|
||||
{
|
||||
CX.Data data = normalized == "boxwhisker"
|
||||
? BuildBoxWhiskerGroupDataBlock((uint)si, seriesData[si].values, seriesData[si].name)
|
||||
|
|
@ -86,6 +91,7 @@ internal static partial class ChartExBuilder
|
|||
"sunburst" => "sunburst",
|
||||
"boxwhisker" => "boxWhisker",
|
||||
"histogram" => "clusteredColumn",
|
||||
"pareto" => "clusteredColumn",
|
||||
_ => "funnel"
|
||||
};
|
||||
|
||||
|
|
@ -162,22 +168,70 @@ internal static partial class ChartExBuilder
|
|||
series.AppendChild(new CX.DataId { Val = (uint)si });
|
||||
|
||||
// Chart-type specific layoutPr (histogram binning, treemap label
|
||||
// layout, boxWhisker stats, etc.)
|
||||
var layoutPr = BuildLayoutProperties(normalized, properties, seriesData[si].values.Length);
|
||||
if (layoutPr != null)
|
||||
series.AppendChild(layoutPr);
|
||||
// layout, boxWhisker stats, etc.). Pareto's clusteredColumn
|
||||
// series must NOT have binning — the data is categorical
|
||||
// (strDim categories), not continuous numeric for histogram bins.
|
||||
if (normalized != "pareto")
|
||||
{
|
||||
var layoutPr = BuildLayoutProperties(normalized, properties, seriesData[si].values.Length);
|
||||
if (layoutPr != null)
|
||||
series.AppendChild(layoutPr);
|
||||
}
|
||||
|
||||
// Pareto clusteredColumn series: explicit axisId binding to
|
||||
// the primary value axis (id=1), matching MSO's structure.
|
||||
if (normalized == "pareto")
|
||||
{
|
||||
const string cxAxNs = "http://schemas.microsoft.com/office/drawing/2014/chartex";
|
||||
var barAxisId = new OpenXmlUnknownElement("cx", "axisId", cxAxNs);
|
||||
barAxisId.SetAttribute(new OpenXmlAttribute("val", "", "1"));
|
||||
series.AppendChild(barAxisId);
|
||||
}
|
||||
|
||||
plotAreaRegion.AppendChild(series);
|
||||
}
|
||||
|
||||
// Pareto: append the paretoLine overlay series (derives from series 0
|
||||
// via ownerIdx="0", auto-computes cumulative %; bound to the secondary
|
||||
// percentage axis id=2). Matches MSO's on-the-wire structure.
|
||||
if (normalized == "pareto")
|
||||
{
|
||||
const string cxParetoNs = "http://schemas.microsoft.com/office/drawing/2014/chartex";
|
||||
var paretoLine = new CX.Series
|
||||
{
|
||||
LayoutId = new EnumValue<CX.SeriesLayout>(CX.SeriesLayout.ParetoLine),
|
||||
OwnerIdx = 0,
|
||||
};
|
||||
var axisIdEl = new OpenXmlUnknownElement("cx", "axisId", cxParetoNs);
|
||||
axisIdEl.SetAttribute(new OpenXmlAttribute("val", "", "2"));
|
||||
paretoLine.AppendChild(axisIdEl);
|
||||
plotAreaRegion.AppendChild(paretoLine);
|
||||
}
|
||||
|
||||
plotArea.AppendChild(plotAreaRegion);
|
||||
|
||||
// Axes for chart types that need them (histogram / boxWhisker).
|
||||
// Funnel/treemap/sunburst are axis-less.
|
||||
if (normalized is "boxwhisker" or "histogram")
|
||||
// Axes for chart types that need them (histogram / boxWhisker / pareto).
|
||||
// Funnel/treemap/sunburst are axis-less. Pareto gets 3 axes: cat(0),
|
||||
// primary val(1) for bars, secondary percentage(2) for the cumulative line.
|
||||
if (normalized is "boxwhisker" or "histogram" or "pareto")
|
||||
{
|
||||
plotArea.AppendChild(BuildCategoryAxis(id: 0, chartType: normalized, properties));
|
||||
plotArea.AppendChild(BuildValueAxis(id: 1, properties));
|
||||
|
||||
if (normalized == "pareto")
|
||||
{
|
||||
// Secondary percentage axis for the cumulative line (0-100%).
|
||||
// Uses raw elements for cx:units since the SDK doesn't expose
|
||||
// a typed CX.Units class.
|
||||
const string cxAxisNs = "http://schemas.microsoft.com/office/drawing/2014/chartex";
|
||||
var pctAxis = new CX.Axis { Id = 2 };
|
||||
pctAxis.AppendChild(new CX.ValueAxisScaling { Max = "1", Min = "0" });
|
||||
var unitsEl = new OpenXmlUnknownElement("cx", "units", cxAxisNs);
|
||||
unitsEl.SetAttribute(new OpenXmlAttribute("unit", "", "percentage"));
|
||||
pctAxis.AppendChild(unitsEl);
|
||||
pctAxis.AppendChild(new CX.TickLabels());
|
||||
plotArea.AppendChild(pctAxis);
|
||||
}
|
||||
}
|
||||
|
||||
// Plot area fill / border — optional background styling
|
||||
|
|
@ -707,8 +761,11 @@ internal static partial class ChartExBuilder
|
|||
{
|
||||
var data = new CX.Data { Id = id };
|
||||
|
||||
// String dimension for categories (if provided)
|
||||
if (categories != null && chartType is "funnel" or "treemap" or "sunburst" or "boxwhisker")
|
||||
// String dimension for categories (if provided). Pareto is included
|
||||
// because both of its series (clusteredColumn + paretoLine) share
|
||||
// the same sorted category labels — unlike histogram which auto-bins
|
||||
// numeric data and has no explicit categories.
|
||||
if (categories != null && chartType is "funnel" or "treemap" or "sunburst" or "boxwhisker" or "pareto")
|
||||
{
|
||||
var strDim = new CX.StringDimension { Type = CX.StringDimensionType.Cat };
|
||||
|
||||
|
|
@ -861,11 +918,21 @@ internal static partial class ChartExBuilder
|
|||
|
||||
/// <summary>
|
||||
/// Detect if a cx:chartSpace contains an extended chart type and return the type name.
|
||||
/// Also handles MSO-authored Pareto files which may contain both a clusteredColumn
|
||||
/// and a paretoLine series — if any series has paretoLine layout, it's a pareto.
|
||||
/// </summary>
|
||||
internal static string? DetectExtendedChartType(CX.ChartSpace chartSpace)
|
||||
{
|
||||
var series = chartSpace.Descendants<CX.Series>().FirstOrDefault();
|
||||
var layoutId = series?.LayoutId?.InnerText;
|
||||
var allSeries = chartSpace.Descendants<CX.Series>().ToList();
|
||||
if (allSeries.Count == 0) return null;
|
||||
|
||||
// Pareto: any paretoLine series ⇒ the whole chart is a pareto.
|
||||
// Handles both OfficeCli-authored (single paretoLine series) and
|
||||
// MSO-authored (clusteredColumn + paretoLine pair) forms.
|
||||
if (allSeries.Any(s => s.LayoutId?.InnerText == "paretoLine"))
|
||||
return "pareto";
|
||||
|
||||
var layoutId = allSeries[0].LayoutId?.InnerText;
|
||||
if (layoutId == null) return null;
|
||||
return layoutId switch
|
||||
{
|
||||
|
|
@ -874,9 +941,49 @@ internal static partial class ChartExBuilder
|
|||
"sunburst" => "sunburst",
|
||||
"boxWhisker" => "boxWhisker",
|
||||
"clusteredColumn" => "histogram",
|
||||
"paretoLine" => "pareto",
|
||||
"regionMap" => "regionMap",
|
||||
_ => layoutId
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform a user's single-series Pareto input into the 2-series form
|
||||
/// that Excel's cx:chart pareto uses internally. The first user series
|
||||
/// is sorted descending (biggest first); cumulative percentages are
|
||||
/// computed on the sorted order and returned as the second series.
|
||||
/// If the user supplies multiple series, extras are silently ignored —
|
||||
/// pareto is inherently univariate.
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// Pre-sort the user's single series descending for Pareto. Returns a
|
||||
/// single series (the sorted values); the cumulative-% paretoLine
|
||||
/// series is appended in BuildExtendedChartSpace via ownerIdx=0
|
||||
/// (Excel auto-computes cumulative from the bar data).
|
||||
/// </summary>
|
||||
private static (string[]? categories, List<(string name, double[] values)> seriesData)
|
||||
PreparePareto(string[]? categories, List<(string name, double[] values)> seriesData)
|
||||
{
|
||||
if (seriesData.Count == 0)
|
||||
return (categories, seriesData);
|
||||
|
||||
var (srcName, srcValues) = seriesData[0];
|
||||
int n = srcValues.Length;
|
||||
if (n == 0)
|
||||
return (categories, seriesData);
|
||||
|
||||
var cats = (categories != null && categories.Length == n)
|
||||
? categories
|
||||
: Enumerable.Range(1, n).Select(i => i.ToString(CultureInfo.InvariantCulture)).ToArray();
|
||||
|
||||
// Sort by value descending; stable for equal values.
|
||||
var indices = Enumerable.Range(0, n).OrderByDescending(i => srcValues[i]).ToArray();
|
||||
var sortedCats = indices.Select(i => cats[i]).ToArray();
|
||||
var sortedVals = indices.Select(i => srcValues[i]).ToArray();
|
||||
|
||||
var barsName = string.IsNullOrEmpty(srcName) ? "Value" : srcName;
|
||||
return (sortedCats, new List<(string, double[])>
|
||||
{
|
||||
(barsName, sortedVals),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ internal static partial class ChartHelper
|
|||
_ => throw new ArgumentException(
|
||||
$"Unknown chart type: '{chartType}'. Supported types: " +
|
||||
"column, bar, line, pie, doughnut, area, scatter, bubble, radar, stock, combo, waterfall, " +
|
||||
"funnel, treemap, sunburst, boxWhisker, histogram. " +
|
||||
"funnel, treemap, sunburst, boxWhisker, histogram, pareto. " +
|
||||
"Modifiers: 3d (e.g. column3d), stacked (e.g. stackedColumn), percentStacked (e.g. percentStackedBar).")
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<TargetFramework>net10.0</TargetFramework>
|
||||
<RootNamespace>OfficeCli</RootNamespace>
|
||||
<AssemblyName>officecli</AssemblyName>
|
||||
<Version>1.0.47</Version>
|
||||
<Version>1.0.48</Version>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<SelfContained>true</SelfContained>
|
||||
|
|
|
|||
Loading…
Reference in a new issue