diff --git a/src/arr/trove/statistics.arr b/src/arr/trove/statistics.arr index 933b76f01..4aabb05c5 100644 --- a/src/arr/trove/statistics.arr +++ b/src/arr/trove/statistics.arr @@ -184,32 +184,6 @@ fun stdev-sample(l :: List) -> Number: num-sqrt(variance-sample(l)) end -fun linear-regression(x :: List, y :: List) -> (Number -> Number): - doc: "returns a linear predictor function calculated with ordinary least squares regression" - if x.length() <> y.length(): - raise(E.message-exception("linear-regression: input lists must have equal lengths")) - else if x.length() < 2: - raise(E.message-exception("linear-regression: input lists must have at least 2 elements each")) - else: - shadow y = map(num-to-roughnum, y) - shadow x = map(num-to-roughnum, x) - xpt-xy = math.sum(map2(lam(xi, yi): xi * yi end, x, y)) - xpt-x-xpt-y = (math.sum(x) * math.sum(y)) / x.length() - covariance = xpt-xy - xpt-x-xpt-y - v1 = math.sum(map(lam(n): n * n end, x)) - v2 = (math.sum(x) * math.sum(x)) / x.length() - variance1 = v1 - v2 - beta = covariance / variance1 - alpha = mean(y) - (beta * mean(x)) - - fun predictor(in :: Number) -> Number: - (beta * in) + alpha - end - - predictor - end -end - # please see: https://online.stat.psu.edu/stat462/ fun multiple-regression(x_s_s :: List, y_s :: List) -> (Any -> Number): @@ -217,6 +191,22 @@ fun multiple-regression(x_s_s :: List, y_s :: List) -> (Any -> Numb MR.multiple-regression(x_s_s, y_s) end +fun linear-regression(x-s :: List, y-s :: List) -> (Number -> Number): + doc: "returns a linear predictor function for a single independent variable" + x-s-n = x-s.length() + if x-s-n <> y-s.length(): + raise(E.message-exception("linear-regression: input lists must have equal lengths")) + else if x-s-n < 2: + raise(E.message-exception("linear-regression: input lists must have at least 2 elements each")) + else: + predictor1 = MR.multiple-regression(x-s.map(lam(x1 :: Number): {x1} end), y-s) + fun predictor2(x2 :: Number) -> Number: + predictor1({x2}) + end + predictor2 + end +end + fun r-squared(x :: List, y :: List, f :: (Number -> Number)) -> Number: shadow x = map(num-to-roughnum, x) shadow y = map(num-to-roughnum, y) diff --git a/src/js/trove/multiple-regression.js b/src/js/trove/multiple-regression.js index 2e4740899..dccd0f55b 100644 --- a/src/js/trove/multiple-regression.js +++ b/src/js/trove/multiple-regression.js @@ -72,7 +72,7 @@ } let sign = (r + c) % 2; sign = (sign === 0) ? 1 : -1; - return sign * matrixDeterminant(mm) + return sign * matrixDeterminant(mm); } function matrixAdjoint(m) { @@ -84,7 +84,7 @@ mc[r][c] = matrixCofactor(m, r, c); } } - return matrixTranspose(mc) + return matrixTranspose(mc); } function matrixInverse(m) { @@ -93,8 +93,8 @@ let size = m.length; for (let r = 0; r < size; r++) { for (let c = 0; c < size; c++) { - let x = mI[r][c] - mI[r][c] = x/det + let x = mI[r][c]; + mI[r][c] = x/det; } } return mI; @@ -126,7 +126,10 @@ let js_y_s = runtime.ffi.toArray(y_s); let num_mappings = js_x_s_s.length; if (js_y_s.length !== num_mappings) { - throw runtime.ffi.throwMessageException("multiple-regression: number of inputs doesn't match number of outputs"); + throw runtime.ffi.throwMessageException("multiple-regression: lists must have equal lengths"); + if (num_mappings < 2) { + throw runtime.ffi.throwMessageException("multiple-regression: lists must have at least 2 elements each"); + } } let X = new Array(num_mappings); let Y = new Array(num_mappings); @@ -140,7 +143,7 @@ } else if (x_s_n !== x_s_len) { throw runtime.ffi.throwMessageException("multiple-regression: lengths of input tuples are different"); } - X[r] = new Array(x_s_len + 1) + X[r] = new Array(x_s_len + 1); let Xr = X[r]; Xr[0] = 1; js_x_s.forEach(function(x, c) {