Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
### Added

- Added a message after `npm i hyperformula` that points AI assistants to the SKILLS file. The message needs `--foreground-scripts` to be visible in npm, and pnpm 10 needs the `postinstall` script to be approved first. [#1829](https://github.com/handsontable/hyperformula/pull/1829)
- Added the statistical functions `KURT`, `MODE.SNGL` (with the `MODE` alias), `PERCENTRANK.EXC` and `TRIMMEAN`. [#1800](https://github.com/handsontable/hyperformula/pull/1800)

### Fixed

Expand Down
1 change: 1 addition & 0 deletions hyperformula/src/error-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export class ErrorMessage {
public static TwoValues = 'Range needs to contain at least two elements.'
public static ThreeValues = 'Range needs to contain at least three elements.'
public static EqualXValues = 'All x values are equal.'
public static FourValues = 'Range needs to contain at least four elements.'
public static IndexBounds = 'Index out of bounds.'
public static IndexLarge = 'Index too large.'
public static Formula = 'Expected formula.'
Expand Down
5 changes: 5 additions & 0 deletions hyperformula/src/i18n/languages/csCZ.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ const dictionary: RawTranslationPackage = {
PERCENTILE: 'PERCENTIL',
'PERCENTILE.INC': 'PERCENTIL.INC',
'PERCENTILE.EXC': 'PERCENTIL.EXC',
'PERCENTRANK.EXC': 'PERCENTRANK.EXC',
KURT: 'KURT',
MODE: 'MODE',
'MODE.SNGL': 'MODE.SNGL',
TRIMMEAN: 'TRIMMEAN',
QUARTILE: 'QUARTIL',
'QUARTILE.INC': 'QUARTIL.INC',
'QUARTILE.EXC': 'QUARTIL.EXC',
Expand Down
5 changes: 5 additions & 0 deletions hyperformula/src/i18n/languages/daDK.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ const dictionary: RawTranslationPackage = {
PERCENTILE: 'FRAKTIL',
'PERCENTILE.INC': 'FRAKTIL.MEDTAG',
'PERCENTILE.EXC': 'FRAKTIL.UDELAD',
'PERCENTRANK.EXC': 'PROCENTPLADS.UDELAD',
KURT: 'TOPSTEJL',
MODE: 'HYPPIGST',
'MODE.SNGL': 'HYPPIGST.ENKELT',
TRIMMEAN: 'TRIMMIDDELVÆRDI',
QUARTILE: 'KVARTIL',
'QUARTILE.INC': 'KVARTIL.MEDTAG',
'QUARTILE.EXC': 'KVARTIL.UDELAD',
Expand Down
5 changes: 5 additions & 0 deletions hyperformula/src/i18n/languages/deDE.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ const dictionary: RawTranslationPackage = {
PERCENTILE: 'QUANTIL',
'PERCENTILE.INC': 'QUANTIL.INKL',
'PERCENTILE.EXC': 'QUANTIL.EXKL',
'PERCENTRANK.EXC': 'QUANTILSRANG.EXKL',
KURT: 'KURT',
MODE: 'MODALWERT',
'MODE.SNGL': 'MODUS.EINF',
TRIMMEAN: 'GESTUTZTMITTEL',
QUARTILE: 'QUARTILE',
'QUARTILE.INC': 'QUARTILE.INKL',
'QUARTILE.EXC': 'QUARTILE.EXKL',
Expand Down
5 changes: 5 additions & 0 deletions hyperformula/src/i18n/languages/enGB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ const dictionary: RawTranslationPackage = {
PERCENTILE: 'PERCENTILE',
'PERCENTILE.INC': 'PERCENTILE.INC',
'PERCENTILE.EXC': 'PERCENTILE.EXC',
'PERCENTRANK.EXC': 'PERCENTRANK.EXC',
KURT: 'KURT',
MODE: 'MODE',
'MODE.SNGL': 'MODE.SNGL',
TRIMMEAN: 'TRIMMEAN',
QUARTILE: 'QUARTILE',
'QUARTILE.INC': 'QUARTILE.INC',
'QUARTILE.EXC': 'QUARTILE.EXC',
Expand Down
5 changes: 5 additions & 0 deletions hyperformula/src/i18n/languages/esES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ export const dictionary: RawTranslationPackage = {
PERCENTILE: 'PERCENTIL',
'PERCENTILE.INC': 'PERCENTIL.INC',
'PERCENTILE.EXC': 'PERCENTIL.EXC',
'PERCENTRANK.EXC': 'RANGO.PERCENTIL.EXC',
KURT: 'CURTOSIS',
MODE: 'MODA',
'MODE.SNGL': 'MODA.UNO',
TRIMMEAN: 'MEDIA.ACOTADA',
QUARTILE: 'CUARTIL',
'QUARTILE.INC': 'CUARTIL.INC',
'QUARTILE.EXC': 'CUARTIL.EXC',
Expand Down
5 changes: 5 additions & 0 deletions hyperformula/src/i18n/languages/fiFI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ const dictionary: RawTranslationPackage = {
PERCENTILE: 'PROSENTTIPISTE',
'PERCENTILE.INC': 'PROSENTTIPISTE.SIS',
'PERCENTILE.EXC': 'PROSENTTIPISTE.ULK',
'PERCENTRANK.EXC': 'PROSENTTIJÄRJESTYS.ULK',
KURT: 'KURT',
MODE: 'MOODI',
'MODE.SNGL': 'MOODI.YKSI',
TRIMMEAN: 'KESKIARVO.TASATTU',
QUARTILE: 'NELJÄNNES',
'QUARTILE.INC': 'NELJÄNNES.SIS',
'QUARTILE.EXC': 'NELJÄNNES.ULK',
Expand Down
5 changes: 5 additions & 0 deletions hyperformula/src/i18n/languages/frFR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ const dictionary: RawTranslationPackage = {
PERCENTILE: 'CENTILE',
'PERCENTILE.INC': 'CENTILE.INCLURE',
'PERCENTILE.EXC': 'CENTILE.EXCLURE',
'PERCENTRANK.EXC': 'RANG.POURCENTAGE.EXCLURE',
KURT: 'KURTOSIS',
MODE: 'MODE',
'MODE.SNGL': 'MODE.SIMPLE',
TRIMMEAN: 'MOYENNE.REDUITE',
QUARTILE: 'QUARTILE',
'QUARTILE.INC': 'QUARTILE.INCLURE',
'QUARTILE.EXC': 'QUARTILE.EXCLURE',
Expand Down
5 changes: 5 additions & 0 deletions hyperformula/src/i18n/languages/huHU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ const dictionary: RawTranslationPackage = {
PERCENTILE: 'PERCENTILIS',
'PERCENTILE.INC': 'PERCENTILIS.TARTALMAZ',
'PERCENTILE.EXC': 'PERCENTILIS.KIZÁR',
'PERCENTRANK.EXC': 'SZÁZALÉKRANG.KIZÁR',
KURT: 'CSÚCSOSSÁG',
MODE: 'MÓDUSZ',
'MODE.SNGL': 'MÓDUSZ.EGY',
TRIMMEAN: 'RÉSZÁTLAG',
QUARTILE: 'KVARTILIS',
'QUARTILE.INC': 'KVARTILIS.TARTALMAZ',
'QUARTILE.EXC': 'KVARTILIS.KIZÁR',
Expand Down
5 changes: 5 additions & 0 deletions hyperformula/src/i18n/languages/idID.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ const dictionary: RawTranslationPackage = {
PERCENTILE: 'PERCENTILE',
'PERCENTILE.INC': 'PERCENTILE.INC',
'PERCENTILE.EXC': 'PERCENTILE.EXC',
'PERCENTRANK.EXC': 'PERCENTRANK.EXC',
KURT: 'KURT',
MODE: 'MODE',
'MODE.SNGL': 'MODE.SNGL',
TRIMMEAN: 'TRIMMEAN',
QUARTILE: 'QUARTILE',
'QUARTILE.INC': 'QUARTILE.INC',
'QUARTILE.EXC': 'QUARTILE.EXC',
Expand Down
5 changes: 5 additions & 0 deletions hyperformula/src/i18n/languages/itIT.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ const dictionary: RawTranslationPackage = {
PERCENTILE: 'PERCENTILE',
'PERCENTILE.INC': 'INC.PERCENTILE',
'PERCENTILE.EXC': 'ESC.PERCENTILE',
'PERCENTRANK.EXC': 'ESC.PERCENT.RANGO',
KURT: 'CURTOSI',
MODE: 'MODA',
'MODE.SNGL': 'MODA.SNGL',
TRIMMEAN: 'MEDIA.TRONCATA',
QUARTILE: 'QUARTILE',
'QUARTILE.INC': 'INC.QUARTILE',
'QUARTILE.EXC': 'ESC.QUARTILE',
Expand Down
5 changes: 5 additions & 0 deletions hyperformula/src/i18n/languages/nbNO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ const dictionary: RawTranslationPackage = {
PERCENTILE: 'PERSENTIL',
'PERCENTILE.INC': 'PERSENTIL.INK',
'PERCENTILE.EXC': 'PERSENTIL.EKS',
'PERCENTRANK.EXC': 'PROSENTDEL.EKS',
KURT: 'KURT',
MODE: 'MODUS',
'MODE.SNGL': 'MODUS.SNGL',
TRIMMEAN: 'TRIMMET.GJENNOMSNITT',
QUARTILE: 'KVARTIL',
'QUARTILE.INC': 'KVARTIL.INK',
'QUARTILE.EXC': 'KVARTIL.EKS',
Expand Down
5 changes: 5 additions & 0 deletions hyperformula/src/i18n/languages/nlNL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ const dictionary: RawTranslationPackage = {
PERCENTILE: 'PERCENTIEL',
'PERCENTILE.INC': 'PERCENTIEL.INC',
'PERCENTILE.EXC': 'PERCENTIEL.EXC',
'PERCENTRANK.EXC': 'PROCENTRANG.EXC',
KURT: 'KURTOSIS',
MODE: 'MODUS',
'MODE.SNGL': 'MODUS.ENKELV',
TRIMMEAN: 'GETRIMD.GEM',
QUARTILE: 'KWARTIEL',
'QUARTILE.INC': 'KWARTIEL.INC',
'QUARTILE.EXC': 'KWARTIEL.EXC',
Expand Down
5 changes: 5 additions & 0 deletions hyperformula/src/i18n/languages/plPL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ const dictionary: RawTranslationPackage = {
PERCENTILE: 'PERCENTYL',
'PERCENTILE.INC': 'PERCENTYL.PRZEDZ.ZAMK',
'PERCENTILE.EXC': 'PERCENTYL.PRZEDZ.OTW',
'PERCENTRANK.EXC': 'PROC.POZ.PRZEDZ.OTW',
KURT: 'KURTOZA',
MODE: 'WYST.NAJCZĘŚCIEJ',
'MODE.SNGL': 'WYST.NAJCZĘŚCIEJ.WART',
TRIMMEAN: 'ŚREDNIA.WEWN',
QUARTILE: 'KWARTYL',
'QUARTILE.INC': 'KWARTYL.PRZEDZ.ZAMK',
'QUARTILE.EXC': 'KWARTYL.PRZEDZ.OTW',
Expand Down
5 changes: 5 additions & 0 deletions hyperformula/src/i18n/languages/ptPT.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ const dictionary: RawTranslationPackage = {
PERCENTILE: 'PERCENTIL',
'PERCENTILE.INC': 'PERCENTIL.INC',
'PERCENTILE.EXC': 'PERCENTIL.EXC',
'PERCENTRANK.EXC': 'ORDEM.PERCENTUAL.EXC',
KURT: 'CURT',
MODE: 'MODO',
'MODE.SNGL': 'MODO.SIMPLES',
TRIMMEAN: 'MÉDIA.INTERNA',
QUARTILE: 'QUARTIL',
'QUARTILE.INC': 'QUARTIL.INC',
'QUARTILE.EXC': 'QUARTIL.EXC',
Expand Down
5 changes: 5 additions & 0 deletions hyperformula/src/i18n/languages/ruRU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ const dictionary: RawTranslationPackage = {
PERCENTILE: 'ПЕРСЕНТИЛЬ',
'PERCENTILE.INC': 'ПРОЦЕНТИЛЬ.ВКЛ',
'PERCENTILE.EXC': 'ПРОЦЕНТИЛЬ.ИСКЛ',
'PERCENTRANK.EXC': 'ПРОЦЕНТРАНГ.ИСКЛ',
KURT: 'ЭКСЦЕСС',
MODE: 'МОДА',
'MODE.SNGL': 'МОДА.ОДН',
TRIMMEAN: 'УРЕЗСРЕДНЕЕ',
QUARTILE: 'КВАРТИЛЬ',
'QUARTILE.INC': 'КВАРТИЛЬ.ВКЛ',
'QUARTILE.EXC': 'КВАРТИЛЬ.ИСКЛ',
Expand Down
5 changes: 5 additions & 0 deletions hyperformula/src/i18n/languages/svSE.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ const dictionary: RawTranslationPackage = {
PERCENTILE: 'PERCENTIL',
'PERCENTILE.INC': 'PERCENTIL.INK',
'PERCENTILE.EXC': 'PERCENTIL.EXK',
'PERCENTRANK.EXC': 'PROCENTRANG.EXK',
KURT: 'TOPPIGHET',
MODE: 'TYPVÄRDE',
'MODE.SNGL': 'TYPVÄRDE.ETT',
TRIMMEAN: 'TRIMMEDEL',
QUARTILE: 'KVARTIL',
'QUARTILE.INC': 'KVARTIL.INK',
'QUARTILE.EXC': 'KVARTIL.EXK',
Expand Down
5 changes: 5 additions & 0 deletions hyperformula/src/i18n/languages/trTR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ const dictionary: RawTranslationPackage = {
PERCENTILE: 'YÜZDEBİRLİK',
'PERCENTILE.INC': 'YÜZDEBİRLİK.DHL',
'PERCENTILE.EXC': 'YÜZDEBİRLİK.HRC',
'PERCENTRANK.EXC': 'YÜZDERANK.HRC',
KURT: 'BASIKLIK',
MODE: 'ENÇOK_OLAN',
'MODE.SNGL': 'ENÇOK_OLAN.TEK',
TRIMMEAN: 'KIRPORTALAMA',
QUARTILE: 'DÖRTTEBİRLİK',
'QUARTILE.INC': 'DÖRTTEBİRLİK.DHL',
'QUARTILE.EXC': 'DÖRTTEBİRLİK.HRC',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,13 @@ export const STATISTICAL_DOCS: Record<string, FunctionDoc> = {
documentationUrl: 'https://hyperformula.handsontable.com/docs/guide/built-in-functions.html',
examples: ['=HYPGEOM.DIST(1, 4, 8, 20, FALSE())', '=HYPGEOM.DIST(1, 4, 8, 20, TRUE())'],
},
KURT: {
category: 'Statistical',
shortDescription: 'Returns the sample excess kurtosis of a set of numbers.',
parameters: [{name: 'number1', description: 'A number, cell reference, or range included in the calculation; at least four numbers with a non-zero spread are needed. Further numbers or ranges can be passed as additional arguments.'}],
documentationUrl: 'https://hyperformula.handsontable.com/docs/guide/built-in-functions.html',
examples: ['=KURT(1, 2, 3, 10)', '=KURT(A1:A10)'],
},
LARGE: {
category: 'Statistical',
shortDescription: 'Returns k-th largest value in a range.',
Expand Down Expand Up @@ -402,6 +409,13 @@ export const STATISTICAL_DOCS: Record<string, FunctionDoc> = {
documentationUrl: 'https://hyperformula.handsontable.com/docs/guide/built-in-functions.html',
examples: ['=NORM.S.INV(0.5)'],
},
'MODE.SNGL': {
category: 'Statistical',
shortDescription: 'Returns the most frequently occurring number; on a tie, the one that occurs first.',
parameters: [{name: 'number1', description: 'A number, cell reference, or range to search; text and boolean values in ranges are ignored. Further numbers or ranges can be passed as additional arguments.'}],
documentationUrl: 'https://hyperformula.handsontable.com/docs/guide/built-in-functions.html',
examples: ['=MODE.SNGL(1, 2, 2, 3)', '=MODE.SNGL(A1:A10)'],
},
'PERCENTILE.EXC': {
category: 'Statistical',
shortDescription: 'Returns the k-th percentile of values in a range, exclusive of 0 and 1.',
Expand All @@ -416,6 +430,13 @@ export const STATISTICAL_DOCS: Record<string, FunctionDoc> = {
documentationUrl: 'https://hyperformula.handsontable.com/docs/guide/built-in-functions.html',
examples: ['=PERCENTILE.INC(A1:A10, 0.9)'],
},
'PERCENTRANK.EXC': {
category: 'Statistical',
shortDescription: 'Returns the rank of a value in a data set as a fraction between 0 and 1, exclusive of 0 and 1.',
parameters: [{name: 'data', description: 'The range of values to evaluate.'}, {name: 'x', description: 'The value to rank; it must lie between the smallest and the largest value in `data`. A value between two elements is interpolated.'}, {name: 'significance', description: 'Optional. The number of significant digits the result is truncated to; defaults to 3.'}],
documentationUrl: 'https://hyperformula.handsontable.com/docs/guide/built-in-functions.html',
examples: ['=PERCENTRANK.EXC(A1:A10, 5)', '=PERCENTRANK.EXC(A1:A10, 5.5, 4)'],
},
PHI: {
category: 'Statistical',
shortDescription: 'Returns probability density of normal distribution.',
Expand Down Expand Up @@ -563,6 +584,13 @@ export const STATISTICAL_DOCS: Record<string, FunctionDoc> = {
documentationUrl: 'https://hyperformula.handsontable.com/docs/guide/built-in-functions.html',
examples: ['=T.TEST(A1:A10, B1:B10, 2, 1)'],
},
TRIMMEAN: {
category: 'Statistical',
shortDescription: 'Returns the mean of the values left after trimming the given fraction of the smallest and largest ones.',
parameters: [{name: 'data', description: 'The range of values to average.'}, {name: 'percent', description: 'The fraction of values to exclude, at least 0 and less than 1; the excluded count is rounded down to an even number, split equally between both ends.'}],
documentationUrl: 'https://hyperformula.handsontable.com/docs/guide/built-in-functions.html',
examples: ['=TRIMMEAN(A1:A10, 0.2)'],
},
TDIST: {
category: 'Statistical',
shortDescription: 'Returns density of Student-t distribution, both-sided or right-tailed.',
Expand Down
47 changes: 47 additions & 0 deletions hyperformula/src/interpreter/plugin/PercentilePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ export class PercentilePlugin extends FunctionPlugin implements FunctionPluginTy
{argumentType: FunctionArgumentType.NUMBER, greaterThan: 0, lessThan: 1},
],
},
'PERCENTRANK.EXC': {
method: 'percentrankExc',
parameters: [
{argumentType: FunctionArgumentType.RANGE},
{argumentType: FunctionArgumentType.NUMBER},
{argumentType: FunctionArgumentType.NUMBER, defaultValue: 3},
],
},
'QUARTILE.INC': {
method: 'quartile',
parameters: [
Expand Down Expand Up @@ -144,6 +152,45 @@ export class PercentilePlugin extends FunctionPlugin implements FunctionPluginTy
)
}

/**
* Corresponds to PERCENTRANK.EXC(array, x, [significance]).
*
* Returns the exclusive rank of x in the array, i.e. the inverse of PERCENTILE.EXC, truncated (not rounded)
* to the given number of significant digits. Values between two array elements are linearly interpolated.
*
* @param ast - procedure AST node
* @param state - interpreter state
* @returns rank in (0, 1), #N/A if x is outside of the array, or CellError on invalid input
*/
public percentrankExc(ast: ProcedureAst, state: InterpreterState): InterpreterValue {
return this.runFunction(ast.args, state, this.metadata('PERCENTRANK.EXC'),
(range: SimpleRangeValue, x: number, significance: number) => {
const digits = Math.trunc(significance)
if (digits < 1) {
return new CellError(ErrorType.NUM, ErrorMessage.LessThanOne)
}
const vals = this.arithmeticHelper.manyToExactNumbers(range.valuesFromTopLeftCorner())
if (vals instanceof CellError) {
return vals
}
vals.sort((a, b) => a - b)
const n = vals.length
if (n === 0 || x < vals[0] || x > vals[n - 1]) {
return new CellError(ErrorType.NA, ErrorMessage.ValueNotFound)
}
Comment thread
marcin-kordas-hoc marked this conversation as resolved.
if (n === 1) {
return 1
}
const upperIndex = vals.findIndex(val => val >= x)
const rank = vals[upperIndex] === x
? upperIndex + 1
: upperIndex + (x - vals[upperIndex - 1]) / (vals[upperIndex] - vals[upperIndex - 1])
const scale = Math.pow(10, digits)
return Math.floor(rank / (n + 1) * scale + 1e-9) / scale
}
)
}

/**
* Corresponds to QUARTILE(array, quart) and QUARTILE.INC(array, quart).
*
Expand Down
Loading
Loading