Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: homogenize and generalize output #63

Merged
merged 9 commits into from
Oct 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [10.x, 12.x, 14.x, 15.x]
node-version: [12.x, 14.x, 16.x]
fail-fast: false
steps:
- uses: actions/checkout@v2
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"eslint-fix": "npm run eslint -- --fix",
"compile": "rollup -c",
"prepack": "npm run compile",
"prettier": "prettier --check src",
"prettier-write": "prettier --write src",
"test": "npm run test-coverage && npm run eslint",
"test-only": "jest",
"test-coverage": "jest --coverage"
Expand Down Expand Up @@ -77,7 +79,7 @@
"cheminfo-types": "^0.5.0",
"ml-peak-shape-generator": "^2.0.2",
"ml-savitzky-golay-generalized": "2.0.3",
"ml-spectra-fitting": "^1.0.0",
"ml-spectra-fitting": "^2.0.0",
"ml-spectra-processing": "^6.8.0"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('Global spectra deconvolution NMR spectra', () => {
},
);

joinBroadPeaks(pp, { width: 0.25 });
pp = joinBroadPeaks(pp, { width: 0.25 });

expect(pp).toHaveLength(91);
});
Expand Down
8 changes: 4 additions & 4 deletions src/__tests__/broadNMR.js → src/__tests__/broadNMR.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ describe('Global spectra deconvolution NMR spectra', () => {
},
},
);
joinBroadPeaks(result, { width: 0.25, shape: { kind: 'lorentzian' } });
expect(result).toHaveLength(14);
result.forEach((peak) => {
const newResult = joinBroadPeaks(result, { width: 0.25, shape: { kind: 'lorentzian' } });
expect(newResult).toHaveLength(14);
newResult.forEach((peak) => {
if (Math.abs(peak.x - 4.31) < 0.01) {
expect(peak.width).toBeCloseTo(0.39, 2);
expect(peak.shape.width).toBeCloseTo(0.39, 2);
}
});
});
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
12 changes: 6 additions & 6 deletions src/__tests__/massPeakPicking.js → src/__tests__/massPeakPicking.test.js
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -43,26 +43,26 @@ describe('Check the peak picking of a simulated mass spectrum', () => {
});
expect(result[0].x).toBeCloseTo(69.938, 1);
expect(result[0].y).toBeCloseTo(max, 2);
expect(result[0].width).toBeCloseTo(0.01, 4);
expect(result[0].shape.width).toBeCloseTo(0.01, 4);

expect(result[1].x).toBeCloseTo(71.935, 2);
expect(result[1].y).toBeCloseTo((63.99155 * max) / 100, 3);
expect(result[1].width).toBeCloseTo(0.01, 4);
expect(result[1].shape.width).toBeCloseTo(0.01, 4);

expect(result[2].x).toBeCloseTo(73.932, 1);
expect(result[2].y).toBeCloseTo((10.2373 * max) / 100, 2);
expect(result[2].width).toBeCloseTo(0.01, 4);
expect(result[2].shape.width).toBeCloseTo(0.01, 4);

expect(result[3].x).toBeCloseTo(157.837, 1);
expect(result[3].y).toBeCloseTo((51.39931 * max) / 100, 2);
expect(result[3].width).toBeCloseTo(0.01, 4);
expect(result[3].shape.width).toBeCloseTo(0.01, 4);

expect(result[4].x).toBeCloseTo(159.835, 1);
expect(result[4].y).toBeCloseTo(max, 2);
expect(result[4].width).toBeCloseTo(0.01, 4);
expect(result[4].shape.width).toBeCloseTo(0.01, 4);

expect(result[5].x).toBeCloseTo(161.833, 1);
expect(result[5].y).toBeCloseTo((48.63878 * max) / 100, 2);
expect(result[5].width).toBeCloseTo(0.01, 4);
expect(result[5].shape.width).toBeCloseTo(0.01, 4);
});
});
39 changes: 12 additions & 27 deletions src/__tests__/simple.js → src/__tests__/simple.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { toMatchCloseTo } from 'jest-matcher-deep-close-to';
import { getShape1D } from 'ml-peak-shape-generator';

import { gsd } from '..';

Expand All @@ -25,6 +26,8 @@ describe('Simple test cases', () => {
negY.push(0);
}

let widthToFWHM = getShape1D('gaussian').widthToFWHM;

it('gsd not realtop', () => {
let peaks = gsd(
{ x, y },
Expand All @@ -39,8 +42,8 @@ describe('Simple test cases', () => {
);

expect(peaks[0].y).toBeCloseTo(4.657, 3);
expect(peaks[0].base).toBeCloseTo(1.1956, 3);
expect(peaks[0].x).toStrictEqual(15);
expect(peaks[0].shape.noiseLevel).toBeCloseTo(1.1956, 3);
expect(peaks[0].x).toBeCloseTo(15, 2);
});

it('gsd negative peak', () => {
Expand All @@ -57,8 +60,8 @@ describe('Simple test cases', () => {
},
);
expect(peaks[0].y).toBeCloseTo(-4.657, 3);
expect(peaks[0].base).toBeCloseTo(1.1956, 3);
expect(peaks[0].x).toStrictEqual(15);
expect(peaks[0].shape.noiseLevel).toBeCloseTo(1.1956, 3);
expect(peaks[0].x).toBeCloseTo(15,2);
});

it('gsd not realtop asymetric', () => {
Expand All @@ -79,18 +82,11 @@ describe('Simple test cases', () => {
expect(peaks).toMatchCloseTo(
[
{
base: 1.2434539324230613,
index: 15,
left: {
index: 13,
x: 13,
},
right: {
index: 16,
x: 16,
shape: {
noiseLevel: 1.2434539324230613,
soft: false,
width: widthToFWHM(3),
},
soft: false,
width: 3,
x: 15,
y: 5,
},
Expand All @@ -116,18 +112,7 @@ describe('Simple test cases', () => {
expect(peaks).toMatchCloseTo(
[
{
base: 1.2434539324230613,
index: 15,
left: {
index: 13,
x: 13,
},
right: {
index: 16,
x: 16,
},
soft: false,
width: 3,
shape: { noiseLevel: 1.2434539324230613, soft: false, width: widthToFWHM(3) },
x: 14.5,
y: 4.006546067576939,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { toMatchCloseTo } from 'jest-matcher-deep-close-to';
import { getShape1D } from 'ml-peak-shape-generator';

import { gsd } from '..';

Expand Down Expand Up @@ -37,16 +38,16 @@ describe('Simple shifted baseline test cases', () => {
},
},
);
let widthToFWHM = getShape1D('gaussian').widthToFWHM;
expect(peaks).toHaveLength(1);
expect(peaks[0]).toMatchCloseTo({
index: 15,
x: 15,
y: 4.657142857142857,
width: 2,
soft: false,
left: { x: 14, index: 14 },
right: { x: 16, index: 16 },
base: 0.6695521174585716,
shape: {
width: widthToFWHM(2),
soft: false,
noiseLevel: 0.6695521174585716,
},
});
});

Expand All @@ -63,16 +64,16 @@ describe('Simple shifted baseline test cases', () => {
},
},
);
let widthToFWHM = getShape1D('gaussian').widthToFWHM;
expect(peaks).toHaveLength(1);
expect(peaks[0]).toMatchCloseTo({
index: 15,
x: 15,
y: -4.657142857142857,
width: 2,
soft: false,
left: { x: 14, index: 14 },
right: { x: 16, index: 16 },
base: 0.6695521174585716,
shape: {
width: widthToFWHM(2),
soft: false,
noiseLevel: 0.6695521174585716,
},
});
});
});
61 changes: 11 additions & 50 deletions src/__tests__/simulated.js → src/__tests__/simulated.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,18 @@ describe('Global spectra deconvolution with simulated spectra', () => {

expect(peakList[0].x).toBeCloseTo(-0.1, 2);
expect(peakList[0].y).toBeCloseTo(0.2, 2);
expect(peakList[0].width).toBeCloseTo(0.03, 2);
expect(peakList[0].shape.width).toBeCloseTo(0.03, 2);
expect(peakList[1].x).toBeCloseTo(0.1, 2);
expect(peakList[1].y).toBeCloseTo(0.2, 2);
expect(peakList[1].width).toBeCloseTo(0.01, 2);
expect(peakList[1].shape.width).toBeCloseTo(0.01, 2);


expect(optimizedPeaks[0].x).toBeCloseTo(-0.1, 2);
expect(optimizedPeaks[0].y).toBeCloseTo(0.2, 2);
expect(optimizedPeaks[0].width).toBeCloseTo(0.03, 2);
expect(optimizedPeaks[0].group).toBe(0);
expect(optimizedPeaks[0].shape.width).toBeCloseTo(0.03, 2);
expect(optimizedPeaks[1].x).toBeCloseTo(0.1, 2);
expect(optimizedPeaks[1].y).toBeCloseTo(0.2, 2);
expect(optimizedPeaks[1].width).toBeCloseTo(0.01, 2);
expect(optimizedPeaks[1].group).toBe(1);
expect(optimizedPeaks[1].shape.width).toBeCloseTo(0.01, 2);
});

it('Check gaussian shapes with shape specification', () => {
Expand All @@ -62,62 +61,24 @@ describe('Global spectra deconvolution with simulated spectra', () => {
realTopDetection: false,
smoothY: false,
heightFactor: 1,
shape: { kind: 'gaussian' }, // we specifiy we are expecting a gaussian shape
});

expect(peakList[0].x).toBeCloseTo(-0.5, 2);
expect(peakList[0].y).toBeCloseTo(1, 2);
expect(peakList[0].width).toBeCloseTo(0.2, 2); // inflection points in gaussian are higher tha FWHM
expect(peakList[1].x).toBeCloseTo(0.5, 2);
expect(peakList[1].y).toBeCloseTo(1, 2);
expect(peakList[1].width).toBeCloseTo(0.1, 2);

let optimizedPeaks = optimizePeaks(data, peakList);

expect(optimizedPeaks[0].x).toBeCloseTo(-0.5, 2);
expect(optimizedPeaks[0].y).toBeCloseTo(1, 2);
expect(optimizedPeaks[0].width).toBeCloseTo(0.2, 2); // optimization by default expect a gaussian shape
expect(optimizedPeaks[0].group).toBe(0);
expect(optimizedPeaks[1].x).toBeCloseTo(0.5, 2);
expect(optimizedPeaks[1].y).toBeCloseTo(1, 2);
expect(optimizedPeaks[1].width).toBeCloseTo(0.1, 2);
expect(optimizedPeaks[1].group).toBe(1);
});

it('Check gaussian shapes without specifying shape', () => {
const peaks = [
{ x: -0.5, y: 1, width: 0.2 },
{ x: 0.5, y: 1, width: 0.1 },
];

const data = generateSpectrum(peaks, {
generator: { from: -1, to: 1, nbPoints: 10001 },
});

let peakList = gsd(data, {
minMaxRatio: 0,
realTopDetection: false,
smoothY: false,
heightFactor: 1,
shape: { kind: 'gaussian' },
});

expect(peakList[0].x).toBeCloseTo(-0.5, 2);
expect(peakList[0].y).toBeCloseTo(1, 2);
expect(peakList[0].width).toBeCloseTo(0.17, 2); // inflection points in gaussian are higher tha FWHM
expect(peakList[0].shape.width).toBeCloseTo(0.2, 2); // inflection points in gaussian are higher tha FWHM
expect(peakList[1].x).toBeCloseTo(0.5, 2);
expect(peakList[1].y).toBeCloseTo(1, 2);
expect(peakList[1].width).toBeCloseTo(0.085, 2);
expect(peakList[1].shape.width).toBeCloseTo(0.1, 2);

let optimizedPeaks = optimizePeaks(data, peakList);

expect(optimizedPeaks[0].x).toBeCloseTo(-0.5, 2);
expect(optimizedPeaks[0].y).toBeCloseTo(1, 2);
expect(optimizedPeaks[0].width).toBeCloseTo(0.2, 2); // optimization by default expect a gaussian shape
expect(optimizedPeaks[0].group).toBe(0);
expect(optimizedPeaks[0].shape.width).toBeCloseTo(0.2, 2); // optimization by default expect a gaussian shape
expect(optimizedPeaks[1].x).toBeCloseTo(0.5, 2);
expect(optimizedPeaks[1].y).toBeCloseTo(1, 2);
expect(optimizedPeaks[1].width).toBeCloseTo(0.1, 2);
expect(optimizedPeaks[1].group).toBe(1);
expect(optimizedPeaks[1].shape.width).toBeCloseTo(0.1, 2);
});

it('Should provide 1 peak', () => {
Expand Down Expand Up @@ -145,6 +106,6 @@ describe('Global spectra deconvolution with simulated spectra', () => {
expect(peakList).toHaveLength(1);
expect(peakList[0].x).toBeCloseTo(0, 2);
expect(peakList[0].y).toBeCloseTo(1, 2);
expect(peakList[0].width).toBeCloseTo(0.12, 3);
expect(peakList[0].shape.width).toBeCloseTo(0.12, 3);
});
});
File renamed without changes.
File renamed without changes.
21 changes: 9 additions & 12 deletions src/gsd.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import SG from 'ml-savitzky-golay-generalized';
* @param {object} [options={}] - Options object
* @param {object} [options.shape={}] - Object that specified the kind of shape to calculate the FWHM instead of width between inflection points. see https://mljs.github.io/peak-shape-generator/#inflectionpointswidthtofwhm
* @param {object} [options.shape.kind='gaussian']
* @param {object} [options.shape.options={}]
* @param {object} [options.sgOptions] - Options object for Savitzky-Golay filter. See https://github.com/mljs/savitzky-golay-generalized#options
* @param {number} [options.sgOptions.windowSize = 9] - points to use in the approximations
* @param {number} [options.sgOptions.polynomial = 3] - degree of the polynomial to use in the approximations
Expand All @@ -32,7 +31,7 @@ export function gsd(data, options = {}) {
windowSize: 9,
polynomial: 3,
},
shape = {},
shape = { kind: 'gaussian' },
smoothY = true,
heightFactor = 0,
broadRatio = 0.0,
Expand Down Expand Up @@ -170,9 +169,7 @@ export function gsd(data, options = {}) {
}
}

let widthProcessor = shape.kind
? getShape1D(shape.kind, shape.options).widthToFWHM
: (x) => x;
let widthProcessor = getShape1D(shape.kind, shape.options).widthToFWHM;

let signals = [];
let lastK = -1;
Expand Down Expand Up @@ -209,17 +206,17 @@ export function gsd(data, options = {}) {
y: maxCriteria
? yData[minddY[j]] + noiseLevel
: -yData[minddY[j]] - noiseLevel,
width: widthProcessor(width),
soft: broadMask[j],
shape: {
kind: shape.kind,
width: widthProcessor(width),
soft: broadMask[j],
},
});

signals[signals.length - 1].left = intervalL[possible];
signals[signals.length - 1].right = intervalR[possible];

if (heightFactor) {
let yLeft = yData[intervalL[possible].index];
let yRight = yData[intervalR[possible].index];
signals[signals.length - 1].height =
signals[signals.length - 1].shape.height =
heightFactor *
(signals[signals.length - 1].y - (yLeft + yRight) / 2);
}
Expand All @@ -233,7 +230,7 @@ export function gsd(data, options = {}) {

// Correct the values to fit the original spectra data
for (let j = 0; j < signals.length; j++) {
signals[j].base = noiseLevel;
signals[j].shape.noiseLevel = noiseLevel;
}

signals.sort((a, b) => {
Expand Down
Loading