commit
message
[LINEST] formula added and tested
author
Ben Vogt <[email protected]>
date
2017-07-02 17:12:57
stats
8 file(s) changed,
151 insertions(+),
4 deletions(-)
files
DOCS.md
TODO.md
dist/Formulas/AllFormulas.js
dist/Formulas/Range.js
src/Formulas/AllFormulas.ts
src/Formulas/Range.ts
tests/Formulas/RangeTest.ts
tests/SheetFormulaTest.ts
1diff --git a/DOCS.md b/DOCS.md
2index 204a770..4ccd508 100644
3--- a/DOCS.md
4+++ b/DOCS.md
5@@ -1380,6 +1380,16 @@
6 @returns {Array}
7 @constructor TODO: Returns RowArray (values stacked in X-direction)
8 ```
9+
10+### LINEST
11+
12+```
13+ Returns the parameters of a linear trend.
14+@param dataY - The range of data representing Y values.
15+@param dataX - The range of data representing X values.
16+@returns {number[]}
17+@constructor
18+```
19 ## Statistical
20
21
22diff --git a/TODO.md b/TODO.md
23index fc591c6..4c3340b 100644
24--- a/TODO.md
25+++ b/TODO.md
26@@ -118,7 +118,6 @@ For example 64 tbs to a qt.
27 * T
28 * TEXT
29 * VALUE
30-* LINEST
31 * LOGEST
32 * MDETERM
33 * MINVERSE
34diff --git a/dist/Formulas/AllFormulas.js b/dist/Formulas/AllFormulas.js
35index 5916e90..2b7c47a 100644
36--- a/dist/Formulas/AllFormulas.js
37+++ b/dist/Formulas/AllFormulas.js
38@@ -76,6 +76,7 @@ exports.FACTDOUBLE = Math_1.FACTDOUBLE;
39 var Range_1 = require("./Range");
40 exports.FREQUENCY = Range_1.FREQUENCY;
41 exports.GROWTH = Range_1.GROWTH;
42+exports.LINEST = Range_1.LINEST;
43 var Info_1 = require("./Info");
44 exports.NA = Info_1.NA;
45 exports.ISTEXT = Info_1.ISTEXT;
46diff --git a/dist/Formulas/Range.js b/dist/Formulas/Range.js
47index 559f833..dba186f 100644
48--- a/dist/Formulas/Range.js
49+++ b/dist/Formulas/Range.js
50@@ -4,6 +4,7 @@ var ArgsChecker_1 = require("../Utilities/ArgsChecker");
51 var Filter_1 = require("../Utilities/Filter");
52 var TypeConverter_1 = require("../Utilities/TypeConverter");
53 var Errors_1 = require("../Errors");
54+var MathHelpers_1 = require("../Utilities/MathHelpers");
55 /**
56 * Calculates the frequency distribution of a range into specified classes or "bins".
57 * @param range - to get frequency for.
58@@ -70,6 +71,7 @@ exports.FREQUENCY = FREQUENCY;
59 * TODO: Returns RowArray (values stacked in X-direction)
60 */
61 var GROWTH = function (knownY, knownX, newX, shouldUseConstant) {
62+ ArgsChecker_1.ArgsChecker.checkLengthWithin(arguments, 1, 4, "GROWTH");
63 // Credits: Ilmari Karonen, FormulaJs (https://github.com/sutoiku/formula.js/)
64 knownY = Filter_1.Filter.flattenAndThrow(knownY).map(function (value) {
65 if (typeof value !== "number") {
66@@ -131,3 +133,38 @@ var GROWTH = function (knownY, knownX, newX, shouldUseConstant) {
67 return new_y;
68 };
69 exports.GROWTH = GROWTH;
70+/**
71+ * Returns the parameters of a linear trend.
72+ * @param dataY - The range of data representing Y values.
73+ * @param dataX - The range of data representing X values.
74+ * @returns {number[]}
75+ * @constructor
76+ */
77+var LINEST = function (dataY, dataX) {
78+ ArgsChecker_1.ArgsChecker.checkLength(arguments, 2, "LINEST");
79+ var rangeY = Filter_1.Filter.flattenAndThrow(dataY).map(function (value) {
80+ return TypeConverter_1.TypeConverter.valueToNumber(value);
81+ });
82+ var rangeX = Filter_1.Filter.flattenAndThrow(dataX).map(function (value) {
83+ return TypeConverter_1.TypeConverter.valueToNumber(value);
84+ });
85+ if (rangeX.length < 2) {
86+ throw new Errors_1.NAError("LINEST requires more data points. Expected: 2, found: " + rangeX.length + ".");
87+ }
88+ if (rangeY.length < 2) {
89+ throw new Errors_1.NAError("LINEST requires more data points. Expected: 2, found: " + rangeY.length + ".");
90+ }
91+ var xMean = MathHelpers_1.mean(rangeX);
92+ var yMean = MathHelpers_1.mean(rangeY);
93+ var n = rangeX.length;
94+ var num = 0;
95+ var den = 0;
96+ for (var i = 0; i < n; i++) {
97+ num += (rangeX[i] - xMean) * (rangeY[i] - yMean);
98+ den += Math.pow(rangeX[i] - xMean, 2);
99+ }
100+ var m = num / den;
101+ var b = yMean - m * xMean;
102+ return [m, b];
103+};
104+exports.LINEST = LINEST;
105diff --git a/src/Formulas/AllFormulas.ts b/src/Formulas/AllFormulas.ts
106index 0bf1855..2aadb28 100644
107--- a/src/Formulas/AllFormulas.ts
108+++ b/src/Formulas/AllFormulas.ts
109@@ -74,7 +74,8 @@ import {
110 } from "./Math";
111 import {
112 FREQUENCY,
113- GROWTH
114+ GROWTH,
115+ LINEST
116 } from "./Range";
117 import {
118 NA,
119@@ -387,5 +388,6 @@ export {
120 IPMT,
121 FV,
122 ISEMAIL,
123- ISURL
124+ ISURL,
125+ LINEST
126 }
127\ No newline at end of file
128diff --git a/src/Formulas/Range.ts b/src/Formulas/Range.ts
129index 22f8e28..9d4a21a 100644
130--- a/src/Formulas/Range.ts
131+++ b/src/Formulas/Range.ts
132@@ -1,9 +1,18 @@
133 import {
134 ArgsChecker
135 } from "../Utilities/ArgsChecker";
136-import {Filter} from "../Utilities/Filter";
137-import {TypeConverter} from "../Utilities/TypeConverter";
138-import {ValueError} from "../Errors";
139+import {
140+ Filter
141+} from "../Utilities/Filter";
142+import {
143+ TypeConverter
144+} from "../Utilities/TypeConverter";
145+import {
146+ ValueError, NAError
147+} from "../Errors";
148+import {
149+ mean
150+} from "../Utilities/MathHelpers";
151
152
153 /**
154@@ -72,6 +81,7 @@ var FREQUENCY = function (range, bins) : Array<number> {
155 * TODO: Returns RowArray (values stacked in X-direction)
156 */
157 var GROWTH = function (knownY, knownX?, newX?, shouldUseConstant?) {
158+ ArgsChecker.checkLengthWithin(arguments, 1, 4, "GROWTH");
159 // Credits: Ilmari Karonen, FormulaJs (https://github.com/sutoiku/formula.js/)
160
161 knownY = Filter.flattenAndThrow(knownY).map(function (value) {
162@@ -137,7 +147,46 @@ var GROWTH = function (knownY, knownX?, newX?, shouldUseConstant?) {
163 return new_y;
164 };
165
166+/**
167+ * Returns the parameters of a linear trend.
168+ * @param dataY - The range of data representing Y values.
169+ * @param dataX - The range of data representing X values.
170+ * @returns {number[]}
171+ * @constructor
172+ */
173+var LINEST = function (dataY, dataX) {
174+ ArgsChecker.checkLength(arguments, 2, "LINEST");
175+ var rangeY = Filter.flattenAndThrow(dataY).map(function (value) {
176+ return TypeConverter.valueToNumber(value);
177+ });
178+ var rangeX = Filter.flattenAndThrow(dataX).map(function (value) {
179+ return TypeConverter.valueToNumber(value);
180+ });
181+
182+ if (rangeX.length < 2) {
183+ throw new NAError("LINEST requires more data points. Expected: 2, found: " + rangeX.length + ".");
184+ }
185+ if (rangeY.length < 2) {
186+ throw new NAError("LINEST requires more data points. Expected: 2, found: " + rangeY.length + ".");
187+ }
188+
189+ var xMean = mean(rangeX);
190+ var yMean = mean(rangeY);
191+ var n = rangeX.length;
192+ var num = 0;
193+ var den = 0;
194+ for (var i = 0; i < n; i++) {
195+ num += (rangeX[i] - xMean) * (rangeY[i] - yMean);
196+ den += Math.pow(rangeX[i] - xMean, 2);
197+ }
198+ var m = num / den;
199+ var b = yMean - m * xMean;
200+ return [m, b];
201+};
202+
203+
204 export {
205 FREQUENCY,
206- GROWTH
207+ GROWTH,
208+ LINEST
209 }
210\ No newline at end of file
211diff --git a/tests/Formulas/RangeTest.ts b/tests/Formulas/RangeTest.ts
212index b0007a5..e0efeca 100644
213--- a/tests/Formulas/RangeTest.ts
214+++ b/tests/Formulas/RangeTest.ts
215@@ -1,6 +1,7 @@
216 import {
217 FREQUENCY,
218- GROWTH
219+ GROWTH,
220+ LINEST
221 } from "../../src/Formulas/Range";
222 import {
223 assertArrayEquals,
224@@ -53,4 +54,42 @@ test("GROWTH", function(){
225 [10, 11, 12]
226 );
227 }, ERRORS.REF_ERROR);
228+});
229+
230+
231+test("GROWTH", function(){
232+ assertArrayEquals(LINEST(
233+ [15.53, 19.99, 20.43, 21.18, 25.93, 30.00, 30.00, 34.01, 36.47],
234+ [1, 2, 3, 4, 5, 6, 7, 8, 9]
235+ ), [2.563, 13.13388888888889]);
236+ assertArrayEquals(LINEST(
237+ [15.53, 19.99, 20.43, 21.18, 25.93, 30],
238+ [1, 2, 3, 4, 5, 6]
239+ ), [2.5977142857142863, 13.08466666666666]);
240+ catchAndAssertEquals(function() {
241+ LINEST(
242+ [15.53, 19.99, 20.43, 21.18, 25.93, "string", 30],
243+ [1, 2, 3, 4, 5, 6]
244+ );
245+ }, ERRORS.VALUE_ERROR);
246+ catchAndAssertEquals(function() {
247+ LINEST(
248+ [15.53],
249+ [1]
250+ );
251+ }, ERRORS.NA_ERROR);
252+ catchAndAssertEquals(function() {
253+ LINEST.apply(
254+ this,
255+ [[15.53, 19.99, 20.43, 21.18, 25.93, 30],
256+ [1, 2, 3, 4, 5, 6],
257+ "another"]
258+ );
259+ }, ERRORS.NA_ERROR);
260+ catchAndAssertEquals(function() {
261+ LINEST.apply(
262+ this,
263+ [[15.53, 19.99, 20.43, 21.18, 25.93, 30]]
264+ );
265+ }, ERRORS.NA_ERROR);
266 });
267\ No newline at end of file
268diff --git a/tests/SheetFormulaTest.ts b/tests/SheetFormulaTest.ts
269index 2336543..bf28619 100644
270--- a/tests/SheetFormulaTest.ts
271+++ b/tests/SheetFormulaTest.ts
272@@ -763,6 +763,10 @@ test("Sheet ISURL", function(){
273 assertFormulaEquals('=ISURL("example.com")', true);
274 });
275
276+test("Sheet LINEST", function(){
277+ assertFormulaEqualsArray('=LINEST([15.53, 19.99, 20.43, 21.18, 25.93, 30], [1, 2, 3, 4, 5, 6])', [2.5977142857142863, 13.08466666666666]);
278+});
279+
280 test("Sheet *", function(){
281 assertFormulaEquals('= 10 * 10', 100);
282 assertFormulaEquals('= 10 * 0', 0);