ts-arima-forecast
Version:
TypeScript library for ARIMA, SARIMA (soon), and SARIMAX (soon) forecasting
122 lines • 4.39 kB
JavaScript
;
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