@datagrok/eda 1.4.12 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +0 -1
- package/CHANGELOG.md +10 -0
- package/CLAUDE.md +185 -0
- package/css/pmpo.css +9 -0
- package/dist/111.js +1 -1
- package/dist/111.js.map +1 -1
- package/dist/128.js +1 -1
- package/dist/128.js.map +1 -1
- package/dist/153.js +1 -1
- package/dist/153.js.map +1 -1
- package/dist/23.js +1 -1
- package/dist/23.js.map +1 -1
- package/dist/234.js +1 -1
- package/dist/234.js.map +1 -1
- package/dist/242.js +1 -1
- package/dist/242.js.map +1 -1
- package/dist/260.js +1 -1
- package/dist/260.js.map +1 -1
- package/dist/33.js +1 -1
- package/dist/33.js.map +1 -1
- package/dist/348.js +1 -1
- package/dist/348.js.map +1 -1
- package/dist/377.js +1 -1
- package/dist/377.js.map +1 -1
- package/dist/397.js +2 -0
- package/dist/397.js.map +1 -0
- package/dist/412.js +1 -1
- package/dist/412.js.map +1 -1
- package/dist/415.js +1 -1
- package/dist/415.js.map +1 -1
- package/dist/501.js +1 -1
- package/dist/501.js.map +1 -1
- package/dist/531.js +1 -1
- package/dist/531.js.map +1 -1
- package/dist/583.js +1 -1
- package/dist/583.js.map +1 -1
- package/dist/589.js +1 -1
- package/dist/589.js.map +1 -1
- package/dist/603.js +1 -1
- package/dist/603.js.map +1 -1
- package/dist/656.js +1 -1
- package/dist/656.js.map +1 -1
- package/dist/682.js +1 -1
- package/dist/682.js.map +1 -1
- package/dist/705.js +1 -1
- package/dist/705.js.map +1 -1
- package/dist/727.js +1 -1
- package/dist/727.js.map +1 -1
- package/dist/731.js +1 -1
- package/dist/731.js.map +1 -1
- package/dist/738.js +1 -1
- package/dist/738.js.map +1 -1
- package/dist/763.js +1 -1
- package/dist/763.js.map +1 -1
- package/dist/778.js +1 -1
- package/dist/778.js.map +1 -1
- package/dist/783.js +1 -1
- package/dist/783.js.map +1 -1
- package/dist/793.js +1 -1
- package/dist/793.js.map +1 -1
- package/dist/810.js +1 -1
- package/dist/810.js.map +1 -1
- package/dist/860.js +1 -1
- package/dist/860.js.map +1 -1
- package/dist/907.js +1 -1
- package/dist/907.js.map +1 -1
- package/dist/950.js +1 -1
- package/dist/950.js.map +1 -1
- package/dist/980.js +1 -1
- package/dist/980.js.map +1 -1
- package/dist/990.js +1 -1
- package/dist/990.js.map +1 -1
- package/dist/package-test.js +1 -1
- package/dist/package-test.js.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/eslintrc.json +0 -1
- package/files/drugs-props-train-scores.csv +664 -0
- package/package.json +11 -7
- package/src/package-api.ts +7 -3
- package/src/package-test.ts +4 -1
- package/src/package.g.ts +21 -9
- package/src/package.ts +33 -23
- package/src/pareto-optimization/pareto-computations.ts +6 -0
- package/src/pareto-optimization/pareto-optimizer.ts +1 -1
- package/src/pls/pls-constants.ts +3 -1
- package/src/pls/pls-tools.ts +73 -69
- package/src/probabilistic-scoring/data-generator.ts +202 -0
- package/src/probabilistic-scoring/nelder-mead.ts +204 -0
- package/src/probabilistic-scoring/pmpo-defs.ts +141 -3
- package/src/probabilistic-scoring/pmpo-utils.ts +240 -126
- package/src/probabilistic-scoring/prob-scoring.ts +862 -135
- package/src/probabilistic-scoring/stat-tools.ts +141 -6
- package/src/tests/anova-tests.ts +1 -1
- package/src/tests/classifiers-tests.ts +1 -1
- package/src/tests/dim-reduction-tests.ts +1 -1
- package/src/tests/linear-methods-tests.ts +1 -1
- package/src/tests/mis-vals-imputation-tests.ts +1 -1
- package/src/tests/pareto-tests.ts +251 -0
- package/src/tests/pmpo-tests.ts +797 -0
- package/test-console-output-1.log +303 -239
- package/test-record-1.mp4 +0 -0
- package/files/mpo-done.ipynb +0 -2123
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
// Utility functions for probabilistic scoring (pMPO)
|
|
2
|
-
//
|
|
2
|
+
// Source paper https://pmc.ncbi.nlm.nih.gov/articles/PMC4716604/
|
|
3
3
|
|
|
4
4
|
import * as grok from 'datagrok-api/grok';
|
|
5
5
|
import * as ui from 'datagrok-api/ui';
|
|
6
6
|
import * as DG from 'datagrok-api/dg';
|
|
7
7
|
|
|
8
|
+
import {generateMpoFileName, MPO_PROFILE_CHANGED_EVENT} from '@datagrok-libraries/statistics/src/mpo/utils';
|
|
8
9
|
import '../../css/pmpo.css';
|
|
9
10
|
|
|
10
11
|
import {COLORS, DESCR_TABLE_TITLE, DESCR_TITLE, DescriptorStatistics, DesirabilityProfileProperties,
|
|
11
12
|
DESIRABILITY_COL_NAME, FOLDER, P_VAL, PMPO_COMPUTE_FAILED, PmpoParams, SCORES_TITLE,
|
|
12
|
-
SELECTED_TITLE, STAT_TO_TITLE_MAP, TINY, WEIGHT_TITLE,
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
SELECTED_TITLE, STAT_TO_TITLE_MAP, TINY, WEIGHT_TITLE, CorrelationTriple,
|
|
14
|
+
BASIC_RANGE_SIGMA_COEFFS, EXTENDED_RANGE_SIGMA_COEFFS, EQUALITY_SIGN,
|
|
15
|
+
PREFERABLE_CATEGORIES} from './pmpo-defs';
|
|
16
|
+
import {computeSigmoidParamsFromX0, getCutoffs, gaussDesirabilityFunc, sigmoidS,
|
|
17
|
+
solveNormalIntersection} from './stat-tools';
|
|
15
18
|
import {getColorScaleDiv} from '../pareto-optimization/utils';
|
|
16
19
|
import {OPT_TYPE} from '../pareto-optimization/defs';
|
|
17
20
|
|
|
@@ -243,11 +246,22 @@ export function getModelParams(desired: DG.DataFrame, nonDesired: DG.DataFrame,
|
|
|
243
246
|
// Unbiased standard deviation
|
|
244
247
|
const sigmaNonDes = nonDesCol.stats.stdev * Math.sqrt((nonDesLen - 1) / nonDesLen);
|
|
245
248
|
|
|
246
|
-
// Compute cutoffs
|
|
249
|
+
// Compute cutoffs
|
|
247
250
|
const cutoffs = getCutoffs(muDes, sigmaDes, muNonDes, sigmaNonDes);
|
|
251
|
+
|
|
252
|
+
// column_stats['inflection'] = np.exp(-np.square((column_stats['cutoff'] - column_stats['good_mean'])) /
|
|
253
|
+
// (2 * np.square(column_stats['good_std'])))
|
|
254
|
+
// Compute inflection point
|
|
255
|
+
const inflection = Math.exp(-((cutoffs.cutoff - muDes) ** 2) / (2 * (sigmaDes ** 2)));
|
|
256
|
+
|
|
257
|
+
// Compute intersections of the two normal distributions
|
|
248
258
|
const intersections = solveNormalIntersection(muDes, sigmaDes, muNonDes, sigmaNonDes);
|
|
249
259
|
|
|
250
|
-
|
|
260
|
+
const b = (Math.pow(inflection, -1.0) - 1.0);
|
|
261
|
+
const n = (Math.pow(qCutoff, -1.0) - 1.0);
|
|
262
|
+
const c = Math.pow(10.0, ((Math.log10(n / b)) / (-1.0 * (muNonDes - cutoffs.cutoff))));
|
|
263
|
+
|
|
264
|
+
// Compute parameters for the generalized sigmoid function TODO: delete
|
|
251
265
|
|
|
252
266
|
let x0: number | null = null;
|
|
253
267
|
|
|
@@ -279,17 +293,20 @@ export function getModelParams(desired: DG.DataFrame, nonDesired: DG.DataFrame,
|
|
|
279
293
|
desStd: sigmaDes,
|
|
280
294
|
nonDesAvg: muNonDes,
|
|
281
295
|
nonDesStd: sigmaNonDes,
|
|
296
|
+
min: Math.min(desCol.stats.min, nonDesCol.stats.min),
|
|
297
|
+
max: Math.max(desCol.stats.max, nonDesCol.stats.max),
|
|
282
298
|
cutoff: cutoffs.cutoff,
|
|
283
299
|
cutoffDesired: cutoffs.cutoffDesired,
|
|
284
300
|
cutoffNotDesired: cutoffs.cutoffNotDesired,
|
|
285
301
|
pX0: sigmoidParams.pX0,
|
|
286
|
-
b:
|
|
287
|
-
c:
|
|
302
|
+
b: b,
|
|
303
|
+
c: c,
|
|
288
304
|
zScore: z,
|
|
289
305
|
weight: z,
|
|
290
306
|
intersections: intersections,
|
|
291
307
|
x0: x0,
|
|
292
308
|
xBound: xBound,
|
|
309
|
+
inflection: inflection,
|
|
293
310
|
});
|
|
294
311
|
});
|
|
295
312
|
|
|
@@ -338,12 +355,13 @@ export async function loadPmpoParams(file: DG.FileInfo): Promise<Map<string, Pmp
|
|
|
338
355
|
* @param name Name of the desirability profile.
|
|
339
356
|
* @param description Description of the desirability profile.
|
|
340
357
|
*/
|
|
341
|
-
export function getDesirabilityProfileJson(params: Map<string, PmpoParams>,
|
|
358
|
+
export function getDesirabilityProfileJson(params: Map<string, PmpoParams>, useSigmoidalCorrection: boolean,
|
|
359
|
+
name: string, description: string, truncatedRange: boolean): any {
|
|
342
360
|
return {
|
|
343
361
|
'type': 'MPO Desirability Profile',
|
|
344
362
|
'name': name,
|
|
345
363
|
'description': description,
|
|
346
|
-
'properties': getDesirabilityProfileProperties(params),
|
|
364
|
+
'properties': getDesirabilityProfileProperties(params, useSigmoidalCorrection, truncatedRange),
|
|
347
365
|
};
|
|
348
366
|
}
|
|
349
367
|
|
|
@@ -351,45 +369,23 @@ export function getDesirabilityProfileJson(params: Map<string, PmpoParams>, name
|
|
|
351
369
|
* @param params Map of descriptor names to their pMPO parameters.
|
|
352
370
|
* @param modelName Suggested model name (used as default file name).
|
|
353
371
|
*/
|
|
354
|
-
export async function saveModel(params: Map<string, PmpoParams>, modelName: string
|
|
355
|
-
|
|
356
|
-
const nameInput = ui.input.string('
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
dlg.getButton('Save').disabled = (fileName.length < 1) || (folderName.length < 1);
|
|
362
|
-
},
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
let folderName = FOLDER;
|
|
366
|
-
const folderInput = ui.input.string('Folder', {
|
|
367
|
-
value: folderName,
|
|
368
|
-
nullable: false,
|
|
369
|
-
onValueChanged: (val) => {
|
|
370
|
-
folderName = val;
|
|
371
|
-
dlg.getButton('Save').disabled = (fileName.length < 1) || (folderName.length < 1);
|
|
372
|
-
},
|
|
372
|
+
export async function saveModel(params: Map<string, PmpoParams>, modelName: string,
|
|
373
|
+
useSigmoidalCorrection: boolean): Promise<void> {
|
|
374
|
+
const nameInput = ui.input.string('Name', {value: modelName, nullable: false});
|
|
375
|
+
const descriptionInput = ui.input.textArea('Description', {value: ' ', nullable: true});
|
|
376
|
+
const typeInput = ui.input.bool('Desirability Profile', {
|
|
377
|
+
value: true,
|
|
378
|
+
tooltipText: 'Save the model as an MPO Desirability Profile. If disabled, the model is saved in the pMPO format.',
|
|
373
379
|
});
|
|
374
380
|
|
|
375
|
-
const save = async () => {
|
|
376
|
-
const path = `${folderName}/${fileName}.json`;
|
|
377
|
-
try {
|
|
378
|
-
const jsonString = JSON.stringify(objectToSave(), null, 2);
|
|
379
|
-
await grok.dapi.files.writeAsText(path, jsonString);
|
|
380
|
-
grok.shell.info(`Saved to ${path}`);
|
|
381
|
-
} catch (err) {
|
|
382
|
-
grok.shell.error(`Failed to save: ${err instanceof Error ? err.message : 'the platform issue'}.`);
|
|
383
|
-
}
|
|
384
|
-
dlg.close();
|
|
385
|
-
};
|
|
386
|
-
|
|
387
381
|
const objectToSave = () => {
|
|
388
382
|
if (typeInput.value) {
|
|
389
383
|
return getDesirabilityProfileJson(
|
|
390
384
|
params,
|
|
385
|
+
useSigmoidalCorrection,
|
|
391
386
|
nameInput.value,
|
|
392
387
|
descriptionInput.value,
|
|
388
|
+
false,
|
|
393
389
|
);
|
|
394
390
|
}
|
|
395
391
|
|
|
@@ -401,34 +397,29 @@ export async function saveModel(params: Map<string, PmpoParams>, modelName: stri
|
|
|
401
397
|
};
|
|
402
398
|
};
|
|
403
399
|
|
|
404
|
-
const modelNameInput = ui.input.string('Name', {value: modelName, nullable: true});
|
|
405
|
-
const descriptionInput = ui.input.textArea('Description', {value: ' ', nullable: true});
|
|
406
|
-
const typeInput = ui.input.bool('Desirability Profile', {
|
|
407
|
-
value: true,
|
|
408
|
-
tooltipText: 'Save the model as an MPO Desirability Profile. If disabled, the model is saved in the pMPO format.',
|
|
409
|
-
});
|
|
410
|
-
|
|
411
400
|
const dlg = ui.dialog({title: 'Save model'})
|
|
412
|
-
.add(ui.h2('Path'))
|
|
413
|
-
.add(folderInput)
|
|
414
401
|
.add(nameInput)
|
|
415
|
-
.add(ui.h2('Model'))
|
|
416
|
-
.add(modelNameInput)
|
|
417
402
|
.add(descriptionInput)
|
|
418
403
|
.add(typeInput)
|
|
419
404
|
.addButton('Save', async () => {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
405
|
+
try {
|
|
406
|
+
const files = await grok.dapi.files.list(FOLDER);
|
|
407
|
+
const existingFileNames = new Set(files.map((f) => f.name));
|
|
408
|
+
const fileName = generateMpoFileName(nameInput.value, existingFileNames);
|
|
409
|
+
const path = `${FOLDER}/${fileName}`;
|
|
410
|
+
const jsonString = JSON.stringify(objectToSave(), null, 2);
|
|
411
|
+
await grok.dapi.files.writeAsText(path, jsonString);
|
|
412
|
+
grok.events.fireCustomEvent(MPO_PROFILE_CHANGED_EVENT, {});
|
|
413
|
+
grok.shell.info(`Saved to ${path}`);
|
|
414
|
+
} catch (err) {
|
|
415
|
+
grok.shell.error(`Failed to save: ${err instanceof Error ? err.message : 'the platform issue'}.`);
|
|
429
416
|
}
|
|
417
|
+
dlg.close();
|
|
430
418
|
})
|
|
431
419
|
.show();
|
|
420
|
+
|
|
421
|
+
dlg.getButton('Save').disabled = !nameInput.validate();
|
|
422
|
+
nameInput.onInput.subscribe(() => dlg.getButton('Save').disabled = !nameInput.validate());
|
|
432
423
|
} // saveModel
|
|
433
424
|
|
|
434
425
|
/** Adds columns with correlation coefficients between descriptors.
|
|
@@ -462,7 +453,10 @@ export function addCorrelationColumns(df: DG.DataFrame, descriptorNames: string[
|
|
|
462
453
|
return df;
|
|
463
454
|
} // addCorrelationColumns
|
|
464
455
|
|
|
465
|
-
|
|
456
|
+
/** Sets color coding for the p-value column in the statistics table
|
|
457
|
+
* @param table DataFrame with descriptor statistics.
|
|
458
|
+
* @param pValTresh P-value threshold.
|
|
459
|
+
*/
|
|
466
460
|
export function setPvalColumnColorCoding(table: DG.DataFrame, pValTresh: number): void {
|
|
467
461
|
const pValCol = table.col(P_VAL);
|
|
468
462
|
if (pValCol == null)
|
|
@@ -475,7 +469,11 @@ export function setPvalColumnColorCoding(table: DG.DataFrame, pValTresh: number)
|
|
|
475
469
|
pValCol.meta.colors.setConditional(rules);
|
|
476
470
|
} // setPvalColumnColorCoding
|
|
477
471
|
|
|
478
|
-
|
|
472
|
+
/** Sets color coding for the correlation columns in the statistics table.
|
|
473
|
+
* @param table DataFrame with descriptor statistics.
|
|
474
|
+
* @param descriptorNames List of descriptor names.
|
|
475
|
+
* @param r2Tresh R-squared threshold.
|
|
476
|
+
*/
|
|
479
477
|
export function setCorrColumnColorCoding(table: DG.DataFrame, descriptorNames: string[], r2Tresh: number): void {
|
|
480
478
|
descriptorNames.forEach((name) => {
|
|
481
479
|
const col = table.col(name);
|
|
@@ -483,8 +481,8 @@ export function setCorrColumnColorCoding(table: DG.DataFrame, descriptorNames: s
|
|
|
483
481
|
return;
|
|
484
482
|
|
|
485
483
|
const rules: Record<string, string> = {};
|
|
486
|
-
rules[
|
|
487
|
-
rules[
|
|
484
|
+
rules[`>=${r2Tresh}`] = COLORS.SKIPPED;
|
|
485
|
+
rules[`<${r2Tresh}`] = COLORS.SELECTED;
|
|
488
486
|
|
|
489
487
|
col.meta.colors.setConditional(rules);
|
|
490
488
|
});
|
|
@@ -492,20 +490,18 @@ export function setCorrColumnColorCoding(table: DG.DataFrame, descriptorNames: s
|
|
|
492
490
|
|
|
493
491
|
/** Returns desirability profile properties for the given pMPO parameters.
|
|
494
492
|
* @param params Map of descriptor names to their pMPO parameters.
|
|
493
|
+
* @param useSigmoidalCorrection Whether to use sigmoidal correction in desirability functions.
|
|
494
|
+
* @param displayProfile Whether to create a profile to be displayed in the stat grid (true - truncated range).
|
|
495
495
|
*/
|
|
496
|
-
function getDesirabilityProfileProperties(params: Map<string, PmpoParams
|
|
496
|
+
function getDesirabilityProfileProperties(params: Map<string, PmpoParams>,
|
|
497
|
+
useSigmoidalCorrection: boolean, truncatedRange: boolean): DesirabilityProfileProperties {
|
|
497
498
|
const props: DesirabilityProfileProperties = {};
|
|
498
499
|
|
|
499
|
-
let maxWeight = 0;
|
|
500
|
-
params.forEach((param) => maxWeight = Math.max(maxWeight, param.weight));
|
|
501
|
-
|
|
502
|
-
const scale = (maxWeight > 0) ? (1 / maxWeight) : 1;
|
|
503
|
-
|
|
504
500
|
params.forEach((param, name) => {
|
|
505
|
-
const range = significantPoints(param);
|
|
501
|
+
const range = significantPoints(param, truncatedRange);
|
|
506
502
|
props[name] = {
|
|
507
|
-
weight: param.weight
|
|
508
|
-
line: getLine(param),
|
|
503
|
+
weight: param.weight,
|
|
504
|
+
line: getLine(param, useSigmoidalCorrection, truncatedRange),
|
|
509
505
|
min: Math.min(...range),
|
|
510
506
|
max: Math.max(...range),
|
|
511
507
|
};
|
|
@@ -514,67 +510,185 @@ function getDesirabilityProfileProperties(params: Map<string, PmpoParams>) {
|
|
|
514
510
|
return props;
|
|
515
511
|
} // getDesirabilityProfileProperties
|
|
516
512
|
|
|
517
|
-
/** Returns array of arguments for Gaussian function centered at mu with stddev sigma.
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
mu
|
|
526
|
-
mu
|
|
527
|
-
mu,
|
|
528
|
-
mu + 0.25 * sigma,
|
|
529
|
-
mu + 0.5 * sigma,
|
|
530
|
-
mu + sigma,
|
|
531
|
-
mu + 1.5 * sigma,
|
|
532
|
-
mu + 2 * sigma,
|
|
533
|
-
mu + 2.5 * sigma,
|
|
534
|
-
mu + 3 * sigma,
|
|
535
|
-
];
|
|
513
|
+
/** Returns array of arguments for Gaussian function centered at mu with stddev sigma.
|
|
514
|
+
* @param mu Mean of the Gaussian function.
|
|
515
|
+
* @param sigma Standard deviation of the Gaussian function.
|
|
516
|
+
* @param truncatedRange Whether to use truncated range (for interactive app) or extended range (for full profile).
|
|
517
|
+
* @return Array of arguments for the Gaussian function.
|
|
518
|
+
*/
|
|
519
|
+
function getArgsOfGaussFunc(mu: number, sigma: number, truncatedRange: boolean): number[] {
|
|
520
|
+
return truncatedRange ?
|
|
521
|
+
BASIC_RANGE_SIGMA_COEFFS.map((coeff) => mu + coeff * sigma) : // range for interactive app
|
|
522
|
+
EXTENDED_RANGE_SIGMA_COEFFS.map((coeff) => mu + coeff * sigma); // actual full range for desirability profile
|
|
536
523
|
} // getArgsOfGaussFunc
|
|
537
524
|
|
|
538
|
-
/**
|
|
539
|
-
|
|
540
|
-
|
|
525
|
+
/** Basic pMPO function combining Gaussian and sigmoid functions.
|
|
526
|
+
* @param x Argument.
|
|
527
|
+
* @param param pMPO parameters.
|
|
528
|
+
* @param useSigmoidalCorrection Whether to use sigmoidal correction.
|
|
529
|
+
* @return Value of the basic pMPO function at x.
|
|
530
|
+
*/
|
|
531
|
+
function basicFunction(x: number, param: PmpoParams, useSigmoidalCorrection: boolean): number {
|
|
532
|
+
return gaussDesirabilityFunc(x, param.desAvg, param.desStd) *
|
|
533
|
+
(useSigmoidalCorrection ? sigmoidS(x, param.cutoff, param.b, param.c) : 1);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/** Returns line points for the given pMPO parameters.
|
|
537
|
+
* @param param pMPO parameters.
|
|
538
|
+
* @param useSigmoidalCorrection Whether to use sigmoidal correction.
|
|
539
|
+
* @param truncatedRange Whether to use truncated range (for interactive app) or extended range (for full profile).
|
|
540
|
+
* @return Array of [x, y] points representing the desirability function line.
|
|
541
|
+
*/
|
|
542
|
+
function getLine(param: PmpoParams, useSigmoidalCorrection: boolean, truncatedRange: boolean): [number, number][] {
|
|
543
|
+
const range = significantPoints(param, truncatedRange);
|
|
544
|
+
|
|
545
|
+
return range.map((x) => [x, basicFunction(x, param, useSigmoidalCorrection)]);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/** Returns significant points for the given pMPO parameters.
|
|
549
|
+
* @param param pMPO parameters.
|
|
550
|
+
* @param truncatedRange Whether to use truncated range (for interactive app) or extended range (for full profile).
|
|
551
|
+
* @return Array of significant points for the desirability function.
|
|
552
|
+
*/
|
|
553
|
+
function significantPoints(param: PmpoParams, truncatedRange: boolean): number[] {
|
|
554
|
+
const points = getArgsOfGaussFunc(param.desAvg, param.desStd, truncatedRange);
|
|
555
|
+
|
|
556
|
+
/* Truncate range to show less points */
|
|
557
|
+
if (truncatedRange) {
|
|
558
|
+
const min = Math.min(param.min, param.desAvg - 3 * param.desStd);
|
|
559
|
+
const max = Math.max(param.max, param.desAvg + 3 * param.desStd);
|
|
560
|
+
|
|
561
|
+
return points
|
|
562
|
+
.filter((x) => (min <= x) && (x <= max))
|
|
563
|
+
.sort();
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return points;
|
|
567
|
+
} // significantPoints
|
|
541
568
|
|
|
542
|
-
|
|
569
|
+
/** Custom error class for pMPO-related errors. */
|
|
570
|
+
export class PmpoError extends Error {
|
|
571
|
+
constructor(message: string) {
|
|
572
|
+
super(message);
|
|
573
|
+
this.name = 'PmpoError';
|
|
574
|
+
}
|
|
543
575
|
}
|
|
544
576
|
|
|
545
|
-
/**
|
|
546
|
-
|
|
547
|
-
|
|
577
|
+
/** Returns the initial column for the desirability input, preferring boolean columns.
|
|
578
|
+
* @param cols List of columns to choose from.
|
|
579
|
+
* @return The initial column for the desirability input.
|
|
580
|
+
*/
|
|
581
|
+
export function getInitCol(cols: DG.Column[]): DG.Column {
|
|
582
|
+
for (const col of cols) {
|
|
583
|
+
if ((col.type === DG.COLUMN_TYPE.BOOL) && (col.stats.stdev > 0))
|
|
584
|
+
return col;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
for (const col of cols) {
|
|
588
|
+
if ((col.isNumerical) && (col.stats.stdev > 0))
|
|
589
|
+
return col;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return cols[0];
|
|
548
593
|
}
|
|
549
594
|
|
|
550
|
-
/** Returns
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
595
|
+
/** Returns a comparator function based on the given equality sign.
|
|
596
|
+
* @param sign Equality sign ('<', '<=', '>', '>=').
|
|
597
|
+
* @return Comparator function that takes two numbers and returns a boolean.
|
|
598
|
+
*/
|
|
599
|
+
function getComparator(sign: EQUALITY_SIGN): (a: number, b: number) => boolean {
|
|
600
|
+
switch (sign) {
|
|
601
|
+
case EQUALITY_SIGN.LESS: return (a, b) => a < b;
|
|
602
|
+
case EQUALITY_SIGN.LESS_OR_EQUAL: return (a, b) => a <= b;
|
|
603
|
+
case EQUALITY_SIGN.GREATER: return (a, b) => a > b;
|
|
604
|
+
case EQUALITY_SIGN.GREATER_OR_EQUAL: return (a, b) => a >= b;
|
|
605
|
+
|
|
606
|
+
default:
|
|
607
|
+
throw new Error(`Unsupported sign: ${sign}`);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
555
610
|
|
|
556
|
-
|
|
611
|
+
/** Converts a numeric column to a boolean column based on the given threshold and equality sign.
|
|
612
|
+
* @param numericCol Numeric column to convert.
|
|
613
|
+
* @param threshold Threshold value for comparison.
|
|
614
|
+
* @param sign Equality sign for comparison ('<', '<=', '>', '>=').
|
|
615
|
+
* @return Boolean column resulting from the comparison.
|
|
616
|
+
*/
|
|
617
|
+
export function getBoolDesirabilityColData(numericCol: DG.Column,
|
|
618
|
+
threshold: number, sign: EQUALITY_SIGN): {column: DG.Column, tooltip: string} {
|
|
619
|
+
const boolArr = new Array<boolean>(numericCol.length);
|
|
620
|
+
const numericArr = numericCol.getRawData();
|
|
621
|
+
|
|
622
|
+
const comparator = getComparator(sign);
|
|
623
|
+
|
|
624
|
+
for (let i = 0; i < numericCol.length; ++i)
|
|
625
|
+
boolArr[i] = comparator(numericArr[i], threshold);
|
|
626
|
+
|
|
627
|
+
return {
|
|
628
|
+
column: DG.Column.fromList(DG.COLUMN_TYPE.BOOL, '', boolArr),
|
|
629
|
+
tooltip: `Desirability based on the condition:\n\n **${numericCol.name} ${sign} ${threshold}**`,
|
|
630
|
+
};
|
|
557
631
|
}
|
|
558
632
|
|
|
559
|
-
/**
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
633
|
+
/** Checks whether the desirability column is valid based on the given threshold and equality sign.
|
|
634
|
+
* @param desCol Desirability column to check.
|
|
635
|
+
* @param threshold Threshold value for comparison.
|
|
636
|
+
* @param sign Equality sign for comparison ('<', '<=', '>', '>=').
|
|
637
|
+
* @return True if the desirability column is valid, false otherwise.
|
|
638
|
+
*/
|
|
639
|
+
export function isDesirabilityValid(desCol: DG.Column, threshold: number, sign: EQUALITY_SIGN): boolean {
|
|
640
|
+
const min = desCol.stats.min;
|
|
641
|
+
const max = desCol.stats.max;
|
|
642
|
+
|
|
643
|
+
switch (sign) {
|
|
644
|
+
case EQUALITY_SIGN.LESS:
|
|
645
|
+
return (max >= threshold) && (min < threshold);
|
|
646
|
+
|
|
647
|
+
case EQUALITY_SIGN.LESS_OR_EQUAL:
|
|
648
|
+
return (max > threshold) && (min <= threshold);
|
|
649
|
+
|
|
650
|
+
case EQUALITY_SIGN.GREATER:
|
|
651
|
+
return (min <= threshold) && (max > threshold);
|
|
652
|
+
|
|
653
|
+
default:
|
|
654
|
+
return (min < threshold) && (max >= threshold);
|
|
577
655
|
}
|
|
656
|
+
}
|
|
578
657
|
|
|
579
|
-
|
|
580
|
-
|
|
658
|
+
/** Converts a string column to a boolean column based on the given desirable categories.
|
|
659
|
+
* @param stringCol String column to convert.
|
|
660
|
+
* @param desirableCategories List of categories that should be considered as desirable.
|
|
661
|
+
* @return Boolean column resulting from the conversion and a tooltip describing the desirability.
|
|
662
|
+
*/
|
|
663
|
+
export function getDesirabilityColumnFromCategories(stringCol: DG.Column, desirableCategories: string[]):
|
|
664
|
+
{column: DG.Column, tooltip: string} {
|
|
665
|
+
const boolArr = new Array<boolean>(stringCol.length);
|
|
666
|
+
const raw = stringCol.getRawData();
|
|
667
|
+
const categories = stringCol.categories;
|
|
668
|
+
|
|
669
|
+
for (let i = 0; i < stringCol.length; ++i)
|
|
670
|
+
boolArr[i] = desirableCategories.includes(categories[raw[i]]);
|
|
671
|
+
|
|
672
|
+
const nonDesirableCategories = categories.filter((cat) => !desirableCategories.includes(cat));
|
|
673
|
+
|
|
674
|
+
const c = `\u2705 ${desirableCategories.join(', ')}`;
|
|
675
|
+
const unchecked = `\u274c ${nonDesirableCategories.join(', ')}`;
|
|
676
|
+
|
|
677
|
+
return {
|
|
678
|
+
column: DG.Column.fromList(DG.COLUMN_TYPE.BOOL, '', boolArr),
|
|
679
|
+
tooltip: `Desirability based on the selected categories:\n\n **${c}**\n\n **${unchecked}**`,
|
|
680
|
+
};
|
|
681
|
+
} // getDesirabilityColumnFromCategories
|
|
682
|
+
|
|
683
|
+
/** Returns a list of selected categories based on the given list of categories and preferable categories.
|
|
684
|
+
* @param categories List of categories to select from.
|
|
685
|
+
* @return List of selected categories.
|
|
686
|
+
*/
|
|
687
|
+
export function getSelectedCategories(categories: string[]): string[] {
|
|
688
|
+
const selected = categories.filter((cat) => PREFERABLE_CATEGORIES.includes(cat));
|
|
689
|
+
|
|
690
|
+
if (selected.length > 0)
|
|
691
|
+
return selected;
|
|
692
|
+
|
|
693
|
+
return [categories[0]];
|
|
694
|
+
}
|