UNPKG

locutus

Version:

Locutus other languages' standard libraries to JavaScript for fun and educational purposes

65 lines (64 loc) 2.98 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.correlation = correlation; const _statistics_ts_1 = require("../_helpers/_statistics.js"); function correlation(x, y, method = 'linear') { // discuss at: https://locutus.io/python/statistics/correlation/ // parity verified: Python 3.12 // original by: Kevin van Zonneveld (https://kvz.io) // note 1: Returns Pearson correlation by default and supports Python's ranked mode for Spearman correlation. // example 1: correlation([1, 2, 3], [1, 5, 7]) // returns 1: 0.9819805060619659 // example 2: correlation([1, 2, 3], [7, 5, 3]) // returns 2: -1 // example 3: correlation([1, 2, 3], [3, 2, 1], 'ranked') // returns 3: -1 const left = (0, _statistics_ts_1.assertStatisticsArray)(x, 'correlation').map((value) => (0, _statistics_ts_1.toStatisticNumber)(value, 'correlation')); const right = (0, _statistics_ts_1.assertStatisticsArray)(y, 'correlation').map((value) => (0, _statistics_ts_1.toStatisticNumber)(value, 'correlation')); const n = left.length; if (right.length !== n) { throw new Error('correlation requires that both inputs have same number of data points'); } if (n < 2) { throw new Error('correlation requires at least two data points'); } if (method !== 'linear' && method !== 'ranked') { throw new TypeError(`Unknown method: '${String(method)}'`); } const xValues = method === 'ranked' ? rankValues(left) : centerValues(left); const yValues = method === 'ranked' ? rankValues(right) : centerValues(right); const sxy = (0, _statistics_ts_1.sumProducts)(xValues, yValues); const sxx = (0, _statistics_ts_1.sumProducts)(xValues, xValues); const syy = (0, _statistics_ts_1.sumProducts)(yValues, yValues); const denominator = Math.sqrt(sxx * syy); if (denominator === 0) { throw new Error('at least one of the inputs is constant'); } return sxy / denominator; } function centerValues(values) { const mean = values.reduce((sum, value) => sum + value, 0) / values.length; return values.map((value) => value - mean); } function rankValues(values) { const n = values.length; const start = (n - 1) / -2; const indexed = values.map((value, index) => ({ value, index })).sort((a, b) => a.value - b.value); const ranks = new Array(n); let position = 0; while (position < indexed.length) { let end = position + 1; while (end < indexed.length && indexed[end]?.value === indexed[position]?.value) { end += 1; } const averageRank = start + (position + end - 1) / 2; for (let cursor = position; cursor < end; cursor += 1) { const entry = indexed[cursor]; if (entry) { ranks[entry.index] = averageRank; } } position = end; } return ranks; }