@datagrok/peptides 1.4.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/dist/package-test.js +1480 -1006
- package/dist/package.js +1252 -921
- package/dist/vendors-node_modules_datagrok-libraries_ml_src_workers_dimensionality-reducer_js.js +3 -3
- package/files/tests/aligned_5k.d42 +0 -0
- package/package.json +2 -2
- package/src/__jest__/remote.test.ts +12 -5
- package/src/__jest__/test-node.ts +1 -1
- package/src/model.ts +432 -479
- package/src/package-test.ts +1 -0
- package/src/package.ts +15 -14
- package/src/tests/algorithms.ts +29 -20
- package/src/tests/core.ts +3 -6
- package/src/tests/utils.ts +4 -4
- package/src/utils/algorithms.ts +25 -22
- package/src/utils/cell-renderer.ts +22 -16
- package/src/utils/constants.ts +3 -36
- package/src/utils/misc.ts +14 -15
- package/src/utils/peptide-similarity-space.ts +4 -7
- package/src/utils/statistics.ts +18 -9
- package/src/utils/types.ts +6 -0
- package/src/viewers/logo-summary.ts +108 -39
- package/src/viewers/peptide-space-viewer.ts +5 -5
- package/src/viewers/sar-viewer.ts +229 -56
- package/src/widgets/distribution.ts +35 -10
- package/src/widgets/manual-alignment.ts +1 -1
- package/src/widgets/mutation-cliffs.ts +2 -3
- package/src/widgets/peptides.ts +67 -36
- package/src/widgets/settings.ts +20 -16
- package/{test-Peptides-62cc009524f3-d4fc804f.html → test-Peptides-4775b69ad08a-1bc1a2b4.html} +33 -32
- package/tsconfig.json +3 -3
package/src/package-test.ts
CHANGED
package/src/package.ts
CHANGED
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
import * as grok from 'datagrok-api/grok';
|
|
3
3
|
import * as ui from 'datagrok-api/ui';
|
|
4
4
|
import * as DG from 'datagrok-api/dg';
|
|
5
|
-
import * as C from './utils/constants';
|
|
6
5
|
|
|
7
6
|
import {analyzePeptidesUI} from './widgets/peptides';
|
|
8
7
|
import {PeptideSimilaritySpaceWidget} from './utils/peptide-similarity-space';
|
|
9
8
|
import {manualAlignmentWidget} from './widgets/manual-alignment';
|
|
10
|
-
import {
|
|
9
|
+
import {MonomerPosition, MostPotentResiduesViewer} from './viewers/sar-viewer';
|
|
11
10
|
|
|
12
11
|
import {PeptideSpaceViewer} from './viewers/peptide-space-viewer';
|
|
13
12
|
import {LogoSummary} from './viewers/logo-summary';
|
|
@@ -19,7 +18,7 @@ export const _package = new DG.Package();
|
|
|
19
18
|
let currentTable: DG.DataFrame;
|
|
20
19
|
let alignedSequenceColumn: DG.Column;
|
|
21
20
|
|
|
22
|
-
export function getMonomerWorks() {
|
|
21
|
+
export function getMonomerWorks(): MonomerWorks | null {
|
|
23
22
|
return monomerWorks;
|
|
24
23
|
};
|
|
25
24
|
|
|
@@ -41,7 +40,7 @@ export async function Peptides(): Promise<void> {
|
|
|
41
40
|
const wikiLink = ui.link('wiki', 'https://github.com/datagrok-ai/public/blob/master/help/domains/bio/peptides.md');
|
|
42
41
|
const textLink = ui.inlineText(['For more details, see our ', wikiLink, '.']);
|
|
43
42
|
if (monomerWorks == null) {
|
|
44
|
-
|
|
43
|
+
const lib = await grok.functions.call('Bio:getBioLib');
|
|
45
44
|
monomerWorks = new MonomerWorks(lib);
|
|
46
45
|
}
|
|
47
46
|
const appDescription = ui.info(
|
|
@@ -82,10 +81,13 @@ export async function Peptides(): Promise<void> {
|
|
|
82
81
|
|
|
83
82
|
//top-menu: Bio | Peptides...
|
|
84
83
|
//name: Bio Peptides
|
|
85
|
-
export
|
|
86
|
-
const analyzeObject =
|
|
87
|
-
const dialog = ui.dialog('Analyze Peptides').add(analyzeObject.host).onOK(
|
|
88
|
-
|
|
84
|
+
export function peptidesDialog(): DG.Dialog {
|
|
85
|
+
const analyzeObject = analyzePeptidesUI(grok.shell.t);
|
|
86
|
+
const dialog = ui.dialog('Analyze Peptides').add(analyzeObject.host).onOK(async () => {
|
|
87
|
+
const startSuccess = analyzeObject.callback();
|
|
88
|
+
if (!startSuccess)
|
|
89
|
+
dialog.show();
|
|
90
|
+
});
|
|
89
91
|
return dialog.show();
|
|
90
92
|
}
|
|
91
93
|
|
|
@@ -93,9 +95,9 @@ export async function peptidesDialog(): Promise<DG.Dialog> {
|
|
|
93
95
|
//tags: panel, widgets
|
|
94
96
|
//input: column col {semType: Macromolecule}
|
|
95
97
|
//output: widget result
|
|
96
|
-
export
|
|
98
|
+
export function peptidesPanel(col: DG.Column): DG.Widget {
|
|
97
99
|
[currentTable, alignedSequenceColumn] = getOrDefine(col.dataFrame, col);
|
|
98
|
-
const analyzeObject =
|
|
100
|
+
const analyzeObject = analyzePeptidesUI(currentTable, alignedSequenceColumn);
|
|
99
101
|
return new DG.Widget(analyzeObject.host);
|
|
100
102
|
}
|
|
101
103
|
|
|
@@ -103,8 +105,8 @@ export async function peptidesPanel(col: DG.Column): Promise<DG.Widget> {
|
|
|
103
105
|
//description: Peptides SAR Viewer
|
|
104
106
|
//tags: viewer
|
|
105
107
|
//output: viewer result
|
|
106
|
-
export function sar():
|
|
107
|
-
return new
|
|
108
|
+
export function sar(): MonomerPosition {
|
|
109
|
+
return new MonomerPosition();
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
//name: peptide-sar-viewer-vertical
|
|
@@ -174,8 +176,7 @@ export function getPeptidesStructure(col: DG.Column): DG.Widget {
|
|
|
174
176
|
|
|
175
177
|
function getOrDefine(dataframe?: DG.DataFrame, column?: DG.Column | null): [DG.DataFrame, DG.Column] {
|
|
176
178
|
dataframe ??= grok.shell.t;
|
|
177
|
-
|
|
178
|
-
column ??= dataframe.getCol(C.COLUMNS_NAMES.MACROMOLECULE);
|
|
179
|
+
column ??= dataframe.columns.bySemType(DG.SEMTYPE.MACROMOLECULE)!;
|
|
179
180
|
if (column === null)
|
|
180
181
|
throw new Error('Table does not contain aligned sequence columns');
|
|
181
182
|
|
package/src/tests/algorithms.ts
CHANGED
|
@@ -1,29 +1,27 @@
|
|
|
1
|
-
import * as grok from 'datagrok-api/grok';
|
|
2
1
|
import * as DG from 'datagrok-api/dg';
|
|
3
2
|
|
|
4
|
-
import {category, test, expect,
|
|
3
|
+
import {category, test, expect, before} from '@datagrok-libraries/utils/src/test';
|
|
5
4
|
|
|
6
5
|
import {_package} from '../package-test';
|
|
7
|
-
import {startAnalysis} from '../widgets/peptides';
|
|
8
|
-
import {PeptidesModel} from '../model';
|
|
9
|
-
import * as C from '../utils/constants';
|
|
10
|
-
import {scaleActivity} from '../utils/misc';
|
|
11
|
-
import {ALPHABET, TAGS, NOTATION, ALIGNMENT} from '@datagrok-libraries/bio';
|
|
12
6
|
import {findMutations} from '../utils/algorithms';
|
|
13
7
|
import * as type from '../utils/types';
|
|
14
8
|
|
|
15
9
|
category('Algorithms', () => {
|
|
16
|
-
let activityCol:
|
|
17
|
-
let monomerColumns:
|
|
10
|
+
let activityCol: type.RawData;
|
|
11
|
+
let monomerColumns: type.RawColumn[];
|
|
18
12
|
let settings: type.PeptidesSettings;
|
|
19
13
|
|
|
20
14
|
before(async () => {
|
|
21
|
-
activityCol = DG.Column.fromList('int', 'test', [1, 2, 5]);
|
|
15
|
+
activityCol = DG.Column.fromList('int', 'test', [1, 2, 5]).getRawData();
|
|
22
16
|
monomerColumns = [
|
|
23
|
-
DG.Column.fromList('string', '1', '
|
|
24
|
-
DG.Column.fromList('string', '2', '
|
|
25
|
-
DG.Column.fromList('string', '3', '
|
|
26
|
-
]
|
|
17
|
+
DG.Column.fromList('string', '1', 'AAA'.split('')),
|
|
18
|
+
DG.Column.fromList('string', '2', 'BCC'.split('')),
|
|
19
|
+
DG.Column.fromList('string', '3', 'CCD'.split('')),
|
|
20
|
+
].map((col) => ({
|
|
21
|
+
name: col.name,
|
|
22
|
+
rawData: col.getRawData(),
|
|
23
|
+
cat: col.categories,
|
|
24
|
+
}));
|
|
27
25
|
settings = {maxMutations: 1, minActivityDelta: 2};
|
|
28
26
|
});
|
|
29
27
|
|
|
@@ -40,12 +38,23 @@ category('Algorithms', () => {
|
|
|
40
38
|
|
|
41
39
|
const c3 = c.get('3')!;
|
|
42
40
|
const d3 = d.get('3')!;
|
|
43
|
-
expect(c3.has(
|
|
44
|
-
expect(d3.has(
|
|
41
|
+
expect(c3.has(1), true);
|
|
42
|
+
expect(d3.has(2), true);
|
|
45
43
|
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
expect(
|
|
49
|
-
expect(
|
|
44
|
+
const c31 = c3.get(1)!;
|
|
45
|
+
const d32 = d3.get(2)!;
|
|
46
|
+
expect(c31[0], 2);
|
|
47
|
+
expect(d32[0], 1);
|
|
50
48
|
});
|
|
49
|
+
|
|
50
|
+
test('MutationCliffs - Benchmark 5k', async () => {
|
|
51
|
+
const df = (await _package.files.readBinaryDataFrames('tests/aligned_5k.d42'))[0];
|
|
52
|
+
const activityCol: type.RawData = df.getCol('Activity').getRawData();
|
|
53
|
+
const monomerCols: type.RawColumn[] = [];
|
|
54
|
+
for (let i = 1; i < 16; ++i) {
|
|
55
|
+
const col = df.getCol(i.toString());
|
|
56
|
+
monomerCols.push({name: col.name, rawData: col.getRawData(), cat: col.categories});
|
|
57
|
+
}
|
|
58
|
+
DG.time('MutationCliffs', () => findMutations(activityCol, monomerCols));
|
|
59
|
+
}, {skipReason: 'Benchmark'});
|
|
51
60
|
});
|
package/src/tests/core.ts
CHANGED
|
@@ -15,14 +15,11 @@ category('Core', () => {
|
|
|
15
15
|
let simpleActivityCol: DG.Column<number>;
|
|
16
16
|
let simpleAlignedSeqCol: DG.Column<string>;
|
|
17
17
|
let simpleScaledCol: DG.Column<number>;
|
|
18
|
-
let scalingFormula: (x: number) => number;
|
|
19
|
-
let simpleScaledColName: string;
|
|
20
18
|
|
|
21
19
|
let complexTable: DG.DataFrame;
|
|
22
20
|
let complexActivityCol: DG.Column<number>;
|
|
23
21
|
let complexAlignedSeqCol: DG.Column<string>;
|
|
24
22
|
let complexScaledCol: DG.Column<number>;
|
|
25
|
-
let complexScaledColName: string;
|
|
26
23
|
const alignedSequenceCol = 'AlignedSequence';
|
|
27
24
|
|
|
28
25
|
let model: PeptidesModel | null = null;
|
|
@@ -32,7 +29,7 @@ category('Core', () => {
|
|
|
32
29
|
simpleTable = DG.DataFrame.fromCsv(await _package.files.readAsText('aligned.csv'));
|
|
33
30
|
simpleActivityCol = simpleTable.getCol(simpleActivityColName);
|
|
34
31
|
simpleAlignedSeqCol = simpleTable.getCol(alignedSequenceCol);
|
|
35
|
-
simpleAlignedSeqCol.semType =
|
|
32
|
+
simpleAlignedSeqCol.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
36
33
|
simpleAlignedSeqCol.setTag(C.TAGS.ALPHABET, ALPHABET.PT);
|
|
37
34
|
simpleAlignedSeqCol.setTag(DG.TAGS.UNITS, NOTATION.FASTA);
|
|
38
35
|
simpleAlignedSeqCol.setTag(TAGS.aligned, ALIGNMENT.SEQ_MSA);
|
|
@@ -52,7 +49,7 @@ category('Core', () => {
|
|
|
52
49
|
complexTable = DG.DataFrame.fromCsv(await _package.files.readAsText('aligned_2.csv'));
|
|
53
50
|
complexActivityCol = complexTable.getCol(complexActivityColName);
|
|
54
51
|
complexAlignedSeqCol = complexTable.getCol('MSA');
|
|
55
|
-
complexAlignedSeqCol.semType =
|
|
52
|
+
complexAlignedSeqCol.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
56
53
|
complexAlignedSeqCol.setTag(C.TAGS.ALPHABET, ALPHABET.UN);
|
|
57
54
|
complexAlignedSeqCol.setTag(DG.TAGS.UNITS, NOTATION.SEPARATOR);
|
|
58
55
|
complexAlignedSeqCol.setTag(TAGS.aligned, ALIGNMENT.SEQ_MSA);
|
|
@@ -74,7 +71,7 @@ category('Core', () => {
|
|
|
74
71
|
simpleTable = DG.DataFrame.fromCsv(await _package.files.readAsText('aligned.csv'));
|
|
75
72
|
simpleActivityCol = simpleTable.getCol(simpleActivityColName);
|
|
76
73
|
simpleAlignedSeqCol = simpleTable.getCol(alignedSequenceCol);
|
|
77
|
-
simpleAlignedSeqCol.semType =
|
|
74
|
+
simpleAlignedSeqCol.semType = DG.SEMTYPE.MACROMOLECULE;
|
|
78
75
|
simpleAlignedSeqCol.setTag(C.TAGS.ALPHABET, ALPHABET.PT);
|
|
79
76
|
simpleAlignedSeqCol.setTag(DG.TAGS.UNITS, NOTATION.FASTA);
|
|
80
77
|
simpleAlignedSeqCol.setTag(TAGS.aligned, ALIGNMENT.SEQ_MSA);
|
package/src/tests/utils.ts
CHANGED
|
@@ -45,8 +45,8 @@ export async function _testDimensionalityReducer(
|
|
|
45
45
|
|
|
46
46
|
const [X, Y] = embcols as Array<Float32Array>;
|
|
47
47
|
|
|
48
|
-
expect(X.every((v) => v !== null && v
|
|
49
|
-
expect(Y.every((v) => v !== null && v
|
|
48
|
+
expect(X.every((v) => v !== null && !Number.isNaN(v)), true);
|
|
49
|
+
expect(Y.every((v) => v !== null && !Number.isNaN(v)), true);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
@@ -64,12 +64,12 @@ export async function _testDimensionalityReducer(
|
|
|
64
64
|
export async function _testPeptideSimilaritySpaceViewer(table: DG.DataFrame, alignedSequencesColumn: DG.Column,
|
|
65
65
|
method: string, measure: string, cyclesCount: number): Promise<void> {
|
|
66
66
|
const viewer = await createPeptideSimilaritySpaceViewer(
|
|
67
|
-
table, method, measure, cyclesCount,
|
|
67
|
+
table, method, measure, cyclesCount, alignedSequencesColumn, undefined);
|
|
68
68
|
const df = viewer.dataFrame;
|
|
69
69
|
|
|
70
70
|
const axesNames = ['~X', '~Y', '~MW'];
|
|
71
71
|
const axes = axesNames.map((v) => df.getCol(v).getRawData() as Float32Array);
|
|
72
72
|
|
|
73
73
|
for (const ax of axes)
|
|
74
|
-
expect(ax.every((v) => v !== null && v
|
|
74
|
+
expect(ax.every((v) => v !== null && !Number.isNaN(v)), true);
|
|
75
75
|
}
|
package/src/utils/algorithms.ts
CHANGED
|
@@ -1,34 +1,32 @@
|
|
|
1
|
-
import * as DG from 'datagrok-api/dg';
|
|
2
|
-
|
|
3
1
|
import * as C from './constants';
|
|
4
2
|
import * as type from './types';
|
|
5
3
|
import {getTypedArrayConstructor} from './misc';
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
type MutationCliffInfo = {pos: string, seq1monomer: string, seq2monomer: string, seq1Idx: number, seq2Idx: number};
|
|
6
|
+
|
|
7
|
+
export function findMutations(activityArray: type.RawData, monomerInfoArray: type.RawColumn[],
|
|
9
8
|
settings: type.PeptidesSettings = {}): type.SubstitutionsInfo {
|
|
10
|
-
const nCols =
|
|
9
|
+
const nCols = monomerInfoArray.length;
|
|
11
10
|
if (nCols == 0)
|
|
12
11
|
throw new Error(`PepAlgorithmError: Couldn't find any column of semType '${C.SEM_TYPES.MONOMER}'`);
|
|
13
12
|
|
|
14
13
|
const substitutionsInfo: type.SubstitutionsInfo = new Map();
|
|
15
|
-
const nRows =
|
|
14
|
+
const nRows = activityArray.length;
|
|
16
15
|
for (let seq1Idx = 0; seq1Idx < nRows - 1; seq1Idx++) {
|
|
17
16
|
for (let seq2Idx = seq1Idx + 1; seq2Idx < nRows; seq2Idx++) {
|
|
18
17
|
let substCounter = 0;
|
|
19
|
-
const activityValSeq1 =
|
|
20
|
-
const activityValSeq2 =
|
|
18
|
+
const activityValSeq1 = activityArray[seq1Idx];
|
|
19
|
+
const activityValSeq2 = activityArray[seq2Idx];
|
|
21
20
|
const delta = activityValSeq1 - activityValSeq2;
|
|
22
21
|
if (Math.abs(delta) < (settings.minActivityDelta ?? 0))
|
|
23
22
|
continue;
|
|
24
23
|
|
|
25
24
|
let substCounterFlag = false;
|
|
26
|
-
const tempData:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
if (seq1monomer == seq2monomer)
|
|
25
|
+
const tempData: MutationCliffInfo[] = [];
|
|
26
|
+
for (const monomerInfo of monomerInfoArray) {
|
|
27
|
+
const seq1category = monomerInfo.rawData[seq1Idx];
|
|
28
|
+
const seq2category = monomerInfo.rawData[seq2Idx];
|
|
29
|
+
if (seq1category == seq2category)
|
|
32
30
|
continue;
|
|
33
31
|
|
|
34
32
|
substCounter++;
|
|
@@ -37,9 +35,9 @@ export function findMutations(activityCol: DG.Column<number>, monomerColumns: DG
|
|
|
37
35
|
break;
|
|
38
36
|
|
|
39
37
|
tempData.push({
|
|
40
|
-
pos:
|
|
41
|
-
seq1monomer:
|
|
42
|
-
seq2monomer:
|
|
38
|
+
pos: monomerInfo.name,
|
|
39
|
+
seq1monomer: monomerInfo.cat![seq1category],
|
|
40
|
+
seq2monomer: monomerInfo.cat![seq2category],
|
|
43
41
|
seq1Idx: seq1Idx,
|
|
44
42
|
seq2Idx: seq2Idx,
|
|
45
43
|
});
|
|
@@ -49,20 +47,22 @@ export function findMutations(activityCol: DG.Column<number>, monomerColumns: DG
|
|
|
49
47
|
continue;
|
|
50
48
|
|
|
51
49
|
for (const tempDataElement of tempData) {
|
|
52
|
-
const position = tempDataElement.pos;
|
|
53
|
-
|
|
54
50
|
//Working with seq1monomer
|
|
55
51
|
const seq1monomer = tempDataElement.seq1monomer;
|
|
56
52
|
if (!substitutionsInfo.has(seq1monomer))
|
|
57
53
|
substitutionsInfo.set(seq1monomer, new Map());
|
|
58
54
|
|
|
55
|
+
const position = tempDataElement.pos;
|
|
56
|
+
|
|
59
57
|
let positionsMap = substitutionsInfo.get(seq1monomer)!;
|
|
60
58
|
if (!positionsMap.has(position))
|
|
61
59
|
positionsMap.set(position, new Map());
|
|
62
60
|
|
|
63
61
|
let indexes = positionsMap.get(position)!;
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
if (indexes.has(seq1Idx))
|
|
63
|
+
(indexes.get(seq1Idx)! as number[]).push(seq2Idx);
|
|
64
|
+
else
|
|
65
|
+
indexes.set(seq1Idx, [seq2Idx]);
|
|
66
66
|
|
|
67
67
|
//Working with seq2monomer
|
|
68
68
|
const seq2monomer = tempDataElement.seq2monomer;
|
|
@@ -74,7 +74,10 @@ export function findMutations(activityCol: DG.Column<number>, monomerColumns: DG
|
|
|
74
74
|
positionsMap.set(position, new Map());
|
|
75
75
|
|
|
76
76
|
indexes = positionsMap.get(position)!;
|
|
77
|
-
|
|
77
|
+
if (indexes.has(seq2Idx))
|
|
78
|
+
(indexes.get(seq2Idx)! as number[]).push(seq1Idx);
|
|
79
|
+
else
|
|
80
|
+
indexes.set(seq2Idx, [seq1Idx]);
|
|
78
81
|
}
|
|
79
82
|
}
|
|
80
83
|
}
|
|
@@ -11,11 +11,10 @@ function renderCellSelection(canvasContext: CanvasRenderingContext2D, bound: DG.
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/** A function that sets amino acid residue cell renderer to the specified column */
|
|
14
|
-
export function setAARRenderer(col: DG.Column, alphabet: string
|
|
14
|
+
export function setAARRenderer(col: DG.Column, alphabet: string): void {
|
|
15
15
|
col.semType = C.SEM_TYPES.MONOMER;
|
|
16
16
|
col.setTag('cell.renderer', C.SEM_TYPES.MONOMER);
|
|
17
17
|
col.tags[C.TAGS.ALPHABET] = alphabet;
|
|
18
|
-
// setTimeout(() => grid.columns.byName(col.name)!.width = 60, timeout);
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D, currentAAR: string,
|
|
@@ -62,9 +61,10 @@ export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D,
|
|
|
62
61
|
if (substitutionsInfo.size > 0) {
|
|
63
62
|
canvasContext.textBaseline = 'middle';
|
|
64
63
|
canvasContext.textAlign = 'center';
|
|
65
|
-
// canvasContext.fillStyle = DG.Color.toHtml(DG.Color.getContrastColor(DG.Color.fromHtml(coef)));
|
|
66
64
|
canvasContext.fillStyle = DG.Color.toHtml(DG.Color.black);
|
|
67
65
|
canvasContext.font = '13px Roboto, Roboto Local, sans-serif';
|
|
66
|
+
canvasContext.shadowBlur = 5;
|
|
67
|
+
canvasContext.shadowColor = DG.Color.toHtml(DG.Color.white);
|
|
68
68
|
let substValue = 0;
|
|
69
69
|
substitutionsInfo.get(currentAAR)?.get(currentPosition)?.forEach((idxs) => substValue += idxs.length);
|
|
70
70
|
if (substValue && substValue != 0)
|
|
@@ -77,7 +77,10 @@ export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D,
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
export function renderInvaraintMapCell(canvasContext: CanvasRenderingContext2D, currentAAR: string,
|
|
80
|
-
currentPosition: string, invariantMapSelection: types.PositionToAARList, cellValue: number, bound: DG.Rect
|
|
80
|
+
currentPosition: string, invariantMapSelection: types.PositionToAARList, cellValue: number, bound: DG.Rect,
|
|
81
|
+
color: number): void {
|
|
82
|
+
canvasContext.fillStyle = DG.Color.toHtml(color);
|
|
83
|
+
canvasContext.fillRect(bound.x, bound.y, bound.width, bound.height);
|
|
81
84
|
canvasContext.font = '13px Roboto, Roboto Local, sans-serif';
|
|
82
85
|
canvasContext.textAlign = 'center';
|
|
83
86
|
canvasContext.textBaseline = 'middle';
|
|
@@ -89,7 +92,7 @@ export function renderInvaraintMapCell(canvasContext: CanvasRenderingContext2D,
|
|
|
89
92
|
renderCellSelection(canvasContext, bound);
|
|
90
93
|
}
|
|
91
94
|
|
|
92
|
-
export function renderLogoSummaryCell(canvasContext: CanvasRenderingContext2D, cellValue: number,
|
|
95
|
+
export function renderLogoSummaryCell(canvasContext: CanvasRenderingContext2D, cellValue: string, cellRawData: number,
|
|
93
96
|
clusterSelection: number[], bound: DG.Rect): void {
|
|
94
97
|
canvasContext.font = '13px Roboto, Roboto Local, sans-serif';
|
|
95
98
|
canvasContext.textAlign = 'center';
|
|
@@ -97,7 +100,7 @@ export function renderLogoSummaryCell(canvasContext: CanvasRenderingContext2D, c
|
|
|
97
100
|
canvasContext.fillStyle = '#000';
|
|
98
101
|
canvasContext.fillText(cellValue.toString(), bound.x + (bound.width / 2), bound.y + (bound.height / 2), bound.width);
|
|
99
102
|
|
|
100
|
-
if (clusterSelection.includes(
|
|
103
|
+
if (clusterSelection.includes(cellRawData))
|
|
101
104
|
renderCellSelection(canvasContext, bound);
|
|
102
105
|
}
|
|
103
106
|
|
|
@@ -111,25 +114,26 @@ export function drawLogoInBounds(ctx: CanvasRenderingContext2D, bounds: DG.Rect,
|
|
|
111
114
|
drawOptions.marginVertical ??= 5;
|
|
112
115
|
drawOptions.marginHorizontal ??= 5;
|
|
113
116
|
|
|
117
|
+
const pr = window.devicePixelRatio;
|
|
114
118
|
const totalSpaceBetweenLetters = (statsInfo.orderedIndexes.length - 1) * drawOptions.upperLetterAscent;
|
|
115
|
-
const barHeight = bounds.height - 2 * drawOptions.marginVertical - totalSpaceBetweenLetters;
|
|
119
|
+
const barHeight = (bounds.height - 2 * drawOptions.marginVertical - totalSpaceBetweenLetters) * pr;
|
|
116
120
|
const leftShift = drawOptions.marginHorizontal * 2;
|
|
117
|
-
const barWidth = bounds.width - leftShift * 2;
|
|
118
|
-
const xStart = bounds.x + leftShift;
|
|
119
|
-
const selectionWidth = 4;
|
|
120
|
-
const xSelection = bounds.x + 3;
|
|
121
|
-
let currentY = bounds.y + drawOptions.marginVertical;
|
|
121
|
+
const barWidth = (bounds.width - leftShift * 2) * pr;
|
|
122
|
+
const xStart = (bounds.x + leftShift) * pr;
|
|
123
|
+
const selectionWidth = 4 * pr;
|
|
124
|
+
const xSelection = (bounds.x + 3) * pr;
|
|
125
|
+
let currentY = (bounds.y + drawOptions.marginVertical) * pr;
|
|
122
126
|
|
|
123
127
|
const monomerBounds: {[monomer: string]: DG.Rect} = {};
|
|
124
128
|
for (const index of statsInfo.orderedIndexes) {
|
|
125
129
|
const monomer = statsInfo.monomerCol.get(index)!;
|
|
126
130
|
const monomerHeight = barHeight * (statsInfo.countCol.get(index)! / rowCount);
|
|
127
131
|
const selectionHeight = barHeight * ((monomerSelectionStats[monomer] ?? 0) / rowCount);
|
|
128
|
-
const currentBound = new DG.Rect(xStart, currentY, barWidth, monomerHeight);
|
|
132
|
+
const currentBound = new DG.Rect(xStart / pr, currentY / pr, barWidth / pr, monomerHeight / pr);
|
|
129
133
|
monomerBounds[monomer] = currentBound;
|
|
130
134
|
|
|
131
135
|
ctx.resetTransform();
|
|
132
|
-
if (monomer !== '-') {
|
|
136
|
+
if (monomer !== '-' && monomer !== '') {
|
|
133
137
|
const monomerTxt = bio.monomerToShort(monomer, 5);
|
|
134
138
|
const mTm: TextMetrics = ctx.measureText(monomerTxt);
|
|
135
139
|
|
|
@@ -142,10 +146,12 @@ export function drawLogoInBounds(ctx: CanvasRenderingContext2D, bounds: DG.Rect,
|
|
|
142
146
|
ctx.textBaseline = 'top';
|
|
143
147
|
ctx.font = drawOptions.fontStyle;
|
|
144
148
|
// Hacks to scale uppercase characters to target rectangle
|
|
145
|
-
|
|
149
|
+
const widthTransform = barWidth / mTm.width;
|
|
150
|
+
const heightTransfrom = monomerHeight / drawOptions.upperLetterHeight;
|
|
151
|
+
ctx.setTransform(widthTransform, 0, 0, heightTransfrom, xStart, currentY);
|
|
146
152
|
ctx.fillText(monomerTxt, 0, 0);
|
|
147
153
|
}
|
|
148
|
-
currentY += monomerHeight + drawOptions.upperLetterAscent;
|
|
154
|
+
currentY += monomerHeight + drawOptions.upperLetterAscent * pr;
|
|
149
155
|
}
|
|
150
156
|
|
|
151
157
|
return monomerBounds;
|
package/src/utils/constants.ts
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
export enum COLUMNS_NAMES {
|
|
2
2
|
SPLIT_COL = '~split',
|
|
3
|
-
|
|
4
|
-
ACTIVITY_SCALED = 'activity_scaled',
|
|
5
|
-
ALIGNED_SEQUENCE = '~aligned_sequence',
|
|
3
|
+
ACTIVITY_SCALED = 'Scaled activity',
|
|
6
4
|
MONOMER = 'AAR',
|
|
7
5
|
POSITION = 'Pos',
|
|
8
6
|
P_VALUE = 'pValue',
|
|
9
7
|
MEAN_DIFFERENCE = 'Mean difference',
|
|
10
8
|
COUNT = 'Count',
|
|
11
9
|
RATIO = 'Ratio',
|
|
12
|
-
|
|
13
|
-
MACROMOLECULE = 'macromolecule',
|
|
10
|
+
MEMBERS = 'Members',
|
|
14
11
|
}
|
|
15
12
|
|
|
16
13
|
export enum CATEGORIES {
|
|
@@ -25,47 +22,17 @@ export enum TAGS {
|
|
|
25
22
|
SELECTION = 'selection',
|
|
26
23
|
ALPHABET = 'alphabet',
|
|
27
24
|
FILTER = 'filter',
|
|
28
|
-
CLUSTERS = 'clusters',
|
|
29
25
|
SAR_MODE = 'sarMode',
|
|
30
26
|
CLUSTER_SELECTION = 'clusterSelection',
|
|
31
27
|
VISIBLE = 'visible',
|
|
28
|
+
SETTINGS = 'settings',
|
|
32
29
|
}
|
|
33
30
|
|
|
34
31
|
export enum SEM_TYPES {
|
|
35
32
|
MONOMER = 'Monomer',
|
|
36
|
-
MACROMOLECULE = 'Macromolecule',
|
|
37
33
|
MACROMOLECULE_DIFFERENCE = 'MacromoleculeDifference',
|
|
38
|
-
ACTIVITY = 'activity',
|
|
39
|
-
ACTIVITY_SCALED = 'activityScaled',
|
|
40
34
|
}
|
|
41
35
|
|
|
42
|
-
export const STATS = 'stats';
|
|
43
|
-
|
|
44
36
|
export const EMBEDDING_STATUS = 'embeddingStatus';
|
|
45
37
|
|
|
46
38
|
export const PEPTIDES_ANALYSIS = 'isPeptidesAnalysis';
|
|
47
|
-
|
|
48
|
-
export enum FLAGS {
|
|
49
|
-
CELL_CHANGING = 'isCellChanging',
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export const aarGroups = {
|
|
53
|
-
'R': 'PC', 'H': 'PC', 'K': 'PC',
|
|
54
|
-
'D': 'NC', 'E': 'NC',
|
|
55
|
-
'S': 'U', 'T': 'U', 'N': 'U', 'Q': 'U',
|
|
56
|
-
'C': 'SC', 'U': 'SC', 'G': 'SC', 'P': 'SC',
|
|
57
|
-
'A': 'H', 'V': 'H', 'I': 'H', 'L': 'H', 'M': 'H', 'F': 'H', 'Y': 'H', 'W': 'H',
|
|
58
|
-
'-': '-',
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export const groupDescription: {[key: string]: {'description': string, aminoAcids: string[]}} = {
|
|
62
|
-
'PC': {'description': 'Positive Amino Acids, with Electrically Charged Side Chains', 'aminoAcids': ['R', 'H', 'K']},
|
|
63
|
-
'NC': {'description': 'Negative Amino Acids, with Electrically Charged Side Chains', 'aminoAcids': ['D', 'E']},
|
|
64
|
-
'U': {'description': 'Amino Acids with Polar Uncharged Side Chains', 'aminoAcids': ['S', 'T', 'N', 'Q']},
|
|
65
|
-
'SC': {'description': 'Special Cases', 'aminoAcids': ['C', 'U', 'G', 'P']},
|
|
66
|
-
'H': {
|
|
67
|
-
'description': 'Amino Acids with Hydrophobic Side Chain',
|
|
68
|
-
'aminoAcids': ['A', 'V', 'I', 'L', 'M', 'F', 'Y', 'W'],
|
|
69
|
-
},
|
|
70
|
-
'-': {'description': 'Unknown Amino Acid', 'aminoAcids': ['-']},
|
|
71
|
-
};
|
package/src/utils/misc.ts
CHANGED
|
@@ -2,11 +2,6 @@ import * as DG from 'datagrok-api/dg';
|
|
|
2
2
|
import * as C from './constants';
|
|
3
3
|
import * as type from './types';
|
|
4
4
|
|
|
5
|
-
import {AminoacidsPalettes} from '@datagrok-libraries/bio/src/aminoacids';
|
|
6
|
-
import {NucleotidesPalettes} from '@datagrok-libraries/bio/src/nucleotides';
|
|
7
|
-
import {UnknownSeqPalettes} from '@datagrok-libraries/bio/src/unknown';
|
|
8
|
-
import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
|
|
9
|
-
|
|
10
5
|
export function getTypedArrayConstructor(
|
|
11
6
|
maxNum: number): Uint8ArrayConstructor | Uint16ArrayConstructor | Uint32ArrayConstructor {
|
|
12
7
|
return maxNum < 256 ? Uint8Array :
|
|
@@ -20,27 +15,23 @@ export function getSeparator(col: DG.Column<string>): string {
|
|
|
20
15
|
|
|
21
16
|
export function scaleActivity(activityCol: DG.Column<number>, scaling: string = 'none'): DG.Column<number> {
|
|
22
17
|
let formula = (x: number): number => x;
|
|
23
|
-
let newColName = 'activity';
|
|
24
18
|
switch (scaling) {
|
|
25
19
|
case 'none':
|
|
26
20
|
break;
|
|
27
21
|
case 'lg':
|
|
28
22
|
formula = (x: number): number => Math.log10(x);
|
|
29
|
-
newColName = `Log10(${newColName})`;
|
|
30
23
|
break;
|
|
31
24
|
case '-lg':
|
|
32
25
|
formula = (x: number): number => -Math.log10(x);
|
|
33
|
-
newColName = `-Log10(${newColName})`;
|
|
34
26
|
break;
|
|
35
27
|
default:
|
|
36
28
|
throw new Error(`ScalingError: method \`${scaling}\` is not available.`);
|
|
37
29
|
}
|
|
38
|
-
const scaledCol = DG.Column.float(C.COLUMNS_NAMES.ACTIVITY_SCALED, activityCol.length)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
scaledCol.setTag('gridName', newColName);
|
|
30
|
+
const scaledCol: DG.Column<number> = DG.Column.float(C.COLUMNS_NAMES.ACTIVITY_SCALED, activityCol.length)
|
|
31
|
+
.init((i) => {
|
|
32
|
+
const val = activityCol.get(i);
|
|
33
|
+
return val ? formula(val) : val;
|
|
34
|
+
});
|
|
44
35
|
|
|
45
36
|
return scaledCol;
|
|
46
37
|
}
|
|
@@ -53,7 +44,7 @@ export function calculateSelected(df: DG.DataFrame): type.MonomerSelectionStats
|
|
|
53
44
|
const monomer = col.get(idx);
|
|
54
45
|
if (!monomer)
|
|
55
46
|
continue;
|
|
56
|
-
|
|
47
|
+
|
|
57
48
|
selectedObj[col.name] ??= {};
|
|
58
49
|
selectedObj[col.name][monomer] ??= 0;
|
|
59
50
|
selectedObj[col.name][monomer] += 1;
|
|
@@ -67,3 +58,11 @@ export function isGridCellInvalid(gc: DG.GridCell | null): boolean {
|
|
|
67
58
|
return !gc || !gc.cell.value || !gc.tableColumn || gc.tableRowIndex == null || gc.tableRowIndex == -1 ||
|
|
68
59
|
gc.cell.value == DG.INT_NULL || gc.cell.value == DG.FLOAT_NULL;
|
|
69
60
|
}
|
|
61
|
+
|
|
62
|
+
export function extractMonomerInfo(col: DG.Column<string>): type.RawColumn {
|
|
63
|
+
return {
|
|
64
|
+
name: col.name,
|
|
65
|
+
cat: col.categories,
|
|
66
|
+
rawData: col.getRawData(),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -38,14 +38,11 @@ export function cleanAlignedSequencesColumn(col: DG.Column): Array<string> {
|
|
|
38
38
|
* @param {boolean} [zoom=false] Whether to fit view.
|
|
39
39
|
* @return {Promise<DG.ScatterPlotViewer>} A viewer.
|
|
40
40
|
*/
|
|
41
|
-
export async function createPeptideSimilaritySpaceViewer(
|
|
42
|
-
|
|
43
|
-
): Promise<DG.ScatterPlotViewer> {
|
|
41
|
+
export async function createPeptideSimilaritySpaceViewer(table: DG.DataFrame, method: string, measure: string,
|
|
42
|
+
cyclesCount: number, col: DG.Column, activityColName?: string, view?: DG.TableView): Promise<DG.ScatterPlotViewer> {
|
|
44
43
|
const pi = DG.TaskBarProgressIndicator.create('Creating embedding...');
|
|
45
44
|
|
|
46
45
|
const axesNames = ['~X', '~Y', '~MW'];
|
|
47
|
-
// col ??= table.columns.bySemType(C.SEM_TYPES.MACROMOLECULE)!;
|
|
48
|
-
col ??= table.getCol(C.COLUMNS_NAMES.MACROMOLECULE);
|
|
49
46
|
const columnData = col.toList().map((v) => AlignedSequenceEncoder.clean(v));
|
|
50
47
|
|
|
51
48
|
const reduceDimRes: IReduceDimensionalityResult = await createDimensinalityReducingWorker(
|
|
@@ -81,7 +78,7 @@ export async function createPeptideSimilaritySpaceViewer(
|
|
|
81
78
|
table.columns.insert(newCol);
|
|
82
79
|
}
|
|
83
80
|
|
|
84
|
-
const colorColName =
|
|
81
|
+
const colorColName = activityColName ?? '~MW';
|
|
85
82
|
const viewerOptions = {
|
|
86
83
|
x: '~X', y: '~Y', color: colorColName, size: '~MW', title: 'Peptide Space',
|
|
87
84
|
showYSelector: false, showXSelector: false, showColorSelector: false, showSizeSelector: false,
|
|
@@ -137,7 +134,7 @@ export class PeptideSimilaritySpaceWidget {
|
|
|
137
134
|
*/
|
|
138
135
|
public async drawViewer(): Promise<DG.Viewer> {
|
|
139
136
|
const viewer = await createPeptideSimilaritySpaceViewer(
|
|
140
|
-
this.currentDf, this.method, this.metrics, this.cycles,
|
|
137
|
+
this.currentDf, this.method, this.metrics, this.cycles, this.alignedSequencesColumn, undefined);
|
|
141
138
|
viewer.root.style.width = 'auto';
|
|
142
139
|
return viewer;
|
|
143
140
|
}
|
package/src/utils/statistics.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import * as DG from 'datagrok-api/dg';
|
|
2
|
-
|
|
3
1
|
import {tTest} from '@datagrok-libraries/statistics/src/tests';
|
|
2
|
+
import {RawData} from './types';
|
|
4
3
|
|
|
5
4
|
export type Stats = {
|
|
6
5
|
count: number,
|
|
@@ -9,21 +8,31 @@ export type Stats = {
|
|
|
9
8
|
ratio: number,
|
|
10
9
|
};
|
|
11
10
|
|
|
12
|
-
type
|
|
11
|
+
export type MaskInfo = {
|
|
12
|
+
trueCount: number,
|
|
13
|
+
falseCount: number,
|
|
14
|
+
mask: boolean[] | Int32Array,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function getStats(data: RawData | number[], maskInfo: MaskInfo): Stats {
|
|
18
|
+
const selected = new Float32Array(maskInfo.trueCount);
|
|
19
|
+
const rest = new Float32Array(maskInfo.falseCount);
|
|
13
20
|
|
|
14
|
-
export function getStats(data: StatsData, mask: DG.BitSet): Stats {
|
|
15
|
-
const selected = new Float32Array(mask.trueCount);
|
|
16
|
-
const rest = new Float32Array(mask.falseCount);
|
|
17
21
|
let selectedIndex = 0;
|
|
18
22
|
let restIndex = 0;
|
|
19
|
-
|
|
23
|
+
for (let i = 0; i < data.length; ++i) {
|
|
24
|
+
if (maskInfo.mask[i])
|
|
25
|
+
selected[selectedIndex++] = data[i];
|
|
26
|
+
else
|
|
27
|
+
rest[restIndex++] = data[i];
|
|
28
|
+
}
|
|
20
29
|
|
|
21
30
|
const testResult = tTest(selected, rest);
|
|
22
31
|
const currentMeanDiff = testResult['Mean difference']!;
|
|
23
32
|
return {
|
|
24
33
|
count: selected.length,
|
|
25
|
-
pValue: testResult[currentMeanDiff >= 0 ? 'p-value more' : 'p-value less'],
|
|
26
|
-
meanDifference: currentMeanDiff,
|
|
34
|
+
pValue: testResult[currentMeanDiff >= 0 ? 'p-value more' : 'p-value less'] || 0,
|
|
35
|
+
meanDifference: currentMeanDiff || 0,
|
|
27
36
|
ratio: selected.length / data.length,
|
|
28
37
|
};
|
|
29
38
|
}
|