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

ml-spectra-processing

Package Overview
Dependencies
Maintainers
6
Versions
150
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ml-spectra-processing - npm Package Compare versions

Comparing version 1.2.0 to 2.0.0

src/util/index.js

18

History.md

@@ -0,1 +1,19 @@

# [2.0.0](https://github.com/cheminfo/spectra-processing/compare/v1.3.1...v2.0.0) (2020-02-08)
### Bug Fixes
* eslint ([af497eb](https://github.com/cheminfo/spectra-processing/commit/af497eb5fd8e20e2b9111a174b8259894f240617))
* use Number.NEGATIVE_INFINITY and not Number.MIN_VALUE ([06827c1](https://github.com/cheminfo/spectra-processing/commit/06827c1b58f4fa75263e05a839c798130b430474))
### Features
* add XY.extract ([5dae851](https://github.com/cheminfo/spectra-processing/commit/5dae851b16653b97b072a7da5a871f9a0e8eb4c1))
* add zones ([bbe4a2f](https://github.com/cheminfo/spectra-processing/commit/bbe4a2f473d758fc4dc001d318a01e162a4b40ea))
* add zones in reduce ([9a2c573](https://github.com/cheminfo/spectra-processing/commit/9a2c573b5b3f7f10399e443b2cf9d2da442922c2))
* normalizeZones allow from / to ([a7e0762](https://github.com/cheminfo/spectra-processing/commit/a7e0762731a6919dbe56009a5590e6e9d5049645))
# [1.2.0](https://github.com/cheminfo/spectra-processing/compare/v1.1.0...v1.2.0) (2019-12-13)

@@ -2,0 +20,0 @@

367

lib/index.js

@@ -21,2 +21,91 @@ 'use strict';

/**
* Normalize an array of zones:
* - ensure than from < to
* - merge overlapping zones
* @param {object} [zones=[]]
* @param {object} [options={}]
* @param {number} [options.from=Number.MIN_VALUE]
* @param {number} [options.to=Number.MAX_VALUE]
*/
function normalizeZones(zones = [], options = {}) {
if (zones.length === 0) return [];
zones = JSON.parse(JSON.stringify(zones)).map((zone) =>
zone.from > zone.to ? { from: zone.to, to: zone.from } : zone,
);
let {
from = Number.NEGATIVE_INFINITY,
to = Number.POSITIVE_INFINITY,
} = options;
if (from > to) {
[from, to] = [to, from];
}
zones = zones.sort((a, b) => {
if (a.from !== b.from) return a.from - b.from;
return a.to - b.to;
});
zones.forEach((zone) => {
if (from > zone.from) zone.from = from;
if (to < zone.to) zone.to = to;
});
zones = zones.filter((zone) => zone.from <= zone.to);
if (zones.length === 0) return [];
let currentZone = zones[0];
let result = [currentZone];
for (let i = 1; i < zones.length; i++) {
let zone = zones[i];
if (zone.from <= currentZone.to) {
currentZone.to = zone.to;
} else {
currentZone = zone;
result.push(currentZone);
}
}
return result;
}
/**
* Extract zones from a XY data
* @param {object} [points={}] - Object of points contains property x (an ordered increasing array) and y (an array)
* @param {object} [options={}]
* @param {Array} [options.zones=[]]
* @return {Array} Array of points
*/
function extract(points = {}, options = {}) {
check(points);
const { x, y } = points;
let { zones } = options;
zones = normalizeZones(zones);
if (!Array.isArray(zones) || zones.length === 0) return points;
let newX = [];
let newY = [];
let currentZone = zones[0];
let position = 0;
loop: for (let i = 0; i < x.length; i++) {
while (currentZone.to < x[i]) {
position++;
currentZone = zones[position];
if (!currentZone) {
i = x.length;
break loop;
}
}
if (x[i] >= currentZone.from) {
newX.push(x[i]);
newY.push(y[i]);
}
}
return { x: newX, y: newY };
}
/**
* Returns the closest index of a `target` in an ordered array

@@ -232,4 +321,3 @@ * @param {array} array

* display many spectra as SVG
* @param {array} x
* @param {array} y
* @param {object} [points={}] - Object of points contains property x (an ordered increasing array) and y (an array)
* @param {object} [options={}]

@@ -239,6 +327,9 @@ * @param {number} [options.from=x[0]]

* @param {number} [options.nbPoints=4001] Number of points
* @param {number} [options.zones=[]] Array of zones to keep (from/to object)
* @param {number} [options.optimize=false] If optimize we may have less than nbPoints at the end
*/
function reduce(x, y, options = {}) {
function reduce(points, options = {}) {
check(points);
const { x, y } = points;
let {

@@ -249,44 +340,104 @@ from = x[0],

optimize = false,
zones = [],
} = options;
let fromIndex = findClosestIndex(x, from);
let toIndex = findClosestIndex(x, to);
zones = normalizeZones(zones, { from, to });
if (zones.length === 0) zones = [{ from, to }]; // we take everything
if (fromIndex > 0 && x[fromIndex] > from) fromIndex--;
if (toIndex < x.length - 1 && x[toIndex] < to) toIndex++;
// for each zone we should know the first index, the last index and the number of points
if (toIndex - fromIndex < nbPoints) {
let totalPoints = 0;
for (let zone of zones) {
zone.fromIndex = findClosestIndex(x, zone.from);
zone.toIndex = findClosestIndex(x, zone.to);
if (zone.fromIndex > 0 && x[zone.fromIndex] > zone.from) {
zone.fromIndex--;
}
if (zone.toIndex < x.length - 1 && x[zone.toIndex] < zone.to) {
zone.toIndex++;
}
zone.nbPoints = zone.toIndex - zone.fromIndex + 1;
totalPoints += zone.nbPoints;
}
// we calculate the number of points per zone that we should keep
if (totalPoints > nbPoints) {
// need to reduce number of points
let ratio = nbPoints / totalPoints;
let currentTotal = 0;
for (let i = 0; i < zones.length - 1; i++) {
const zone = zones[i];
zone.nbPoints = Math.round(zone.nbPoints * ratio);
currentTotal += zone.nbPoints;
}
zones[zones.length - 1].nbPoints = nbPoints - currentTotal;
} else {
let newX = new Float64Array(totalPoints);
let newY = new Float64Array(totalPoints);
let index = 0;
for (let zone of zones) {
for (let i = zone.fromIndex; i < zone.toIndex + 1; i++) {
newX[index] = x[i];
newY[index] = y[i];
index++;
}
}
return {
x: x.slice(fromIndex, toIndex + 1),
y: y.slice(fromIndex, toIndex + 1),
x: newX,
y: newY,
};
}
let newX = [x[fromIndex]];
let newY = [y[fromIndex]];
let minY = Number.MAX_VALUE;
let maxY = Number.MIN_VALUE;
if (nbPoints % 2 === 0) {
nbPoints = nbPoints / 2 + 1;
} else {
nbPoints = (nbPoints - 1) / 2 + 1;
let newX = [];
let newY = [];
for (let zone of zones) {
if (!zone.nbPoints) continue;
appendFromTo(zone.fromIndex, zone.toIndex, zone.nbPoints);
}
return { x: newX, y: newY };
let slot = (x[toIndex] - x[fromIndex]) / (nbPoints - 1);
let currentX = x[fromIndex] + slot;
let first = true;
for (let i = fromIndex + 1; i <= toIndex; i++) {
if (first) {
minY = y[i];
maxY = y[i];
first = false;
function appendFromTo(fromIndex, toIndex, zoneNbPoints) {
if (zoneNbPoints === 1) {
newX.push(x[Math.round((toIndex - fromIndex) / 2)]);
newY.push(y[Math.round((toIndex - fromIndex) / 2)]);
return;
}
if (zoneNbPoints === 2) {
newX.push(x[fromIndex], x[toIndex]);
newY.push(y[fromIndex], y[toIndex]);
return;
}
newX.push(x[fromIndex]);
newY.push(y[fromIndex]);
let minY = Number.MAX_VALUE;
let maxY = Number.MIN_VALUE;
if (zoneNbPoints % 2 === 0) {
zoneNbPoints = zoneNbPoints / 2 + 1;
} else {
if (y[i] < minY) minY = y[i];
if (y[i] > maxY) maxY = y[i];
zoneNbPoints = (zoneNbPoints - 1) / 2 + 1;
}
if (x[i] >= currentX || i === toIndex) {
if (optimize) {
if (minY > newY[newX.length - 1]) ; else if (maxY < newY[newX.length - 1]) {
// we can skip the intermediate value
maxY = minY;
// we will need to make some kind of min / max because there are too many points
// we will always keep the first point and the last point
let slot = (x[toIndex] - x[fromIndex]) / (zoneNbPoints - 1);
let currentX = x[fromIndex] + slot;
let first = true;
for (let i = fromIndex + 1; i <= toIndex; i++) {
if (first) {
minY = y[i];
maxY = y[i];
first = false;
} else {
if (y[i] < minY) minY = y[i];
if (y[i] > maxY) maxY = y[i];
}
if (x[i] >= currentX || i === toIndex) {
if (optimize) {
if (minY > newY[newX.length - 1]) ; else if (maxY < newY[newX.length - 1]) {
// we can skip the intermediate value
maxY = minY;
} else {
newX.push(currentX - slot / 2);
newY.push(minY);
}
} else {

@@ -296,19 +447,11 @@ newX.push(currentX - slot / 2);

}
} else {
newX.push(currentX - slot / 2);
newY.push(minY);
}
newX.push(currentX);
newY.push(maxY);
newX.push(currentX);
newY.push(maxY);
currentX += slot;
first = true;
currentX += slot;
first = true;
}
}
}
// we will need to make some kind of min / max because there are too many points
// we will always keep the first point and the last point
return { x: newX, y: newY };
}

@@ -653,2 +796,3 @@

check,
extract,
integral,

@@ -877,2 +1021,92 @@ integration,

/**
* This function multiply the first array by the second array or a constant value to each element of the first array
* @param {Array} array1 - the array that will be rotated
* @param {Array|Number} array2
* @return {Float64Array}
*/
function multiply(array1, array2) {
let isConstant = false;
let constant;
if (Array.isArray(array2)) {
if (array1.length !== array2.length) {
throw new Error('sub: size of array1 and array2 must be identical');
}
} else {
isConstant = true;
constant = Number(array2);
}
let array3 = new Float64Array(array1.length);
if (isConstant) {
for (let i = 0; i < array1.length; i++) {
array3[i] = array1[i] * constant;
}
} else {
for (let i = 0; i < array1.length; i++) {
array3[i] = array1[i] * array2[i];
}
}
return array3;
}
function dotProduct(A, B) {
let g = multiply(A, B);
let result = 0;
for (let i = 0; i < A.length; i++) {
result += g[i];
}
return result;
}
/**
* Calculates the cross-correlation between 2 vectors
* @param {Array} [A] - fixed array
* @param {Array} [B] - sweeping array
* @param {object} [options={}]
* @param {number} [options.tau = 1] - sweep increment size (in number of points, min = 1, max = A.length)
* @param {number} [options.lag = A.length - 1] - scalar lag parameter
*/
function crossCorrelation(A, B, options = {}) {
let { tau = 1, lag = A.length - 1 } = options;
let result = new Float64Array(1 + (2 * lag) / tau);
if (A.length === B.length) {
let n = B.length;
let g = new Float64Array(2 * n);
let q = new Float64Array(2 * n);
for (let i = 0; i < n; i++) {
q[n + i] = B[i];
}
for (let i = n * 2 - (tau - 1); i > 0; i -= tau) {
let k = 0;
for (let j = i; j < n * 2; j++) {
g[k] = q[j];
k++;
}
let w = [];
for (let l = 0; l < n; l++) {
w[l] = g[l];
}
result[(k - (n - lag)) / tau] = dotProduct(A, w);
}
}
return result;
}
/**
* Calculates the auto-correlation of a vector
* @param {Array} [A] - the array that will be fixed
* @param {object} [options={}]
* @param {number} [options.tau = 1] - sweep increment size (in number of points, min = 1, max = A.length)
* @param {number} [options.lag = A.length - 1] - scalar lag parameter
*/
function autoCorrelation(A, options = {}) {
return crossCorrelation(A, A, options);
}
/**
/**
* Calculates the correlation between 2 vectors

@@ -942,36 +1176,2 @@ * https://en.wikipedia.org/wiki/Correlation_and_dependence

/**
/**
* This function multiply the first array by the second array or a constant value to each element of the first array
* @param {Array} array1 - the array that will be rotated
* @param {Array|Number} array2
* @return {Array}
*/
function multiply(array1, array2) {
let isConstant = false;
let constant;
if (Array.isArray(array2)) {
if (array1.length !== array2.length) {
throw new Error('sub: size of array1 and array2 must be identical');
}
} else {
isConstant = true;
constant = Number(array2);
}
let array3 = new Array(array1.length);
if (isConstant) {
for (let i = 0; i < array1.length; i++) {
array3[i] = array1[i] * constant;
}
} else {
for (let i = 0; i < array1.length; i++) {
array3[i] = array1[i] * array2[i];
}
}
return array3;
}
/**
* This function performs a circular shift to a new array

@@ -1027,4 +1227,6 @@ * Positive values of shifts will shift to the right and negative values will do to the left

add,
autoCorrelation,
boxPlot,
correlation,
crossCorrelation,
divide,

@@ -1128,3 +1330,8 @@ findClosestIndex,

const Util = {
normalizeZones,
};
exports.ReIm = ReIm;
exports.Util = Util;
exports.X = X;

@@ -1131,0 +1338,0 @@ exports.XReIm = XReIm;

{
"name": "ml-spectra-processing",
"version": "1.2.0",
"version": "2.0.0",
"description": "Various method to process spectra",

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

@@ -6,1 +6,2 @@ export { XY } from './xy/index.js';

export { XYObject } from './xyObject/index.js';
export { Util } from './util/index.js';
import { add } from './add';
import { boxPlot } from './boxPlot';
import { autoCorrelation } from './autoCorrelation';
import { crossCorrelation } from './crossCorrelation';
import { correlation } from './correlation';

@@ -14,4 +16,6 @@ import { divide } from './divide';

add,
autoCorrelation,
boxPlot,
correlation,
crossCorrelation,
divide,

@@ -18,0 +22,0 @@ findClosestIndex,

@@ -7,3 +7,3 @@ /**

* @param {Array|Number} array2
* @return {Array}
* @return {Float64Array}
*/

@@ -22,3 +22,3 @@ export function multiply(array1, array2) {

let array3 = new Array(array1.length);
let array3 = new Float64Array(array1.length);
if (isConstant) {

@@ -25,0 +25,0 @@ for (let i = 0; i < array1.length; i++) {

import { check } from './check';
import { extract } from './extract';
import { integration } from './integration';

@@ -20,2 +21,3 @@ import { integral } from './integral';

check,
extract,
integral,

@@ -22,0 +24,0 @@ integration,

import { findClosestIndex } from '../x/findClosestIndex';
import { normalizeZones } from '../util/normalizeZones';
import { check } from './check';
/**
* Reduce the number of points while keeping visually the same noise. Practical to
* display many spectra as SVG
* @param {array} x
* @param {array} y
* @param {object} [points={}] - Object of points contains property x (an ordered increasing array) and y (an array)
* @param {object} [options={}]

@@ -12,6 +13,9 @@ * @param {number} [options.from=x[0]]

* @param {number} [options.nbPoints=4001] Number of points
* @param {number} [options.zones=[]] Array of zones to keep (from/to object)
* @param {number} [options.optimize=false] If optimize we may have less than nbPoints at the end
*/
export function reduce(x, y, options = {}) {
export function reduce(points, options = {}) {
check(points);
const { x, y } = points;
let {

@@ -22,46 +26,106 @@ from = x[0],

optimize = false,
zones = [],
} = options;
let fromIndex = findClosestIndex(x, from);
let toIndex = findClosestIndex(x, to);
zones = normalizeZones(zones, { from, to });
if (zones.length === 0) zones = [{ from, to }]; // we take everything
if (fromIndex > 0 && x[fromIndex] > from) fromIndex--;
if (toIndex < x.length - 1 && x[toIndex] < to) toIndex++;
// for each zone we should know the first index, the last index and the number of points
if (toIndex - fromIndex < nbPoints) {
let totalPoints = 0;
for (let zone of zones) {
zone.fromIndex = findClosestIndex(x, zone.from);
zone.toIndex = findClosestIndex(x, zone.to);
if (zone.fromIndex > 0 && x[zone.fromIndex] > zone.from) {
zone.fromIndex--;
}
if (zone.toIndex < x.length - 1 && x[zone.toIndex] < zone.to) {
zone.toIndex++;
}
zone.nbPoints = zone.toIndex - zone.fromIndex + 1;
totalPoints += zone.nbPoints;
}
// we calculate the number of points per zone that we should keep
if (totalPoints > nbPoints) {
// need to reduce number of points
let ratio = nbPoints / totalPoints;
let currentTotal = 0;
for (let i = 0; i < zones.length - 1; i++) {
const zone = zones[i];
zone.nbPoints = Math.round(zone.nbPoints * ratio);
currentTotal += zone.nbPoints;
}
zones[zones.length - 1].nbPoints = nbPoints - currentTotal;
} else {
let newX = new Float64Array(totalPoints);
let newY = new Float64Array(totalPoints);
let index = 0;
for (let zone of zones) {
for (let i = zone.fromIndex; i < zone.toIndex + 1; i++) {
newX[index] = x[i];
newY[index] = y[i];
index++;
}
}
return {
x: x.slice(fromIndex, toIndex + 1),
y: y.slice(fromIndex, toIndex + 1),
x: newX,
y: newY,
};
}
let newX = [x[fromIndex]];
let newY = [y[fromIndex]];
let minY = Number.MAX_VALUE;
let maxY = Number.MIN_VALUE;
if (nbPoints % 2 === 0) {
nbPoints = nbPoints / 2 + 1;
} else {
nbPoints = (nbPoints - 1) / 2 + 1;
let newX = [];
let newY = [];
for (let zone of zones) {
if (!zone.nbPoints) continue;
appendFromTo(zone.fromIndex, zone.toIndex, zone.nbPoints);
}
return { x: newX, y: newY };
let slot = (x[toIndex] - x[fromIndex]) / (nbPoints - 1);
let currentX = x[fromIndex] + slot;
let first = true;
for (let i = fromIndex + 1; i <= toIndex; i++) {
if (first) {
minY = y[i];
maxY = y[i];
first = false;
function appendFromTo(fromIndex, toIndex, zoneNbPoints) {
if (zoneNbPoints === 1) {
newX.push(x[Math.round((toIndex - fromIndex) / 2)]);
newY.push(y[Math.round((toIndex - fromIndex) / 2)]);
return;
}
if (zoneNbPoints === 2) {
newX.push(x[fromIndex], x[toIndex]);
newY.push(y[fromIndex], y[toIndex]);
return;
}
newX.push(x[fromIndex]);
newY.push(y[fromIndex]);
let minY = Number.MAX_VALUE;
let maxY = Number.MIN_VALUE;
if (zoneNbPoints % 2 === 0) {
zoneNbPoints = zoneNbPoints / 2 + 1;
} else {
if (y[i] < minY) minY = y[i];
if (y[i] > maxY) maxY = y[i];
zoneNbPoints = (zoneNbPoints - 1) / 2 + 1;
}
if (x[i] >= currentX || i === toIndex) {
if (optimize) {
if (minY > newY[newX.length - 1]) {
// we can skip the intermediate value
} else if (maxY < newY[newX.length - 1]) {
// we can skip the intermediate value
maxY = minY;
// we will need to make some kind of min / max because there are too many points
// we will always keep the first point and the last point
let slot = (x[toIndex] - x[fromIndex]) / (zoneNbPoints - 1);
let currentX = x[fromIndex] + slot;
let first = true;
for (let i = fromIndex + 1; i <= toIndex; i++) {
if (first) {
minY = y[i];
maxY = y[i];
first = false;
} else {
if (y[i] < minY) minY = y[i];
if (y[i] > maxY) maxY = y[i];
}
if (x[i] >= currentX || i === toIndex) {
if (optimize) {
if (minY > newY[newX.length - 1]) {
// we can skip the intermediate value
} else if (maxY < newY[newX.length - 1]) {
// we can skip the intermediate value
maxY = minY;
} else {
newX.push(currentX - slot / 2);
newY.push(minY);
}
} else {

@@ -71,19 +135,11 @@ newX.push(currentX - slot / 2);

}
} else {
newX.push(currentX - slot / 2);
newY.push(minY);
}
newX.push(currentX);
newY.push(maxY);
newX.push(currentX);
newY.push(maxY);
currentX += slot;
first = true;
currentX += slot;
first = true;
}
}
}
// we will need to make some kind of min / max because there are too many points
// we will always keep the first point and the last point
return { x: newX, y: newY };
}
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