Skip to content

Add PERCENTRANK.INC, MODE.MULT, FREQUENCY and PROB functions (HF-421, HF-424, HF-425, HF-428) - #1818

Open
marcin-kordas-hoc wants to merge 8 commits into
developfrom
feat/hf-stats2-percentrank-inc-modemult-frequency-prob
Open

marcin-kordas-hoc wants to merge 8 commits into
developfrom
feat/hf-stats2-percentrank-inc-modemult-frequency-prob

Conversation

@marcin-kordas-hoc

@marcin-kordas-hoc marcin-kordas-hoc commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds four statistical functions: PERCENTRANK.INC (with its PERCENTRANK alias), MODE.MULT, FREQUENCY and PROB (HF-421, HF-424, HF-425, HF-428).

  • PERCENTRANK.INC(array, x, [significance]) returns the rank of x as a fraction from 0 to 1, interpolating between neighbours, truncated to significance digits (3 by default). x outside the data is #N/A, and significance is truncated to an integer and must be at least 1 (otherwise #NUM!).
  • MODE.MULT(number1, ...) returns a column with every most frequent value, in order of first occurrence, and #N/A when no value repeats.
  • FREQUENCY(data_array, bins_array) returns a column with the count of values up to each bin and one more row for the values above the last bin. Text and blank cells are ignored.
  • PROB(x_range, prob_range, lower_limit, [upper_limit]) returns the probability that a value lies between the limits. The probabilities must be greater than 0, and their sum, added without rounding error, must be at least 1 and equal 1 when rounded to 15 significant digits (otherwise #NUM!): 0.1 added ten times is accepted, sums of 0.9999999999999995 and 1.000000000000006 are rejected. Ranges of different sizes give #N/A.
  • Implemented in PercentilePlugin and StatisticalAggregationPlugin, with metadata, translations, the license capability table entries, a line in docs/guide/known-limitations.md and a CHANGELOG entry.

MODE.MULT and FREQUENCY reserve their result area before evaluation: half as many rows as values passed to MODE.MULT (rounded down, at least one), and one row per cell of bins_array plus one for FREQUENCY. Rows the result does not fill stay empty, and a value in any reserved cell gives #SPILL!. This is the line added to the known limitations.

Behaviour check against Excel

122 formulas were evaluated in Excel Online (MS Graph live session) and in HyperFormula: 118 identical, 0 different, 4 not evaluable (calls with a wrong number of arguments, which Excel rejects when the formula is entered). They cover typed numbers (percentage, date and currency cells), booleans, text and numeric text, blank cells, errors, ranges and scalars, ties, bin ordering, limit ordering and the significance argument. Array results are read through INDEX, ROWS, COLUMNS and SUM, because they spill.

Later runs measured 26 more formulas in Excel. MODE.MULT(1,1,2,3,4,5) and FREQUENCY({1,2,3},{"a";2;"b"}) spill only the rows they fill (1 and 2), which is the reserved-area difference above. MODE.MULT with direct numeric text ("7","7") is #N/A, as in HyperFormula. For PROB, 19 probability sums were measured: Excel rejects every measured sum below 1 (the closest is 0.9999999999999995) and every one from 1.000000000000006 up, and accepts the sums from 1 to 1.000000000000004, as well as 0.1 added ten times, 0.2 added five times, {0.1, 0.2, 0.7}, {0.2, 0.3, 0.5} and {0.3, 0.3, 0.4}. PROB now gives the same result for all 19 (it used to accept sums off by up to 1e-9).

Translations

Each localized name was checked on Microsoft's page for the function in that language (support.microsoft.com/<locale>/excel/functions/<function>): the name used here is in the page's title or heading and in its syntax line, with one exception. The Dutch page names FREQUENCY INTERVAL, which HyperFormula's own INTERVAL function already uses in nlNL, so the English name is kept there. On the Swedish pages of PERCENTRANK.INC and PERCENTRANK, one of title and heading shows the English name, while the other and the syntax show PROCENTRANG.INK and PROCENTRANG. The Czech pages keep the English names for PERCENTRANK.INC, PERCENTRANK, MODE.MULT and PROB, and so does csCZ. enGB and idID keep the English names, which their Microsoft pages also use.

Tests

Paired tests branch: feat/hf-stats2-percentrank-inc-modemult-frequency-prob in the tests repository (185 tests, including MODE.MULT over 300000 values, 18 measured probability sums and one German-name test per function). All 185 fail against develop. Full local run on this head: 524 suites, 6927 passed, 3 skipped.

🤖 Generated with Claude Code


Note

Low Risk
Additive statistical builtins with localized names and spill sizing; no changes to auth, persistence, or core evaluation paths beyond new function handlers.

Overview
Adds four Excel-style statistical functions: PERCENTRANK.INC (alias PERCENTRANK) in PercentilePlugin, and MODE.MULT, FREQUENCY, and PROB in StatisticalAggregationPlugin.

PERCENTRANK.INC ranks a value within a range as a 0–1 fraction with inclusive interpolation, truncates to optional significance digits, and returns #N/A / #NUM! for out-of-range data or invalid significance. MODE.MULT spills a column of all tied modes (or #N/A if nothing repeats). FREQUENCY returns bin counts in bins_array order plus an overflow row. PROB sums probabilities over inclusive limits using compensated double-double summation and Excel-like 15-digit “sum equals 1” validation, with a new ProbabilityBounds error message.

MODE.MULT and FREQUENCY register parse-time sizeOfResultArrayMethod handlers so spill areas are reserved up front (documented in known limitations). Metadata, license capability lists, CHANGELOG, and locale translations are updated across supported languages.

Reviewed by Cursor Bugbot for commit 9487276. Bugbot is set up for automated code reviews on this repo. Configure here.

marcin-kordas-hoc and others added 3 commits October 15, 2026 07:10
…ions

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
hyperformula-docs 9487276 Commit Preview URL

Branch Preview URL
Oct 25 2026, 12:48 PM

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Performance comparison of head (9487276) vs base (d143456)

                                     testName |    base |    head | change
--------------------------------------------------------------------------
                                      Sheet A |  506.14 |  493.95 | -2.41%
                                      Sheet B |  165.42 |  157.04 | -5.07%
                                      Sheet T |  141.37 |  138.29 | -2.18%
                                Column ranges |  472.06 |  468.24 | -0.81%
                                Sorted lookup | 13665.7 | 13896.9 | +1.69%
Sheet A:  change value, add/remove row/column |   14.57 |    14.8 | +1.58%
 Sheet B: change value, add/remove row/column |  127.47 |  127.47 |  0.00%
                   Column ranges - add column |  146.83 |  150.48 | +2.49%
                Column ranges - without batch |  473.97 |  459.37 | -3.08%
                        Column ranges - batch |  123.08 |   116.4 | -5.43%

marcin-kordas-hoc and others added 2 commits October 23, 2026 00:11
…centrank-inc-modemult-frequency-prob

# Conflicts:
#	CHANGELOG.md
…ust the stack

Spreading every value into Math.max and Array.push threw a RangeError for about 200000 values.
Also documents that MODE.MULT and FREQUENCY reserve their result area before evaluation, and
restores the spacing of an unrelated error message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2d8f790. Configure here.

}
}
return numbers
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MODE.MULT skips scalar coercible values

Medium Severity

MODE.MULT collects numbers through numbersOfArguments, which applies manyToExactNumbers to scalars and therefore drops booleans and numeric text instead of coercing them. Excel counts direct TRUE/FALSE and numeric strings and returns #VALUE! for other text. coerceNumbersExactRanges already implements that split.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2d8f790. Configure here.

if (numericBins instanceof CellError) {
return numericBins
}
const bins = numericBins.length === 0 ? [-Infinity] : numericBins

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FREQUENCY fabricates a dummy empty bin

Medium Severity

When bins_array has no numbers, FREQUENCY inserts a -Infinity bin and returns a leading 0 plus the data count. Excel returns only the count of data_array. An empty numericBins list already sends every value to the overflow row, so the dummy bin changes both the shape and the values.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2d8f790. Configure here.

}
vals.sort((a, b) => a - b)
if (vals.length === 0 || x < vals[0] || x > vals[vals.length - 1]) {
return new CellError(ErrorType.NA, ErrorMessage.ValueNotFound)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty PERCENTRANK.INC returns wrong error

Low Severity

PERCENTRANK.INC returns #N/A when array contains no numbers, because the empty case is grouped with “x is outside the data”. Excel returns #NUM! for an empty array and #N/A only when x lies outside a non-empty set. getSortedValues already surfaces #NUM! for that empty case.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2d8f790. Configure here.

marcin-kordas-hoc and others added 3 commits October 25, 2026 02:00
…ificant digits

The 1e-9 tolerance accepted sums that are off by up to 1e-9, which Excel rejects with #NUM!.
Rounding the sum to 15 significant digits still accepts floating-point sums such as 0.1 added ten times.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s with a compensated sum

Rounding the plain sum to 15 significant digits accepted 0.9999999999999995, which Excel rejects.
The sum is now exact to double-double precision (0.1 added ten times is 1), must be at least 1,
and must be 1 when rounded to 15 significant digits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.32%. Comparing base (d143456) to head (9487276).

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #1818      +/-   ##
===========================================
+ Coverage    97.28%   97.32%   +0.04%     
===========================================
  Files          207      207              
  Lines        16218    16323     +105     
  Branches      3556     3610      +54     
===========================================
+ Hits         15777    15887     +110     
+ Misses         433      428       -5     
  Partials         8        8              
Files with missing lines Coverage Δ
hyperformula/src/error-message.ts 100.00% <100.00%> (ø)
hyperformula/src/i18n/languages/csCZ.ts 100.00% <ø> (ø)
hyperformula/src/i18n/languages/daDK.ts 100.00% <ø> (ø)
hyperformula/src/i18n/languages/deDE.ts 100.00% <ø> (ø)
hyperformula/src/i18n/languages/enGB.ts 100.00% <ø> (ø)
hyperformula/src/i18n/languages/esES.ts 100.00% <ø> (ø)
hyperformula/src/i18n/languages/fiFI.ts 100.00% <ø> (ø)
hyperformula/src/i18n/languages/frFR.ts 100.00% <ø> (ø)
hyperformula/src/i18n/languages/huHU.ts 100.00% <ø> (ø)
hyperformula/src/i18n/languages/idID.ts 100.00% <ø> (ø)
... and 12 more

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant