UNPKG

ts-arima-forecast

Version:

TypeScript library for ARIMA, SARIMA (soon), and SARIMAX (soon) forecasting

122 lines 4.39 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const statistics_1 = __importDefault(require("./statistics")); const helpers_1 = require("./helpers"); const difference = (data, order = 1) => { if (data.length === 0 || order < 0) return []; if (order === 0) return [...data]; let result = [...data]; for (let d = 0; d < order; d++) { if (result.length <= 1) break; const temp = []; for (let i = 1; i < result.length; i++) { temp.push(result[i] - result[i - 1]); } result = temp; } return result; }; const seasonalDifference = (data, seasonalPeriod, order = 1) => { if (data.length === 0 || order < 0 || seasonalPeriod <= 0) return []; if (order === 0) return [...data]; let result = [...data]; for (let d = 0; d < order; d++) { if (result.length <= seasonalPeriod) break; const temp = []; for (let i = seasonalPeriod; i < result.length; i++) { temp.push(result[i] - result[i - seasonalPeriod]); } result = temp; } return result; }; const undifference = (originalData, diffData, order = 1) => { if (diffData.length === 0 || order < 0) return []; if (order === 0) return [...diffData]; if (originalData.length < order) return []; let result = [...diffData]; for (let d = 0; d < order; d++) { const temp = []; temp.push(originalData[d]); for (let i = 0; i < result.length; i++) { temp.push(temp[temp.length - 1] + result[i]); } result = temp.slice(1); } return result; }; const checkStationarity = (0, helpers_1.memoize)((data, threshold = 0.95) => { if (data.length < 2) return true; // simple ADF test using autocorrelation const acf = statistics_1.default.autocorrelation(data, 1); return Math.abs(acf) < threshold; }); const createDifferencingPipeline = (orders) => (data) => orders.reduce((acc, order) => difference(acc, order), data); const createSeasonalDifferencingPipeline = (seasonalPeriods, orders = []) => (data) => { const orderArray = orders.length > 0 ? orders : new Array(seasonalPeriods.length).fill(1); return seasonalPeriods.reduce((acc, period, index) => seasonalDifference(acc, period, orderArray[index] || 1), data); }; const applyPreprocessing = (...steps) => (data) => steps.reduce((acc, step) => step(acc), data); // find optimal d for differencing (useful for non-stationary data) const findOptimalDifferencingOrder = (data, maxOrder = 3, threshold = 0.95) => { for (let order = 0; order <= maxOrder; order++) { const diffData = difference(data, order); if (checkStationarity(diffData, threshold)) { return order; } } return maxOrder; }; const findOptimalSeasonalPeriod = (data, candidatePeriods, threshold = 0.95) => { for (const period of candidatePeriods) { const seasonalDiffData = seasonalDifference(data, period); if (checkStationarity(seasonalDiffData, threshold)) { return period; } } return null; }; const compose = (...fns) => (value) => fns.reduceRight((acc, fn) => fn(acc), value); const createPreprocessor = (config) => { const { differenceOrder = 0, seasonalPeriod, seasonalOrder = 1, stationarityThreshold = 0.95 } = config; return (data) => { let result = [...data]; if (differenceOrder > 0) { result = difference(result, differenceOrder); } if (seasonalPeriod && seasonalPeriod > 0) { result = seasonalDifference(result, seasonalPeriod, seasonalOrder); } if (!checkStationarity(result, stationarityThreshold)) { throw new Error('Processed data may not be stationary'); } return result; }; }; exports.default = { difference, seasonalDifference, undifference, checkStationarity, createDifferencingPipeline, createSeasonalDifferencingPipeline, applyPreprocessing, findOptimalDifferencingOrder, findOptimalSeasonalPeriod, createPreprocessor, compose }; //# sourceMappingURL=preprocessing.js.map