Charting
This is the same document you'll find inside Spredin under Help.
Spredin charts are powered by Apache ECharts. A chart is a floating card bound to a cell range: edit the cells and the chart updates live. You can build and fully customise charts from the UI, from Python, or from an LLM/agent — all three drive the same options, and the raw ECharts option is always reachable as an escape hatch.
Create a chart#
- UI: select a range → Insert → Chart from Selection (or ⌘K → "Chart"). The first column is the category axis; each remaining column of numbers is a series (a column of text only — a date, a region, a rep's name — is a label, not a series, as in Excel); a non-numeric first row is treated as headers (category + series names). Hidden rows and hidden columns are left out, as Excel leaves them out by default.
- Python / agent:
sheet.chart("A1:E5", "bar", "Revenue").
A new chart is named from its data: a chart of one series takes that series' name, as in Excel; one of several has no title until you give it one (Excel shows a placeholder; a script's title wins). It goes beside its table — right of it when it fits in the window, else below — and where neither fits, where it covers the least of the table.
A drag or a resize of the card is one step to undo, and a box in the ⚙ panel changes the chart when you press Enter or leave the box, not at every key.
Chart types#
bar · stackedBar · horizontalBar · line · stepLine · area ·
stackedArea · pie · donut · rose · scatter · radar · funnel ·
polarBar · treemap · sunburst · heatmap · boxplot · candlestick
Seven appear as quick-switch icons on the chart card — bar, stacked bar, line, area, stacked area, pie and scatter. All are in the ⚙ panel's Type picker, each with a one-line hint on when to use it. Notes on the extra types:
donut — a pie with a hole in the middle; slices work the same way.
stepLine — a line that holds its value, then jumps to the next one.
horizontalBar — bars run left→right (value on X, categories on Y).
rose — Nightingale pie (slice radius ∝ value); sums each row like pie.
radar — one spoke per row (category), one shape per value column (series), all on one scale, as Excel's radar.
funnel — one stage per row, summed across value columns (like pie).
polarBar — bars wrapped around a polar axis (angle = category).
treemap — one tile per row, sized by the row's total.
sunburst — two rings: each row is an inner wedge, its value columns the outer ring.
heatmap — a matrix: X = rows, Y = value columns, colour = value.
boxplot — one box per column of numbers: its quartiles and median, with Excel's whiskers (see Box plots below).
candlestick (OHLC) — needs 4 value columns per row: headed Open, High, Low, Close in any order, or without those headers in Excel's open, high, low, close order (the order a price download has).
Pie — one slice per row (category), valued by the sum of all value columns in that row, so it uses the whole selected region (not just the first column).
Scatter — the first column is the shared X axis (numbers make a value axis, text a category axis); each remaining column is a separate, colour-coded Y series named from the header row. Two columns → one series; more → a multi-series scatter that uses the full 2-D region.
Swap rows / columns — the ⚙ panel's Swap rows / columns (or
transpose=Truein Python) transposes the data, so categories become series and vice-versa. Use it to pie-by-column instead of by-row, or to re-group a scatter's series.
Detailed editing (the ⚙ panel — Excel-style)#
Click ⚙ (Edit chart) on a chart card for:
| Option | What it does |
|---|---|
| Type | Switch chart type |
| Data range | Re-bind to a different A1 range |
| X / Y axis title | Axis names |
| Y min / max | Fix the value-axis bounds (blank = automatic, see The automatic value axis below) |
| Legend | Hidden / Top / Bottom / Left / Right |
| Swap rows / columns | Chart the data the other way round (see above) |
| First row is headers | Automatic (text over numbers), Yes or No — say Yes when the headers are numbers, such as years |
| Swap X / Y axes | Turn a bar, line or area chart on its side — categories down the side, values across |
| Series types (combo) | Draw each series as a bar, line or area in one chart; type a column range (B:D) to set several at once |
| Gridlines | Toggle value-axis gridlines |
| Smooth lines | Curved vs. straight line/area |
| Data labels + format | Show values on points/bars/slices; format is an ECharts template ({c} value, {b} name, {d}% percent) |
| Log scale (Y) | Logarithmic value axis |
| Zoom slider | A slider under the chart to zoom into part of a long series |
| Average line | A dashed line at each series' average |
| Toolbox (save / zoom) | Chart buttons to save an image, zoom into a box, see the data as a table, and reset |
| Empty cells | What an empty cell (or #N/A) does to a line or bar: a gap (the default, as in Excel), zero, or connect with a line. A formula that reads an empty cell shows 0 and is plotted at 0; a formula that gives "" counts as an empty cell here. |
| Dual axis (combo) | Plot the last series on a secondary Y (e.g. bars + a %-margin line) |
| Series colours | Comma-separated hex overrides |
| Advanced (ECharts JSON) | A raw ECharts option fragment deep-merged over the generated chart — the full library |
Swap X / Y axes, Series types, Zoom slider, Average line, Toolbox and Empty cells appear for bar, line and area charts (stacked and step versions included).
Values look as they do in the cells. The value axis, data labels and
tooltips use each column's number format: a $#,##0 column reads $1,507,108,
a 0.0% column 12.3% (Excel's "linked to source"). When the series on one
axis have different formats, that axis shows plain numbers. With rows and
columns swapped, each series is a row, so it takes the format the value
columns share, and shows plain numbers when they differ. A data-label
format you set in the panel wins over the cells'. The category labels follow the
first column's format too, so a column of payments formatted $#,##0 reads
$300 along the axis, and a date column reads as the grid shows it — Jul 2025
for a column formatted mmm yyyy, not the date's serial number.
A single column charts as one series over its positions (1, 2, 3…), named by its header, as in Excel.
Box plots follow Excel's box and whisker: the whiskers reach the furthest value within 1.5 × the box's height (the IQR), and anything further out is drawn as a separate point. A box plot summarises columns, so a first column of numbers is a box of its own, unless it reads as row labels (names, or a run like 1, 2, 3 or 2019…2023).
Other niceties: drag the title bar to move, drag the corner to resize, double-click the title to rename, ⤓ exports a PNG (in the desktop app, through the save panel).
The automatic value axis#
With Y min and Y max blank, a line, step-line or scatter chart scales its value axis the way Excel's automatic axis does:
- Values close together leave out zero. When the smallest value is at least 5/6 of the largest, the axis starts just below the data. A price between 9.4 and 10.2 fills the chart, on an axis from 9 to 10.4, instead of lying flat near the top of an axis from 0. Change the data and the axis follows it. All-negative values work the same way, toward zero.
- Otherwise the axis starts at zero, as before.
- A scatter chart's X axis follows the same rule. Years 2000–2024 run from 1995 to 2030, not from 0.
- Bars, columns, areas and stacked charts always start at zero, because their length shows the value.
- To keep zero on a line chart, set Y min to 0. A bound you set is always kept as you set it.
- With the Zoom slider on, the axis follows the part of the series shown, still without zero.
- A log axis, or an ECharts fragment that sets the axis's bounds (below), decides for itself.
Python / agent API#
# Simple
sheet.chart("A1:E5", "line", "Sales by quarter")
# Excel-style options (snake_case keywords)
sheet.chart("A1:E5", "bar", "Revenue",
x_title="Quarter", y_title="USD",
y_min=0, legend_pos="top", labels=True, label_fmt="{c}",
gridlines=False, colors=["#4f6bed", "#e5604d"])
# Combo (bars + a line on a second axis)
sheet.chart("A1:C13", "bar", "Revenue vs margin", dual_axis=True)
# Change a chart already on the sheet (its id, or its place in sheet.charts())
sheet.update_chart(0, type="line", title="By month", y_min=0)
sheet.clear_charts() # before re-adding on a re-run: charts are added, never replaced
A chart shows data from its own sheet: to chart another sheet's data, make it
there, sheet.book("Data").chart("A1:C9", "bar"). A range that names another
sheet raises an error that says so.
Full ECharts power — the echarts= escape hatch#
Anything the structured options don't cover, pass as a raw ECharts option
fragment; it is deep-merged over the generated option (objects merge,
arrays/scalars replace). This unlocks the whole library — markLine, markArea,
dataZoom, visualMap, custom tooltip formatters, multiple grids, etc.
The fragment is saved inside the workbook, so it travels with every copy you
send. For that reason it cannot put anything into the page: a string containing
< or >, a javascript: link, a title link, and the tooltip's CSS and DOM
options are dropped, and a custom tooltip template is drawn on the chart itself
rather than as HTML. Templates such as {b}: {c} units and rich-text styles
work as usual.
sheet.chart("A1:E5", "line", "With an average line", echarts={
"tooltip": {"trigger": "axis"},
"series": [{"markLine": {"data": [{"type": "average", "name": "Avg"}]}}],
"dataZoom": [{"type": "inside"}],
})
Keyword options map to chart fields (
x_title→xTitle,y_title→yTitle,y_min→yMin,y_max→yMax,log_y→logY,dual_axis→dualAxis,legend_pos→legendPos,label_fmt→labelFmt,series_types→seriesTypes,mean_line→meanLine). The others keep their names:legend=False,labels=True,gridlines=False,smooth=True,colors=[…],transpose=True,horizontal=True(swap X / Y),zoom=True,toolbox=True,empty='zero'(or'gap','connect'), andheader=Truewhen the series names in the first row are numbers (2023, 2024).echarts=takes any ECharts JSON. An option or value a chart cannot use raises and names what is allowed.
Charts and Excel files#
- Opening an
.xlsx: its charts open as Spredin charts, drawn from the same cells — even when the series are on other sheets, in columns that are not side by side, or named by numbers (2023, 2024). The chart keeps the list of series Excel chose; the ⚙ panel says so, and typing a range there plots the range instead. - The Excel badge on such a card opens a note: how Spredin draws it, how it will be saved, and what Spredin's drawing leaves out (error bars, 3-D, labels on some series only…). It never blocks anything.
- A kind Spredin cannot draw (a map, a 3-D surface) shows as a placeholder card. Its title cannot be edited: it is kept as Excel wrote it.
- Saving as
.xlsx: a chart you have not changed is written back exactly as Excel had it — colours, fonts, error bars and all. Moving or resizing the card is not a change; its type, title, data or options are. A changed chart, or one made here, becomes an Excel chart; a kind Excel has no chart for becomes a picture of Spredin's drawing. Saving asks first, naming each chart that is simplified, made a picture or left out, and why — and does not ask when nothing is lost. - Back in Spredin: a chart Spredin wrote to
.xlsxopens as it was here (a Spredin Pro waterfall saved as a picture opens as a waterfall), unless it was changed in Excel in between.
Notes & limits#
- Charts are per-sheet and are saved in the
.sprdworkbook, including all of the above options and anyechartsfragment. - The card recomputes live on cell edits and on theme changes (series + chrome
colours follow the active theme unless you override
colors). - A fragment in
echarts=always wins (it's merged last), so it can override any generated setting.