Skip to content

Analysis formulas

Summarize, forecast, and clean data with the full formula engine. Worked examples for every function family in @sheetwrite/formulas.
On this page

Use MAP with a LAMBDA to calculate one result for each input value.

Map values with LAMBDA
{ op: "set", addr: { sheet: "sheet1", row: 0, col: 0 },
value: { kind: "formula", src: "=MAP({1;2;3},LAMBDA(x,x*2))" } },
],
});
A
12
24
36
Result: A1 anchors the three doubled values

Try it live — formula showcase. Edit a formula to try this example.

Full setup
Map values with LAMBDA in a grid
import { createGrid, initSheetwrite } from "@sheetwrite/core";
import "@sheetwrite/core/styles.css";
import * as formulas from "@sheetwrite/formulas";
const host = document.querySelector<HTMLElement>("#grid");
if (host === null) throw new Error("Missing #grid host");
host.style.height = "320px";
await initSheetwrite(undefined, formulas);
const grid = createGrid(host, {
activeSheet: "sheet1",
sheets: [{
id: "sheet1", name: "Sheet1", rowCount: 3,
{ key: "result", header: "Result", type: "number", width: 120 },
],
}],
},
data: { rowCount: 3, columns: { result: [null, null, null] } },
});
{ op: "set", addr: { sheet: "sheet1", row: 0, col: 0 },
value: { kind: "formula", src: "=MAP({1;2;3},LAMBDA(x,x*2))" } },
],
});

The full formula engine, @sheetwrite/formulas, adds analysis functions to the default engine: grouping and pivot tables, LAMBDA helpers, statistics, regression, distributions, finance, dates, text and regex, reshaping, database functions, and matrices. This guide shows one task for each family. The formula function contract lists every function, its signature, and its build. The formulas guide explains syntax, references, errors, and spills. To see these functions recalculate live over 20,000 orders, open the formula analysis showcase.

Select the engine

Select the full engine one time, before you create a Grid or a store. The rest of the API does not change.

Select the full engine
import { initSheetwrite } from "@sheetwrite/core";
import * as formulas from "@sheetwrite/formulas";
await initSheetwrite(undefined, formulas);

With the default engine, these functions return #NAME?, and formula assist does not suggest them.

The example data

All examples use this sheet of 12 orders in A1:G12. Column E holds Units × Price, and column F holds order dates.

RowA RegionB ProductC UnitsD PriceE RevenueF DateG Note
1NorthDesk42501,0002026-01-05Ref INV-1042; ship by Friday
2SouthChair10808002026-01-09Ref INV-1043; gift wrap
3NorthChair6804802026-01-14Ref INV-1044
4EastDesk22505002026-01-20Ref INV-1045; call 555-0142
5SouthLamp15304502026-02-02Ref INV-1046
6EastLamp8302402026-02-06Ref INV-1047; ship by Monday
7NorthLamp12303602026-02-11Ref INV-1048
8SouthDesk32507502026-02-17Ref INV-1049
9EastChair7805602026-02-23Ref INV-1050; call 555-0187
10NorthDesk52501,2502026-03-03Ref INV-1051
11SouthChair9807202026-03-10Ref INV-1052
12EastDesk12502502026-03-16Ref INV-1053; gift wrap

Cash flows for the finance examples are in H1:I4: −10,000 on 2026-01-01, 2,500 on 2026-04-01, 4,200 on 2026-09-01, and 6,000 on 2026-12-31.

Every result below comes from the full engine. Results that spill are shown as the cells they fill.

Summarize: GROUPBY, PIVOTBY, PERCENTOF

GROUPBY groups rows by one or more key columns and aggregates the values. It adds a total row by default.

Revenue by region
=GROUPBY(A1:A12, E1:E12, SUM)
RegionRevenue
East1550
North3090
South2720
Total7360

PIVOTBY adds column keys:

Revenue by region and product
=PIVOTBY(A1:A12, B1:B12, E1:E12, SUM)
ChairDeskLampTotal
East5607502401550
North48022503603090
South15207504502720
Total2560375010507360

The optional arguments control headers, totals, and sort order. PERCENTOF as the function gives each group's share, and a LAMBDA can be the function:

FormulaResult
=GROUPBY(A1:A12, E1:E12, SUM, 0, 0, -2)North 3090, South 2720, East 1550 (no total, largest first)
=GROUPBY(A1:A12, E1:E12, PERCENTOF, 0, 0)East 0.2106, North 0.4198, South 0.3696
=GROUPBY(B1:B12, C1:C12, LAMBDA(units, MAX(units)), 0, 0)Chair 10, Desk 5, Lamp 15

A vector of functions, such as HSTACK(SUM, AVERAGE), returns #VALUE!. Use one function per GROUPBY.

Your own functions: LAMBDA, MAP, REDUCE, SCAN, BYROW

LAMBDA makes a function from parameters and a calculation. The helpers call it for each value, row, or column.

FormulaResult
=MAP(C1:C3, D1:D3, LAMBDA(units, price, units * price))1000; 800; 480 (spills down)
=REDUCE(0, E1:E12, LAMBDA(total, x, IF(x >= 1000, total + x, total)))2250
=SCAN(0, E1:E4, LAMBDA(running, x, running + x))1000; 1800; 2280; 2780 (running total)
=BYROW(C1:D3, LAMBDA(row, PRODUCT(row)))1000; 800; 480
=LET(big, LAMBDA(x, x >= 1000), FILTER(B1:B12, MAP(E1:E12, big)))Desk; Desk

LET can name a LAMBDA and call it later. An uncalled LAMBDA returns #CALC!, and a call with the wrong number of arguments returns #VALUE!. Calls nest up to 64 deep.

Describe: statistics and ranking

FormulaResultMeaning
=TRIMMEAN(E1:E12, 0.2)587Mean revenue without the top and bottom 10%
=PERCENTILE.EXC(E1:E12, 0.9)117590th percentile order
=RANK.AVG(E2, E1:E12)3The South chair order is the third largest
=SKEW(E1:E12)0.784Revenue has a tail of large orders
=FREQUENCY(E1:E12, {500, 1000})6; 5; 1Orders up to 500, up to 1,000, and above
=MODE.MULT({4, 10, 6, 4, 15, 10, 8})4; 10Every most frequent value

Forecast: regression

Monthly revenue for six months was 1200, 1350, 1500, 1580, 1700, and 1850.

FormulaResult
=FORECAST.LINEAR(7, {1200,1350,1500,1580,1700,1850}, {1,2,3,4,5,6})1968
=TREND({1200;1350;1500;1580;1700;1850}, {1;2;3;4;5;6}, {7;8;9})1968; 2093.14; 2218.29
=LINEST({1200;1350;1500;1580;1700;1850}, {1;2;3;4;5;6})125.14, 1092 (slope, intercept)

LINEST and LOGEST accept more than one predictor column, the const argument, and stats = TRUE for five rows of fit statistics. TREND and GROWTH project new values from the same fit.

Estimate risk: distributions

FormulaResultMeaning
=NORM.INV(0.95, 1000, 120)1197.38Demand that covers 95% of days, with mean 1000 and deviation 120
=BINOM.DIST(3, 10, 0.2, FALSE)0.2013Chance of exactly 3 returns in 10 orders at a 20% return rate
=CONFIDENCE.T(0.05, STDEV.S(E1:E12), 12)193.05Half-width of the 95% interval for mean revenue

The engine has 36 distribution functions: normal, Student t, chi-squared, F, binomial, Poisson, exponential, gamma, beta, lognormal, and Weibull, with their inverses.

Plan money: finance

FormulaResultMeaning
=XIRR(H1:H4, I1:I4)0.3897Annual return of irregular cash flows (39%)
=XNPV(0.08, H1:H4, I1:I4)1999.96Their value today at 8% a year
=PMT(0.06/12, 36, -25000)760.55Monthly payment on a 25,000 loan over 3 years at 6%
=CUMIPMT(0.06/12, 36, 25000, 1, 12, 0)−1286.73Interest paid in the first year of that loan

The full engine also has MIRR, NPER, CUMPRINC, EFFECT, NOMINAL, and depreciation functions (SLN, DB, DDB, SYD).

Schedule: working days and dates

FormulaResult
=NETWORKDAYS.INTL(DATE(2026,1,1), DATE(2026,1,31), 1)22 working days
=WORKDAY.INTL(DATE(2026,1,30), 5, "0000011")46059, that is 2026-02-06
=DATEDIF(DATE(2024,3,15), DATE(2026,1,5), "m")21 full months
=ISOWEEKNUM(DATE(2026,1,5))2

The weekend argument is a number or a seven-character mask that starts on Monday: "0000011" means Saturday and Sunday. A holiday range is the optional last argument.

Clean text: split, extract, and regex

FormulaResult
=TEXTSPLIT("North,South,East", ",")North, South, East (spills across)
=TEXTBEFORE(G1, ";")Ref INV-1042
=TEXTAFTER(G1, "; ")ship by Friday
=REGEXEXTRACT(G1, "INV-[0-9]+")INV-1042
=REGEXREPLACE(G4, "[0-9]{3}-[0-9]{4}", "***-****")Ref INV-1045; call ***-****
=REGEXTEST(G6, "ship by")TRUE
=ARRAYTOTEXT({"North", "South"})North, South

Regex uses the regex-lite syntax. Patterns with lookaround, backreferences, or Unicode classes return #VALUE!, never a wrong match. Case-insensitive matching folds ASCII letters only.

Reshape arrays

FormulaResult
=VSTACK({"Region","Units"}, HSTACK(A1:A3, C1:C3))A header row on top of North 4, South 10, North 6
=TAKE(SORTBY(HSTACK(B1:B12, E1:E12), E1:E12, -1), 3)Desk 1250, Desk 1000, Chair 800
=WRAPROWS(SEQUENCE(6), 3)1, 2, 3 / 4, 5, 6
=TOCOL({1,2;3,4})1; 2; 3; 4
=EXPAND({1,2;3,4}, 3, 3, 0)The 2 × 2 array padded to 3 × 3 with 0

SORTBY keeps equal items in their order. TOCOL and TOROW can skip blanks and errors and read by row or by column.

Query tables: database functions

Database functions take a table with a header row, a field, and a criteria table with the same headers. Criteria columns use AND, and criteria rows use OR.

FormulaResult
=DSUM({"Region","Units";"North",4;"South",10;"North",6}, "Units", {"Region";"North"})10
=DAVERAGE({"Region","Units";"North",4;"South",10;"North",6}, "Units", {"Region";"North"})5

In a workbook, the table and the criteria are usually ranges, such as =DSUM(A1:E200, "Revenue", H1:H2).

Matrices

FormulaResult
=MMULT({1,2;3,4}, {5;6})17; 39
=MINVERSE({4,7;2,6})0.6, −0.7 / −0.2, 0.4
=MDETERM({4,7;2,6})10
=SUMSQ(C1:C3)152

Limits

  • All these functions use the normal spill, shape, memory, and work limits. A result that does not fit returns an error, not a partial result.
  • AGGREGATE options that skip hidden rows have no effect, because formulas do not see hidden-row state.
  • Each family's exact argument rules are in the formula function contract.

Full formula engine

Sheetwrite has two engine builds with the same API. @sheetwrite/wasm is the default engine: it has the standard function set and the smallest download. @sheetwrite/formulas is the full engine: the same engine with the analysis families below. An app selects one engine, one time, before it creates a Grid or a store:

Select the full engine
import { initSheetwrite } from "@sheetwrite/core";
import * as formulas from "@sheetwrite/formulas";
await initSheetwrite(undefined, formulas);
FamilyExamples
DistributionsNORM.DIST, T.INV.2T, CHISQ.DIST.RT, BINOM.DIST, GAMMA.INV, CONFIDENCE.T
Descriptive statisticsTRIMMEAN, PERCENTILE.EXC, RANK.AVG, SKEW, KURT, FORECAST.LINEAR
RegressionLINEST, LOGEST, TREND, GROWTH, FREQUENCY, MODE.MULT
FinanceXIRR, XNPV, MIRR, NPER, CUMIPMT, DDB
DatesNETWORKDAYS.INTL, WORKDAY.INTL, DATEDIF, ISOWEEKNUM
Text and regexTEXTSPLIT, TEXTBEFORE, TEXTAFTER, REGEXEXTRACT, REGEXREPLACE, ARRAYTOTEXT
DatabaseDSUM, DAVERAGE, DCOUNT, DGET, DMAX, DSTDEV
Math and matricesAGGREGATE, MMULT, MINVERSE, MDETERM, SUMSQ, COMBIN
ReshapingSORTBY, HSTACK, VSTACK, TOCOL, WRAPROWS, EXPAND
LAMBDALAMBDA, MAP, REDUCE, SCAN, BYROW, BYCOL, MAKEARRAY, ISOMITTED
GroupingGROUPBY, PIVOTBY, PERCENTOF

The default engine returns #NAME? for these functions, and formula assist does not suggest them. The formula analysis showcase runs thirteen panels of them live over 20,000 orders, and the analysis formulas guide shows each family with examples.

Limits of the full engine:

  • LAMBDA parameters bind lexically, and LET can bind a LAMBDA. An uncalled LAMBDA returns #CALC!. A call with the wrong number of arguments returns #VALUE!. Calls are limited to a depth of 64 and 2,000,000 work units per outer helper; more returns #NUM!.
  • GROUPBY and PIVOTBY accept one built-in function name, such as SUM, or a LAMBDA with one or two parameters. A vector of functions, such as HSTACK(SUM,AVERAGE), returns #VALUE!.
  • Regex functions do not support lookaround, backreferences, or Unicode classes. Such patterns return #VALUE!, never a wrong match.
  • AGGREGATE options that skip hidden rows have no effect, because formulas do not see hidden-row state.
Documentation