Comparing version 4.0.1 to 5.0.0
@@ -1,33 +0,19 @@ | ||
export interface DataXY { | ||
x: number[]; | ||
y: number[]; | ||
} | ||
export interface ParseXYOptions { | ||
rescale?: boolean; | ||
uniqueX?: boolean; | ||
xColumn?: number; | ||
yColumn?: number; | ||
keepInfo?: boolean; | ||
bestGuess?: boolean; | ||
numberColumns?: number; | ||
maxNumberColumns?: number; | ||
minNumberColumns?: number; | ||
} | ||
import { ParseXYOptions } from './ParseXYOptions'; | ||
export * from './ParseXYOptions'; | ||
/** | ||
* Parse a text-file and convert it to an array of XY points. | ||
* Parse a text-file and convert it to an object {x:[], y:[]} | ||
* | ||
* @param text - Csv or tsv strings. | ||
* @param [options={}] - | ||
* @param [options.rescale = false] - Will set the maximum value to 1. | ||
* @param [options.uniqueX = false] - Make the X values unique (works only with 'xxyy' format). If the X value is repeated the sum of Y is done. | ||
* @param [options.xColumn = 0] - A number that specifies the x column. | ||
* @param [options.yColumn = 1] - A number that specifies the y column. | ||
* @param [options.bestGuess=false] - Will try to guess which columns are the best. | ||
* @param [options.numberColumns=Number.MAX_SAFE_INTEGER] - If the file has 10 columns and you specify here 2 it will reflow the file. | ||
* @param [options.maxNumberColumns = (Math.max(xColumn, yColumn)+1)] - A number that specifies the maximum number of y columns. | ||
* @param [options.minNumberColumns = (Math.min(xColumn, yColumn)+1)] - A number that specifies the minimum number of y columns. | ||
* @param [options.keepInfo = false] - Should we keep the non numeric lines. In this case the system will return an object {data, info}. | ||
* @returns - | ||
* @param options - Parsing options | ||
* @returns - The parsed data | ||
*/ | ||
export declare function parseXY(text: string, options?: ParseXYOptions): { | ||
export declare function parseXY(text: string, options?: ParseXYOptions): import("cheminfo-types").DataXY<import("cheminfo-types").DoubleArray>; | ||
/** | ||
* Parse a text-file and returns the parsed data and information about the columns | ||
* | ||
* @param text - Csv or tsv strings. | ||
* @param options - Parsing options | ||
* @returns - The parsed data with information about the columns | ||
*/ | ||
export declare function parseXYAndKeepInfo(text: string, options?: ParseXYOptions): { | ||
info: { | ||
@@ -37,4 +23,4 @@ position: number; | ||
}[]; | ||
data: DataXY; | ||
} | DataXY; | ||
data: import("cheminfo-types").DataXY<import("cheminfo-types").DoubleArray>; | ||
}; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -1,107 +0,23 @@ | ||
import { ensureString } from 'ensure-string'; | ||
import mlArrayMax from 'ml-array-max'; | ||
import uniqueXFunction from 'ml-arrayxy-uniquex'; | ||
import { xIsMonotone } from 'ml-spectra-processing'; | ||
import { parse } from './parse'; | ||
export * from './ParseXYOptions'; | ||
/** | ||
* Parse a text-file and convert it to an array of XY points. | ||
* Parse a text-file and convert it to an object {x:[], y:[]} | ||
* | ||
* @param text - Csv or tsv strings. | ||
* @param [options={}] - | ||
* @param [options.rescale = false] - Will set the maximum value to 1. | ||
* @param [options.uniqueX = false] - Make the X values unique (works only with 'xxyy' format). If the X value is repeated the sum of Y is done. | ||
* @param [options.xColumn = 0] - A number that specifies the x column. | ||
* @param [options.yColumn = 1] - A number that specifies the y column. | ||
* @param [options.bestGuess=false] - Will try to guess which columns are the best. | ||
* @param [options.numberColumns=Number.MAX_SAFE_INTEGER] - If the file has 10 columns and you specify here 2 it will reflow the file. | ||
* @param [options.maxNumberColumns = (Math.max(xColumn, yColumn)+1)] - A number that specifies the maximum number of y columns. | ||
* @param [options.minNumberColumns = (Math.min(xColumn, yColumn)+1)] - A number that specifies the minimum number of y columns. | ||
* @param [options.keepInfo = false] - Should we keep the non numeric lines. In this case the system will return an object {data, info}. | ||
* @returns - | ||
* @param options - Parsing options | ||
* @returns - The parsed data | ||
*/ | ||
export function parseXY(text, options = {}) { | ||
let { rescale = false, uniqueX = false, xColumn = 0, yColumn = 1, keepInfo = false, bestGuess = false, numberColumns = Number.MAX_SAFE_INTEGER, maxNumberColumns = Number.MAX_SAFE_INTEGER, minNumberColumns = 2, } = options; | ||
text = ensureString(text); | ||
maxNumberColumns = Math.max(maxNumberColumns, xColumn + 1, yColumn + 1); | ||
minNumberColumns = Math.max(xColumn + 1, yColumn + 1, minNumberColumns); | ||
let lines = text.split(/[\r\n]+/); | ||
let matrix = []; | ||
let info = []; | ||
let position = 0; | ||
lines.forEach((line) => { | ||
line = line.trim(); | ||
// we will consider only lines that contains only numbers | ||
if (/[0-9]+/.test(line) && /^[0-9eE,;. \t+-]+$/.test(line)) { | ||
let fields = line.split(/,[; \t]+|[; \t]+/); | ||
if (fields.length === 1) { | ||
fields = line.split(/[,; \t]+/); | ||
} | ||
if (fields && | ||
fields.length >= minNumberColumns && // we filter lines that have not enough or too many columns | ||
fields.length <= maxNumberColumns) { | ||
matrix.push(fields.map((value) => parseFloat(value.replace(',', '.')))); | ||
position++; | ||
} | ||
} | ||
else if (line) { | ||
info.push({ position, value: line }); | ||
} | ||
}); | ||
if (bestGuess) { | ||
if (matrix[0] && | ||
matrix[0].length === 3 && | ||
options.xColumn === undefined && | ||
options.yColumn === undefined) { | ||
// is the first column a seuqnetial number ? | ||
let skipFirstColumn = true; | ||
for (let i = 0; i < matrix.length - 1; i++) { | ||
if (Math.abs(matrix[i][0] - matrix[i + 1][0]) !== 1) { | ||
skipFirstColumn = false; | ||
} | ||
} | ||
if (skipFirstColumn) { | ||
xColumn = 1; | ||
yColumn = 2; | ||
} | ||
} | ||
if (matrix[0] && matrix[0].length > 3) { | ||
const xs = []; | ||
for (let row of matrix) { | ||
for (let i = xColumn; i < row.length; i += 2) { | ||
xs.push(row[i]); | ||
} | ||
} | ||
if (xIsMonotone(xs)) { | ||
numberColumns = 2; | ||
} | ||
} | ||
} | ||
if (numberColumns) { | ||
const newMatrix = []; | ||
for (const row of matrix) { | ||
for (let i = 0; i < row.length; i += numberColumns) { | ||
newMatrix.push(row.slice(i, i + numberColumns)); | ||
} | ||
} | ||
matrix = newMatrix; | ||
} | ||
const result = { | ||
x: matrix.map((row) => row[xColumn]), | ||
y: matrix.map((row) => row[yColumn]), | ||
}; | ||
if (uniqueX) { | ||
uniqueXFunction(result); | ||
} | ||
if (rescale) { | ||
let maxY = mlArrayMax(result.y); | ||
for (let i = 0; i < result.y.length; i++) { | ||
result.y[i] /= maxY; | ||
} | ||
} | ||
if (!keepInfo) | ||
return result; | ||
return { | ||
info, | ||
data: result, | ||
}; | ||
return parse(text, options).data; | ||
} | ||
/** | ||
* Parse a text-file and returns the parsed data and information about the columns | ||
* | ||
* @param text - Csv or tsv strings. | ||
* @param options - Parsing options | ||
* @returns - The parsed data with information about the columns | ||
*/ | ||
export function parseXYAndKeepInfo(text, options = {}) { | ||
return parse(text, options); | ||
} | ||
//# sourceMappingURL=index.js.map |
@@ -1,33 +0,19 @@ | ||
export interface DataXY { | ||
x: number[]; | ||
y: number[]; | ||
} | ||
export interface ParseXYOptions { | ||
rescale?: boolean; | ||
uniqueX?: boolean; | ||
xColumn?: number; | ||
yColumn?: number; | ||
keepInfo?: boolean; | ||
bestGuess?: boolean; | ||
numberColumns?: number; | ||
maxNumberColumns?: number; | ||
minNumberColumns?: number; | ||
} | ||
import { ParseXYOptions } from './ParseXYOptions'; | ||
export * from './ParseXYOptions'; | ||
/** | ||
* Parse a text-file and convert it to an array of XY points. | ||
* Parse a text-file and convert it to an object {x:[], y:[]} | ||
* | ||
* @param text - Csv or tsv strings. | ||
* @param [options={}] - | ||
* @param [options.rescale = false] - Will set the maximum value to 1. | ||
* @param [options.uniqueX = false] - Make the X values unique (works only with 'xxyy' format). If the X value is repeated the sum of Y is done. | ||
* @param [options.xColumn = 0] - A number that specifies the x column. | ||
* @param [options.yColumn = 1] - A number that specifies the y column. | ||
* @param [options.bestGuess=false] - Will try to guess which columns are the best. | ||
* @param [options.numberColumns=Number.MAX_SAFE_INTEGER] - If the file has 10 columns and you specify here 2 it will reflow the file. | ||
* @param [options.maxNumberColumns = (Math.max(xColumn, yColumn)+1)] - A number that specifies the maximum number of y columns. | ||
* @param [options.minNumberColumns = (Math.min(xColumn, yColumn)+1)] - A number that specifies the minimum number of y columns. | ||
* @param [options.keepInfo = false] - Should we keep the non numeric lines. In this case the system will return an object {data, info}. | ||
* @returns - | ||
* @param options - Parsing options | ||
* @returns - The parsed data | ||
*/ | ||
export declare function parseXY(text: string, options?: ParseXYOptions): { | ||
export declare function parseXY(text: string, options?: ParseXYOptions): import("cheminfo-types").DataXY<import("cheminfo-types").DoubleArray>; | ||
/** | ||
* Parse a text-file and returns the parsed data and information about the columns | ||
* | ||
* @param text - Csv or tsv strings. | ||
* @param options - Parsing options | ||
* @returns - The parsed data with information about the columns | ||
*/ | ||
export declare function parseXYAndKeepInfo(text: string, options?: ParseXYOptions): { | ||
info: { | ||
@@ -37,4 +23,4 @@ position: number; | ||
}[]; | ||
data: DataXY; | ||
} | DataXY; | ||
data: import("cheminfo-types").DataXY<import("cheminfo-types").DoubleArray>; | ||
}; | ||
//# sourceMappingURL=index.d.ts.map |
130
lib/index.js
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.parseXY = void 0; | ||
const ensure_string_1 = require("ensure-string"); | ||
const ml_array_max_1 = __importDefault(require("ml-array-max")); | ||
const ml_arrayxy_uniquex_1 = __importDefault(require("ml-arrayxy-uniquex")); | ||
const ml_spectra_processing_1 = require("ml-spectra-processing"); | ||
exports.parseXYAndKeepInfo = exports.parseXY = void 0; | ||
const parse_1 = require("./parse"); | ||
__exportStar(require("./ParseXYOptions"), exports); | ||
/** | ||
* Parse a text-file and convert it to an array of XY points. | ||
* Parse a text-file and convert it to an object {x:[], y:[]} | ||
* | ||
* @param text - Csv or tsv strings. | ||
* @param [options={}] - | ||
* @param [options.rescale = false] - Will set the maximum value to 1. | ||
* @param [options.uniqueX = false] - Make the X values unique (works only with 'xxyy' format). If the X value is repeated the sum of Y is done. | ||
* @param [options.xColumn = 0] - A number that specifies the x column. | ||
* @param [options.yColumn = 1] - A number that specifies the y column. | ||
* @param [options.bestGuess=false] - Will try to guess which columns are the best. | ||
* @param [options.numberColumns=Number.MAX_SAFE_INTEGER] - If the file has 10 columns and you specify here 2 it will reflow the file. | ||
* @param [options.maxNumberColumns = (Math.max(xColumn, yColumn)+1)] - A number that specifies the maximum number of y columns. | ||
* @param [options.minNumberColumns = (Math.min(xColumn, yColumn)+1)] - A number that specifies the minimum number of y columns. | ||
* @param [options.keepInfo = false] - Should we keep the non numeric lines. In this case the system will return an object {data, info}. | ||
* @returns - | ||
* @param options - Parsing options | ||
* @returns - The parsed data | ||
*/ | ||
function parseXY(text, options = {}) { | ||
let { rescale = false, uniqueX = false, xColumn = 0, yColumn = 1, keepInfo = false, bestGuess = false, numberColumns = Number.MAX_SAFE_INTEGER, maxNumberColumns = Number.MAX_SAFE_INTEGER, minNumberColumns = 2, } = options; | ||
text = (0, ensure_string_1.ensureString)(text); | ||
maxNumberColumns = Math.max(maxNumberColumns, xColumn + 1, yColumn + 1); | ||
minNumberColumns = Math.max(xColumn + 1, yColumn + 1, minNumberColumns); | ||
let lines = text.split(/[\r\n]+/); | ||
let matrix = []; | ||
let info = []; | ||
let position = 0; | ||
lines.forEach((line) => { | ||
line = line.trim(); | ||
// we will consider only lines that contains only numbers | ||
if (/[0-9]+/.test(line) && /^[0-9eE,;. \t+-]+$/.test(line)) { | ||
let fields = line.split(/,[; \t]+|[; \t]+/); | ||
if (fields.length === 1) { | ||
fields = line.split(/[,; \t]+/); | ||
} | ||
if (fields && | ||
fields.length >= minNumberColumns && // we filter lines that have not enough or too many columns | ||
fields.length <= maxNumberColumns) { | ||
matrix.push(fields.map((value) => parseFloat(value.replace(',', '.')))); | ||
position++; | ||
} | ||
} | ||
else if (line) { | ||
info.push({ position, value: line }); | ||
} | ||
}); | ||
if (bestGuess) { | ||
if (matrix[0] && | ||
matrix[0].length === 3 && | ||
options.xColumn === undefined && | ||
options.yColumn === undefined) { | ||
// is the first column a seuqnetial number ? | ||
let skipFirstColumn = true; | ||
for (let i = 0; i < matrix.length - 1; i++) { | ||
if (Math.abs(matrix[i][0] - matrix[i + 1][0]) !== 1) { | ||
skipFirstColumn = false; | ||
} | ||
} | ||
if (skipFirstColumn) { | ||
xColumn = 1; | ||
yColumn = 2; | ||
} | ||
} | ||
if (matrix[0] && matrix[0].length > 3) { | ||
const xs = []; | ||
for (let row of matrix) { | ||
for (let i = xColumn; i < row.length; i += 2) { | ||
xs.push(row[i]); | ||
} | ||
} | ||
if ((0, ml_spectra_processing_1.xIsMonotone)(xs)) { | ||
numberColumns = 2; | ||
} | ||
} | ||
} | ||
if (numberColumns) { | ||
const newMatrix = []; | ||
for (const row of matrix) { | ||
for (let i = 0; i < row.length; i += numberColumns) { | ||
newMatrix.push(row.slice(i, i + numberColumns)); | ||
} | ||
} | ||
matrix = newMatrix; | ||
} | ||
const result = { | ||
x: matrix.map((row) => row[xColumn]), | ||
y: matrix.map((row) => row[yColumn]), | ||
}; | ||
if (uniqueX) { | ||
(0, ml_arrayxy_uniquex_1.default)(result); | ||
} | ||
if (rescale) { | ||
let maxY = (0, ml_array_max_1.default)(result.y); | ||
for (let i = 0; i < result.y.length; i++) { | ||
result.y[i] /= maxY; | ||
} | ||
} | ||
if (!keepInfo) | ||
return result; | ||
return { | ||
info, | ||
data: result, | ||
}; | ||
return (0, parse_1.parse)(text, options).data; | ||
} | ||
exports.parseXY = parseXY; | ||
/** | ||
* Parse a text-file and returns the parsed data and information about the columns | ||
* | ||
* @param text - Csv or tsv strings. | ||
* @param options - Parsing options | ||
* @returns - The parsed data with information about the columns | ||
*/ | ||
function parseXYAndKeepInfo(text, options = {}) { | ||
return (0, parse_1.parse)(text, options); | ||
} | ||
exports.parseXYAndKeepInfo = parseXYAndKeepInfo; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "xy-parser", | ||
"version": "4.0.1", | ||
"version": "5.0.0", | ||
"description": "Parse a text-file and convert it to an array of XY points", | ||
@@ -44,20 +44,21 @@ "main": "./lib/index.js", | ||
"devDependencies": { | ||
"@types/jest": "^27.0.3", | ||
"@types/jest": "^27.4.0", | ||
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", | ||
"cheminfo-tools": "^1.23.3", | ||
"codecov": "^3.8.3", | ||
"eslint": "^8.4.1", | ||
"eslint-config-cheminfo-typescript": "^10.2.4", | ||
"jest": "^27.4.5", | ||
"eslint": "^8.9.0", | ||
"eslint-config-cheminfo-typescript": "^10.3.0", | ||
"jest": "^27.5.1", | ||
"prettier": "^2.5.1", | ||
"rimraf": "^3.0.2", | ||
"ts-jest": "^27.1.1", | ||
"typescript": "^4.5.4" | ||
"ts-jest": "^27.1.3", | ||
"typescript": "^4.5.5" | ||
}, | ||
"dependencies": { | ||
"cheminfo-types": "^0.10.1", | ||
"ensure-string": "^1.1.0", | ||
"ml-array-max": "^1.2.3", | ||
"ml-array-max": "^1.2.4", | ||
"ml-arrayxy-uniquex": "1.0.2", | ||
"ml-spectra-processing": "^8.0.3" | ||
"ml-spectra-processing": "^10.1.2" | ||
} | ||
} |
@@ -29,3 +29,3 @@ # xy-parser | ||
```js | ||
import { parseXY } from "xy-parser"; | ||
import { parseXY } from 'xy-parser'; | ||
const data = `My file | ||
@@ -45,3 +45,3 @@ 1 2 | ||
const result2 = parseXY(data, { keepInfo: true }); | ||
const result2 = parseXYAndKeppInfo(data); | ||
/* result2 -> | ||
@@ -48,0 +48,0 @@ data: { |
import { readFileSync } from 'fs'; | ||
import { DataXY, parseXY } from '../index'; | ||
import { parseXY } from '../index'; | ||
@@ -11,3 +11,3 @@ const path = `${__dirname}/../../testFiles/`; | ||
let result = parseXY(data) as DataXY; | ||
let result = parseXY(data); | ||
expect(result.x).toBeInstanceOf(Array); | ||
@@ -23,3 +23,3 @@ expect(result.y).toBeInstanceOf(Array); | ||
let result = parseXY(data) as DataXY; | ||
let result = parseXY(data); | ||
@@ -26,0 +26,0 @@ let min = Math.min(...result.y); |
import { readFileSync } from 'fs'; | ||
import { DataXY, parseXY } from '..'; | ||
import { parseXY } from '..'; | ||
@@ -12,3 +12,3 @@ const path = `${__dirname}/../../testFiles/`; | ||
let result = parseXY(data) as DataXY; | ||
let result = parseXY(data); | ||
expect(result.x).toHaveLength(20); | ||
@@ -19,3 +19,3 @@ expect(result.y).toHaveLength(20); | ||
let result2 = parseXY(data, { bestGuess: true }) as DataXY; | ||
let result2 = parseXY(data, { bestGuess: true }); | ||
expect(result2.x).toHaveLength(20); | ||
@@ -31,3 +31,3 @@ expect(result2.y).toHaveLength(20); | ||
let result = parseXY(data, { xColumn: 1, yColumn: 2 }) as DataXY; | ||
let result = parseXY(data, { xColumn: 1, yColumn: 2 }); | ||
expect(result.x).toHaveLength(20); | ||
@@ -38,3 +38,3 @@ expect(result.y).toHaveLength(20); | ||
let result2 = parseXY(data, { bestGuess: true }) as DataXY; | ||
let result2 = parseXY(data, { bestGuess: true }); | ||
expect(result2.x).toHaveLength(20); | ||
@@ -50,3 +50,3 @@ expect(result2.y).toHaveLength(20); | ||
let result = parseXY(data) as DataXY; | ||
let result = parseXY(data); | ||
expect(result.x).toHaveLength(20); | ||
@@ -57,3 +57,3 @@ expect(result.y).toHaveLength(20); | ||
let result2 = parseXY(data, { bestGuess: true }) as DataXY; | ||
let result2 = parseXY(data, { bestGuess: true }); | ||
expect(result2.x).toHaveLength(20); | ||
@@ -68,3 +68,3 @@ expect(result2.y).toHaveLength(20); | ||
let data = readFileSync(path + filename).toString(); | ||
let result = parseXY(data, { numberColumns: 2 }) as DataXY; | ||
let result = parseXY(data, { numberColumns: 2 }); | ||
expect(result.x).toHaveLength(34); | ||
@@ -75,3 +75,3 @@ expect(result.y).toHaveLength(34); | ||
let result2 = parseXY(data, { bestGuess: true }) as DataXY; | ||
let result2 = parseXY(data, { bestGuess: true }); | ||
expect(result2.x).toHaveLength(34); | ||
@@ -78,0 +78,0 @@ expect(result2.y).toHaveLength(34); |
import { readFileSync } from 'fs'; | ||
import { DataXY, parseXY } from '..'; | ||
import { parseXY } from '..'; | ||
@@ -11,3 +11,3 @@ const path = `${__dirname}/../../testFiles/`; | ||
let result = parseXY(data) as DataXY; | ||
let result = parseXY(data); | ||
@@ -14,0 +14,0 @@ expect(result.x).toBeInstanceOf(Array); |
import { readFileSync } from 'fs'; | ||
import { DataXY, parseXY } from '..'; | ||
import { parseXY, parseXYAndKeepInfo } from '..'; | ||
@@ -10,3 +10,3 @@ const path = `${__dirname}/../../testFiles/`; | ||
let data = readFileSync(path + filename).toString(); | ||
let result = parseXY(data) as DataXY; | ||
let result = parseXY(data); | ||
@@ -22,3 +22,3 @@ expect(result.x).toBeInstanceOf(Array); | ||
let data = readFileSync(path + filename).toString(); | ||
let result = parseXY(data) as DataXY; | ||
let result = parseXY(data); | ||
@@ -36,3 +36,3 @@ expect(result.x).toBeInstanceOf(Array); | ||
uniqueX: true, | ||
}) as DataXY; | ||
}); | ||
@@ -48,3 +48,3 @@ expect(result.x).toBeInstanceOf(Array); | ||
uniqueX: true, | ||
}) as DataXY; | ||
}); | ||
@@ -75,5 +75,3 @@ expect(result.x).toBeInstanceOf(Array); | ||
let data = readFileSync(path + filename).toString(); | ||
let result = parseXY(data, { | ||
keepInfo: true, | ||
}); | ||
let result = parseXYAndKeepInfo(data); | ||
expect(result).toStrictEqual({ | ||
@@ -96,8 +94,15 @@ data: { x: [1, 3, 5], y: [4, 6, 8] }, | ||
test('should not use keepInfo', () => { | ||
expect(() => { | ||
// @ts-expect-error we are testing an old option property | ||
parseXY('', { keepInfo: true }); | ||
}).toThrow( | ||
'keepInfo has been deprecated, pelase use the new method parseXYAndKeepInfo', | ||
); | ||
}); | ||
test('with scientific notation', () => { | ||
let filename = 'text8.txt'; | ||
let data = readFileSync(path + filename).toString(); | ||
let result = parseXY(data, { | ||
keepInfo: true, | ||
}); | ||
let result = parseXYAndKeepInfo(data); | ||
expect(result).toStrictEqual({ | ||
@@ -112,5 +117,5 @@ data: { x: [0.11, -11, 0.11], y: [0.22, -22, 0.22] }, | ||
let data = readFileSync(path + filename).toString(); | ||
let result = parseXY(data, {}) as DataXY; | ||
let result = parseXY(data, {}); | ||
expect(result.x).toHaveLength(6472); | ||
expect(result.y).toHaveLength(6472); | ||
}); |
164
src/index.ts
@@ -1,152 +0,26 @@ | ||
import { ensureString } from 'ensure-string'; | ||
import mlArrayMax from 'ml-array-max'; | ||
import uniqueXFunction from 'ml-arrayxy-uniquex'; | ||
import { xIsMonotone } from 'ml-spectra-processing'; | ||
import { ParseXYOptions } from './ParseXYOptions'; | ||
import { parse } from './parse'; | ||
export interface DataXY { | ||
x: number[]; | ||
y: number[]; | ||
export * from './ParseXYOptions'; | ||
/** | ||
* Parse a text-file and convert it to an object {x:[], y:[]} | ||
* | ||
* @param text - Csv or tsv strings. | ||
* @param options - Parsing options | ||
* @returns - The parsed data | ||
*/ | ||
export function parseXY(text: string, options: ParseXYOptions = {}) { | ||
return parse(text, options).data; | ||
} | ||
export interface ParseXYOptions { | ||
rescale?: boolean; | ||
uniqueX?: boolean; | ||
xColumn?: number; | ||
yColumn?: number; | ||
keepInfo?: boolean; | ||
bestGuess?: boolean; | ||
numberColumns?: number; | ||
maxNumberColumns?: number; | ||
minNumberColumns?: number; | ||
} | ||
/** | ||
* Parse a text-file and convert it to an array of XY points. | ||
* Parse a text-file and returns the parsed data and information about the columns | ||
* | ||
* @param text - Csv or tsv strings. | ||
* @param [options={}] - | ||
* @param [options.rescale = false] - Will set the maximum value to 1. | ||
* @param [options.uniqueX = false] - Make the X values unique (works only with 'xxyy' format). If the X value is repeated the sum of Y is done. | ||
* @param [options.xColumn = 0] - A number that specifies the x column. | ||
* @param [options.yColumn = 1] - A number that specifies the y column. | ||
* @param [options.bestGuess=false] - Will try to guess which columns are the best. | ||
* @param [options.numberColumns=Number.MAX_SAFE_INTEGER] - If the file has 10 columns and you specify here 2 it will reflow the file. | ||
* @param [options.maxNumberColumns = (Math.max(xColumn, yColumn)+1)] - A number that specifies the maximum number of y columns. | ||
* @param [options.minNumberColumns = (Math.min(xColumn, yColumn)+1)] - A number that specifies the minimum number of y columns. | ||
* @param [options.keepInfo = false] - Should we keep the non numeric lines. In this case the system will return an object {data, info}. | ||
* @returns - | ||
* @param options - Parsing options | ||
* @returns - The parsed data with information about the columns | ||
*/ | ||
export function parseXY( | ||
text: string, | ||
options: ParseXYOptions = {}, | ||
): | ||
| { | ||
info: { position: number; value: string }[]; | ||
data: DataXY; | ||
} | ||
| DataXY { | ||
let { | ||
rescale = false, | ||
uniqueX = false, | ||
xColumn = 0, | ||
yColumn = 1, | ||
keepInfo = false, | ||
bestGuess = false, | ||
numberColumns = Number.MAX_SAFE_INTEGER, | ||
maxNumberColumns = Number.MAX_SAFE_INTEGER, | ||
minNumberColumns = 2, | ||
} = options; | ||
text = ensureString(text); | ||
maxNumberColumns = Math.max(maxNumberColumns, xColumn + 1, yColumn + 1); | ||
minNumberColumns = Math.max(xColumn + 1, yColumn + 1, minNumberColumns); | ||
let lines = text.split(/[\r\n]+/); | ||
let matrix: number[][] = []; | ||
let info: { position: number; value: string }[] = []; | ||
let position = 0; | ||
lines.forEach((line) => { | ||
line = line.trim(); | ||
// we will consider only lines that contains only numbers | ||
if (/[0-9]+/.test(line) && /^[0-9eE,;. \t+-]+$/.test(line)) { | ||
let fields = line.split(/,[; \t]+|[; \t]+/); | ||
if (fields.length === 1) { | ||
fields = line.split(/[,; \t]+/); | ||
} | ||
if ( | ||
fields && | ||
fields.length >= minNumberColumns && // we filter lines that have not enough or too many columns | ||
fields.length <= maxNumberColumns | ||
) { | ||
matrix.push(fields.map((value) => parseFloat(value.replace(',', '.')))); | ||
position++; | ||
} | ||
} else if (line) { | ||
info.push({ position, value: line }); | ||
} | ||
}); | ||
if (bestGuess) { | ||
if ( | ||
matrix[0] && | ||
matrix[0].length === 3 && | ||
options.xColumn === undefined && | ||
options.yColumn === undefined | ||
) { | ||
// is the first column a seuqnetial number ? | ||
let skipFirstColumn = true; | ||
for (let i = 0; i < matrix.length - 1; i++) { | ||
if (Math.abs(matrix[i][0] - matrix[i + 1][0]) !== 1) { | ||
skipFirstColumn = false; | ||
} | ||
} | ||
if (skipFirstColumn) { | ||
xColumn = 1; | ||
yColumn = 2; | ||
} | ||
} | ||
if (matrix[0] && matrix[0].length > 3) { | ||
const xs: number[] = []; | ||
for (let row of matrix) { | ||
for (let i = xColumn; i < row.length; i += 2) { | ||
xs.push(row[i]); | ||
} | ||
} | ||
if (xIsMonotone(xs)) { | ||
numberColumns = 2; | ||
} | ||
} | ||
} | ||
if (numberColumns) { | ||
const newMatrix: number[][] = []; | ||
for (const row of matrix) { | ||
for (let i = 0; i < row.length; i += numberColumns) { | ||
newMatrix.push(row.slice(i, i + numberColumns)); | ||
} | ||
} | ||
matrix = newMatrix; | ||
} | ||
const result = { | ||
x: matrix.map((row) => row[xColumn]), | ||
y: matrix.map((row) => row[yColumn]), | ||
}; | ||
if (uniqueX) { | ||
uniqueXFunction(result); | ||
} | ||
if (rescale) { | ||
let maxY = mlArrayMax(result.y); | ||
for (let i = 0; i < result.y.length; i++) { | ||
result.y[i] /= maxY; | ||
} | ||
} | ||
if (!keepInfo) return result; | ||
return { | ||
info, | ||
data: result, | ||
}; | ||
export function parseXYAndKeepInfo(text: string, options: ParseXYOptions = {}) { | ||
return parse(text, options); | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
43299
35
854
5
1
+ Addedcheminfo-types@^0.10.1
+ Addedbinary-search@1.3.6(transitive)
+ Addedcheminfo-types@0.10.1(transitive)
+ Addedml-spectra-processing@10.3.0(transitive)
+ Addedml-xsadd@2.0.0(transitive)
- Removedcheminfo-types@0.9.1(transitive)
- Removedml-array-mean@1.1.6(transitive)
- Removedml-array-median@1.1.6(transitive)
- Removedml-array-sequential-fill@1.1.8(transitive)
- Removedml-array-standard-deviation@1.1.8(transitive)
- Removedml-array-sum@1.1.6(transitive)
- Removedml-array-variance@1.1.8(transitive)
- Removedml-spectra-processing@8.3.1(transitive)
Updatedml-array-max@^1.2.4