commit
message
[PERCENTRANK, PERCENTRANK.INC] formulas added and tested
author
Ben Vogt <[email protected]>
date
2017-07-03 15:47:25
stats
10 file(s) changed,
169 insertions(+),
11 deletions(-)
files
DOCS.md
TODO.md
dist/Formulas/AllFormulas.js
dist/Formulas/Statistical.js
dist/Utilities/Filter.js
src/Formulas/AllFormulas.ts
src/Formulas/Statistical.ts
src/Utilities/Filter.ts
tests/Formulas/StatisticalTest.ts
tests/SheetFormulaTest.ts
1diff --git a/DOCS.md b/DOCS.md
2index 86d963c..ad58531 100644
3--- a/DOCS.md
4+++ b/DOCS.md
5@@ -1711,13 +1711,24 @@
6 ### POISSON
7
8 ```
9- Returns the Poisson distribution for the given number.
10+ Returns the Poisson distribution for the given number. Functions the same as POISSON.DIST.
11 @param x - Number to use.
12 @param meanValue - The middle value for the Poisson distribution.
13 @param cumulative - [OPTIONAL] - 0 calculates the density function, 1 calculates the distribution. Defaults to 0.
14 @returns {number}
15 @constructor
16 ```
17+
18+### RCENTRANK
19+
20+```
21+ Returns the percentage rank (percentile) of the given value in a sample. Functions the same as PERCENTRANK.INC.
22+@param data - The array or range of data in the sample.
23+@param x - The value.
24+@param significance - [OPTIONAL] - The number of significant digits to use in the calculation.
25+@returns {number}
26+@constructor
27+```
28 ## Text
29
30
31diff --git a/TODO.md b/TODO.md
32index 0f59ee1..88112eb 100644
33--- a/TODO.md
34+++ b/TODO.md
35@@ -73,9 +73,7 @@ For example 64 tbs to a qt.
36 * NORMINV
37 * NORMSDIST
38 * NORMSINV
39-* PERCENTRANK
40 * PERCENTRANK.EXC
41-* PERCENTRANK.INC
42 * PERMUT
43 * PROB
44 * RANK
45diff --git a/dist/Formulas/AllFormulas.js b/dist/Formulas/AllFormulas.js
46index 1caa9d5..adde802 100644
47--- a/dist/Formulas/AllFormulas.js
48+++ b/dist/Formulas/AllFormulas.js
49@@ -156,6 +156,7 @@ exports.KURT = Statistical_1.KURT;
50 exports.INTERCEPT = Statistical_1.INTERCEPT;
51 exports.FORECAST = Statistical_1.FORECAST;
52 exports.POISSON = Statistical_1.POISSON;
53+exports.PERCENTRANK = Statistical_1.PERCENTRANK;
54 var Text_1 = require("./Text");
55 exports.ARABIC = Text_1.ARABIC;
56 exports.CHAR = Text_1.CHAR;
57@@ -196,6 +197,7 @@ var __COMPLEX = {
58 "F.DIST": Statistical_1.FDIST$LEFTTAILED,
59 "NETWORKDAYS.INTL": Date_1.NETWORKDAYS$INTL,
60 "WORKDAY.INTL": Date_1.WORKDAY$INTL,
61- "POISSON.DIST": Statistical_1.POISSON
62+ "POISSON.DIST": Statistical_1.POISSON,
63+ "PERCENTRANK.INC": Statistical_1.PERCENTRANK
64 };
65 exports.__COMPLEX = __COMPLEX;
66diff --git a/dist/Formulas/Statistical.js b/dist/Formulas/Statistical.js
67index 3c2d373..5e24370 100644
68--- a/dist/Formulas/Statistical.js
69+++ b/dist/Formulas/Statistical.js
70@@ -933,7 +933,7 @@ var FORECAST = function (x, rangeY, rangeX) {
71 };
72 exports.FORECAST = FORECAST;
73 /**
74- * Returns the Poisson distribution for the given number.
75+ * Returns the Poisson distribution for the given number. Functions the same as POISSON.DIST.
76 * @param x - Number to use.
77 * @param meanValue - The middle value for the Poisson distribution.
78 * @param cumulative - [OPTIONAL] - 0 calculates the density function, 1 calculates the distribution. Defaults to 0.
79@@ -970,3 +970,49 @@ var POISSON = function (x, meanValue, cumulative) {
80 return (cumulative) ? poissonCDF(x, meanValue) : poissonPDF(x, meanValue);
81 };
82 exports.POISSON = POISSON;
83+/**
84+ * Returns the percentage rank (percentile) of the given value in a sample. Functions the same as PERCENTRANK.INC.
85+ * @param data - The array or range of data in the sample.
86+ * @param x - The value.
87+ * @param significance - [OPTIONAL] - The number of significant digits to use in the calculation.
88+ * @returns {number}
89+ * @constructor
90+ */
91+var PERCENTRANK = function (data, x, significance) {
92+ ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 2, 3, "PERCENTRANK");
93+ data = Filter_1.Filter.flattenAndThrow(data).map(TypeConverter_1.TypeConverter.valueToNumber).sort(function (a, b) {
94+ return a - b;
95+ });
96+ x = TypeConverter_1.TypeConverter.firstValueAsNumber(x);
97+ var uniques = Filter_1.Filter.unique(data);
98+ var n = data.length;
99+ var m = uniques.length;
100+ if (x < uniques[0] || x > uniques[m - 1]) {
101+ throw new Errors_1.NAError("PERCENTRANK does not have valid input data.");
102+ }
103+ if (m === 1 && uniques[0] === x) {
104+ return 1;
105+ }
106+ significance = (typeof significance === 'undefined') ? 3 : TypeConverter_1.TypeConverter.firstValueAsNumber(significance);
107+ var power = Math.pow(10, significance);
108+ var result = 0;
109+ var match = false;
110+ var i = 0;
111+ while (!match && i < m) {
112+ if (x === uniques[i]) {
113+ result = data.indexOf(uniques[i]) / (n - 1);
114+ match = true;
115+ }
116+ else if (x >= uniques[i] && (x < uniques[i + 1] || i === m - 1)) {
117+ result = (data.indexOf(uniques[i]) + (x - uniques[i]) / (uniques[i + 1] - uniques[i])) / (n - 1);
118+ match = true;
119+ }
120+ i++;
121+ }
122+ var v = Math.floor(result * power) / power;
123+ if (isNaN(v)) {
124+ throw new Errors_1.NAError("PERCENTRANK does not have valid input data.");
125+ }
126+ return v;
127+};
128+exports.PERCENTRANK = PERCENTRANK;
129diff --git a/dist/Utilities/Filter.js b/dist/Utilities/Filter.js
130index e565e62..48ce227 100644
131--- a/dist/Utilities/Filter.js
132+++ b/dist/Utilities/Filter.js
133@@ -78,6 +78,20 @@ var Filter = (function () {
134 }
135 return toReturn;
136 };
137+ /**
138+ * Returns an array as unique values.
139+ * @param arr - to filter down to uniques.
140+ * @returns {Array}
141+ */
142+ Filter.unique = function (arr) {
143+ var a = [];
144+ for (var i = 0, l = arr.length; i < l; i++) {
145+ if (a.indexOf(arr[i]) === -1 && arr[i] !== '') {
146+ a.push(arr[i]);
147+ }
148+ }
149+ return a;
150+ };
151 return Filter;
152 }());
153 exports.Filter = Filter;
154diff --git a/src/Formulas/AllFormulas.ts b/src/Formulas/AllFormulas.ts
155index f50780a..14416fa 100644
156--- a/src/Formulas/AllFormulas.ts
157+++ b/src/Formulas/AllFormulas.ts
158@@ -161,7 +161,8 @@ import {
159 KURT,
160 INTERCEPT,
161 FORECAST,
162- POISSON
163+ POISSON,
164+ PERCENTRANK
165 } from "./Statistical";
166 import {
167 ARABIC,
168@@ -206,7 +207,8 @@ var __COMPLEX = {
169 "F.DIST": FDIST$LEFTTAILED,
170 "NETWORKDAYS.INTL": NETWORKDAYS$INTL,
171 "WORKDAY.INTL": WORKDAY$INTL,
172- "POISSON.DIST": POISSON
173+ "POISSON.DIST": POISSON,
174+ "PERCENTRANK.INC": PERCENTRANK
175 };
176
177 export {
178@@ -392,5 +394,6 @@ export {
179 ISEMAIL,
180 ISURL,
181 LINEST,
182- POISSON
183+ POISSON,
184+ PERCENTRANK
185 }
186\ No newline at end of file
187diff --git a/src/Formulas/Statistical.ts b/src/Formulas/Statistical.ts
188index c813c6a..b170058 100644
189--- a/src/Formulas/Statistical.ts
190+++ b/src/Formulas/Statistical.ts
191@@ -922,7 +922,7 @@ var FORECAST = function (x, rangeY, rangeX) {
192
193
194 /**
195- * Returns the Poisson distribution for the given number.
196+ * Returns the Poisson distribution for the given number. Functions the same as POISSON.DIST.
197 * @param x - Number to use.
198 * @param meanValue - The middle value for the Poisson distribution.
199 * @param cumulative - [OPTIONAL] - 0 calculates the density function, 1 calculates the distribution. Defaults to 0.
200@@ -962,6 +962,52 @@ var POISSON = function (x, meanValue, cumulative?) {
201 };
202
203
204+/**
205+ * Returns the percentage rank (percentile) of the given value in a sample. Functions the same as PERCENTRANK.INC.
206+ * @param data - The array or range of data in the sample.
207+ * @param x - The value.
208+ * @param significance - [OPTIONAL] - The number of significant digits to use in the calculation.
209+ * @returns {number}
210+ * @constructor
211+ */
212+var PERCENTRANK = function (data, x, significance?) {
213+ ArgsChecker.checkLengthWithin(arguments, 2, 3, "PERCENTRANK");
214+ data = Filter.flattenAndThrow(data).map(TypeConverter.valueToNumber).sort(function (a, b) {
215+ return a - b;
216+ });
217+ x = TypeConverter.firstValueAsNumber(x);
218+ var uniques = Filter.unique(data);
219+ var n = data.length;
220+ var m = uniques.length;
221+ if (x < uniques[0] || x > uniques[m - 1]) {
222+ throw new NAError("PERCENTRANK does not have valid input data.");
223+ }
224+ if (m === 1 && uniques[0] === x) {
225+ return 1;
226+ }
227+ significance = (typeof significance === 'undefined') ? 3 : TypeConverter.firstValueAsNumber(significance);
228+ var power = Math.pow(10, significance);
229+ var result = 0;
230+ var match = false;
231+ var i = 0;
232+ while (!match && i < m) {
233+ if (x === uniques[i]) {
234+ result = data.indexOf(uniques[i]) / (n - 1);
235+ match = true;
236+ } else if (x >= uniques[i] && (x < uniques[i + 1] || i === m - 1)) {
237+ result = (data.indexOf(uniques[i]) + (x - uniques[i]) / (uniques[i + 1] - uniques[i])) / (n - 1);
238+ match = true;
239+ }
240+ i++;
241+ }
242+ var v = Math.floor(result * power) / power;
243+ if (isNaN(v)) {
244+ throw new NAError("PERCENTRANK does not have valid input data.");
245+ }
246+ return v;
247+};
248+
249+
250 export {
251 AVERAGE,
252 AVERAGEA,
253@@ -996,5 +1042,6 @@ export {
254 KURT,
255 INTERCEPT,
256 FORECAST,
257- POISSON
258+ POISSON,
259+ PERCENTRANK
260 }
261\ No newline at end of file
262diff --git a/src/Utilities/Filter.ts b/src/Utilities/Filter.ts
263index 676ea81..4ee6015 100644
264--- a/src/Utilities/Filter.ts
265+++ b/src/Utilities/Filter.ts
266@@ -80,6 +80,21 @@ class Filter {
267 }
268 return toReturn;
269 }
270+
271+ /**
272+ * Returns an array as unique values.
273+ * @param arr - to filter down to uniques.
274+ * @returns {Array}
275+ */
276+ static unique(arr: Array<any>) : Array<any> {
277+ var a = [];
278+ for (var i = 0, l = arr.length; i < l; i++) {
279+ if (a.indexOf(arr[i]) === -1 && arr[i] !== '') {
280+ a.push(arr[i]);
281+ }
282+ }
283+ return a;
284+ }
285 }
286
287 export {
288diff --git a/tests/Formulas/StatisticalTest.ts b/tests/Formulas/StatisticalTest.ts
289index 19beb83..84dae7f 100644
290--- a/tests/Formulas/StatisticalTest.ts
291+++ b/tests/Formulas/StatisticalTest.ts
292@@ -32,7 +32,8 @@ import {
293 KURT,
294 INTERCEPT,
295 FORECAST,
296- POISSON
297+ POISSON,
298+ PERCENTRANK
299 } from "../../src/Formulas/Statistical";
300 import * as ERRORS from "../../src/Errors";
301 import {
302@@ -716,3 +717,19 @@ test("POISSON", function() {
303 POISSON.apply(this, [1]);
304 }, ERRORS.NA_ERROR);
305 });
306+
307+
308+test("PERCENTRANK", function() {
309+ assertEquals(PERCENTRANK([1, 5, 3, 7, 3, 2, 6, 8, 4, 9, 0, 3, 1], 4), 0.583);
310+ assertEquals(PERCENTRANK([1, 5, 3, 7, 3, 2, 6, 8, 4, 9, 0, 3, 1], 5), 0.666);
311+ assertEquals(PERCENTRANK([1, 5, 3, 7, 3, 2, 6, 8, 4, 9, 0, 3, 1], 0), 0);
312+ assertEquals(PERCENTRANK([1, 5, 3, 7, 3, 2, 6, 8, 4, 9, 0, 3, 1, -4], -1), 0.057);
313+ assertEquals(PERCENTRANK([1], 1), 1);
314+ assertEquals(PERCENTRANK([44], 44), 1);
315+ catchAndAssertEquals(function() {
316+ PERCENTRANK([1, 5, 3, 7, 3, 2, 6, 8, 4, 9, 0, 3, 1], 10);
317+ }, ERRORS.NA_ERROR);
318+ catchAndAssertEquals(function() {
319+ PERCENTRANK([1, 5, 3, 7, 3, 2, 6, 8, 4, 9, 0, 3, 1], -1);
320+ }, ERRORS.NA_ERROR);
321+});
322\ No newline at end of file
323diff --git a/tests/SheetFormulaTest.ts b/tests/SheetFormulaTest.ts
324index cfa25d8..8a8a1ed 100644
325--- a/tests/SheetFormulaTest.ts
326+++ b/tests/SheetFormulaTest.ts
327@@ -772,6 +772,11 @@ test("Sheet POISSON, POISSON.DIST", function(){
328 assertFormulaEquals('=POISSON.DIST(3, 5, true)', 0.26502591529736175);
329 });
330
331+test("Sheet PERCENTRANK, PERCENTRANK.INC", function(){
332+ assertFormulaEquals('=PERCENTRANK([1], 1)', 1);
333+ assertFormulaEquals('=PERCENTRANK.INC([1], 1)', 1);
334+});
335+
336 test("Sheet *", function(){
337 assertFormulaEquals('= 10 * 10', 100);
338 assertFormulaEquals('= 10 * 0', 0);