diff --git a/bower.json b/bower.json index da39575..b82fe99 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "ml", - "version": "0.10.1", + "version": "0.11.0", "main": [ "dist/ml.js", "dist/ml.min.js" diff --git a/dist/ml.js b/dist/ml.js index 52c7cb8..3c079d8 100644 --- a/dist/ml.js +++ b/dist/ml.js @@ -83,35 +83,35 @@ return /******/ (function(modules) { // webpackBootstrap Stat.array = __webpack_require__(6); Stat.matrix = __webpack_require__(7); Stat.PCA = __webpack_require__(122); - Stat.Performance = __webpack_require__(127); + Stat.Performance = __webpack_require__(123); // Random number generation var RNG = exports.RNG = {}; - RNG.XSadd = __webpack_require__(129); + RNG.XSadd = __webpack_require__(125); // Supervised learning var SL = exports.SL = {}; - SL.SVM = __webpack_require__(130); - SL.KNN = __webpack_require__(133); - SL.NaiveBayes = __webpack_require__(136); - SL.PLS = __webpack_require__(141); + SL.SVM = __webpack_require__(126); + SL.KNN = __webpack_require__(129); + SL.NaiveBayes = __webpack_require__(132); + SL.PLS = __webpack_require__(137); // Clustering var Clust = exports.Clust = {}; - Clust.kmeans = __webpack_require__(145); - Clust.hclust = __webpack_require__(147); + Clust.kmeans = __webpack_require__(141); + Clust.hclust = __webpack_require__(143); // Neural networks var NN = exports.NN = exports.nn = {}; - NN.SOM = __webpack_require__(157); - NN.FNN = __webpack_require__(160); + NN.SOM = __webpack_require__(153); + NN.FNN = __webpack_require__(156); @@ -16735,1171 +16735,179 @@ return /******/ (function(modules) { // webpackBootstrap /* 122 */ /***/ function(module, exports, __webpack_require__) { - module.exports = __webpack_require__(123); - - -/***/ }, -/* 123 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - var Matrix = __webpack_require__(14); - var Stat = __webpack_require__(124); - var SVD = Matrix.DC.SVD; - - module.exports = PCA; - - /** - * Creates new PCA (Principal Component Analysis) from the dataset - * @param {Matrix} dataset - * @param {Object} options - options for the PCA algorithm - * @param {boolean} reload - for load purposes - * @param {Object} model - for load purposes - * @constructor - * */ - function PCA(dataset, options, reload, model) { - - if (reload) { - this.U = model.U; - this.S = model.S; - this.means = model.means; - this.std = model.std; - this.standardize = model.standardize - } else { - if(options === undefined) { - options = { - standardize: false - }; - } - - this.standardize = options.standardize; - - if (!Matrix.isMatrix(dataset)) { - dataset = new Matrix(dataset); - } else { - dataset = dataset.clone(); - } - - var normalization = adjust(dataset, this.standardize); - var normalizedDataset = normalization.result; - - var covarianceMatrix = normalizedDataset.transpose().mmul(normalizedDataset).divS(dataset.rows); - - var target = new SVD(covarianceMatrix, { - computeLeftSingularVectors: true, - computeRightSingularVectors: true, - autoTranspose: false - }); - - this.U = target.leftSingularVectors; - this.S = target.diagonal; - this.means = normalization.means; - this.std = normalization.std; - } - } - - /** - * Load a PCA model from JSON - * @oaram {Object} model - * @return {PCA} - * */ - PCA.load = function (model) { - if(model.modelName !== 'PCA') - throw new RangeError("The current model is invalid!"); - - return new PCA(null, null, true, model); - }; - - /** - * Exports the current model to an Object - * @return {Object} model - * */ - PCA.prototype.export = function () { - return { - modelName: "PCA", - U: this.U, - S: this.S, - means: this.means, - std: this.std, - standardize: this.standardize - }; - }; - - /** - * Function that project the dataset into new space of k dimensions, - * this method doesn't modify your dataset. - * @param {Matrix} dataset. - * @param {Number} k - dimensions to project. - * @return {Matrix} dataset projected in k dimensions. - * @throws {RangeError} if k is larger than the number of eigenvector - * of the model. - * */ - PCA.prototype.project = function (dataset, k) { - var dimensions = k - 1; - if(k > this.U.columns) - throw new RangeError("the number of dimensions must not be larger than " + this.U.columns); - - if (!Matrix.isMatrix(dataset)) { - dataset = new Matrix(dataset); - } else { - dataset = dataset.clone(); - } - - var X = adjust(dataset, this.standardize).result; - return X.mmul(this.U.subMatrix(0, this.U.rows - 1, 0, dimensions)); - }; - - /** - * This method returns the percentage variance of each eigenvector. - * @return {Number} percentage variance of each eigenvector. - * */ - PCA.prototype.getExplainedVariance = function () { - var sum = this.S.reduce(function (previous, value) { - return previous + value; - }); - return this.S.map(function (value) { - return value / sum; - }); - }; - - /** - * Function that returns the Eigenvectors of the covariance matrix. - * @returns {Matrix} - */ - PCA.prototype.getEigenvectors = function () { - return this.U; - }; - - /** - * Function that returns the Eigenvalues (on the diagonal). - * @returns {*} - */ - PCA.prototype.getEigenvalues = function () { - return this.S; - }; - - /** - * This method returns a dataset normalized in the following form: - * X = (X - mean) / std - * @param dataset. - * @param {Boolean} standarize - do standardization - * @return A dataset normalized. - * */ - function adjust(dataset, standarize) { - var means = Stat.matrix.mean(dataset); - var std = standarize ? Stat.matrix.standardDeviation(dataset, means, true) : undefined; - - var result = dataset.subRowVector(means); - return { - result: standarize ? result.divRowVector(std) : result, - means: means, - std: std - } - } - - -/***/ }, -/* 124 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - exports.array = __webpack_require__(125); - exports.matrix = __webpack_require__(126); - - -/***/ }, -/* 125 */ -/***/ function(module, exports) { - 'use strict'; - function compareNumbers(a, b) { - return a - b; - } - - /** - * Computes the sum of the given values - * @param {Array} values - * @returns {number} - */ - exports.sum = function sum(values) { - var sum = 0; - for (var i = 0; i < values.length; i++) { - sum += values[i]; - } - return sum; - }; - - /** - * Computes the maximum of the given values - * @param {Array} values - * @returns {number} - */ - exports.max = function max(values) { - var max = -Infinity; - var l = values.length; - for (var i = 0; i < l; i++) { - if (values[i] > max) max = values[i]; - } - return max; - }; - - /** - * Computes the minimum of the given values - * @param {Array} values - * @returns {number} - */ - exports.min = function min(values) { - var min = Infinity; - var l = values.length; - for (var i = 0; i < l; i++) { - if (values[i] < min) min = values[i]; - } - return min; - }; - - /** - * Computes the min and max of the given values - * @param {Array} values - * @returns {{min: number, max: number}} - */ - exports.minMax = function minMax(values) { - var min = Infinity; - var max = -Infinity; - var l = values.length; - for (var i = 0; i < l; i++) { - if (values[i] < min) min = values[i]; - if (values[i] > max) max = values[i]; - } - return { - min: min, - max: max - }; - }; - - /** - * Computes the arithmetic mean of the given values - * @param {Array} values - * @returns {number} - */ - exports.arithmeticMean = function arithmeticMean(values) { - var sum = 0; - var l = values.length; - for (var i = 0; i < l; i++) { - sum += values[i]; - } - return sum / l; - }; - - /** - * {@link arithmeticMean} - */ - exports.mean = exports.arithmeticMean; - - /** - * Computes the geometric mean of the given values - * @param {Array} values - * @returns {number} - */ - exports.geometricMean = function geometricMean(values) { - var mul = 1; - var l = values.length; - for (var i = 0; i < l; i++) { - mul *= values[i]; - } - return Math.pow(mul, 1 / l); - }; - - /** - * Computes the mean of the log of the given values - * If the return value is exponentiated, it gives the same result as the - * geometric mean. - * @param {Array} values - * @returns {number} - */ - exports.logMean = function logMean(values) { - var lnsum = 0; - var l = values.length; - for (var i = 0; i < l; i++) { - lnsum += Math.log(values[i]); - } - return lnsum / l; - }; - - /** - * Computes the weighted grand mean for a list of means and sample sizes - * @param {Array} means - Mean values for each set of samples - * @param {Array} samples - Number of original values for each set of samples - * @returns {number} - */ - exports.grandMean = function grandMean(means, samples) { - var sum = 0; - var n = 0; - var l = means.length; - for (var i = 0; i < l; i++) { - sum += samples[i] * means[i]; - n += samples[i]; - } - return sum / n; - }; - - /** - * Computes the truncated mean of the given values using a given percentage - * @param {Array} values - * @param {number} percent - The percentage of values to keep (range: [0,1]) - * @param {boolean} [alreadySorted=false] - * @returns {number} - */ - exports.truncatedMean = function truncatedMean(values, percent, alreadySorted) { - if (alreadySorted === undefined) alreadySorted = false; - if (!alreadySorted) { - values = values.slice().sort(compareNumbers); - } - var l = values.length; - var k = Math.floor(l * percent); - var sum = 0; - for (var i = k; i < (l - k); i++) { - sum += values[i]; - } - return sum / (l - 2 * k); - }; - - /** - * Computes the harmonic mean of the given values - * @param {Array} values - * @returns {number} - */ - exports.harmonicMean = function harmonicMean(values) { - var sum = 0; - var l = values.length; - for (var i = 0; i < l; i++) { - if (values[i] === 0) { - throw new RangeError('value at index ' + i + 'is zero'); - } - sum += 1 / values[i]; - } - return l / sum; - }; + const Matrix = __webpack_require__(14); + const SVD = Matrix.DC.SVD; + const Stat = __webpack_require__(5).matrix; + const mean = Stat.mean; + const stdev = Stat.standardDeviation; - /** - * Computes the contraharmonic mean of the given values - * @param {Array} values - * @returns {number} - */ - exports.contraHarmonicMean = function contraHarmonicMean(values) { - var r1 = 0; - var r2 = 0; - var l = values.length; - for (var i = 0; i < l; i++) { - r1 += values[i] * values[i]; - r2 += values[i]; - } - if (r2 < 0) { - throw new RangeError('sum of values is negative'); - } - return r1 / r2; - }; - - /** - * Computes the median of the given values - * @param {Array} values - * @param {boolean} [alreadySorted=false] - * @returns {number} - */ - exports.median = function median(values, alreadySorted) { - if (alreadySorted === undefined) alreadySorted = false; - if (!alreadySorted) { - values = values.slice().sort(compareNumbers); - } - var l = values.length; - var half = Math.floor(l / 2); - if (l % 2 === 0) { - return (values[half - 1] + values[half]) * 0.5; - } else { - return values[half]; - } - }; - - /** - * Computes the variance of the given values - * @param {Array} values - * @param {boolean} [unbiased=true] - if true, divide by (n-1); if false, divide by n. - * @returns {number} - */ - exports.variance = function variance(values, unbiased) { - if (unbiased === undefined) unbiased = true; - var theMean = exports.mean(values); - var theVariance = 0; - var l = values.length; - - for (var i = 0; i < l; i++) { - var x = values[i] - theMean; - theVariance += x * x; - } - - if (unbiased) { - return theVariance / (l - 1); - } else { - return theVariance / l; - } - }; - - /** - * Computes the standard deviation of the given values - * @param {Array} values - * @param {boolean} [unbiased=true] - if true, divide by (n-1); if false, divide by n. - * @returns {number} - */ - exports.standardDeviation = function standardDeviation(values, unbiased) { - return Math.sqrt(exports.variance(values, unbiased)); - }; - - exports.standardError = function standardError(values) { - return exports.standardDeviation(values) / Math.sqrt(values.length); - }; - - exports.quartiles = function quartiles(values, alreadySorted) { - if (typeof(alreadySorted) === 'undefined') alreadySorted = false; - if (!alreadySorted) { - values = values.slice(); - values.sort(compareNumbers); - } - - var quart = values.length / 4; - var q1 = values[Math.ceil(quart) - 1]; - var q2 = exports.median(values, true); - var q3 = values[Math.ceil(quart * 3) - 1]; - - return {q1: q1, q2: q2, q3: q3}; - }; - - exports.pooledStandardDeviation = function pooledStandardDeviation(samples, unbiased) { - return Math.sqrt(exports.pooledVariance(samples, unbiased)); - }; - - exports.pooledVariance = function pooledVariance(samples, unbiased) { - if (typeof(unbiased) === 'undefined') unbiased = true; - var sum = 0; - var length = 0, l = samples.length; - for (var i = 0; i < l; i++) { - var values = samples[i]; - var vari = exports.variance(values); - - sum += (values.length - 1) * vari; - - if (unbiased) - length += values.length - 1; - else - length += values.length; - } - return sum / length; - }; - - exports.mode = function mode(values) { - var l = values.length, - itemCount = new Array(l), - i; - for (i = 0; i < l; i++) { - itemCount[i] = 0; - } - var itemArray = new Array(l); - var count = 0; - - for (i = 0; i < l; i++) { - var index = itemArray.indexOf(values[i]); - if (index >= 0) - itemCount[index]++; - else { - itemArray[count] = values[i]; - itemCount[count] = 1; - count++; - } - } - - var maxValue = 0, maxIndex = 0; - for (i = 0; i < count; i++) { - if (itemCount[i] > maxValue) { - maxValue = itemCount[i]; - maxIndex = i; - } - } - - return itemArray[maxIndex]; - }; - - exports.covariance = function covariance(vector1, vector2, unbiased) { - if (typeof(unbiased) === 'undefined') unbiased = true; - var mean1 = exports.mean(vector1); - var mean2 = exports.mean(vector2); - - if (vector1.length !== vector2.length) - throw "Vectors do not have the same dimensions"; - - var cov = 0, l = vector1.length; - for (var i = 0; i < l; i++) { - var x = vector1[i] - mean1; - var y = vector2[i] - mean2; - cov += x * y; - } - - if (unbiased) - return cov / (l - 1); - else - return cov / l; - }; - - exports.skewness = function skewness(values, unbiased) { - if (typeof(unbiased) === 'undefined') unbiased = true; - var theMean = exports.mean(values); - - var s2 = 0, s3 = 0, l = values.length; - for (var i = 0; i < l; i++) { - var dev = values[i] - theMean; - s2 += dev * dev; - s3 += dev * dev * dev; - } - var m2 = s2 / l; - var m3 = s3 / l; - - var g = m3 / (Math.pow(m2, 3 / 2.0)); - if (unbiased) { - var a = Math.sqrt(l * (l - 1)); - var b = l - 2; - return (a / b) * g; - } - else { - return g; - } - }; - - exports.kurtosis = function kurtosis(values, unbiased) { - if (typeof(unbiased) === 'undefined') unbiased = true; - var theMean = exports.mean(values); - var n = values.length, s2 = 0, s4 = 0; - - for (var i = 0; i < n; i++) { - var dev = values[i] - theMean; - s2 += dev * dev; - s4 += dev * dev * dev * dev; - } - var m2 = s2 / n; - var m4 = s4 / n; - - if (unbiased) { - var v = s2 / (n - 1); - var a = (n * (n + 1)) / ((n - 1) * (n - 2) * (n - 3)); - var b = s4 / (v * v); - var c = ((n - 1) * (n - 1)) / ((n - 2) * (n - 3)); - - return a * b - 3 * c; - } - else { - return m4 / (m2 * m2) - 3; - } - }; - - exports.entropy = function entropy(values, eps) { - if (typeof(eps) === 'undefined') eps = 0; - var sum = 0, l = values.length; - for (var i = 0; i < l; i++) - sum += values[i] * Math.log(values[i] + eps); - return -sum; - }; - - exports.weightedMean = function weightedMean(values, weights) { - var sum = 0, l = values.length; - for (var i = 0; i < l; i++) - sum += values[i] * weights[i]; - return sum; - }; - - exports.weightedStandardDeviation = function weightedStandardDeviation(values, weights) { - return Math.sqrt(exports.weightedVariance(values, weights)); - }; - - exports.weightedVariance = function weightedVariance(values, weights) { - var theMean = exports.weightedMean(values, weights); - var vari = 0, l = values.length; - var a = 0, b = 0; - - for (var i = 0; i < l; i++) { - var z = values[i] - theMean; - var w = weights[i]; - - vari += w * (z * z); - b += w; - a += w * w; - } - - return vari * (b / (b * b - a)); - }; - - exports.center = function center(values, inPlace) { - if (typeof(inPlace) === 'undefined') inPlace = false; - - var result = values; - if (!inPlace) - result = values.slice(); - - var theMean = exports.mean(result), l = result.length; - for (var i = 0; i < l; i++) - result[i] -= theMean; - }; - - exports.standardize = function standardize(values, standardDev, inPlace) { - if (typeof(standardDev) === 'undefined') standardDev = exports.standardDeviation(values); - if (typeof(inPlace) === 'undefined') inPlace = false; - var l = values.length; - var result = inPlace ? values : new Array(l); - for (var i = 0; i < l; i++) - result[i] = values[i] / standardDev; - return result; - }; - - exports.cumulativeSum = function cumulativeSum(array) { - var l = array.length; - var result = new Array(l); - result[0] = array[0]; - for (var i = 1; i < l; i++) - result[i] = result[i - 1] + array[i]; - return result; - }; - - -/***/ }, -/* 126 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - var arrayStat = __webpack_require__(125); - - // https://github.com/accord-net/framework/blob/development/Sources/Accord.Statistics/Tools.cs - - function entropy(matrix, eps) { - if (typeof(eps) === 'undefined') { - eps = 0; - } - var sum = 0, - l1 = matrix.length, - l2 = matrix[0].length; - for (var i = 0; i < l1; i++) { - for (var j = 0; j < l2; j++) { - sum += matrix[i][j] * Math.log(matrix[i][j] + eps); - } - } - return -sum; - } - - function mean(matrix, dimension) { - if (typeof(dimension) === 'undefined') { - dimension = 0; - } - var rows = matrix.length, - cols = matrix[0].length, - theMean, N, i, j; - - if (dimension === -1) { - theMean = [0]; - N = rows * cols; - for (i = 0; i < rows; i++) { - for (j = 0; j < cols; j++) { - theMean[0] += matrix[i][j]; - } - } - theMean[0] /= N; - } else if (dimension === 0) { - theMean = new Array(cols); - N = rows; - for (j = 0; j < cols; j++) { - theMean[j] = 0; - for (i = 0; i < rows; i++) { - theMean[j] += matrix[i][j]; - } - theMean[j] /= N; - } - } else if (dimension === 1) { - theMean = new Array(rows); - N = cols; - for (j = 0; j < rows; j++) { - theMean[j] = 0; - for (i = 0; i < cols; i++) { - theMean[j] += matrix[j][i]; - } - theMean[j] /= N; - } - } else { - throw new Error('Invalid dimension'); - } - return theMean; - } - - function standardDeviation(matrix, means, unbiased) { - var vari = variance(matrix, means, unbiased), l = vari.length; - for (var i = 0; i < l; i++) { - vari[i] = Math.sqrt(vari[i]); - } - return vari; - } - - function variance(matrix, means, unbiased) { - if (typeof(unbiased) === 'undefined') { - unbiased = true; - } - means = means || mean(matrix); - var rows = matrix.length; - if (rows === 0) return []; - var cols = matrix[0].length; - var vari = new Array(cols); - - for (var j = 0; j < cols; j++) { - var sum1 = 0, sum2 = 0, x = 0; - for (var i = 0; i < rows; i++) { - x = matrix[i][j] - means[j]; - sum1 += x; - sum2 += x * x; - } - if (unbiased) { - vari[j] = (sum2 - ((sum1 * sum1) / rows)) / (rows - 1); - } else { - vari[j] = (sum2 - ((sum1 * sum1) / rows)) / rows; - } - } - return vari; - } - - function median(matrix) { - var rows = matrix.length, cols = matrix[0].length; - var medians = new Array(cols); - - for (var i = 0; i < cols; i++) { - var data = new Array(rows); - for (var j = 0; j < rows; j++) { - data[j] = matrix[j][i]; - } - data.sort(); - var N = data.length; - if (N % 2 === 0) { - medians[i] = (data[N / 2] + data[(N / 2) - 1]) * 0.5; - } else { - medians[i] = data[Math.floor(N / 2)]; - } - } - return medians; - } - - function mode(matrix) { - var rows = matrix.length, - cols = matrix[0].length, - modes = new Array(cols), - i, j; - for (i = 0; i < cols; i++) { - var itemCount = new Array(rows); - for (var k = 0; k < rows; k++) { - itemCount[k] = 0; - } - var itemArray = new Array(rows); - var count = 0; - - for (j = 0; j < rows; j++) { - var index = itemArray.indexOf(matrix[j][i]); - if (index >= 0) { - itemCount[index]++; - } else { - itemArray[count] = matrix[j][i]; - itemCount[count] = 1; - count++; - } - } - - var maxValue = 0, maxIndex = 0; - for (j = 0; j < count; j++) { - if (itemCount[j] > maxValue) { - maxValue = itemCount[j]; - maxIndex = j; - } - } - - modes[i] = itemArray[maxIndex]; - } - return modes; - } - - function skewness(matrix, unbiased) { - if (typeof(unbiased) === 'undefined') unbiased = true; - var means = mean(matrix); - var n = matrix.length, l = means.length; - var skew = new Array(l); - - for (var j = 0; j < l; j++) { - var s2 = 0, s3 = 0; - for (var i = 0; i < n; i++) { - var dev = matrix[i][j] - means[j]; - s2 += dev * dev; - s3 += dev * dev * dev; - } - - var m2 = s2 / n; - var m3 = s3 / n; - var g = m3 / Math.pow(m2, 3 / 2); - - if (unbiased) { - var a = Math.sqrt(n * (n - 1)); - var b = n - 2; - skew[j] = (a / b) * g; - } else { - skew[j] = g; - } - } - return skew; - } - - function kurtosis(matrix, unbiased) { - if (typeof(unbiased) === 'undefined') unbiased = true; - var means = mean(matrix); - var n = matrix.length, m = matrix[0].length; - var kurt = new Array(m); - - for (var j = 0; j < m; j++) { - var s2 = 0, s4 = 0; - for (var i = 0; i < n; i++) { - var dev = matrix[i][j] - means[j]; - s2 += dev * dev; - s4 += dev * dev * dev * dev; - } - var m2 = s2 / n; - var m4 = s4 / n; - - if (unbiased) { - var v = s2 / (n - 1); - var a = (n * (n + 1)) / ((n - 1) * (n - 2) * (n - 3)); - var b = s4 / (v * v); - var c = ((n - 1) * (n - 1)) / ((n - 2) * (n - 3)); - kurt[j] = a * b - 3 * c; - } else { - kurt[j] = m4 / (m2 * m2) - 3; - } - } - return kurt; - } - - function standardError(matrix) { - var samples = matrix.length; - var standardDeviations = standardDeviation(matrix), l = standardDeviations.length; - var standardErrors = new Array(l); - var sqrtN = Math.sqrt(samples); - - for (var i = 0; i < l; i++) { - standardErrors[i] = standardDeviations[i] / sqrtN; - } - return standardErrors; - } - - function covariance(matrix, dimension) { - return scatter(matrix, undefined, dimension); - } - - function scatter(matrix, divisor, dimension) { - if (typeof(dimension) === 'undefined') { - dimension = 0; - } - if (typeof(divisor) === 'undefined') { - if (dimension === 0) { - divisor = matrix.length - 1; - } else if (dimension === 1) { - divisor = matrix[0].length - 1; - } - } - var means = mean(matrix, dimension), - rows = matrix.length; - if (rows === 0) { - return [[]]; - } - var cols = matrix[0].length, - cov, i, j, s, k; - - if (dimension === 0) { - cov = new Array(cols); - for (i = 0; i < cols; i++) { - cov[i] = new Array(cols); - } - for (i = 0; i < cols; i++) { - for (j = i; j < cols; j++) { - s = 0; - for (k = 0; k < rows; k++) { - s += (matrix[k][j] - means[j]) * (matrix[k][i] - means[i]); - } - s /= divisor; - cov[i][j] = s; - cov[j][i] = s; - } - } - } else if (dimension === 1) { - cov = new Array(rows); - for (i = 0; i < rows; i++) { - cov[i] = new Array(rows); - } - for (i = 0; i < rows; i++) { - for (j = i; j < rows; j++) { - s = 0; - for (k = 0; k < cols; k++) { - s += (matrix[j][k] - means[j]) * (matrix[i][k] - means[i]); - } - s /= divisor; - cov[i][j] = s; - cov[j][i] = s; - } - } - } else { - throw new Error('Invalid dimension'); - } - - return cov; - } - - function correlation(matrix) { - var means = mean(matrix), - standardDeviations = standardDeviation(matrix, true, means), - scores = zScores(matrix, means, standardDeviations), - rows = matrix.length, - cols = matrix[0].length, - i, j; - - var cor = new Array(cols); - for (i = 0; i < cols; i++) { - cor[i] = new Array(cols); - } - for (i = 0; i < cols; i++) { - for (j = i; j < cols; j++) { - var c = 0; - for (var k = 0, l = scores.length; k < l; k++) { - c += scores[k][j] * scores[k][i]; - } - c /= rows - 1; - cor[i][j] = c; - cor[j][i] = c; - } - } - return cor; - } - - function zScores(matrix, means, standardDeviations) { - means = means || mean(matrix); - if (typeof(standardDeviations) === 'undefined') standardDeviations = standardDeviation(matrix, true, means); - return standardize(center(matrix, means, false), standardDeviations, true); - } - - function center(matrix, means, inPlace) { - means = means || mean(matrix); - var result = matrix, - l = matrix.length, - i, j, jj; - - if (!inPlace) { - result = new Array(l); - for (i = 0; i < l; i++) { - result[i] = new Array(matrix[i].length); - } - } - - for (i = 0; i < l; i++) { - var row = result[i]; - for (j = 0, jj = row.length; j < jj; j++) { - row[j] = matrix[i][j] - means[j]; - } - } - return result; - } - - function standardize(matrix, standardDeviations, inPlace) { - if (typeof(standardDeviations) === 'undefined') standardDeviations = standardDeviation(matrix); - var result = matrix, - l = matrix.length, - i, j, jj; - - if (!inPlace) { - result = new Array(l); - for (i = 0; i < l; i++) { - result[i] = new Array(matrix[i].length); - } - } + const defaultOptions = { + center: true, + scale: false + }; - for (i = 0; i < l; i++) { - var resultRow = result[i]; - var sourceRow = matrix[i]; - for (j = 0, jj = resultRow.length; j < jj; j++) { - if (standardDeviations[j] !== 0 && !isNaN(standardDeviations[j])) { - resultRow[j] = sourceRow[j] / standardDeviations[j]; - } - } - } - return result; - } + class PCA { + /** + * Creates new PCA (Principal Component Analysis) from the dataset + * @param {Matrix} dataset + * @param {Object} options - options for the PCA algorithm + * @param {boolean} reload - for load purposes + * @param {Object} model - for load purposes + * @constructor + * */ + constructor(dataset, options, reload, model) { + if (reload) { + this.center = model.center; + this.scale = model.scale; + this.means = model.means; + this.stdevs = model.stdevs; + this.U = Matrix.checkMatrix(model.U); + this.S = model.S; + } else { + options = Object.assign({}, defaultOptions, options); - function weightedVariance(matrix, weights) { - var means = mean(matrix); - var rows = matrix.length; - if (rows === 0) return []; - var cols = matrix[0].length; - var vari = new Array(cols); + this.center = !!options.center; + this.scale = !!options.scale; - for (var j = 0; j < cols; j++) { - var sum = 0; - var a = 0, b = 0; + dataset = new Matrix(dataset); - for (var i = 0; i < rows; i++) { - var z = matrix[i][j] - means[j]; - var w = weights[i]; + if (this.center) { + const means = mean(dataset); + const stdevs = this.scale ? stdev(dataset, means, true) : null; + this.means = means; + dataset.subRowVector(means); + if (this.scale) { + this.stdevs = stdevs; + dataset.divRowVector(stdevs); + } + } - sum += w * (z * z); - b += w; - a += w * w; - } + var covarianceMatrix = dataset.transpose().mmul(dataset).divS(dataset.rows - 1); + var target = new SVD(covarianceMatrix, { + computeLeftSingularVectors: true, + computeRightSingularVectors: true, + autoTranspose: false + }); - vari[j] = sum * (b / (b * b - a)); + this.U = target.leftSingularVectors; + this.S = target.diagonal; + } } - return vari; - } + /** + * Load a PCA model from JSON + * @oaram {Object} model + * @return {PCA} + */ + static load(model) { + if (model.name !== 'PCA') + throw new RangeError('Invalid model: ' + model.name); + return new PCA(null, null, true, model); + } - function weightedMean(matrix, weights, dimension) { - if (typeof(dimension) === 'undefined') { - dimension = 0; + /** + * Exports the current model to an Object + * @return {Object} model + */ + toJSON() { + return { + name: 'PCA', + center: this.center, + scale: this.scale, + means: this.means, + stdevs: this.stdevs, + U: this.U, + S: this.S, + }; } - var rows = matrix.length; - if (rows === 0) return []; - var cols = matrix[0].length, - means, i, ii, j, w, row; - if (dimension === 0) { - means = new Array(cols); - for (i = 0; i < cols; i++) { - means[i] = 0; - } - for (i = 0; i < rows; i++) { - row = matrix[i]; - w = weights[i]; - for (j = 0; j < cols; j++) { - means[j] += row[j] * w; - } - } - } else if (dimension === 1) { - means = new Array(rows); - for (i = 0; i < rows; i++) { - means[i] = 0; - } - for (j = 0; j < rows; j++) { - row = matrix[j]; - w = weights[j]; - for (i = 0; i < cols; i++) { - means[j] += row[i] * w; + /** + * Projects the dataset into new space of k dimensions. + * @param {Matrix} dataset + * @return {Matrix} dataset projected in the PCA space. + */ + predict(dataset) { + dataset = new Matrix(dataset); + + if (this.center) { + dataset.subRowVector(this.means); + if (this.scale) { + dataset.divRowVector(this.stdevs); } } - } else { - throw new Error('Invalid dimension'); + + return dataset.mmul(this.U); } - var weightSum = arrayStat.sum(weights); - if (weightSum !== 0) { - for (i = 0, ii = means.length; i < ii; i++) { - means[i] /= weightSum; + /** + * Returns the proportion of variance for each component. + * @return {[number]} + */ + getExplainedVariance() { + var sum = 0; + for (var i = 0; i < this.S.length; i++) { + sum += this.S[i]; } + return this.S.map(value => value / sum); } - return means; - } - function weightedCovariance(matrix, weights, means, dimension) { - dimension = dimension || 0; - means = means || weightedMean(matrix, weights, dimension); - var s1 = 0, s2 = 0; - for (var i = 0, ii = weights.length; i < ii; i++) { - s1 += weights[i]; - s2 += weights[i] * weights[i]; + /** + * Returns the cumulative proportion of variance. + * @return {[number]} + */ + getCumulativeVariance() { + var explained = this.getExplainedVariance(); + for (var i = 1; i < explained.length; i++) { + explained[i] += explained[i - 1]; + } + return explained; } - var factor = s1 / (s1 * s1 - s2); - return weightedScatter(matrix, weights, means, factor, dimension); - } - function weightedScatter(matrix, weights, means, factor, dimension) { - dimension = dimension || 0; - means = means || weightedMean(matrix, weights, dimension); - if (typeof(factor) === 'undefined') { - factor = 1; + /** + * Returns the Eigenvectors of the covariance matrix. + * @returns {Matrix} + */ + getEigenvectors() { + return this.U; } - var rows = matrix.length; - if (rows === 0) { - return [[]]; + + /** + * Returns the Eigenvalues (on the diagonal). + * @returns {[number]} + */ + getEigenvalues() { + return this.S; } - var cols = matrix[0].length, - cov, i, j, k, s; - if (dimension === 0) { - cov = new Array(cols); - for (i = 0; i < cols; i++) { - cov[i] = new Array(cols); - } - for (i = 0; i < cols; i++) { - for (j = i; j < cols; j++) { - s = 0; - for (k = 0; k < rows; k++) { - s += weights[k] * (matrix[k][j] - means[j]) * (matrix[k][i] - means[i]); - } - cov[i][j] = s * factor; - cov[j][i] = s * factor; - } - } - } else if (dimension === 1) { - cov = new Array(rows); - for (i = 0; i < rows; i++) { - cov[i] = new Array(rows); - } - for (i = 0; i < rows; i++) { - for (j = i; j < rows; j++) { - s = 0; - for (k = 0; k < cols; k++) { - s += weights[k] * (matrix[j][k] - means[j]) * (matrix[i][k] - means[i]); - } - cov[i][j] = s * factor; - cov[j][i] = s * factor; - } - } - } else { - throw new Error('Invalid dimension'); + /** + * Returns the standard deviations of the principal components + * @returns {[number]} + */ + getStandardDeviations() { + return this.S.map(x => Math.sqrt(x)); } - return cov; + /** + * Returns the loadings matrix + * @return {Matrix} + */ + getLoadings() { + return this.U.transpose(); + } } - module.exports = { - entropy: entropy, - mean: mean, - standardDeviation: standardDeviation, - variance: variance, - median: median, - mode: mode, - skewness: skewness, - kurtosis: kurtosis, - standardError: standardError, - covariance: covariance, - scatter: scatter, - correlation: correlation, - zScores: zScores, - center: center, - standardize: standardize, - weightedVariance: weightedVariance, - weightedMean: weightedMean, - weightedCovariance: weightedCovariance, - weightedScatter: weightedScatter - }; + module.exports = PCA; /***/ }, -/* 127 */ +/* 123 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - const measures = __webpack_require__(128); + const measures = __webpack_require__(124); class Performance { /** @@ -18127,7 +17135,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 128 */ +/* 124 */ /***/ function(module, exports) { 'use strict'; @@ -18271,7 +17279,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 129 */ +/* 125 */ /***/ function(module, exports) { "use strict"; @@ -18380,20 +17388,20 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 130 */ +/* 126 */ /***/ function(module, exports, __webpack_require__) { - module.exports = exports = __webpack_require__(131); - exports.kernel = __webpack_require__(132).kernel; + module.exports = exports = __webpack_require__(127); + exports.kernel = __webpack_require__(128).kernel; /***/ }, -/* 131 */ +/* 127 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - var kernel = __webpack_require__(132).kernel; - var getKernel = __webpack_require__(132).getKernel; + var kernel = __webpack_require__(128).kernel; + var getKernel = __webpack_require__(128).getKernel; /** * Parameters to implement function @@ -18622,7 +17630,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = SVM; /***/ }, -/* 132 */ +/* 128 */ /***/ function(module, exports) { 'use strict'; @@ -18701,22 +17709,22 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 133 */ +/* 129 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - module.exports = __webpack_require__(134); + module.exports = __webpack_require__(130); /***/ }, -/* 134 */ +/* 130 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; module.exports = KNN; - var KDTree = __webpack_require__(135).kdTree; + var KDTree = __webpack_require__(131).kdTree; var Distances = __webpack_require__(38); /** @@ -18849,7 +17857,7 @@ return /******/ (function(modules) { // webpackBootstrap }; /***/ }, -/* 135 */ +/* 131 */ /***/ function(module, exports) { 'use strict'; @@ -19315,23 +18323,23 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 136 */ +/* 132 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - module.exports = exports = __webpack_require__(137).NaiveBayes; - exports.separateClasses = __webpack_require__(137).separateClasses; + module.exports = exports = __webpack_require__(133).NaiveBayes; + exports.separateClasses = __webpack_require__(133).separateClasses; /***/ }, -/* 137 */ +/* 133 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; var Matrix = __webpack_require__(14); - var Stat = __webpack_require__(138); + var Stat = __webpack_require__(134); module.exports.NaiveBayes = NaiveBayes; module.exports.separateClasses = separateClasses; @@ -19508,17 +18516,17 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 138 */ +/* 134 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - exports.array = __webpack_require__(139); - exports.matrix = __webpack_require__(140); + exports.array = __webpack_require__(135); + exports.matrix = __webpack_require__(136); /***/ }, -/* 139 */ +/* 135 */ /***/ function(module, exports) { 'use strict'; @@ -19977,11 +18985,11 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 140 */ +/* 136 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - var arrayStat = __webpack_require__(139); + var arrayStat = __webpack_require__(135); // https://github.com/accord-net/framework/blob/development/Sources/Accord.Statistics/Tools.cs @@ -20503,22 +19511,22 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 141 */ +/* 137 */ /***/ function(module, exports, __webpack_require__) { - module.exports = exports = __webpack_require__(142); - exports.Utils = __webpack_require__(143); - exports.OPLS = __webpack_require__(144); + module.exports = exports = __webpack_require__(138); + exports.Utils = __webpack_require__(139); + exports.OPLS = __webpack_require__(140); /***/ }, -/* 142 */ +/* 138 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; var Matrix = __webpack_require__(14); - var Utils = __webpack_require__(143); + var Utils = __webpack_require__(139); class PLS { constructor(X, Y) { @@ -20744,7 +19752,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 143 */ +/* 139 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; @@ -20795,13 +19803,13 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 144 */ +/* 140 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; var Matrix = __webpack_require__(14); - var Utils = __webpack_require__(143); + var Utils = __webpack_require__(139); module.exports = OPLS; @@ -20879,13 +19887,13 @@ return /******/ (function(modules) { // webpackBootstrap }; /***/ }, -/* 145 */ +/* 141 */ /***/ function(module, exports, __webpack_require__) { - module.exports = __webpack_require__(146); + module.exports = __webpack_require__(142); /***/ }, -/* 146 */ +/* 142 */ /***/ function(module, exports) { 'use strict'; @@ -21077,24 +20085,24 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 147 */ +/* 143 */ /***/ function(module, exports, __webpack_require__) { - exports.agnes = __webpack_require__(148); - exports.diana = __webpack_require__(156); + exports.agnes = __webpack_require__(144); + exports.diana = __webpack_require__(152); //exports.birch = require('./birch'); //exports.cure = require('./cure'); //exports.chameleon = require('./chameleon'); /***/ }, -/* 148 */ +/* 144 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - var euclidean = __webpack_require__(149); - var ClusterLeaf = __webpack_require__(150); - var Cluster = __webpack_require__(151); + var euclidean = __webpack_require__(145); + var ClusterLeaf = __webpack_require__(146); + var Cluster = __webpack_require__(147); /** * @param cluster1 @@ -21326,7 +20334,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = agnes; /***/ }, -/* 149 */ +/* 145 */ /***/ function(module, exports) { 'use strict'; @@ -21348,13 +20356,13 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 150 */ +/* 146 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - var Cluster = __webpack_require__(151); - var util = __webpack_require__(152); + var Cluster = __webpack_require__(147); + var util = __webpack_require__(148); function ClusterLeaf (index) { Cluster.call(this); @@ -21369,7 +20377,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 151 */ +/* 147 */ /***/ function(module, exports) { 'use strict'; @@ -21440,7 +20448,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 152 */ +/* 148 */ /***/ function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(global, process) {// Copyright Joyent, Inc. and other Node contributors. @@ -21968,7 +20976,7 @@ return /******/ (function(modules) { // webpackBootstrap } exports.isPrimitive = isPrimitive; - exports.isBuffer = __webpack_require__(154); + exports.isBuffer = __webpack_require__(150); function objectToString(o) { return Object.prototype.toString.call(o); @@ -22012,7 +21020,7 @@ return /******/ (function(modules) { // webpackBootstrap * prototype. * @param {function} superCtor Constructor function to inherit prototype from. */ - exports.inherits = __webpack_require__(155); + exports.inherits = __webpack_require__(151); exports._extend = function(origin, add) { // Don't do anything if add isn't an object @@ -22030,10 +21038,10 @@ return /******/ (function(modules) { // webpackBootstrap return Object.prototype.hasOwnProperty.call(obj, prop); } - /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(153))) + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(149))) /***/ }, -/* 153 */ +/* 149 */ /***/ function(module, exports) { // shim for using process in browser @@ -22130,7 +21138,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 154 */ +/* 150 */ /***/ function(module, exports) { module.exports = function isBuffer(arg) { @@ -22141,7 +21149,7 @@ return /******/ (function(modules) { // webpackBootstrap } /***/ }, -/* 155 */ +/* 151 */ /***/ function(module, exports) { if (typeof Object.create === 'function') { @@ -22170,14 +21178,14 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 156 */ +/* 152 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - var euclidean = __webpack_require__(149); - var ClusterLeaf = __webpack_require__(150); - var Cluster = __webpack_require__(151); + var euclidean = __webpack_require__(145); + var ClusterLeaf = __webpack_require__(146); + var Cluster = __webpack_require__(147); /** * @param {Array >} cluster1 @@ -22466,13 +21474,13 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = diana; /***/ }, -/* 157 */ +/* 153 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - var NodeSquare = __webpack_require__(158), - NodeHexagonal = __webpack_require__(159); + var NodeSquare = __webpack_require__(154), + NodeHexagonal = __webpack_require__(155); var defaultOptions = { fields: 3, @@ -22892,7 +21900,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = SOM; /***/ }, -/* 158 */ +/* 154 */ /***/ function(module, exports) { function NodeSquare(x, y, weights, som) { @@ -23003,10 +22011,10 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = NodeSquare; /***/ }, -/* 159 */ +/* 155 */ /***/ function(module, exports, __webpack_require__) { - var NodeSquare = __webpack_require__(158); + var NodeSquare = __webpack_require__(154); function NodeHexagonal(x, y, weights, som) { @@ -23038,19 +22046,19 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = NodeHexagonal; /***/ }, -/* 160 */ +/* 156 */ /***/ function(module, exports, __webpack_require__) { - module.exports = __webpack_require__(161); + module.exports = __webpack_require__(157); /***/ }, -/* 161 */ +/* 157 */ /***/ function(module, exports, __webpack_require__) { "use strict"; - var Layer = __webpack_require__(162); + var Layer = __webpack_require__(158); var Matrix = __webpack_require__(14); module.exports = FeedforwardNeuralNetwork; @@ -23229,7 +22237,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 162 */ +/* 158 */ /***/ function(module, exports, __webpack_require__) { "use strict"; diff --git a/package.json b/package.json index 749d5e4..9bc7d7a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ml", - "version": "0.10.1", + "version": "0.11.0", "description": "Machine learning tools", "main": "src/index.js", "scripts": {