diff --git a/CHANGELOG.md b/CHANGELOG.md index 787c642ad9..abc626afde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added - Added support for the new license key format. A proprietary key can now grant a subset of the library: a function your key does not include evaluates to a `#LIC!` error, and the matching parts of the API throw a `LicenseCapabilityMissingError`. `getAvailableFunctions()` and `getFunctionDetails()` describe only the functions your key includes. [#1728](https://github.com/handsontable/hyperformula/pull/1728) +- Added a new function: RANK.AVG. [#1787](https://github.com/handsontable/hyperformula/pull/1787) ### Changed diff --git a/src/i18n/languages/csCZ.ts b/src/i18n/languages/csCZ.ts index fea286083c..90a2fa78b8 100644 --- a/src/i18n/languages/csCZ.ts +++ b/src/i18n/languages/csCZ.ts @@ -390,6 +390,7 @@ const dictionary: RawTranslationPackage = { PERCENTILE: 'PERCENTIL', 'PERCENTILE.INC': 'PERCENTIL.INC', 'PERCENTILE.EXC': 'PERCENTIL.EXC', + 'RANK.AVG': 'RANK.AVG', QUARTILE: 'QUARTIL', 'QUARTILE.INC': 'QUARTIL.INC', 'QUARTILE.EXC': 'QUARTIL.EXC', diff --git a/src/i18n/languages/daDK.ts b/src/i18n/languages/daDK.ts index 9264551249..ca60bd1603 100644 --- a/src/i18n/languages/daDK.ts +++ b/src/i18n/languages/daDK.ts @@ -390,6 +390,7 @@ const dictionary: RawTranslationPackage = { PERCENTILE: 'FRAKTIL', 'PERCENTILE.INC': 'FRAKTIL.MEDTAG', 'PERCENTILE.EXC': 'FRAKTIL.UDELAD', + 'RANK.AVG': 'RANK.AVG', QUARTILE: 'KVARTIL', 'QUARTILE.INC': 'KVARTIL.MEDTAG', 'QUARTILE.EXC': 'KVARTIL.UDELAD', diff --git a/src/i18n/languages/deDE.ts b/src/i18n/languages/deDE.ts index 4d3d39319b..723e5b8a3e 100644 --- a/src/i18n/languages/deDE.ts +++ b/src/i18n/languages/deDE.ts @@ -390,6 +390,7 @@ const dictionary: RawTranslationPackage = { PERCENTILE: 'QUANTIL', 'PERCENTILE.INC': 'QUANTIL.INKL', 'PERCENTILE.EXC': 'QUANTIL.EXKL', + 'RANK.AVG': 'RANK.AVG', QUARTILE: 'QUARTILE', 'QUARTILE.INC': 'QUARTILE.INKL', 'QUARTILE.EXC': 'QUARTILE.EXKL', diff --git a/src/i18n/languages/enGB.ts b/src/i18n/languages/enGB.ts index d271b732cd..3367c5b02c 100644 --- a/src/i18n/languages/enGB.ts +++ b/src/i18n/languages/enGB.ts @@ -390,6 +390,7 @@ const dictionary: RawTranslationPackage = { PERCENTILE: 'PERCENTILE', 'PERCENTILE.INC': 'PERCENTILE.INC', 'PERCENTILE.EXC': 'PERCENTILE.EXC', + 'RANK.AVG': 'RANK.AVG', QUARTILE: 'QUARTILE', 'QUARTILE.INC': 'QUARTILE.INC', 'QUARTILE.EXC': 'QUARTILE.EXC', diff --git a/src/i18n/languages/esES.ts b/src/i18n/languages/esES.ts index 9c6b817b98..f92ff6648e 100644 --- a/src/i18n/languages/esES.ts +++ b/src/i18n/languages/esES.ts @@ -390,6 +390,7 @@ export const dictionary: RawTranslationPackage = { PERCENTILE: 'PERCENTIL', 'PERCENTILE.INC': 'PERCENTIL.INC', 'PERCENTILE.EXC': 'PERCENTIL.EXC', + 'RANK.AVG': 'RANK.AVG', QUARTILE: 'CUARTIL', 'QUARTILE.INC': 'CUARTIL.INC', 'QUARTILE.EXC': 'CUARTIL.EXC', diff --git a/src/i18n/languages/fiFI.ts b/src/i18n/languages/fiFI.ts index 99ff05708f..ddc11ef4c7 100644 --- a/src/i18n/languages/fiFI.ts +++ b/src/i18n/languages/fiFI.ts @@ -390,6 +390,7 @@ const dictionary: RawTranslationPackage = { PERCENTILE: 'PROSENTTIPISTE', 'PERCENTILE.INC': 'PROSENTTIPISTE.SIS', 'PERCENTILE.EXC': 'PROSENTTIPISTE.ULK', + 'RANK.AVG': 'RANK.AVG', QUARTILE: 'NELJÄNNES', 'QUARTILE.INC': 'NELJÄNNES.SIS', 'QUARTILE.EXC': 'NELJÄNNES.ULK', diff --git a/src/i18n/languages/frFR.ts b/src/i18n/languages/frFR.ts index f7b9be45f7..a534a2380d 100644 --- a/src/i18n/languages/frFR.ts +++ b/src/i18n/languages/frFR.ts @@ -390,6 +390,7 @@ const dictionary: RawTranslationPackage = { PERCENTILE: 'CENTILE', 'PERCENTILE.INC': 'CENTILE.INCLURE', 'PERCENTILE.EXC': 'CENTILE.EXCLURE', + 'RANK.AVG': 'RANK.AVG', QUARTILE: 'QUARTILE', 'QUARTILE.INC': 'QUARTILE.INCLURE', 'QUARTILE.EXC': 'QUARTILE.EXCLURE', diff --git a/src/i18n/languages/huHU.ts b/src/i18n/languages/huHU.ts index e40219553a..8dd6491e24 100644 --- a/src/i18n/languages/huHU.ts +++ b/src/i18n/languages/huHU.ts @@ -390,6 +390,7 @@ const dictionary: RawTranslationPackage = { PERCENTILE: 'PERCENTILIS', 'PERCENTILE.INC': 'PERCENTILIS.TARTALMAZ', 'PERCENTILE.EXC': 'PERCENTILIS.KIZÁR', + 'RANK.AVG': 'RANK.AVG', QUARTILE: 'KVARTILIS', 'QUARTILE.INC': 'KVARTILIS.TARTALMAZ', 'QUARTILE.EXC': 'KVARTILIS.KIZÁR', diff --git a/src/i18n/languages/idID.ts b/src/i18n/languages/idID.ts index 719cb11db6..4d3ab417ff 100644 --- a/src/i18n/languages/idID.ts +++ b/src/i18n/languages/idID.ts @@ -390,6 +390,7 @@ const dictionary: RawTranslationPackage = { PERCENTILE: 'PERCENTILE', 'PERCENTILE.INC': 'PERCENTILE.INC', 'PERCENTILE.EXC': 'PERCENTILE.EXC', + 'RANK.AVG': 'RANK.AVG', QUARTILE: 'QUARTILE', 'QUARTILE.INC': 'QUARTILE.INC', 'QUARTILE.EXC': 'QUARTILE.EXC', diff --git a/src/i18n/languages/itIT.ts b/src/i18n/languages/itIT.ts index 1a494628f8..2184f10321 100644 --- a/src/i18n/languages/itIT.ts +++ b/src/i18n/languages/itIT.ts @@ -390,6 +390,7 @@ const dictionary: RawTranslationPackage = { PERCENTILE: 'PERCENTILE', 'PERCENTILE.INC': 'INC.PERCENTILE', 'PERCENTILE.EXC': 'ESC.PERCENTILE', + 'RANK.AVG': 'RANK.AVG', QUARTILE: 'QUARTILE', 'QUARTILE.INC': 'INC.QUARTILE', 'QUARTILE.EXC': 'ESC.QUARTILE', diff --git a/src/i18n/languages/nbNO.ts b/src/i18n/languages/nbNO.ts index ff15415a98..f2a340b5e4 100644 --- a/src/i18n/languages/nbNO.ts +++ b/src/i18n/languages/nbNO.ts @@ -390,6 +390,7 @@ const dictionary: RawTranslationPackage = { PERCENTILE: 'PERSENTIL', 'PERCENTILE.INC': 'PERSENTIL.INK', 'PERCENTILE.EXC': 'PERSENTIL.EKS', + 'RANK.AVG': 'RANK.AVG', QUARTILE: 'KVARTIL', 'QUARTILE.INC': 'KVARTIL.INK', 'QUARTILE.EXC': 'KVARTIL.EKS', diff --git a/src/i18n/languages/nlNL.ts b/src/i18n/languages/nlNL.ts index c491e2f44b..e4e693493b 100644 --- a/src/i18n/languages/nlNL.ts +++ b/src/i18n/languages/nlNL.ts @@ -390,6 +390,7 @@ const dictionary: RawTranslationPackage = { PERCENTILE: 'PERCENTIEL', 'PERCENTILE.INC': 'PERCENTIEL.INC', 'PERCENTILE.EXC': 'PERCENTIEL.EXC', + 'RANK.AVG': 'RANK.AVG', QUARTILE: 'KWARTIEL', 'QUARTILE.INC': 'KWARTIEL.INC', 'QUARTILE.EXC': 'KWARTIEL.EXC', diff --git a/src/i18n/languages/plPL.ts b/src/i18n/languages/plPL.ts index 6fa4f016b4..6aebc64b1e 100644 --- a/src/i18n/languages/plPL.ts +++ b/src/i18n/languages/plPL.ts @@ -390,6 +390,7 @@ const dictionary: RawTranslationPackage = { PERCENTILE: 'PERCENTYL', 'PERCENTILE.INC': 'PERCENTYL.PRZEDZ.ZAMK', 'PERCENTILE.EXC': 'PERCENTYL.PRZEDZ.OTW', + 'RANK.AVG': 'RANK.AVG', QUARTILE: 'KWARTYL', 'QUARTILE.INC': 'KWARTYL.PRZEDZ.ZAMK', 'QUARTILE.EXC': 'KWARTYL.PRZEDZ.OTW', diff --git a/src/i18n/languages/ptPT.ts b/src/i18n/languages/ptPT.ts index 6c1e68c328..ce9e4ee96b 100644 --- a/src/i18n/languages/ptPT.ts +++ b/src/i18n/languages/ptPT.ts @@ -390,6 +390,7 @@ const dictionary: RawTranslationPackage = { PERCENTILE: 'PERCENTIL', 'PERCENTILE.INC': 'PERCENTIL.INC', 'PERCENTILE.EXC': 'PERCENTIL.EXC', + 'RANK.AVG': 'RANK.AVG', QUARTILE: 'QUARTIL', 'QUARTILE.INC': 'QUARTIL.INC', 'QUARTILE.EXC': 'QUARTIL.EXC', diff --git a/src/i18n/languages/ruRU.ts b/src/i18n/languages/ruRU.ts index 6b35c1ce2f..2ba613c484 100644 --- a/src/i18n/languages/ruRU.ts +++ b/src/i18n/languages/ruRU.ts @@ -390,6 +390,7 @@ const dictionary: RawTranslationPackage = { PERCENTILE: 'ПЕРСЕНТИЛЬ', 'PERCENTILE.INC': 'ПРОЦЕНТИЛЬ.ВКЛ', 'PERCENTILE.EXC': 'ПРОЦЕНТИЛЬ.ИСКЛ', + 'RANK.AVG': 'RANK.AVG', QUARTILE: 'КВАРТИЛЬ', 'QUARTILE.INC': 'КВАРТИЛЬ.ВКЛ', 'QUARTILE.EXC': 'КВАРТИЛЬ.ИСКЛ', diff --git a/src/i18n/languages/svSE.ts b/src/i18n/languages/svSE.ts index 5f87580087..1f4e5a1019 100644 --- a/src/i18n/languages/svSE.ts +++ b/src/i18n/languages/svSE.ts @@ -390,6 +390,7 @@ const dictionary: RawTranslationPackage = { PERCENTILE: 'PERCENTIL', 'PERCENTILE.INC': 'PERCENTIL.INK', 'PERCENTILE.EXC': 'PERCENTIL.EXK', + 'RANK.AVG': 'RANK.AVG', QUARTILE: 'KVARTIL', 'QUARTILE.INC': 'KVARTIL.INK', 'QUARTILE.EXC': 'KVARTIL.EXK', diff --git a/src/i18n/languages/trTR.ts b/src/i18n/languages/trTR.ts index 93c2176d37..5eca4325c2 100644 --- a/src/i18n/languages/trTR.ts +++ b/src/i18n/languages/trTR.ts @@ -390,6 +390,7 @@ const dictionary: RawTranslationPackage = { PERCENTILE: 'YÜZDEBİRLİK', 'PERCENTILE.INC': 'YÜZDEBİRLİK.DHL', 'PERCENTILE.EXC': 'YÜZDEBİRLİK.HRC', + 'RANK.AVG': 'RANK.AVG', QUARTILE: 'DÖRTTEBİRLİK', 'QUARTILE.INC': 'DÖRTTEBİRLİK.DHL', 'QUARTILE.EXC': 'DÖRTTEBİRLİK.HRC', diff --git a/src/interpreter/functionMetadata/categories/statistical.ts b/src/interpreter/functionMetadata/categories/statistical.ts index 1752c3eaf6..b3014c35ae 100644 --- a/src/interpreter/functionMetadata/categories/statistical.ts +++ b/src/interpreter/functionMetadata/categories/statistical.ts @@ -430,6 +430,13 @@ export const STATISTICAL_DOCS: Record = { documentationUrl: 'https://hyperformula.handsontable.com/docs/guide/built-in-functions.html', examples: ['=POISSON.DIST(3, 5, FALSE())', '=POISSON.DIST(3, 5, TRUE())'], }, + 'RANK.AVG': { + category: 'Statistical', + shortDescription: 'Returns the rank of a number in a range of numbers; tied values get the average of their ranks.', + parameters: [{name: 'number', description: 'The number whose rank to find.'}, {name: 'ref', description: 'The range of numbers to rank within; non-numeric cells are ignored.'}, {name: 'order', description: 'Optional. 0 or omitted ranks in descending order; any other number ranks in ascending order.'}], + documentationUrl: 'https://hyperformula.handsontable.com/docs/guide/built-in-functions.html', + examples: ['=RANK.AVG(A2, A1:A10)', '=RANK.AVG(A2, A1:A10, 1)'], + }, 'QUARTILE.EXC': { category: 'Statistical', shortDescription: 'Returns the quartile of a data set, based on exclusive percentile values.', diff --git a/src/interpreter/plugin/MedianPlugin.ts b/src/interpreter/plugin/MedianPlugin.ts index a6a2a1078e..d77c798135 100644 --- a/src/interpreter/plugin/MedianPlugin.ts +++ b/src/interpreter/plugin/MedianPlugin.ts @@ -38,6 +38,14 @@ export class MedianPlugin extends FunctionPlugin implements FunctionPluginTypech {argumentType: FunctionArgumentType.NUMBER, minValue: 1}, ], }, + 'RANK.AVG': { + method: 'rankAvg', + parameters: [ + {argumentType: FunctionArgumentType.NUMBER}, + {argumentType: FunctionArgumentType.RANGE}, + {argumentType: FunctionArgumentType.NUMBER, optionalArg: true, defaultValue: 0}, + ], + }, } /** @@ -100,4 +108,30 @@ export class MedianPlugin extends FunctionPlugin implements FunctionPluginTypech } ) } + + /** + * Corresponds to RANK.AVG(number, ref, [order]). + * + * Returns the rank of a number in a range of numbers. Tied values get the average of the ranks they span. + * The rank is descending when order is 0 or omitted, and ascending for any other order. + * + * @param ast + * @param state + */ + public rankAvg(ast: ProcedureAst, state: InterpreterState): InterpreterValue { + return this.runFunction(ast.args, state, this.metadata('RANK.AVG'), + (value: number, range: SimpleRangeValue, order: number) => { + const vals = this.arithmeticHelper.manyToExactNumbers(range.valuesFromTopLeftCorner()) + if (vals instanceof CellError) { + return vals + } + const ties = vals.filter(val => val === value).length + if (ties === 0) { + return new CellError(ErrorType.NA, ErrorMessage.ValueNotFound) + } + const ranksBefore = vals.filter(val => order === 0 ? val > value : val < value).length + return ranksBefore + (ties + 1) / 2 + } + ) + } } diff --git a/src/license/functionCapabilities.ts b/src/license/functionCapabilities.ts index a26733ec5f..62fcf55d80 100644 --- a/src/license/functionCapabilities.ts +++ b/src/license/functionCapabilities.ts @@ -92,7 +92,7 @@ const UNGROUPED_FUNCTIONS = [ 'ISREF', 'LCM', 'LOG10', 'LOGNORM.DIST', 'LOGNORM.INV', 'MAXA', 'MAXPOOL', 'MEDIANPOOL', 'MINA', 'MIRR', 'MMULT', 'MULTINOMIAL', 'NEGBINOM.DIST', 'NETWORKDAYS.INTL', 'NOMINAL', 'NORM.DIST', 'NORM.INV', 'NORM.S.DIST', 'NORM.S.INV', 'NPER', 'OCT2BIN', 'OCT2DEC', 'OCT2HEX', 'PDURATION', 'PERCENTILE.EXC', - 'PHI', 'POISSON.DIST', 'QUARTILE.EXC', 'QUARTILE.INC', 'RADIANS', 'ROMAN', 'RRI', 'RSQ', 'SEC', 'SECH', + 'PHI', 'POISSON.DIST', 'QUARTILE.EXC', 'QUARTILE.INC', 'RADIANS', 'RANK.AVG', 'ROMAN', 'RRI', 'RSQ', 'SEC', 'SECH', 'SERIESSUM', 'SHEET', 'SHEETS', 'SINH', 'SKEW', 'SKEW.P', 'SLOPE', 'SPLIT', 'SQRTPI', 'STANDARDIZE', 'STEYX', 'SUMX2MY2', 'SUMX2PY2', 'SYD', 'T.DIST', 'T.DIST.2T', 'T.DIST.RT', 'T.INV', 'T.INV.2T', 'T.TEST', 'TANH', 'TBILLEQ', 'TBILLPRICE', 'TBILLYIELD', 'TDIST', 'TIMEVALUE', 'UNICODE', 'VARA', 'VARPA',