@datagrok/peptides 1.12.0 → 1.13.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 +22 -0
- package/dist/535.js +2 -2
- package/dist/package-test.js +2 -2
- package/dist/package.js +2 -2
- package/package.json +7 -7
- package/src/demo/fasta.ts +6 -25
- package/src/model.ts +275 -296
- package/src/package.ts +1 -1
- package/src/tests/core.ts +2 -10
- package/src/tests/table-view.ts +48 -48
- package/src/tests/viewers.ts +15 -13
- package/src/tests/widgets.ts +3 -4
- package/src/utils/cell-renderer.ts +33 -39
- package/src/utils/constants.ts +1 -0
- package/src/utils/misc.ts +2 -5
- package/src/utils/statistics.ts +22 -3
- package/src/utils/types.ts +6 -5
- package/src/viewers/logo-summary.ts +55 -42
- package/src/viewers/sar-viewer.ts +167 -107
- package/src/widgets/distribution.ts +60 -59
- package/src/widgets/mutation-cliffs.ts +2 -2
- package/src/widgets/peptides.ts +18 -11
package/src/package.ts
CHANGED
|
@@ -173,7 +173,7 @@ export async function peptideSpacePanel(col: DG.Column): Promise<DG.Widget> {
|
|
|
173
173
|
//name: Macromolecule SAR Analysis
|
|
174
174
|
//description: Macromolecule SAR Analysis demo on peptide sequences in FASTA format
|
|
175
175
|
//meta.demoPath: Bioinformatics | Macromolecule SAR Analysis
|
|
176
|
-
//meta.isDemoScript:
|
|
176
|
+
//meta.isDemoScript: False
|
|
177
177
|
export async function macromoleculeSarFastaDemo(): Promise<void> {
|
|
178
178
|
return macromoleculeSarFastaDemoUI();
|
|
179
179
|
}
|
package/src/tests/core.ts
CHANGED
|
@@ -44,10 +44,6 @@ category('Core', () => {
|
|
|
44
44
|
overlayInit = true;
|
|
45
45
|
grok.log.debug('Overlay initialized');
|
|
46
46
|
});
|
|
47
|
-
model!._analysisView!.grid.onBeforeDrawOverlay.subscribe(() => {
|
|
48
|
-
overlayInit = false;
|
|
49
|
-
grok.log.debug('Overlay is drawing');
|
|
50
|
-
});
|
|
51
47
|
|
|
52
48
|
// Ensure grid finished initializing to prevent Unhandled exceptions
|
|
53
49
|
let accrodionInit = false;
|
|
@@ -58,7 +54,7 @@ category('Core', () => {
|
|
|
58
54
|
await awaitCheck(() => overlayInit, 'Overlay never finished initializing', 2000);
|
|
59
55
|
|
|
60
56
|
model!.mutationCliffsSelection = {'11': ['D']};
|
|
61
|
-
}, {skipReason: '
|
|
57
|
+
}, {skipReason: 'Ignore unhandled exceptions in tests'});
|
|
62
58
|
|
|
63
59
|
test('Start analysis: сomplex', async () => {
|
|
64
60
|
const complexActivityColName = 'Activity';
|
|
@@ -80,10 +76,6 @@ category('Core', () => {
|
|
|
80
76
|
overlayInit = true;
|
|
81
77
|
grok.log.debug('Overlay initialized');
|
|
82
78
|
});
|
|
83
|
-
model!._analysisView!.grid.onBeforeDrawOverlay.subscribe(() => {
|
|
84
|
-
overlayInit = false;
|
|
85
|
-
grok.log.debug('Overlay is drawing');
|
|
86
|
-
});
|
|
87
79
|
|
|
88
80
|
// Ensure grid finished initializing to prevent Unhandled exceptions
|
|
89
81
|
let accrodionInit = false;
|
|
@@ -95,7 +87,7 @@ category('Core', () => {
|
|
|
95
87
|
|
|
96
88
|
if (model !== null)
|
|
97
89
|
model.mutationCliffsSelection = {'13': ['-']};
|
|
98
|
-
}, {skipReason: '
|
|
90
|
+
}, {skipReason: 'Ignore unhandled exceptions in tests'});
|
|
99
91
|
|
|
100
92
|
test('Save and load project', async () => {
|
|
101
93
|
const simpleActivityColName = 'IC50';
|
package/src/tests/table-view.ts
CHANGED
|
@@ -3,7 +3,7 @@ import * as DG from 'datagrok-api/dg';
|
|
|
3
3
|
|
|
4
4
|
import {category, test, before, expect, awaitCheck} from '@datagrok-libraries/utils/src/test';
|
|
5
5
|
import {_package} from '../package-test';
|
|
6
|
-
import {PeptidesModel} from '../model';
|
|
6
|
+
import {CLUSTER_TYPE, PeptidesModel} from '../model';
|
|
7
7
|
import {startAnalysis} from '../widgets/peptides';
|
|
8
8
|
import {scaleActivity} from '../utils/misc';
|
|
9
9
|
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
@@ -19,10 +19,10 @@ category('Table view', () => {
|
|
|
19
19
|
let scaledActivityCol: DG.Column<number>;
|
|
20
20
|
const scaling = 'none' as SCALING_METHODS;
|
|
21
21
|
|
|
22
|
-
const firstPair = {
|
|
23
|
-
const secondPair = {
|
|
24
|
-
const firstCluster = {
|
|
25
|
-
const secondCluster = {
|
|
22
|
+
const firstPair = {monomerOrCluster: 'Aze', positionOrClusterType: '10', mcCount: 3, imCount: 1};
|
|
23
|
+
const secondPair = {monomerOrCluster: 'meI', positionOrClusterType: '1', mcCount: 2, imCount: 10};
|
|
24
|
+
const firstCluster = {monomerOrCluster: '0', positionOrClusterType: CLUSTER_TYPE.ORIGINAL, count: 3};
|
|
25
|
+
const secondCluster = {monomerOrCluster: '1', positionOrClusterType: CLUSTER_TYPE.ORIGINAL, count: 3};
|
|
26
26
|
|
|
27
27
|
before(async () => {
|
|
28
28
|
df = DG.DataFrame.fromCsv(await _package.files.readAsText('tests/HELM_small.csv'));
|
|
@@ -49,8 +49,8 @@ category('Table view', () => {
|
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
test('Tooltip', async () => {
|
|
52
|
-
expect(model.showMonomerTooltip(firstPair.
|
|
53
|
-
`Couldn't structure for monomer ${firstPair.
|
|
52
|
+
expect(model.showMonomerTooltip(firstPair.monomerOrCluster, 0, 0), true,
|
|
53
|
+
`Couldn't structure for monomer ${firstPair.monomerOrCluster}`);
|
|
54
54
|
}, {skipReason: 'Need to find a way to replace _package variable to call for Bio function with tests'});
|
|
55
55
|
|
|
56
56
|
test('Visible columns', async () => {
|
|
@@ -73,29 +73,29 @@ category('Table view', () => {
|
|
|
73
73
|
expect(selectedMonomers.length, 0, `Selection is not empty for position ${position} after initialization`);
|
|
74
74
|
|
|
75
75
|
// Select first monomer-position pair
|
|
76
|
-
model.
|
|
77
|
-
expect(model.mutationCliffsSelection[firstPair.
|
|
78
|
-
`Monomer ${firstPair.
|
|
76
|
+
model.modifyMutationCliffsSelection(firstPair);
|
|
77
|
+
expect(model.mutationCliffsSelection[firstPair.positionOrClusterType].includes(firstPair.monomerOrCluster), true,
|
|
78
|
+
`Monomer ${firstPair.monomerOrCluster} is not selected at position ${firstPair.positionOrClusterType}`);
|
|
79
79
|
expect(selection.trueCount, firstPair.mcCount, `Selection count is not equal to ${firstPair.mcCount} ` +
|
|
80
|
-
`for monomer ${firstPair.
|
|
80
|
+
`for monomer ${firstPair.monomerOrCluster} at position ${firstPair.positionOrClusterType}`);
|
|
81
81
|
|
|
82
82
|
// Select second monomer-position pair
|
|
83
|
-
model.
|
|
84
|
-
expect(model.mutationCliffsSelection[secondPair.
|
|
85
|
-
`Monomer ${secondPair.
|
|
83
|
+
model.modifyMutationCliffsSelection(secondPair, {shiftPressed: true, ctrlPressed: false});
|
|
84
|
+
expect(model.mutationCliffsSelection[secondPair.positionOrClusterType].includes(secondPair.monomerOrCluster), true,
|
|
85
|
+
`Monomer ${secondPair.monomerOrCluster} is not selected at position ${secondPair.positionOrClusterType}`);
|
|
86
86
|
expect(selection.trueCount, secondPair.mcCount + firstPair.mcCount, `Selection count is not equal ` +
|
|
87
|
-
`to ${secondPair.mcCount + firstPair.mcCount} for monomer ${secondPair.
|
|
88
|
-
`position ${secondPair.
|
|
87
|
+
`to ${secondPair.mcCount + firstPair.mcCount} for monomer ${secondPair.monomerOrCluster} at ` +
|
|
88
|
+
`position ${secondPair.positionOrClusterType}`);
|
|
89
89
|
|
|
90
90
|
// Deselect second monomer-position pair
|
|
91
|
-
model.
|
|
92
|
-
expect(model.mutationCliffsSelection[secondPair.
|
|
93
|
-
`Monomer ${secondPair.
|
|
91
|
+
model.modifyMutationCliffsSelection(secondPair, {shiftPressed: true, ctrlPressed: true});
|
|
92
|
+
expect(model.mutationCliffsSelection[secondPair.positionOrClusterType].includes(secondPair.monomerOrCluster), false,
|
|
93
|
+
`Monomer ${secondPair.monomerOrCluster} is still selected at position ${secondPair.positionOrClusterType} after deselection`);
|
|
94
94
|
expect(selection.trueCount, firstPair.mcCount, `Selection count is not equal to ${firstPair.mcCount} ` +
|
|
95
|
-
`for monomer ${firstPair.
|
|
95
|
+
`for monomer ${firstPair.monomerOrCluster} at position ${firstPair.positionOrClusterType}`);
|
|
96
96
|
|
|
97
97
|
// Clear monomer-position selection
|
|
98
|
-
model.initMutationCliffsSelection(
|
|
98
|
+
model.initMutationCliffsSelection();
|
|
99
99
|
for (const [position, selectedMonomers] of Object.entries(model.mutationCliffsSelection)) {
|
|
100
100
|
expect(selectedMonomers.length, 0, `Selection is not empty for position ${position} after clearing ` +
|
|
101
101
|
`monomer-position selection`);
|
|
@@ -103,29 +103,29 @@ category('Table view', () => {
|
|
|
103
103
|
expect(selection.trueCount, 0, `Selection count is not equal to 0 after clearing monomer-position selection`);
|
|
104
104
|
|
|
105
105
|
// Select first cluster
|
|
106
|
-
model.modifyClusterSelection(firstCluster
|
|
107
|
-
expect(model.clusterSelection.includes(firstCluster.
|
|
108
|
-
`Cluster ${firstCluster.
|
|
106
|
+
model.modifyClusterSelection(firstCluster);
|
|
107
|
+
expect(model.clusterSelection[firstCluster.positionOrClusterType].includes(firstCluster.monomerOrCluster), true,
|
|
108
|
+
`Cluster ${firstCluster.monomerOrCluster} is not selected`);
|
|
109
109
|
expect(selection.trueCount, firstCluster.count, `Selection count is not equal to ${firstCluster.count} for ` +
|
|
110
|
-
`cluster ${firstCluster.
|
|
110
|
+
`cluster ${firstCluster.monomerOrCluster}`);
|
|
111
111
|
|
|
112
112
|
// Select second cluster
|
|
113
|
-
model.modifyClusterSelection(secondCluster
|
|
114
|
-
expect(model.clusterSelection.includes(secondCluster.
|
|
115
|
-
`Cluster ${secondCluster.
|
|
113
|
+
model.modifyClusterSelection(secondCluster, {shiftPressed: true, ctrlPressed: false});
|
|
114
|
+
expect(model.clusterSelection[secondCluster.positionOrClusterType].includes(secondCluster.monomerOrCluster), true,
|
|
115
|
+
`Cluster ${secondCluster.monomerOrCluster} is not selected`);
|
|
116
116
|
expect(selection.trueCount, firstCluster.count + secondCluster.count, `Selection count is not equal to ` +
|
|
117
|
-
`${firstCluster.count + secondCluster.count} for cluster ${firstCluster.
|
|
117
|
+
`${firstCluster.count + secondCluster.count} for cluster ${firstCluster.monomerOrCluster} and cluster ${secondCluster.monomerOrCluster}`);
|
|
118
118
|
|
|
119
119
|
// Deselect first cluster
|
|
120
|
-
model.modifyClusterSelection(firstCluster
|
|
121
|
-
expect(model.clusterSelection.includes(firstCluster.
|
|
122
|
-
`Cluster ${firstCluster.
|
|
120
|
+
model.modifyClusterSelection(firstCluster, {shiftPressed: true, ctrlPressed: true});
|
|
121
|
+
expect(model.clusterSelection[firstCluster.positionOrClusterType].includes(firstCluster.monomerOrCluster), false,
|
|
122
|
+
`Cluster ${firstCluster.monomerOrCluster} is still selected after deselection`);
|
|
123
123
|
expect(selection.trueCount, secondCluster.count, `Selection count is not equal to ${secondCluster.count} for ` +
|
|
124
|
-
`cluster ${secondCluster.
|
|
124
|
+
`cluster ${secondCluster.monomerOrCluster} after deselection of cluster ${firstCluster.monomerOrCluster}`);
|
|
125
125
|
|
|
126
126
|
// Clear selection
|
|
127
127
|
model.initClusterSelection();
|
|
128
|
-
expect(model.
|
|
128
|
+
expect(model.isClusterSelectionEmpty, true, `Selection is not empty after clearing cluster selection`);
|
|
129
129
|
expect(selection.trueCount, 0, `Selection count is not equal to 0 after clearing cluster selection`);
|
|
130
130
|
});
|
|
131
131
|
|
|
@@ -136,30 +136,30 @@ category('Table view', () => {
|
|
|
136
136
|
expect(filteredMonomers.length, 0, `Filter is not empty for position ${position} after initialization`);
|
|
137
137
|
|
|
138
138
|
// Select by second monomer-position pair
|
|
139
|
-
model.
|
|
140
|
-
expect(model.invariantMapSelection[secondPair.
|
|
141
|
-
`Monomer ${secondPair.
|
|
139
|
+
model.modifyInvariantMapSelection(secondPair);
|
|
140
|
+
expect(model.invariantMapSelection[secondPair.positionOrClusterType].includes(secondPair.monomerOrCluster), true,
|
|
141
|
+
`Monomer ${secondPair.monomerOrCluster} is not filtered at position ${secondPair.positionOrClusterType}`);
|
|
142
142
|
expect(selection.trueCount, secondPair.imCount, `Filter count is not equal to ${secondPair.imCount} ` +
|
|
143
|
-
`for monomer ${secondPair.
|
|
143
|
+
`for monomer ${secondPair.monomerOrCluster} at position ${secondPair.positionOrClusterType}`);
|
|
144
144
|
|
|
145
145
|
// Select by first monomer-position pair
|
|
146
|
-
model.
|
|
147
|
-
expect(model.invariantMapSelection[firstPair.
|
|
148
|
-
`Monomer ${firstPair.
|
|
146
|
+
model.modifyInvariantMapSelection(firstPair, {shiftPressed: true, ctrlPressed: false});
|
|
147
|
+
expect(model.invariantMapSelection[firstPair.positionOrClusterType].includes(firstPair.monomerOrCluster), true,
|
|
148
|
+
`Monomer ${firstPair.monomerOrCluster} is not filtered at position ${firstPair.positionOrClusterType}`);
|
|
149
149
|
expect(selection.trueCount, secondPair.imCount, `Filter count is not equal to ${secondPair.imCount} ` +
|
|
150
|
-
`for monomer ${firstPair.
|
|
150
|
+
`for monomer ${firstPair.monomerOrCluster} at position ${firstPair.positionOrClusterType}`);
|
|
151
151
|
|
|
152
152
|
// Deselect filter for second monomer-position pair
|
|
153
|
-
model.
|
|
154
|
-
expect(model.invariantMapSelection[secondPair.
|
|
155
|
-
`Monomer ${secondPair.
|
|
153
|
+
model.modifyInvariantMapSelection(secondPair, {shiftPressed: true, ctrlPressed: true});
|
|
154
|
+
expect(model.invariantMapSelection[secondPair.positionOrClusterType].includes(secondPair.monomerOrCluster), false,
|
|
155
|
+
`Monomer ${secondPair.monomerOrCluster} is still filtered at position ${secondPair.positionOrClusterType} after ` +
|
|
156
156
|
`deselection`);
|
|
157
157
|
expect(selection.trueCount, firstPair.imCount, `Filter count is not equal to ${firstPair.imCount} ` +
|
|
158
|
-
`for monomer ${firstPair.
|
|
159
|
-
`monomer ${secondPair.
|
|
158
|
+
`for monomer ${firstPair.monomerOrCluster} at position ${firstPair.positionOrClusterType} after deselection of ` +
|
|
159
|
+
`monomer ${secondPair.monomerOrCluster} at position ${secondPair.positionOrClusterType}`);
|
|
160
160
|
|
|
161
161
|
// Clear selection
|
|
162
|
-
model.initInvariantMapSelection(
|
|
162
|
+
model.initInvariantMapSelection();
|
|
163
163
|
expect(selection.trueCount, 0, `Filter count is not equal to ${0} after clearing monomer-position filter`);
|
|
164
164
|
|
|
165
165
|
for (const [position, filteredMonomers] of Object.entries(model.invariantMapSelection)) {
|
package/src/tests/viewers.ts
CHANGED
|
@@ -3,12 +3,12 @@ import * as DG from 'datagrok-api/dg';
|
|
|
3
3
|
|
|
4
4
|
import {awaitCheck, before, category, expect, test, testViewer} from '@datagrok-libraries/utils/src/test';
|
|
5
5
|
import {aligned1} from './test-data';
|
|
6
|
-
import {PeptidesModel, VIEWER_TYPE} from '../model';
|
|
6
|
+
import {CLUSTER_TYPE, PeptidesModel, VIEWER_TYPE} from '../model';
|
|
7
7
|
import {_package} from '../package-test';
|
|
8
8
|
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
9
9
|
import {scaleActivity} from '../utils/misc';
|
|
10
10
|
import {startAnalysis} from '../widgets/peptides';
|
|
11
|
-
import {
|
|
11
|
+
import {SELECTION_MODE, MonomerPosition, MostPotentResidues} from '../viewers/sar-viewer';
|
|
12
12
|
import {SCALING_METHODS} from '../utils/constants';
|
|
13
13
|
import {LST_PROPERTIES, LogoSummaryTable} from '../viewers/logo-summary';
|
|
14
14
|
import {PositionHeight} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
@@ -63,7 +63,8 @@ category('Viewers: Monomer-Position', () => {
|
|
|
63
63
|
test('Tooltip', async () => {
|
|
64
64
|
const cellCoordinates = {col: '9', row: 6};
|
|
65
65
|
const gc = mpViewer.viewerGrid.cell(cellCoordinates.col, cellCoordinates.row);
|
|
66
|
-
|
|
66
|
+
const mp = mpViewer.getMonomerPosition(gc);
|
|
67
|
+
expect(model.showTooltip(mp, 0, 0), true,
|
|
67
68
|
`Tooltip is not shown for grid cell at column '${cellCoordinates.col}', row ${cellCoordinates.row}`);
|
|
68
69
|
});
|
|
69
70
|
|
|
@@ -71,16 +72,16 @@ category('Viewers: Monomer-Position', () => {
|
|
|
71
72
|
if (mpViewer === null)
|
|
72
73
|
throw new Error('Monomer-Position viewer doesn\'t exist');
|
|
73
74
|
|
|
74
|
-
expect(mpViewer.mode,
|
|
75
|
-
`Default Monomer-Position mode is not ${
|
|
75
|
+
expect(mpViewer.mode, SELECTION_MODE.MUTATION_CLIFFS,
|
|
76
|
+
`Default Monomer-Position mode is not ${SELECTION_MODE.MUTATION_CLIFFS}`);
|
|
76
77
|
|
|
77
|
-
mpViewer.mode =
|
|
78
|
-
expect(mpViewer.mode,
|
|
79
|
-
`Monomer-Position mode is not ${
|
|
78
|
+
mpViewer.mode = SELECTION_MODE.INVARIANT_MAP;
|
|
79
|
+
expect(mpViewer.mode, SELECTION_MODE.INVARIANT_MAP,
|
|
80
|
+
`Monomer-Position mode is not ${SELECTION_MODE.INVARIANT_MAP} after switching`);
|
|
80
81
|
|
|
81
|
-
mpViewer.mode =
|
|
82
|
-
expect(mpViewer.mode,
|
|
83
|
-
`Monomer-Position mode is not ${
|
|
82
|
+
mpViewer.mode = SELECTION_MODE.MUTATION_CLIFFS;
|
|
83
|
+
expect(mpViewer.mode, SELECTION_MODE.MUTATION_CLIFFS,
|
|
84
|
+
`Monomer-Position mode is not ${SELECTION_MODE.MUTATION_CLIFFS} after switching`);
|
|
84
85
|
});
|
|
85
86
|
}, {clear: false});
|
|
86
87
|
|
|
@@ -123,7 +124,8 @@ category('Viewers: Most Potent Residues', () => {
|
|
|
123
124
|
test('Tooltip', async () => {
|
|
124
125
|
const cellCoordinates = {col: 'Diff', row: 6};
|
|
125
126
|
const gc = mprViewer.viewerGrid.cell(cellCoordinates.col, cellCoordinates.row);
|
|
126
|
-
|
|
127
|
+
const mp = mprViewer.getMonomerPosition(gc);
|
|
128
|
+
expect(model.showTooltip(mp, 0, 0), true,
|
|
127
129
|
`Tooltip is not shown for grid cell at column '${cellCoordinates.col}', row ${cellCoordinates.row}`);
|
|
128
130
|
});
|
|
129
131
|
});
|
|
@@ -183,7 +185,7 @@ category('Viewers: Logo Summary Table', () => {
|
|
|
183
185
|
|
|
184
186
|
test('Tooltip', async () => {
|
|
185
187
|
const cluster = '0';
|
|
186
|
-
const tooltipElement = lstViewer.showTooltip(cluster, 0, 0);
|
|
188
|
+
const tooltipElement = lstViewer.showTooltip({monomerOrCluster: cluster, positionOrClusterType: CLUSTER_TYPE.ORIGINAL}, 0, 0);
|
|
187
189
|
expect(tooltipElement !== null, true, `Tooltip is not shown for cluster '${cluster}'`);
|
|
188
190
|
});
|
|
189
191
|
}, {clear: false});
|
package/src/tests/widgets.ts
CHANGED
|
@@ -3,7 +3,7 @@ import * as DG from 'datagrok-api/dg';
|
|
|
3
3
|
|
|
4
4
|
import {category, test, before, expect, awaitCheck} from '@datagrok-libraries/utils/src/test';
|
|
5
5
|
import {_package} from '../package-test';
|
|
6
|
-
import {PeptidesModel, VIEWER_TYPE} from '../model';
|
|
6
|
+
import {CLUSTER_TYPE, PeptidesModel, VIEWER_TYPE} from '../model';
|
|
7
7
|
import {scaleActivity} from '../utils/misc';
|
|
8
8
|
import {startAnalysis} from '../widgets/peptides';
|
|
9
9
|
import {NOTATION} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
@@ -221,13 +221,12 @@ category('Widgets: Actions', () => {
|
|
|
221
221
|
const customClusterList = wu(model.customClusters).toArray();
|
|
222
222
|
expect(customClusterList.length, 1, 'Expected to have 1 custom cluster');
|
|
223
223
|
const clustName = customClusterList[0].name;
|
|
224
|
-
expect(model.df.col(clustName) !== null, true,
|
|
225
|
-
'Expected to have custom cluster column in the table');
|
|
224
|
+
expect(model.df.col(clustName) !== null, true, 'Expected to have custom cluster column in the table');
|
|
226
225
|
expect(lstViewer.viewerGrid.table.getCol(C.LST_COLUMN_NAMES.CLUSTER).categories.indexOf(clustName) !== -1, true,
|
|
227
226
|
'Expected to have custom cluster in the Logo Summary Table');
|
|
228
227
|
|
|
229
228
|
// Remove custom cluster
|
|
230
|
-
model.modifyClusterSelection(clustName);
|
|
229
|
+
model.modifyClusterSelection({monomerOrCluster: clustName, positionOrClusterType: CLUSTER_TYPE.CUSTOM});
|
|
231
230
|
lstViewer.removeCluster();
|
|
232
231
|
expect(wu(model.customClusters).toArray().length, 0, 'Expected to have 0 custom clusters after removing one');
|
|
233
232
|
expect(model.df.col(clustName) === null, true,
|
|
@@ -2,7 +2,7 @@ import * as DG from 'datagrok-api/dg';
|
|
|
2
2
|
|
|
3
3
|
import * as C from './constants';
|
|
4
4
|
import * as types from './types';
|
|
5
|
-
import {PositionStats, MonomerPositionStats} from '../model';
|
|
5
|
+
import {PositionStats, MonomerPositionStats, CLUSTER_TYPE} from '../model';
|
|
6
6
|
import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
|
|
7
7
|
import {monomerToShort} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
8
8
|
|
|
@@ -13,40 +13,34 @@ export function renderCellSelection(canvasContext: CanvasRenderingContext2D, bou
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
/** A function that sets amino acid residue cell renderer to the specified column */
|
|
16
|
-
export function
|
|
16
|
+
export function setMonomerRenderer(col: DG.Column, alphabet: string): void {
|
|
17
17
|
col.semType = C.SEM_TYPES.MONOMER;
|
|
18
18
|
col.setTag('cell.renderer', C.SEM_TYPES.MONOMER);
|
|
19
19
|
col.tags[C.TAGS.ALPHABET] = alphabet;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D,
|
|
22
|
+
export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D, currentMonomer: string,
|
|
23
23
|
currentPosition: string, monomerPositionStats: MonomerPositionStats, bound: DG.Rect,
|
|
24
|
-
mutationCliffsSelection: types.
|
|
25
|
-
|
|
24
|
+
mutationCliffsSelection: types.Selection, substitutionsInfo: types.MutationCliffs | null = null,
|
|
25
|
+
_twoColorMode: boolean = false, renderNums: boolean = true): void {
|
|
26
26
|
const positionStats = monomerPositionStats[currentPosition];
|
|
27
|
-
const pVal
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
Math.max(Math.abs(monomerPositionStats.general.minMeanDifference), monomerPositionStats.general.maxMeanDifference) :
|
|
45
|
-
monomerPositionStats.general.maxMeanDifference;
|
|
46
|
-
const currentMeanDifference = twoColorMode ? Math.abs(currentMeanDiff) : currentMeanDiff;
|
|
47
|
-
|
|
48
|
-
const rCoef = (currentMeanDifference - minMeanDifference) / (maxMeanDifference - minMeanDifference);
|
|
49
|
-
|
|
27
|
+
const pVal = positionStats![currentMonomer]!.pValue;
|
|
28
|
+
const currentMeanDifference = positionStats![currentMonomer]!.meanDifference;
|
|
29
|
+
|
|
30
|
+
// Transform p-value to increase intensity for smaller values and decrease for larger values
|
|
31
|
+
const maxPValComplement = 1 - positionStats!.general.maxPValue;
|
|
32
|
+
const minPValComplement = 1 - positionStats!.general.minPValue;
|
|
33
|
+
const pValCentering = Math.min(maxPValComplement, minPValComplement);
|
|
34
|
+
const centeredMaxPValComplement = maxPValComplement - pValCentering;
|
|
35
|
+
const centeredMinPValComplement = minPValComplement - pValCentering;
|
|
36
|
+
const centeredPValLimit = Math.max(centeredMaxPValComplement, centeredMinPValComplement);
|
|
37
|
+
const pValComplement = pVal === null ? 0 : 1 - pVal - pValCentering;
|
|
38
|
+
|
|
39
|
+
const coef = DG.Color.toHtml(pVal === null ? DG.Color.lightLightGray :
|
|
40
|
+
DG.Color.scaleColor(currentMeanDifference >= 0 ? pValComplement : -pValComplement, -centeredPValLimit, centeredPValLimit));
|
|
41
|
+
|
|
42
|
+
const maxMeanDifference = Math.max(Math.abs(monomerPositionStats.general.minMeanDifference), monomerPositionStats.general.maxMeanDifference);
|
|
43
|
+
const rCoef = Math.abs(currentMeanDifference) / maxMeanDifference;
|
|
50
44
|
const maxRadius = 0.9 * (bound.width > bound.height ? bound.height : bound.width) / 2;
|
|
51
45
|
const radius = Math.floor(maxRadius * rCoef);
|
|
52
46
|
|
|
@@ -54,12 +48,12 @@ export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D,
|
|
|
54
48
|
const midY = bound.y + bound.height / 2;
|
|
55
49
|
canvasContext.beginPath();
|
|
56
50
|
canvasContext.fillStyle = coef;
|
|
57
|
-
canvasContext.arc(midX, midY, radius < 3 ? 3 : radius, 0, Math.PI * 2, true);
|
|
51
|
+
canvasContext.arc(midX, midY, radius < 3 || pVal === null ? 3 : radius, 0, Math.PI * 2, true);
|
|
58
52
|
canvasContext.closePath();
|
|
59
53
|
canvasContext.fill();
|
|
60
54
|
|
|
61
55
|
if (renderNums) {
|
|
62
|
-
const substitutions = substitutionsInfo?.get(
|
|
56
|
+
const substitutions = substitutionsInfo?.get(currentMonomer)?.get(currentPosition)?.entries() ?? null;
|
|
63
57
|
if (substitutions !== null) {
|
|
64
58
|
canvasContext.textBaseline = 'middle';
|
|
65
59
|
canvasContext.textAlign = 'center';
|
|
@@ -79,13 +73,13 @@ export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D,
|
|
|
79
73
|
}
|
|
80
74
|
}
|
|
81
75
|
|
|
82
|
-
const
|
|
83
|
-
if (
|
|
76
|
+
const monomerSelection = mutationCliffsSelection[currentPosition];
|
|
77
|
+
if (monomerSelection && monomerSelection.includes(currentMonomer))
|
|
84
78
|
renderCellSelection(canvasContext, bound);
|
|
85
79
|
}
|
|
86
80
|
|
|
87
|
-
export function renderInvaraintMapCell(canvasContext: CanvasRenderingContext2D,
|
|
88
|
-
currentPosition: string, invariantMapSelection: types.
|
|
81
|
+
export function renderInvaraintMapCell(canvasContext: CanvasRenderingContext2D, currentMonomer: string,
|
|
82
|
+
currentPosition: string, invariantMapSelection: types.Selection, cellValue: number, bound: DG.Rect,
|
|
89
83
|
color: number): void {
|
|
90
84
|
canvasContext.fillStyle = DG.Color.toHtml(color);
|
|
91
85
|
canvasContext.fillRect(bound.x, bound.y, bound.width, bound.height);
|
|
@@ -95,20 +89,20 @@ export function renderInvaraintMapCell(canvasContext: CanvasRenderingContext2D,
|
|
|
95
89
|
canvasContext.fillStyle = '#000';
|
|
96
90
|
canvasContext.fillText(cellValue.toString(), bound.x + (bound.width / 2), bound.y + (bound.height / 2), bound.width);
|
|
97
91
|
|
|
98
|
-
const
|
|
99
|
-
if (
|
|
92
|
+
const monomerSelection = invariantMapSelection[currentPosition];
|
|
93
|
+
if (monomerSelection && monomerSelection.includes(currentMonomer))
|
|
100
94
|
renderCellSelection(canvasContext, bound);
|
|
101
95
|
}
|
|
102
96
|
|
|
103
97
|
export function renderLogoSummaryCell(canvasContext: CanvasRenderingContext2D, cellValue: string,
|
|
104
|
-
clusterSelection:
|
|
98
|
+
clusterSelection: types.Selection, bound: DG.Rect): void {
|
|
105
99
|
canvasContext.font = '13px Roboto, Roboto Local, sans-serif';
|
|
106
100
|
canvasContext.textAlign = 'center';
|
|
107
101
|
canvasContext.textBaseline = 'middle';
|
|
108
102
|
canvasContext.fillStyle = '#000';
|
|
109
103
|
canvasContext.fillText(cellValue.toString(), bound.x + (bound.width / 2), bound.y + (bound.height / 2), bound.width);
|
|
110
104
|
|
|
111
|
-
if (clusterSelection.includes(cellValue))
|
|
105
|
+
if (clusterSelection[CLUSTER_TYPE.CUSTOM].includes(cellValue) || clusterSelection[CLUSTER_TYPE.ORIGINAL].includes(cellValue))
|
|
112
106
|
renderCellSelection(canvasContext, bound);
|
|
113
107
|
}
|
|
114
108
|
|
|
@@ -136,7 +130,7 @@ export function drawLogoInBounds(ctx: CanvasRenderingContext2D, bounds: DG.Rect,
|
|
|
136
130
|
|
|
137
131
|
const monomerBounds: { [monomer: string]: DG.Rect } = {};
|
|
138
132
|
for (const monomer of sortedOrder) {
|
|
139
|
-
const monomerHeight = barHeight * (stats[monomer]
|
|
133
|
+
const monomerHeight = barHeight * (stats[monomer]!.count / rowCount);
|
|
140
134
|
const selectionHeight = barHeight * ((monomerSelectionStats[monomer] ?? 0) / rowCount);
|
|
141
135
|
const currentBound = new DG.Rect(xStart / pr, currentY / pr, barWidth / pr, monomerHeight / pr);
|
|
142
136
|
monomerBounds[monomer] = currentBound;
|
package/src/utils/constants.ts
CHANGED
package/src/utils/misc.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import * as grok from 'datagrok-api/grok';
|
|
2
1
|
import * as ui from 'datagrok-api/ui';
|
|
3
2
|
import * as DG from 'datagrok-api/dg';
|
|
4
3
|
import * as C from './constants';
|
|
5
4
|
import * as type from './types';
|
|
6
5
|
import {StringDictionary} from '@datagrok-libraries/utils/src/type-declarations';
|
|
7
|
-
import {getSplitter} from '@datagrok-libraries/bio/src/utils/macromolecule/utils';
|
|
8
|
-
import {TAGS as bioTags} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
9
6
|
|
|
10
7
|
export function getTypedArrayConstructor(
|
|
11
8
|
maxNum: number): Uint8ArrayConstructor | Uint16ArrayConstructor | Uint32ArrayConstructor {
|
|
@@ -44,9 +41,9 @@ export function scaleActivity(activityCol: DG.Column<number>, scaling: C.SCALING
|
|
|
44
41
|
}
|
|
45
42
|
|
|
46
43
|
//TODO: optimize
|
|
47
|
-
export function calculateSelected(df: DG.DataFrame): type.
|
|
44
|
+
export function calculateSelected(df: DG.DataFrame): type.SelectionStats {
|
|
48
45
|
const monomerColumns: DG.Column<string>[] = df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER);
|
|
49
|
-
const selectedObj: type.
|
|
46
|
+
const selectedObj: type.SelectionStats = {};
|
|
50
47
|
for (const idx of df.selection.getSelectedIndexes()) {
|
|
51
48
|
for (const col of monomerColumns) {
|
|
52
49
|
const monomer = col.get(idx);
|
package/src/utils/statistics.ts
CHANGED
|
@@ -5,12 +5,18 @@ import BitArray from '@datagrok-libraries/utils/src/bit-array';
|
|
|
5
5
|
|
|
6
6
|
export type Stats = {
|
|
7
7
|
count: number,
|
|
8
|
-
pValue: number,
|
|
8
|
+
pValue: number | null,
|
|
9
9
|
meanDifference: number,
|
|
10
10
|
ratio: number,
|
|
11
|
+
mask: BitArray,
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
export function getStats(data: RawData | number[], bitArray: BitArray): Stats {
|
|
15
|
+
if (data.length !== bitArray.length && data.some((v, i) => i >= bitArray.length ? v !== 0 : false))
|
|
16
|
+
throw new Error('PeptidesError: Data and bit array have different lengths');
|
|
17
|
+
if (bitArray.falseCount() === 0 || bitArray.trueCount() === 0)
|
|
18
|
+
throw new Error('PeptidesError: One of the samples is empty');
|
|
19
|
+
|
|
14
20
|
const selected = new Float32Array(bitArray.trueCount());
|
|
15
21
|
const rest = new Float32Array(bitArray.falseCount());
|
|
16
22
|
|
|
@@ -23,13 +29,26 @@ export function getStats(data: RawData | number[], bitArray: BitArray): Stats {
|
|
|
23
29
|
rest[restIndex++] = data[i];
|
|
24
30
|
}
|
|
25
31
|
|
|
32
|
+
if (selected.length === 1 || rest.length === 1) {
|
|
33
|
+
const selectedMean = selected.reduce((a, b) => a + b, 0) / selected.length;
|
|
34
|
+
const restMean = rest.reduce((a, b) => a + b, 0) / rest.length;
|
|
35
|
+
return {
|
|
36
|
+
count: selected.length,
|
|
37
|
+
pValue: null,
|
|
38
|
+
meanDifference: selectedMean - restMean,
|
|
39
|
+
ratio: selected.length / (bitArray.length),
|
|
40
|
+
mask: bitArray,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
26
44
|
const testResult = tTest(selected, rest);
|
|
27
45
|
const currentMeanDiff = testResult['Mean difference']!;
|
|
28
46
|
return {
|
|
29
47
|
count: selected.length,
|
|
30
|
-
pValue: testResult[currentMeanDiff >= 0 ? 'p-value more' : 'p-value less']
|
|
31
|
-
meanDifference: currentMeanDiff
|
|
48
|
+
pValue: testResult[currentMeanDiff >= 0 ? 'p-value more' : 'p-value less'],
|
|
49
|
+
meanDifference: currentMeanDiff,
|
|
32
50
|
ratio: selected.length / (bitArray.length),
|
|
51
|
+
mask: bitArray,
|
|
33
52
|
};
|
|
34
53
|
}
|
|
35
54
|
|
package/src/utils/types.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import * as DG from 'datagrok-api/dg';
|
|
2
2
|
import {SCALING_METHODS} from './constants';
|
|
3
|
+
import {ClusterType} from '../model';
|
|
3
4
|
|
|
4
5
|
export type DataFrameDict = {[key: string]: DG.DataFrame};
|
|
5
6
|
|
|
6
7
|
export type RawData = Int32Array | Uint32Array | Float32Array | Float64Array;
|
|
7
8
|
export type UTypedArray = Uint8Array | Uint16Array | Uint32Array;
|
|
8
|
-
//
|
|
9
|
+
//Monomer: (Position: (index: indexList))
|
|
9
10
|
export type MutationCliffs = Map<string, Map<string, Map<number, number[] | UTypedArray>>>;
|
|
10
|
-
export type
|
|
11
|
-
|
|
12
|
-
export type
|
|
11
|
+
export type Selection = {[positionOrClusterType: string | ClusterType]: string[]};
|
|
12
|
+
export type SelectionItem = {positionOrClusterType: string | ClusterType, monomerOrCluster: string};
|
|
13
|
+
export type SelectionStats = {[positionOrClusterType: string | ClusterType]: { [monomerOrCluster: string]: number}};
|
|
13
14
|
|
|
14
15
|
export type PeptidesSettings = {
|
|
15
16
|
sequenceColumnName?: string,
|
|
@@ -48,4 +49,4 @@ export type StatsInfo = {
|
|
|
48
49
|
|
|
49
50
|
export type RawColumn = {name: string, rawData: RawData, cat?: string[]};
|
|
50
51
|
|
|
51
|
-
export type
|
|
52
|
+
export type SelectionOptions = {shiftPressed: boolean, ctrlPressed: boolean};
|