Skip to content

Add PERCENTILE and QUARTILE function families#1650

Open
marcin-kordas-hoc wants to merge 12 commits intodevelopfrom
feature/percentile-quartile
Open

Add PERCENTILE and QUARTILE function families#1650
marcin-kordas-hoc wants to merge 12 commits intodevelopfrom
feature/percentile-quartile

Conversation

@marcin-kordas-hoc
Copy link
Copy Markdown
Collaborator

@marcin-kordas-hoc marcin-kordas-hoc commented Apr 7, 2026

Summary

  • Implement 6 new functions: PERCENTILE, PERCENTILE.INC, PERCENTILE.EXC, QUARTILE, QUARTILE.INC, QUARTILE.EXC
  • New PercentilePlugin with inclusive/exclusive interpolation helpers
  • i18n translations for all 17 languages (verified against Excel function translator)
  • CHANGELOG entry and built-in-functions.md updated

Changes

  • src/interpreter/plugin/PercentilePlugin.ts — new plugin
  • src/interpreter/plugin/index.ts — export registration
  • src/i18n/languages/*.ts — all 17 languages
  • docs/guide/built-in-functions.md — 6 new entries (alphabetical)
  • CHANGELOG.md — added entry

Test plan

  • 57 unit tests in hyperformula-tests (function-percentile.spec.ts)
  • Excel validation workbook (107 cases) — open in Excel 365 desktop, verify all PASS
  • npm run lint passes
  • npm run compile passes
  • CI green

Note

Medium Risk
Introduces new statistical function implementations and aliases in the interpreter; main risk is correctness/edge-case parity with spreadsheet semantics and potential impacts to function translation tables.

Overview
Adds PERCENTILE/QUARTILE function families, including .INC and .EXC variants, via a new PercentilePlugin that computes percentiles/quartiles with linear interpolation and appropriate #NUM! error handling for out-of-range inputs.

Registers the plugin export, adds function aliases (PERCENTILEPERCENTILE.INC, QUARTILEQUARTILE.INC), and updates built-in function documentation, changelog, and all language packs to include translations for the new function names.

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

marcin-kordas-hoc and others added 3 commits March 20, 2026 17:35
…C, QUARTILE.EXC functions

- New PercentilePlugin with inclusive and exclusive interpolation
- Translations for all 17 supported languages (verified via excel-translator.de)
- Documentation in built-in-functions.md
- CHANGELOG entry

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lidation

- Extract getSortedValues() to eliminate 4x duplicated range-extraction + sort
- Remove dead-code guards from percentileInclusive/percentileExclusive helpers
- Fix quartile validation: truncate BEFORE range check (Excel truncates 3.9→3)
- Remove minValue/maxValue from QUARTILE params — validate post-truncation
- Use correct ValueSmall/ValueLarge error messages for under/over bounds

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ruRU: ПРОЦЕНТИЛЬ.ВКЛ/ИСКЛ → ПЕРСЕНТИЛЬ.ВКЛ/ИСКЛ (match base name)
- csCZ: QUARTIL → KVARTIL (proper Czech Excel name)
- trTR: .DHL → .DAH for INC variants (Dahil, not typo)
- index.ts: fix alphabetical export order (ModuloPlugin before PercentilePlugin)
- PercentilePlugin: use ast.procedureName instead of hardcoded metadata key

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor bot left a comment

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 and found 2 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Wrong error message when percentile rank exceeds dataset size
    • Split the combined rank < 1 || rank > n check into two separate conditions so rank > n now correctly returns ErrorMessage.ValueLarge instead of ErrorMessage.ValueSmall.
  • ✅ Fixed: Russian PERCENTILE.INC/EXC translations use wrong root word
    • Changed the Russian translations for PERCENTILE.INC and PERCENTILE.EXC from ПЕРСЕНТИЛЬ.ВКЛ/ПЕРСЕНТИЛЬ.ИСКЛ to ПРОЦЕНТИЛЬ.ВКЛ/ПРОЦЕНТИЛЬ.ИСКЛ to match official Microsoft Excel Russian locale names.

Create PR

Or push these changes by commenting:

@cursor push 8d62452b48
Preview (8d62452b48)
diff --git a/src/i18n/languages/ruRU.ts b/src/i18n/languages/ruRU.ts
--- a/src/i18n/languages/ruRU.ts
+++ b/src/i18n/languages/ruRU.ts
@@ -369,8 +369,8 @@
     LARGE: 'НАИБОЛЬШИЙ',
     SMALL: 'НАИМЕНЬШИЙ',
     PERCENTILE: 'ПЕРСЕНТИЛЬ',
-    'PERCENTILE.INC': 'ПЕРСЕНТИЛЬ.ВКЛ',
-    'PERCENTILE.EXC': 'ПЕРСЕНТИЛЬ.ИСКЛ',
+    'PERCENTILE.INC': 'ПРОЦЕНТИЛЬ.ВКЛ',
+    'PERCENTILE.EXC': 'ПРОЦЕНТИЛЬ.ИСКЛ',
     QUARTILE: 'КВАРТИЛЬ',
     'QUARTILE.INC': 'КВАРТИЛЬ.ВКЛ',
     'QUARTILE.EXC': 'КВАРТИЛЬ.ИСКЛ',

diff --git a/src/interpreter/plugin/PercentilePlugin.ts b/src/interpreter/plugin/PercentilePlugin.ts
--- a/src/interpreter/plugin/PercentilePlugin.ts
+++ b/src/interpreter/plugin/PercentilePlugin.ts
@@ -34,9 +34,12 @@
 function percentileExclusive(sortedVals: number[], k: number): number | CellError {
   const n = sortedVals.length
   const rank = k * (n + 1)
-  if (rank < 1 || rank > n) {
+  if (rank < 1) {
     return new CellError(ErrorType.NUM, ErrorMessage.ValueSmall)
   }
+  if (rank > n) {
+    return new CellError(ErrorType.NUM, ErrorMessage.ValueLarge)
+  }
   const intPart = Math.floor(rank)
   const fraction = rank - intPart
   if (intPart < n) {

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

…name)

Microsoft uses different roots: legacy ПЕРСЕНТИЛЬ vs newer ПРОЦЕНТИЛЬ.ВКЛ/ИСКЛ.
Confirmed via official ru-ru support.microsoft.com docs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
marcin-kordas-hoc and others added 3 commits April 7, 2026 02:10
…docs)

- csCZ: KVARTIL → QUARTIL (Czech Excel uses QUARTIL, not KVARTIL)
- trTR: .DAH → .DHL (Turkish Excel uses .DHL, confirmed on support.microsoft.com/tr-tr)

Our earlier code review incorrectly changed these. Official MS docs confirm
the original translations were correct.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add @param/@returns tags to all public methods and module-level helpers
- Rename intPart → lowerIndex for clarity in interpolation functions
- Add inline comments explaining quart / 4 conversion
- Improve class-level JSDoc to explain inclusive/exclusive distinction

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@marcin-kordas-hoc marcin-kordas-hoc requested a review from sequba April 7, 2026 10:54
@sequba
Copy link
Copy Markdown
Contributor

sequba commented Apr 7, 2026

@marcin-kordas-hoc resolve the merge conflicts

| NORMSINV | Returns value of inverse normal distribution. | NORMSINV(P) |
| PEARSON | Returns the correlation coefficient between two data sets. | PEARSON(Data1, Data2) |
| PHI | Returns probability densitity of normal distribution. | PHI(X) |
| PERCENTILE | Returns the k-th percentile of values in a range. | PERCENTILE(Data, K) |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If this works the same as PERCENTILE.INC, it should have the same description

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in commit 17a65e5. PERCENTILE now has the same description as PERCENTILE.INC: "inclusive of 0 and 1".

| POISSON | Returns density of Poisson distribution. | POISSON(X, Mean, Mode) |
| POISSON.DIST | Returns density of Poisson distribution. | POISSON.DIST(X, Mean, Mode) |
| POISSONDIST | Returns density of Poisson distribution. | POISSONDIST(X, Mean, Mode) |
| QUARTILE | Returns the quartile of a data set. | QUARTILE(Data, Quart) |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If this works the same as QUARTILE.INC, it should have the same description

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in commit 17a65e5. QUARTILE now has the same description as QUARTILE.INC: "based on inclusive percentile values".

Comment on lines +67 to +80
'PERCENTILE': {
method: 'percentile',
parameters: [
{argumentType: FunctionArgumentType.RANGE},
{argumentType: FunctionArgumentType.NUMBER, minValue: 0, maxValue: 1},
],
},
'PERCENTILE.INC': {
method: 'percentile',
parameters: [
{argumentType: FunctionArgumentType.RANGE},
{argumentType: FunctionArgumentType.NUMBER, minValue: 0, maxValue: 1},
],
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If these 2 function work the same use FunctionPlugin.aliases

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in commit 17a65e5. PERCENTILE is now an alias for PERCENTILE.INC, and QUARTILE is an alias for QUARTILE.INC, using FunctionPlugin.aliases.

marcin-kordas-hoc and others added 3 commits April 7, 2026 12:02
…review)

- PERCENTILE is now an alias for PERCENTILE.INC
- QUARTILE is now an alias for QUARTILE.INC
- Docs: PERCENTILE and QUARTILE descriptions match their .INC variants

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ability

Use ValueLarge for rank > n (upper bound) and ValueSmall for rank < 1
(lower bound) instead of generic ValueSmall for both. ErrorType stays
NUM in both cases (Excel-compatible). Improves detailedError API for
developers and AI agents per 2026 explainability priority.
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 13, 2026

Performance comparison of head (7172a52) vs base (736235e)

                                     testName |   base |   head | change
------------------------------------------------------------------------
                                      Sheet A | 487.22 | 495.96 | +1.79%
                                      Sheet B | 154.46 | 155.99 | +0.99%
                                      Sheet T | 138.01 | 138.01 |  0.00%
                                Column ranges | 467.14 | 466.73 | -0.09%
Sheet A:  change value, add/remove row/column |  15.19 |  14.95 | -1.58%
 Sheet B: change value, add/remove row/column | 122.98 | 125.68 | +2.20%
                   Column ranges - add column | 143.98 | 139.51 | -3.10%
                Column ranges - without batch | 430.49 |  428.9 | -0.37%
                        Column ranges - batch | 112.04 | 111.01 | -0.92%

Copy link
Copy Markdown

@cursor cursor bot left a comment

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 and found 1 potential issue.

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 7172a52. Configure here.

'PERCENTILE.EXC': 'PERCENTILE.EXC',
QUARTILE: 'QUARTILE',
'QUARTILE.INC': 'QUARTILE.INC',
'QUARTILE.EXC': 'QUARTILE.EXC',
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Missing PERCENTILE/QUARTILE translations for enUS locale

High Severity

The new PERCENTILE and QUARTILE function translations are added to 16 of the 17 language files, but enUS.ts is missing from the diff. Users on the enUS locale won't have translations for PERCENTILE, PERCENTILE.INC, PERCENTILE.EXC, QUARTILE, QUARTILE.INC, or QUARTILE.EXC, likely causing those functions to be unrecognized when that locale is active.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 7172a52. Configure here.

Copy link
Copy Markdown
Contributor

@sequba sequba left a comment

Choose a reason for hiding this comment

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

👍

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 14, 2026

Codecov Report

❌ Patch coverage is 95.89041% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.18%. Comparing base (736235e) to head (7172a52).

Files with missing lines Patch % Lines
src/interpreter/plugin/PercentilePlugin.ts 95.83% 3 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #1650      +/-   ##
===========================================
- Coverage    97.19%   97.18%   -0.01%     
===========================================
  Files          173      174       +1     
  Lines        15013    15086      +73     
  Branches      3209     3223      +14     
===========================================
+ Hits         14592    14662      +70     
- Misses         421      424       +3     
Files with missing lines Coverage Δ
src/i18n/languages/csCZ.ts 100.00% <ø> (ø)
src/i18n/languages/daDK.ts 100.00% <ø> (ø)
src/i18n/languages/deDE.ts 100.00% <ø> (ø)
src/i18n/languages/enGB.ts 100.00% <ø> (ø)
src/i18n/languages/esES.ts 100.00% <ø> (ø)
src/i18n/languages/fiFI.ts 100.00% <ø> (ø)
src/i18n/languages/frFR.ts 100.00% <ø> (ø)
src/i18n/languages/huHU.ts 100.00% <ø> (ø)
src/i18n/languages/itIT.ts 100.00% <ø> (ø)
src/i18n/languages/nbNO.ts 100.00% <ø> (ø)
... and 8 more
🚀 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.

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.

2 participants