@datagrok/eda 1.5.0 → 1.5.1
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/CHANGELOG.md +4 -0
- 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/package.json +2 -2
- package/src/pls/pls-constants.ts +5 -0
- package/src/pls/pls-tools.ts +103 -5
- package/src/probabilistic-scoring/prob-scoring.ts +5 -5
- package/test-console-output-1.log +118 -108
- package/test-record-1.mp4 +0 -0
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datagrok/eda",
|
|
3
3
|
"friendlyName": "EDA",
|
|
4
|
-
"version": "1.5.
|
|
4
|
+
"version": "1.5.1",
|
|
5
5
|
"description": "Exploratory Data Analysis Tools",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@datagrok-libraries/math": "^1.2.6",
|
|
8
8
|
"@datagrok-libraries/ml": "^6.10.10",
|
|
9
|
-
"@datagrok-libraries/statistics": "^1.12.
|
|
9
|
+
"@datagrok-libraries/statistics": "^1.12.1",
|
|
10
10
|
"@datagrok-libraries/tutorials": "^1.7.4",
|
|
11
11
|
"@datagrok-libraries/utils": "^4.7.0",
|
|
12
12
|
"@keckelt/tsne": "^1.0.2",
|
package/src/pls/pls-constants.ts
CHANGED
|
@@ -46,6 +46,7 @@ export enum TITLE {
|
|
|
46
46
|
BROWSE = 'Browse',
|
|
47
47
|
ANALYSIS = 'Features Analysis',
|
|
48
48
|
QUADRATIC = 'Quadratic',
|
|
49
|
+
BIAS = 'bias',
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
/** Tooltips */
|
|
@@ -102,6 +103,10 @@ export const X_COORD = 200;
|
|
|
102
103
|
export const Y_COORD = 200;
|
|
103
104
|
export const DELAY = 2000;
|
|
104
105
|
|
|
106
|
+
export const MAX_ROWS_IN_PREDICTION_TOOLTIP = 20;
|
|
107
|
+
|
|
108
|
+
export const NUMS_AFTER_COMMA = 3;
|
|
109
|
+
|
|
105
110
|
/** Curves colors */
|
|
106
111
|
export enum COLOR {
|
|
107
112
|
AXIS = '#838383',
|
package/src/pls/pls-tools.ts
CHANGED
|
@@ -4,9 +4,10 @@ import * as grok from 'datagrok-api/grok';
|
|
|
4
4
|
import * as ui from 'datagrok-api/ui';
|
|
5
5
|
import * as DG from 'datagrok-api/dg';
|
|
6
6
|
|
|
7
|
-
import {PLS_ANALYSIS, ERROR_MSG, TITLE, HINT, LINK, COMPONENTS,
|
|
7
|
+
import {PLS_ANALYSIS, ERROR_MSG, TITLE, HINT, LINK, COMPONENTS,
|
|
8
8
|
RESULT_NAMES, WASM_OUTPUT_IDX, RADIUS, LINE_WIDTH, COLOR, X_COORD, Y_COORD,
|
|
9
|
-
DEMO_INTRO_MD, DEMO_RESULTS_MD, DEMO_RESULTS
|
|
9
|
+
DEMO_INTRO_MD, DEMO_RESULTS_MD, DEMO_RESULTS, NUMS_AFTER_COMMA,
|
|
10
|
+
MAX_ROWS_IN_PREDICTION_TOOLTIP} from './pls-constants';
|
|
10
11
|
import {checkWasmDimensionReducerInputs, checkColumnType, checkMissingVals, describeElements} from '../utils';
|
|
11
12
|
import {_partialLeastSquareRegressionInWebWorker} from '../../wasm/EDAAPI';
|
|
12
13
|
import {carsDataframe} from '../data-generators';
|
|
@@ -54,6 +55,19 @@ function setStyle(valid: boolean, element: HTMLElement, tooltip: string, errorMs
|
|
|
54
55
|
}
|
|
55
56
|
};
|
|
56
57
|
|
|
58
|
+
function getModelFormulaTerms(loadingsRegrCoefsTable: DG.DataFrame, bias: number): Map<string, number> {
|
|
59
|
+
const featureNames = loadingsRegrCoefsTable.col(TITLE.FEATURE)!.toList() as string[];
|
|
60
|
+
const regrCoefs = loadingsRegrCoefsTable.col(TITLE.REGR_COEFS)!.getRawData();
|
|
61
|
+
|
|
62
|
+
const terms = new Map([[TITLE.BIAS as string, bias]]);
|
|
63
|
+
|
|
64
|
+
featureNames.forEach((name, idx) => {
|
|
65
|
+
terms.set(name, regrCoefs[idx]);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return terms;
|
|
69
|
+
}
|
|
70
|
+
|
|
57
71
|
/** Return lines */
|
|
58
72
|
export function getLines(names: string[]): DG.FormulaLine[] {
|
|
59
73
|
const lines: DG.FormulaLine[] = [];
|
|
@@ -115,7 +129,7 @@ export async function getPlsAnalysis(input: PlsInput): Promise<PlsOutput> {
|
|
|
115
129
|
|
|
116
130
|
/** Return debiased predction by PLS regression */
|
|
117
131
|
function debiasedPrediction(features: DG.ColumnList, params: DG.Column,
|
|
118
|
-
target: DG.Column, biasedPrediction: DG.Column): DG.Column {
|
|
132
|
+
target: DG.Column, biasedPrediction: DG.Column): {debiased: DG.Column, bias: number} {
|
|
119
133
|
const samples = target.length;
|
|
120
134
|
const dim = features.length;
|
|
121
135
|
const rawParams = params.getRawData();
|
|
@@ -131,7 +145,7 @@ function debiasedPrediction(features: DG.ColumnList, params: DG.Column,
|
|
|
131
145
|
for (let i = 0; i < samples; ++i)
|
|
132
146
|
debiased[i] = bias + biased[i];
|
|
133
147
|
|
|
134
|
-
return DG.Column.fromFloat32Array('Debiased', debiased, samples);
|
|
148
|
+
return {debiased: DG.Column.fromFloat32Array('Debiased', debiased, samples), bias: bias};
|
|
135
149
|
}
|
|
136
150
|
|
|
137
151
|
/** Return an input for the quadratic PLS regression */
|
|
@@ -223,7 +237,8 @@ async function performMVA(input: PlsInput, analysisType: PLS_ANALYSIS): Promise<
|
|
|
223
237
|
|
|
224
238
|
// 1. Predicted vs Reference scatter plot
|
|
225
239
|
// Debias prediction (since PLS center data)
|
|
226
|
-
const
|
|
240
|
+
const debiased = debiasedPrediction(features, result.regressionCoefficients, input.predict, result.prediction);
|
|
241
|
+
const pred = debiased.debiased;
|
|
227
242
|
pred.name = cols.getUnusedName(`${input.predict.name} ${RESULT_NAMES.SUFFIX}`);
|
|
228
243
|
cols.add(pred);
|
|
229
244
|
const predictVsReferScatter = view.addViewer(DG.Viewer.scatterPlot(sourceTable, {
|
|
@@ -250,6 +265,9 @@ async function performMVA(input: PlsInput, analysisType: PLS_ANALYSIS): Promise<
|
|
|
250
265
|
help: LINK.COEFFS,
|
|
251
266
|
showValueSelector: false,
|
|
252
267
|
showStackSelector: false,
|
|
268
|
+
description: `bias = ${debiased.bias.toFixed(NUMS_AFTER_COMMA)}`,
|
|
269
|
+
descriptionVisibilityMode: 'Always',
|
|
270
|
+
descriptionPosition: 'Bottom',
|
|
253
271
|
}));
|
|
254
272
|
|
|
255
273
|
// 3. Loadings Scatter Plot
|
|
@@ -348,6 +366,10 @@ async function performMVA(input: PlsInput, analysisType: PLS_ANALYSIS): Promise<
|
|
|
348
366
|
['left', 'left', 'right', 'right', 'left'],
|
|
349
367
|
);
|
|
350
368
|
}
|
|
369
|
+
|
|
370
|
+
// Add formula tooltip to the prediction column
|
|
371
|
+
const modelFormulaTerms = getModelFormulaTerms(loadingsRegrCoefsTable, debiased.bias);
|
|
372
|
+
setPredictionTooltip(view, pred, modelFormulaTerms);
|
|
351
373
|
} // performMVA
|
|
352
374
|
|
|
353
375
|
/** Run multivariate analysis (PLS) */
|
|
@@ -559,3 +581,79 @@ export async function runDemoMVA(): Promise<void> {
|
|
|
559
581
|
|
|
560
582
|
await runMVA(PLS_ANALYSIS.DEMO);
|
|
561
583
|
}
|
|
584
|
+
|
|
585
|
+
function setPredictionTooltip(view: DG.TableView, predCol: DG.Column, modelTerms: Map<string, number>): void {
|
|
586
|
+
view.grid.onCellTooltip((cell, x, y) => {
|
|
587
|
+
if (cell.isColHeader) {
|
|
588
|
+
const cellCol = cell.tableColumn;
|
|
589
|
+
|
|
590
|
+
if (cellCol == null)
|
|
591
|
+
return false;
|
|
592
|
+
|
|
593
|
+
if (cellCol.name === predCol.name) {
|
|
594
|
+
ui.tooltip.show(getPredictionTooltip(modelTerms, predCol), x, y);
|
|
595
|
+
return true;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
return false;
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function getPredictionTooltip(modelTerms: Map<string, number>, predCol: DG.Column): HTMLElement {
|
|
603
|
+
let idx = 0;
|
|
604
|
+
const bias = modelTerms.get(TITLE.BIAS) ?? 0;
|
|
605
|
+
const elements: HTMLElement[] = [];
|
|
606
|
+
if (Math.abs(bias) > 0) {
|
|
607
|
+
const biasEl = ui.divText(`${bias}`);
|
|
608
|
+
biasEl.style.marginTop = '2px';
|
|
609
|
+
biasEl.style.marginLeft = '4px';
|
|
610
|
+
elements.push(biasEl);
|
|
611
|
+
++idx;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const sortedTerms = [...modelTerms.entries()]
|
|
615
|
+
.filter(([key]) => key !== TITLE.BIAS)
|
|
616
|
+
.sort((a, b) => Math.abs(b[1]) - Math.abs(a[1]));
|
|
617
|
+
|
|
618
|
+
const maxFeatureRows = MAX_ROWS_IN_PREDICTION_TOOLTIP - elements.length;
|
|
619
|
+
const hasOverflow = sortedTerms.length > maxFeatureRows;
|
|
620
|
+
const visibleTerms = hasOverflow ? sortedTerms.slice(0, maxFeatureRows - 1) : sortedTerms;
|
|
621
|
+
|
|
622
|
+
for (const [key, value] of visibleTerms) {
|
|
623
|
+
const signEl = ui.divText(idx > 0 ? '+ ' : '');
|
|
624
|
+
signEl.style.marginRight = '4px';
|
|
625
|
+
signEl.style.marginLeft = '4px';
|
|
626
|
+
|
|
627
|
+
const featureEl = ui.divText(`${key}`);
|
|
628
|
+
featureEl.style.fontWeight = 'bold';
|
|
629
|
+
|
|
630
|
+
const valueEl = ui.divText(` * ${value > 0 ? value : `(${value})`}`);
|
|
631
|
+
valueEl.style.marginLeft = '4px';
|
|
632
|
+
|
|
633
|
+
const rowEl = ui.divH([signEl, featureEl, valueEl]);
|
|
634
|
+
rowEl.style.marginTop = '4px';
|
|
635
|
+
elements.push(rowEl);
|
|
636
|
+
|
|
637
|
+
++idx;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
if (hasOverflow) {
|
|
641
|
+
const hidden = sortedTerms.length - visibleTerms.length;
|
|
642
|
+
const ellipsisEl = ui.divText(`(${hidden} more term${hidden > 1 ? 's' : ''})`);
|
|
643
|
+
ellipsisEl.style.marginTop = '4px';
|
|
644
|
+
ellipsisEl.style.marginLeft = '4px';
|
|
645
|
+
ellipsisEl.style.fontStyle = 'italic';
|
|
646
|
+
elements.push(ellipsisEl);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
const headerEl = ui.divText('Formula:');
|
|
650
|
+
|
|
651
|
+
const leftEl = ui.divText(`${predCol.name} = `);
|
|
652
|
+
leftEl.style.fontWeight = 'bold';
|
|
653
|
+
leftEl.style.marginTop = '4px';
|
|
654
|
+
|
|
655
|
+
const elementsContainer = ui.divV(elements);
|
|
656
|
+
elementsContainer.style.marginTop = '4px';
|
|
657
|
+
|
|
658
|
+
return ui.divV([headerEl, leftEl, elementsContainer]);
|
|
659
|
+
}
|
|
@@ -540,13 +540,13 @@ export class Pmpo {
|
|
|
540
540
|
const rows = rootsCol.querySelectorAll('div.d4-flex-row.ui-div.statistics-mpo-row');
|
|
541
541
|
|
|
542
542
|
rows.forEach((row, idx) => {
|
|
543
|
-
const
|
|
544
|
-
if (
|
|
543
|
+
const editor = row.querySelector('.statistics-mpo-line-editor') as HTMLElement;
|
|
544
|
+
if (!editor)
|
|
545
545
|
return;
|
|
546
546
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
this.desirabilityProfileRoots.set(names[idx],
|
|
547
|
+
editor.style.width = '100%';
|
|
548
|
+
editor.style.height = '100%';
|
|
549
|
+
this.desirabilityProfileRoots.set(names[idx], editor);
|
|
550
550
|
});
|
|
551
551
|
} // updateDesirabilityProfileData
|
|
552
552
|
|