Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ml-airpls

Package Overview
Dependencies
Maintainers
4
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ml-airpls - npm Package Compare versions

Comparing version 1.0.3 to 2.0.0

128

lib/index.js
'use strict';
var mlSpectraProcessing = require('ml-spectra-processing');
var cuthillMckee = require('cuthill-mckee');

@@ -358,5 +359,37 @@

function getControlPoints(x, y, options = {}) {
const { length } = x;
let { controlPoints = Int8Array.from({ length }).fill(0) } = options;
const { zones = [], weights = Float64Array.from({ length }).fill(1) } =
options;
if (x.length !== y.length) {
throw new RangeError('Y should match the length with X');
} else if (controlPoints.length !== x.length) {
throw new RangeError('controlPoints should match the length with X');
} else if (weights.length !== x.length) {
throw new RangeError('weights should match the length with X');
}
zones.forEach((range) => {
let indexFrom = getCloseIndex(x, range.from);
let indexTo = getCloseIndex(x, range.to);
if (indexFrom > indexTo) [indexFrom, indexTo] = [indexTo, indexFrom];
for (let i = indexFrom; i < indexTo; i++) {
controlPoints[i] = 1;
}
});
return {
weights:
'controlPoints' in options || zones.length > 0
? mlSpectraProcessing.xMultiply(weights, controlPoints)
: weights,
controlPoints,
};
}
/**
* Fit the baseline drift by iteratively changing weights of sum square error between the fitted baseline and original signals,
* for further information about the parameters you can get the [paper of airPLS](https://github.com/zmzhang/airPLS/blob/master/airPLS_manuscript.pdf)
* for further information about the parameters you can get the [paper of airPLS](https://github.com/zmzhang/airPLS/blob/main/airPLS_manuscript.pdf)
* @param {Array<number>} x - x axis data useful when control points or zones are submitted

@@ -369,41 +402,25 @@ * @param {Array<number>} y - Original data

* @param {number} [options.lambda = 100] - Factor of weights matrix in -> [I + lambda D'D]z = x
* @param {Array<number>} [options.controlPoints = []] - Array of x axis values to force that baseline cross those points.
* @param {Array<number>} [options.baseLineZones = []] - Array of x axis values (as from - to), to force that baseline cross those zones.
* @param {Array<number>} [options.controlPoints = []] - Array of 0|1 to force the baseline cross those points.
* @param {Array<number>} [options.zones = []] - Array of x axis values (as from - to), to force that baseline cross those zones.
* @returns {{corrected: Array<number>, error: number, iteration: number, baseline: Array<number>}}
*/
function airPLS(x, y, options = {}) {
let {
maxIterations = 100,
lambda = 100,
tolerance = 0.001,
weights = new Array(y.length).fill(1),
controlPoints = [],
baseLineZones = [],
} = options;
const { weights, controlPoints } = getControlPoints(x, y, options);
let { maxIterations = 100, lambda = 10, tolerance = 0.001 } = options;
if (controlPoints.length > 0) {
controlPoints.forEach((e, i, arr) => (arr[i] = getCloseIndex(x, e)));
}
if (baseLineZones.length > 0) {
baseLineZones.forEach((range) => {
let indexFrom = getCloseIndex(x, range.from);
let indexTo = getCloseIndex(x, range.to);
if (indexFrom > indexTo) [indexFrom, indexTo] = [indexTo, indexFrom];
for (let i = indexFrom; i < indexTo; i++) {
controlPoints.push(i);
}
});
}
let baseline, iteration;
let nbPoints = y.length;
let l = nbPoints - 1;
let sumNegDifferences = Number.MAX_SAFE_INTEGER;
let stopCriterion = tolerance * y.reduce((sum, e) => Math.abs(e) + sum, 0);
const corrected = Float64Array.from(y);
let stopCriterion = getStopCriterion(y, tolerance);
const { length } = y;
let { lowerTriangularNonZeros, permutationEncodedArray } = getDeltaMatrix(
nbPoints,
length,
lambda,
);
let threshold = 1;
const l = length - 1;
let prevNegSum = Number.MAX_SAFE_INTEGER;
for (

@@ -420,29 +437,36 @@ iteration = 0;

let cho = prepare(leftHandSide, nbPoints, permutationEncodedArray);
let cho = prepare(leftHandSide, length, permutationEncodedArray);
baseline = cho(rightHandSide);
sumNegDifferences = 0;
sumNegDifferences = applyCorrection(y, baseline, corrected);
if (iteration === 1) {
const { positive } = mlSpectraProcessing.xNoiseSanPlot(corrected);
threshold = positive;
} else {
const absChange = Math.abs(prevNegSum / sumNegDifferences);
if (absChange < 1.01 && absChange > 0.99) {
break;
}
}
let difference = y.map(calculateError);
let maxNegativeDiff = -1 * Number.MAX_SAFE_INTEGER;
prevNegSum = sumNegDifferences + 0;
for (let i = 1; i < l; i++) {
let diff = difference[i];
if (diff >= 0) {
const diff = corrected[i];
if (controlPoints[i] < 1 && Math.abs(diff) > threshold) {
weights[i] = 0;
} else {
weights[i] = Math.exp((iteration * diff) / sumNegDifferences);
if (maxNegativeDiff < diff) maxNegativeDiff = diff;
const factor = diff > 0 ? -1 : 1;
weights[i] = Math.exp(
(factor * (iteration * diff)) / Math.abs(sumNegDifferences),
);
}
}
let value = Math.exp((iteration * maxNegativeDiff) / sumNegDifferences);
weights[0] = value;
weights[l] = value;
controlPoints.forEach((i) => (weights[i] = value));
weights[0] = 1;
weights[l] = 1;
}
return {
corrected: y.map((e, i) => e - baseline[i]),
corrected,
baseline,

@@ -453,9 +477,19 @@ iteration,

function calculateError(e, i) {
let diff = e - baseline[i];
if (diff < 0) sumNegDifferences += diff;
return diff;
function applyCorrection(y, baseline, corrected) {
let sumNegDifferences = 0;
for (let i = 0; i < y.length; i++) {
let diff = y[i] - baseline[i];
if (diff < 0) sumNegDifferences += diff;
corrected[i] = diff;
}
return sumNegDifferences;
}
}
function getStopCriterion(y, tolerance) {
let sum = mlSpectraProcessing.xAbsoluteSum(y);
return tolerance * sum;
}
module.exports = airPLS;
{
"name": "ml-airpls",
"version": "1.0.3",
"version": "2.0.0",
"description": "Baseline correction using adaptive iteratively reweighted penalized least",

@@ -14,4 +14,5 @@ "main": "lib/index.js",

"scripts": {
"build": "cheminfo-build --entry src/index.js --root AirPLS",
"compile": "rollup -c",
"prepublishOnly": "npm run compile",
"prepack": "npm run compile",
"eslint": "eslint src",

@@ -39,13 +40,15 @@ "eslint-fix": "npm run eslint -- --fix",

"devDependencies": {
"@babel/plugin-transform-modules-commonjs": "^7.16.8",
"eslint": "^8.10.0",
"eslint-config-cheminfo": "^8.0.2",
"jest": "^29.5.0",
"@babel/plugin-transform-modules-commonjs": "^7.24.1",
"cheminfo-build": "^1.2.0",
"eslint": "^8.57.0",
"eslint-config-cheminfo": "^9.2.0",
"jest": "^29.7.0",
"jest-matcher-deep-close-to": "^3.0.2",
"prettier": "^2.5.1",
"rollup": "^3.22.0"
"prettier": "^3.2.5",
"rollup": "^4.17.2"
},
"dependencies": {
"cuthill-mckee": "^1.0.0"
"cuthill-mckee": "^1.0.0",
"ml-spectra-processing": "^14.5.0"
}
}

@@ -9,3 +9,3 @@ # airpls

It is an javascript implementation of [airpls](https://github.com/zmzhang/airPLS/blob/master/airPLS_manuscript.pdf) using cholesky decomposition and reverse Cuthill-Mckee method for reducing the bandwidth of sparse linear systems, obtaining a fast baseline fitter.
It is an javascript implementation of [airpls](https://github.com/zmzhang/airPLS/blob/main/airPLS_manuscript.pdf) using cholesky decomposition and reverse Cuthill-Mckee method for reducing the bandwidth of sparse linear systems, obtaining a fast baseline fitter.

@@ -12,0 +12,0 @@ ## Installation

@@ -0,7 +1,41 @@

import { xMultiply, xNoiseSanPlot, xAbsoluteSum } from 'ml-spectra-processing';
import cholesky from './choleskySolver';
import { updateSystem, getDeltaMatrix, getCloseIndex } from './utils';
function getControlPoints(x, y, options = {}) {
const { length } = x;
let { controlPoints = Int8Array.from({ length }).fill(0) } = options;
const { zones = [], weights = Float64Array.from({ length }).fill(1) } =
options;
if (x.length !== y.length) {
throw new RangeError('Y should match the length with X');
} else if (controlPoints.length !== x.length) {
throw new RangeError('controlPoints should match the length with X');
} else if (weights.length !== x.length) {
throw new RangeError('weights should match the length with X');
}
zones.forEach((range) => {
let indexFrom = getCloseIndex(x, range.from);
let indexTo = getCloseIndex(x, range.to);
if (indexFrom > indexTo) [indexFrom, indexTo] = [indexTo, indexFrom];
for (let i = indexFrom; i < indexTo; i++) {
controlPoints[i] = 1;
}
});
return {
weights:
'controlPoints' in options || zones.length > 0
? xMultiply(weights, controlPoints)
: weights,
controlPoints,
};
}
/**
* Fit the baseline drift by iteratively changing weights of sum square error between the fitted baseline and original signals,
* for further information about the parameters you can get the [paper of airPLS](https://github.com/zmzhang/airPLS/blob/master/airPLS_manuscript.pdf)
* for further information about the parameters you can get the [paper of airPLS](https://github.com/zmzhang/airPLS/blob/main/airPLS_manuscript.pdf)
* @param {Array<number>} x - x axis data useful when control points or zones are submitted

@@ -14,41 +48,25 @@ * @param {Array<number>} y - Original data

* @param {number} [options.lambda = 100] - Factor of weights matrix in -> [I + lambda D'D]z = x
* @param {Array<number>} [options.controlPoints = []] - Array of x axis values to force that baseline cross those points.
* @param {Array<number>} [options.baseLineZones = []] - Array of x axis values (as from - to), to force that baseline cross those zones.
* @param {Array<number>} [options.controlPoints = []] - Array of 0|1 to force the baseline cross those points.
* @param {Array<number>} [options.zones = []] - Array of x axis values (as from - to), to force that baseline cross those zones.
* @returns {{corrected: Array<number>, error: number, iteration: number, baseline: Array<number>}}
*/
export default function airPLS(x, y, options = {}) {
let {
maxIterations = 100,
lambda = 100,
tolerance = 0.001,
weights = new Array(y.length).fill(1),
controlPoints = [],
baseLineZones = [],
} = options;
const { weights, controlPoints } = getControlPoints(x, y, options);
let { maxIterations = 100, lambda = 10, tolerance = 0.001 } = options;
if (controlPoints.length > 0) {
controlPoints.forEach((e, i, arr) => (arr[i] = getCloseIndex(x, e)));
}
if (baseLineZones.length > 0) {
baseLineZones.forEach((range) => {
let indexFrom = getCloseIndex(x, range.from);
let indexTo = getCloseIndex(x, range.to);
if (indexFrom > indexTo) [indexFrom, indexTo] = [indexTo, indexFrom];
for (let i = indexFrom; i < indexTo; i++) {
controlPoints.push(i);
}
});
}
let baseline, iteration;
let nbPoints = y.length;
let l = nbPoints - 1;
let sumNegDifferences = Number.MAX_SAFE_INTEGER;
let stopCriterion = tolerance * y.reduce((sum, e) => Math.abs(e) + sum, 0);
const corrected = Float64Array.from(y);
let stopCriterion = getStopCriterion(y, tolerance);
const { length } = y;
let { lowerTriangularNonZeros, permutationEncodedArray } = getDeltaMatrix(
nbPoints,
length,
lambda,
);
let threshold = 1;
const l = length - 1;
let prevNegSum = Number.MAX_SAFE_INTEGER;
for (

@@ -65,29 +83,36 @@ iteration = 0;

let cho = cholesky(leftHandSide, nbPoints, permutationEncodedArray);
let cho = cholesky(leftHandSide, length, permutationEncodedArray);
baseline = cho(rightHandSide);
sumNegDifferences = 0;
sumNegDifferences = applyCorrection(y, baseline, corrected);
if (iteration === 1) {
const { positive } = xNoiseSanPlot(corrected);
threshold = positive;
} else {
const absChange = Math.abs(prevNegSum / sumNegDifferences);
if (absChange < 1.01 && absChange > 0.99) {
break;
}
}
let difference = y.map(calculateError);
let maxNegativeDiff = -1 * Number.MAX_SAFE_INTEGER;
prevNegSum = sumNegDifferences + 0;
for (let i = 1; i < l; i++) {
let diff = difference[i];
if (diff >= 0) {
const diff = corrected[i];
if (controlPoints[i] < 1 && Math.abs(diff) > threshold) {
weights[i] = 0;
} else {
weights[i] = Math.exp((iteration * diff) / sumNegDifferences);
if (maxNegativeDiff < diff) maxNegativeDiff = diff;
const factor = diff > 0 ? -1 : 1;
weights[i] = Math.exp(
(factor * (iteration * diff)) / Math.abs(sumNegDifferences),
);
}
}
let value = Math.exp((iteration * maxNegativeDiff) / sumNegDifferences);
weights[0] = value;
weights[l] = value;
controlPoints.forEach((i) => (weights[i] = value));
weights[0] = 1;
weights[l] = 1;
}
return {
corrected: y.map((e, i) => e - baseline[i]),
corrected,
baseline,

@@ -98,7 +123,17 @@ iteration,

function calculateError(e, i) {
let diff = e - baseline[i];
if (diff < 0) sumNegDifferences += diff;
return diff;
function applyCorrection(y, baseline, corrected) {
let sumNegDifferences = 0;
for (let i = 0; i < y.length; i++) {
let diff = y[i] - baseline[i];
if (diff < 0) sumNegDifferences += diff;
corrected[i] = diff;
}
return sumNegDifferences;
}
}
function getStopCriterion(y, tolerance) {
let sum = xAbsoluteSum(y);
return tolerance * sum;
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc