@datagrok/peptides 1.14.1 → 1.15.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 +29 -0
- package/dist/196.js +3 -0
- package/dist/196.js.LICENSE.txt +51 -0
- package/dist/209.js +2 -2
- package/dist/361.js +2 -0
- package/dist/381.js +2 -0
- package/dist/436.js +2 -0
- package/dist/694.js +2 -0
- package/dist/831.js +2 -0
- package/dist/868.js +2 -0
- package/dist/package-test.js +3 -2
- package/dist/package-test.js.LICENSE.txt +51 -0
- package/dist/package.js +3 -2
- package/dist/package.js.LICENSE.txt +51 -0
- package/files/help/logo-summary-table.md +23 -0
- package/files/help/monomer-position.md +31 -0
- package/files/help/most-potent-residues.md +17 -0
- package/files/icons/ribbon/logo-summary-viewer.svg +8 -0
- package/files/icons/ribbon/peptide-sar-vertical-viewer.svg +10 -0
- package/files/icons/ribbon/peptide-sar-viewer.svg +11 -0
- package/files/tests/100k.d42 +0 -0
- package/files/tests/200k.d42 +0 -0
- package/files/tests/50k.d42 +0 -0
- package/files/tests/{aligned_5k.d42 → 5k.d42} +0 -0
- package/package.json +5 -5
- package/src/model.ts +85 -175
- package/src/package-test.ts +7 -6
- package/src/tests/benchmarks.ts +95 -0
- package/src/tests/core.ts +4 -75
- package/src/tests/{algorithms.ts → misc.ts} +3 -16
- package/src/tests/model.ts +3 -14
- package/src/tests/table-view.ts +4 -14
- package/src/tests/viewers.ts +7 -1
- package/src/tests/widgets.ts +9 -1
- package/src/utils/algorithms.ts +166 -16
- package/src/utils/cell-renderer.ts +12 -7
- package/src/utils/constants.ts +2 -0
- package/src/utils/misc.ts +2 -1
- package/src/utils/parallel-mutation-cliffs.ts +106 -0
- package/src/utils/types.ts +1 -1
- package/src/viewers/logo-summary.ts +9 -6
- package/src/viewers/sar-viewer.ts +28 -14
- package/src/widgets/mutation-cliffs.ts +2 -2
- package/src/widgets/peptides.ts +15 -5
- package/src/widgets/selection.ts +9 -0
- package/src/widgets/settings.ts +3 -8
- package/src/workers/mutation-cliffs-worker.ts +77 -0
- package/dist/521.js +0 -2
- package/dist/677.js +0 -2
- package/dist/729.js +0 -2
- package/dist/959.js +0 -2
package/src/utils/misc.ts
CHANGED
|
@@ -36,7 +36,7 @@ export function scaleActivity(activityCol: DG.Column<number>, scaling: C.SCALING
|
|
|
36
36
|
const val = activityColData[i];
|
|
37
37
|
return val === DG.FLOAT_NULL || val === DG.INT_NULL ? val : formula(val);
|
|
38
38
|
});
|
|
39
|
-
|
|
39
|
+
scaledCol.setTag(C.TAGS.ANALYSIS_COL, `${true}`);
|
|
40
40
|
return scaledCol;
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -131,4 +131,5 @@ export function setGridProps(grid: DG.Grid): void {
|
|
|
131
131
|
grid.props.showRowHeader = false;
|
|
132
132
|
grid.props.showCurrentRowIndicator = false;
|
|
133
133
|
grid.root.style.width = '100%';
|
|
134
|
+
grid.root.style.maxWidth = '100%';
|
|
134
135
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
|
|
2
|
+
import * as type from './types';
|
|
3
|
+
|
|
4
|
+
export type ParallelMutationReturnType = {
|
|
5
|
+
// monomers1: string[],
|
|
6
|
+
// monomers2: string[],
|
|
7
|
+
pos: string[],
|
|
8
|
+
seq1Idxs: Uint32Array,
|
|
9
|
+
seq2Idxs: Uint32Array,
|
|
10
|
+
}
|
|
11
|
+
export class ParallelMutationCliffs {
|
|
12
|
+
private _workers: Worker[];
|
|
13
|
+
private _workerCount: number;
|
|
14
|
+
|
|
15
|
+
constructor() {
|
|
16
|
+
const threadCount = navigator.hardwareConcurrency;
|
|
17
|
+
this._workerCount = Math.max(threadCount - 2, 1);
|
|
18
|
+
this._workers = new Array(this._workerCount).fill(null)
|
|
19
|
+
.map(() => new Worker(new URL('../workers/mutation-cliffs-worker', import.meta.url)));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public async calc(activityArray: type.RawData, monomerInfoArray: type.RawColumn[],
|
|
23
|
+
settings: type.PeptidesSettings = {},
|
|
24
|
+
targetOptions: {targetCol?: type.RawColumn | null, currentTarget?: string | null} = {},
|
|
25
|
+
): Promise<type.MutationCliffs> {
|
|
26
|
+
const substitutionsInfo: type.MutationCliffs = new Map();
|
|
27
|
+
try {
|
|
28
|
+
const currentTargetIdx = targetOptions.targetCol?.cat!.indexOf(targetOptions.currentTarget!) ?? -1;
|
|
29
|
+
|
|
30
|
+
const len = activityArray.length;
|
|
31
|
+
const promises = new Array<Promise<ParallelMutationReturnType>>(this._workerCount);
|
|
32
|
+
const matSize = len * (len - 1) / 2; // size of reduced upper triangular matrix
|
|
33
|
+
this._workerCount = Math.min(this._workerCount, matSize);
|
|
34
|
+
const chunkSize = matSize / this._workerCount;
|
|
35
|
+
// monomerInfoArray[m].cat and targetCol can contain some function from datagrok-api,
|
|
36
|
+
//which the worker can't serialize and fails. so we need to remove it
|
|
37
|
+
monomerInfoArray.forEach((monomerInfo) => {
|
|
38
|
+
monomerInfo.cat = monomerInfo.cat?.slice();
|
|
39
|
+
});
|
|
40
|
+
targetOptions.targetCol?.cat && (targetOptions.targetCol.cat = targetOptions.targetCol.cat.slice());
|
|
41
|
+
for (let idx = 0; idx < this._workerCount; idx++) {
|
|
42
|
+
promises[idx] = new Promise((resolveWorker, rejectWorker) => {
|
|
43
|
+
const startIdx = Math.floor(idx * chunkSize);
|
|
44
|
+
const endIdx = idx === this._workerCount - 1 ? matSize : Math.floor((idx + 1) * chunkSize);
|
|
45
|
+
this._workers[idx].postMessage(
|
|
46
|
+
{startIdx, endIdx, activityArray, monomerInfoArray, settings, currentTargetIdx, targetOptions});
|
|
47
|
+
this._workers[idx].onmessage = ({data: {pos, seq1Idxs, seq2Idxs, error}}): void => {
|
|
48
|
+
if (error) {
|
|
49
|
+
this._workers[idx]?.terminate();
|
|
50
|
+
rejectWorker(error);
|
|
51
|
+
} else {
|
|
52
|
+
this._workers[idx].terminate();
|
|
53
|
+
resolveWorker({pos, seq1Idxs, seq2Idxs});
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const results = await Promise.all(promises);
|
|
60
|
+
const monomerPositionsMap = new Map<string, number>();
|
|
61
|
+
monomerInfoArray.forEach((monomerInfo, i) => {
|
|
62
|
+
monomerPositionsMap.set(monomerInfo.name, i);
|
|
63
|
+
});
|
|
64
|
+
results.filter(Boolean).forEach((result) => {
|
|
65
|
+
for (let i = 0; i< result.pos.length; i++) {
|
|
66
|
+
//getting monomers from monomerInfoArray by position
|
|
67
|
+
const monomerPos = monomerPositionsMap.get(result.pos[i])!;
|
|
68
|
+
const monomer1Cat = monomerInfoArray[monomerPos].rawData[result.seq1Idxs[i]];
|
|
69
|
+
const monomer1 = monomerInfoArray[monomerPos].cat![monomer1Cat];
|
|
70
|
+
const monomer2Cat = monomerInfoArray[monomerPos].rawData[result.seq2Idxs[i]];
|
|
71
|
+
const monomer2 = monomerInfoArray[monomerPos].cat![monomer2Cat];
|
|
72
|
+
|
|
73
|
+
// filling map
|
|
74
|
+
if (!substitutionsInfo.has(monomer1))
|
|
75
|
+
substitutionsInfo.set(monomer1, new Map());
|
|
76
|
+
if (!substitutionsInfo.has(monomer2))
|
|
77
|
+
substitutionsInfo.set(monomer2, new Map());
|
|
78
|
+
const position1Map = substitutionsInfo.get(monomer1)!;
|
|
79
|
+
const position2Map = substitutionsInfo.get(monomer2)!;
|
|
80
|
+
if (!position1Map.has(result.pos[i]))
|
|
81
|
+
position1Map.set(result.pos[i], new Map());
|
|
82
|
+
if (!position2Map.has(result.pos[i]))
|
|
83
|
+
position2Map.set(result.pos[i], new Map());
|
|
84
|
+
const indexes1Map = position1Map.get(result.pos[i])!;
|
|
85
|
+
const indexes2Map = position2Map.get(result.pos[i])!;
|
|
86
|
+
if (!indexes1Map.has(result.seq1Idxs[i]))
|
|
87
|
+
indexes1Map.set(result.seq1Idxs[i], []);
|
|
88
|
+
if (!indexes2Map.has(result.seq2Idxs[i]))
|
|
89
|
+
indexes2Map.set(result.seq2Idxs[i], []);
|
|
90
|
+
const indexes1 = indexes1Map.get(result.seq1Idxs[i])!;
|
|
91
|
+
const indexes2 = indexes2Map.get(result.seq2Idxs[i])!;
|
|
92
|
+
(indexes1 as number[]).push(result.seq2Idxs[i]);
|
|
93
|
+
(indexes2 as number[]).push(result.seq1Idxs[i]);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
} catch (error) {
|
|
97
|
+
this.terminate();
|
|
98
|
+
console.error(error);
|
|
99
|
+
}
|
|
100
|
+
return substitutionsInfo;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public terminate(): void {
|
|
104
|
+
this._workers?.forEach((worker) => worker?.terminate());
|
|
105
|
+
}
|
|
106
|
+
}
|
package/src/utils/types.ts
CHANGED
|
@@ -18,7 +18,6 @@ export type PeptidesSettings = {
|
|
|
18
18
|
clustersColumnName?: string,
|
|
19
19
|
targetColumnName?: string,
|
|
20
20
|
scaling?: SCALING_METHODS,
|
|
21
|
-
isBidirectional?: boolean,
|
|
22
21
|
showMonomerPosition?: boolean,
|
|
23
22
|
showMostPotentResidues?: boolean,
|
|
24
23
|
showLogoSummaryTable?: boolean,
|
|
@@ -39,6 +38,7 @@ export type DrawOptions = {
|
|
|
39
38
|
marginHorizontal?: number,
|
|
40
39
|
headerStyle?: string,
|
|
41
40
|
textHeight?: number,
|
|
41
|
+
selectionWidth?: number,
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
export type StatsInfo = {
|
|
@@ -13,6 +13,7 @@ import {getActivityDistribution, getDistributionLegend, getStatsTableMap} from '
|
|
|
13
13
|
import {getStatsSummary, prepareTableForHistogram} from '../utils/misc';
|
|
14
14
|
import BitArray from '@datagrok-libraries/utils/src/bit-array';
|
|
15
15
|
import {SelectionItem} from '../utils/types';
|
|
16
|
+
import {_package} from '../package';
|
|
16
17
|
|
|
17
18
|
const getAggregatedColName = (aggF: string, colName: string): string => `${aggF}(${colName})`;
|
|
18
19
|
|
|
@@ -213,7 +214,6 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
213
214
|
this.viewerGrid.sort([C.LST_COLUMN_NAMES.MEMBERS], [false]);
|
|
214
215
|
this.updateFilter();
|
|
215
216
|
const gridClustersCol = this.viewerGrid.col(C.LST_COLUMN_NAMES.CLUSTER)!;
|
|
216
|
-
// gridClustersCol.column!.name = C.LST_COLUMN_NAMES.CLUSTER;
|
|
217
217
|
gridClustersCol.visible = true;
|
|
218
218
|
this.viewerGrid.columns.setOrder([C.LST_COLUMN_NAMES.CLUSTER, C.LST_COLUMN_NAMES.MEMBERS,
|
|
219
219
|
C.LST_COLUMN_NAMES.WEB_LOGO, C.LST_COLUMN_NAMES.DISTRIBUTION, C.LST_COLUMN_NAMES.MEAN_DIFFERENCE,
|
|
@@ -223,7 +223,7 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
223
223
|
|
|
224
224
|
const webLogoCache = new DG.LruCache<number, DG.Viewer & IWebLogoViewer>();
|
|
225
225
|
const distCache = new DG.LruCache<number, DG.Viewer<DG.IHistogramLookSettings>>();
|
|
226
|
-
const maxSequenceLen = this.model.
|
|
226
|
+
const maxSequenceLen = this.model.positionColumns.toArray().length;
|
|
227
227
|
const webLogoGridCol = this.viewerGrid.columns.byName(C.LST_COLUMN_NAMES.WEB_LOGO)!;
|
|
228
228
|
webLogoGridCol.cellType = 'html';
|
|
229
229
|
webLogoGridCol.width = 350;
|
|
@@ -269,7 +269,7 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
269
269
|
const webLogoTable = this.createWebLogoDf(pepCol, clusterBitSet);
|
|
270
270
|
viewer = await webLogoTable.plot
|
|
271
271
|
.fromType('WebLogo', {positionHeight: this.webLogoMode, horizontalAlignment: HorizontalAlignments.LEFT,
|
|
272
|
-
maxHeight: 1000, minHeight: height, positionWidth: positionWidth});
|
|
272
|
+
maxHeight: 1000, minHeight: height, positionWidth: positionWidth, showPositionLabels: false});
|
|
273
273
|
webLogoCache.set(currentRowIdx, viewer);
|
|
274
274
|
}
|
|
275
275
|
gridCell.element = viewer.root;
|
|
@@ -302,7 +302,7 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
302
302
|
}
|
|
303
303
|
});
|
|
304
304
|
this.viewerGrid.root.addEventListener('mouseleave', (_ev) => this.model.unhighlight());
|
|
305
|
-
this.viewerGrid.onCurrentCellChanged.subscribe((gridCell) => {
|
|
305
|
+
DG.debounce(this.viewerGrid.onCurrentCellChanged, 500).subscribe((gridCell) => {
|
|
306
306
|
if (!gridCell.isTableCell)
|
|
307
307
|
return;
|
|
308
308
|
|
|
@@ -328,6 +328,10 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
328
328
|
const selection = this.getCluster(gridCell);
|
|
329
329
|
this.model.modifyClusterSelection(selection, {shiftPressed: ev.shiftKey, ctrlPressed: ev.ctrlKey});
|
|
330
330
|
this.viewerGrid.invalidate();
|
|
331
|
+
|
|
332
|
+
_package.files.readAsText('help/logo-summary-table.md').then((text) => {
|
|
333
|
+
grok.shell.windows.help.showHelp(ui.markdown(text));
|
|
334
|
+
}).catch((e) => grok.log.error(e));
|
|
331
335
|
});
|
|
332
336
|
this.viewerGrid.onCellTooltip((gridCell, x, y) => {
|
|
333
337
|
if (!gridCell.isTableCell) {
|
|
@@ -347,7 +351,6 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
347
351
|
gridProps.allowRowSelection = false;
|
|
348
352
|
gridProps.allowBlockSelection = false;
|
|
349
353
|
gridProps.allowColSelection = false;
|
|
350
|
-
gridProps.showRowHeader = false;
|
|
351
354
|
gridProps.showCurrentRowIndicator = false;
|
|
352
355
|
|
|
353
356
|
return this.viewerGrid;
|
|
@@ -381,7 +384,7 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
381
384
|
|
|
382
385
|
this.bitsets.push(currentSelection.clone());
|
|
383
386
|
|
|
384
|
-
const newClusterName =
|
|
387
|
+
const newClusterName = this.model.df.columns.getUnusedName('New Cluster');
|
|
385
388
|
const aggregatedValues: {[colName: string]: number} = {};
|
|
386
389
|
const aggColsEntries = Object.entries(this.model.settings.columns ?? {});
|
|
387
390
|
for (const [colName, aggFn] of aggColsEntries) {
|
|
@@ -9,6 +9,7 @@ import {PeptidesModel, PositionStats, VIEWER_TYPE} from '../model';
|
|
|
9
9
|
import wu from 'wu';
|
|
10
10
|
import {SelectionItem} from '../utils/types';
|
|
11
11
|
import {Stats} from '../utils/statistics';
|
|
12
|
+
import {_package} from '../package';
|
|
12
13
|
|
|
13
14
|
export enum SELECTION_MODE {
|
|
14
15
|
MUTATION_CLIFFS = 'Mutation Cliffs',
|
|
@@ -26,7 +27,7 @@ export class MonomerPosition extends DG.JsViewer {
|
|
|
26
27
|
_titleHost = ui.divText(SELECTION_MODE.MUTATION_CLIFFS, {id: 'pep-viewer-title'});
|
|
27
28
|
_viewerGrid!: DG.Grid;
|
|
28
29
|
_model!: PeptidesModel;
|
|
29
|
-
|
|
30
|
+
color: string;
|
|
30
31
|
aggregation: string;
|
|
31
32
|
target: string;
|
|
32
33
|
keyPressed: boolean = false;
|
|
@@ -36,7 +37,7 @@ export class MonomerPosition extends DG.JsViewer {
|
|
|
36
37
|
super();
|
|
37
38
|
this.target = this.string(MONOMER_POSITION_PROPERTIES.TARGET, null,
|
|
38
39
|
{category: SELECTION_MODE.MUTATION_CLIFFS, choices: []});
|
|
39
|
-
this.
|
|
40
|
+
this.color = this.string(MONOMER_POSITION_PROPERTIES.COLOR_COLUMN_NAME, C.COLUMNS_NAMES.ACTIVITY_SCALED,
|
|
40
41
|
{category: SELECTION_MODE.INVARIANT_MAP,
|
|
41
42
|
choices: wu(grok.shell.t.columns.numerical).toArray().map((col) => col.name)});
|
|
42
43
|
this.aggregation = this.string(MONOMER_POSITION_PROPERTIES.AGGREGATION, DG.AGG.AVG,
|
|
@@ -81,14 +82,14 @@ export class MonomerPosition extends DG.JsViewer {
|
|
|
81
82
|
onPropertyChanged(property: DG.Property): void {
|
|
82
83
|
super.onPropertyChanged(property);
|
|
83
84
|
if (property.name === MONOMER_POSITION_PROPERTIES.TARGET)
|
|
84
|
-
this.model.updateMutationCliffs();
|
|
85
|
+
this.model.updateMutationCliffs().then(() => this.render(true));
|
|
85
86
|
|
|
86
|
-
this.render();
|
|
87
|
+
this.render(true);
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
createMonomerPositionDf(): DG.DataFrame {
|
|
90
91
|
const uniqueMonomers = new Set<string>();
|
|
91
|
-
const splitSeqCols = this.model.
|
|
92
|
+
const splitSeqCols = this.model.positionColumns.toArray();
|
|
92
93
|
for (const col of splitSeqCols) {
|
|
93
94
|
const colCat = col.categories;
|
|
94
95
|
for (const cat of colCat) {
|
|
@@ -111,12 +112,11 @@ export class MonomerPosition extends DG.JsViewer {
|
|
|
111
112
|
const monomerPositionDf = this.createMonomerPositionDf();
|
|
112
113
|
this.viewerGrid = monomerPositionDf.plot.grid();
|
|
113
114
|
this.viewerGrid.sort([C.COLUMNS_NAMES.MONOMER]);
|
|
114
|
-
this.viewerGrid.columns.setOrder([C.COLUMNS_NAMES.MONOMER, ...this.model.
|
|
115
|
+
this.viewerGrid.columns.setOrder([C.COLUMNS_NAMES.MONOMER, ...this.model.positionColumns.toArray().map((col) => col.name)]);
|
|
115
116
|
const monomerCol = monomerPositionDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
116
117
|
CR.setMonomerRenderer(monomerCol, this.model.alphabet);
|
|
117
118
|
this.viewerGrid.onCellRender.subscribe((args: DG.GridCellRenderArgs) => renderCell(args, this.model,
|
|
118
|
-
this.mode === SELECTION_MODE.INVARIANT_MAP, this.dataFrame.getCol(this.
|
|
119
|
-
this.aggregation as DG.AggregationType));
|
|
119
|
+
this.mode === SELECTION_MODE.INVARIANT_MAP, this.dataFrame.getCol(this.color), this.aggregation as DG.AGG));
|
|
120
120
|
|
|
121
121
|
this.viewerGrid.onCellTooltip((gridCell: DG.GridCell, x: number, y: number) => {
|
|
122
122
|
if (!gridCell.isTableCell) {
|
|
@@ -128,7 +128,7 @@ export class MonomerPosition extends DG.JsViewer {
|
|
|
128
128
|
return this.model.showTooltip(monomerPosition, x, y, true);
|
|
129
129
|
});
|
|
130
130
|
this.viewerGrid.root.addEventListener('mouseleave', (_ev) => this.model.unhighlight());
|
|
131
|
-
this.viewerGrid.onCurrentCellChanged.subscribe((gridCell: DG.GridCell) => {
|
|
131
|
+
DG.debounce(this.viewerGrid.onCurrentCellChanged, 500).subscribe((gridCell: DG.GridCell) => {
|
|
132
132
|
try {
|
|
133
133
|
if (!this.keyPressed)
|
|
134
134
|
return;
|
|
@@ -166,11 +166,19 @@ export class MonomerPosition extends DG.JsViewer {
|
|
|
166
166
|
this.model.modifyMutationCliffsSelection(monomerPosition, {shiftPressed: ev.shiftKey, ctrlPressed: ev.ctrlKey});
|
|
167
167
|
|
|
168
168
|
this.viewerGrid.invalidate();
|
|
169
|
+
|
|
170
|
+
this.showHelp();
|
|
169
171
|
});
|
|
170
172
|
|
|
171
173
|
setViewerGridProps(this.viewerGrid, false);
|
|
172
174
|
}
|
|
173
175
|
|
|
176
|
+
showHelp(): void {
|
|
177
|
+
_package.files.readAsText('help/monomer-position.md').then((text) => {
|
|
178
|
+
grok.shell.windows.help.showHelp(ui.markdown(text));
|
|
179
|
+
}).catch((e) => grok.log.error(e));
|
|
180
|
+
}
|
|
181
|
+
|
|
174
182
|
getMonomerPosition(gridCell: DG.GridCell): SelectionItem {
|
|
175
183
|
return {monomerOrCluster: gridCell.cell.dataFrame.get(C.COLUMNS_NAMES.MONOMER, gridCell!.tableRowIndex!) as string,
|
|
176
184
|
positionOrClusterType: gridCell!.tableColumn!.name};
|
|
@@ -186,6 +194,7 @@ export class MonomerPosition extends DG.JsViewer {
|
|
|
186
194
|
invariantMapMode.value = false;
|
|
187
195
|
mutationCliffsMode.value = true;
|
|
188
196
|
this.mode = SELECTION_MODE.MUTATION_CLIFFS;
|
|
197
|
+
this.showHelp();
|
|
189
198
|
});
|
|
190
199
|
mutationCliffsMode.setTooltip('Statistically significant changes in activity');
|
|
191
200
|
const invariantMapMode = ui.boolInput(SELECTION_MODE.INVARIANT_MAP, this.mode === SELECTION_MODE.INVARIANT_MAP);
|
|
@@ -193,6 +202,7 @@ export class MonomerPosition extends DG.JsViewer {
|
|
|
193
202
|
mutationCliffsMode.value = false;
|
|
194
203
|
invariantMapMode.value = true;
|
|
195
204
|
this.mode = SELECTION_MODE.INVARIANT_MAP;
|
|
205
|
+
this.showHelp();
|
|
196
206
|
});
|
|
197
207
|
invariantMapMode.setTooltip('Number of sequences having monomer-position');
|
|
198
208
|
const setDefaultProperties = (input: DG.InputBase): void => {
|
|
@@ -351,13 +361,13 @@ export class MostPotentResidues extends DG.JsViewer {
|
|
|
351
361
|
return false;
|
|
352
362
|
return this.model.showTooltip(monomerPosition, x, y, true);
|
|
353
363
|
});
|
|
354
|
-
this.viewerGrid.onCurrentCellChanged.subscribe((gridCell: DG.GridCell) => {
|
|
364
|
+
DG.debounce(this.viewerGrid.onCurrentCellChanged, 500).subscribe((gridCell: DG.GridCell) => {
|
|
355
365
|
try {
|
|
356
366
|
if ((this.keyPressed && mostPotentResiduesDf.currentCol.name !== C.COLUMNS_NAMES.MEAN_DIFFERENCE) || !this.keyPressed)
|
|
357
367
|
return;
|
|
358
368
|
const monomerPosition = this.getMonomerPosition(gridCell);
|
|
359
369
|
if (this.currentGridRowIdx !== null) {
|
|
360
|
-
const previousMonomerPosition = this.getMonomerPosition(this.viewerGrid.cell(
|
|
370
|
+
const previousMonomerPosition = this.getMonomerPosition(this.viewerGrid.cell('Diff', this.currentGridRowIdx));
|
|
361
371
|
this.model.modifyMutationCliffsSelection(previousMonomerPosition, {shiftPressed: true, ctrlPressed: true}, false);
|
|
362
372
|
}
|
|
363
373
|
if (this.model.mutationCliffs?.get(monomerPosition.monomerOrCluster)?.get(monomerPosition.positionOrClusterType)?.size)
|
|
@@ -380,6 +390,10 @@ export class MostPotentResidues extends DG.JsViewer {
|
|
|
380
390
|
return;
|
|
381
391
|
this.model.modifyMutationCliffsSelection(monomerPosition, {shiftPressed: ev.shiftKey, ctrlPressed: ev.ctrlKey});
|
|
382
392
|
this.viewerGrid.invalidate();
|
|
393
|
+
|
|
394
|
+
_package.files.readAsText('help/most-potent-residues.md').then((text) => {
|
|
395
|
+
grok.shell.windows.help.showHelp(ui.markdown(text));
|
|
396
|
+
}).catch((e) => grok.log.error(e));
|
|
383
397
|
});
|
|
384
398
|
const mdCol: DG.GridColumn = this.viewerGrid.col(C.COLUMNS_NAMES.MEAN_DIFFERENCE)!;
|
|
385
399
|
mdCol.name = 'Diff';
|
|
@@ -405,8 +419,8 @@ export class MostPotentResidues extends DG.JsViewer {
|
|
|
405
419
|
}
|
|
406
420
|
|
|
407
421
|
function renderCell(args: DG.GridCellRenderArgs, model: PeptidesModel, isInvariantMap?: boolean,
|
|
408
|
-
colorCol?: DG.Column<number>, colorAgg?: DG.
|
|
409
|
-
const renderColNames = [...model.
|
|
422
|
+
colorCol?: DG.Column<number>, colorAgg?: DG.AGG, renderNums?: boolean): void {
|
|
423
|
+
const renderColNames = [...model.positionColumns.toArray().map((col) => col.name), C.COLUMNS_NAMES.MEAN_DIFFERENCE];
|
|
410
424
|
const canvasContext = args.g;
|
|
411
425
|
const bound = args.bounds;
|
|
412
426
|
|
|
@@ -464,7 +478,7 @@ function renderCell(args: DG.GridCellRenderArgs, model: PeptidesModel, isInvaria
|
|
|
464
478
|
canvasContext, currentMonomer, currentPosition, model.invariantMapSelection, value, bound, color);
|
|
465
479
|
} else {
|
|
466
480
|
CR.renderMutationCliffCell(canvasContext, currentMonomer, currentPosition, model.monomerPositionStats, bound,
|
|
467
|
-
model.mutationCliffsSelection, model.mutationCliffs,
|
|
481
|
+
model.mutationCliffsSelection, model.mutationCliffs, renderNums);
|
|
468
482
|
}
|
|
469
483
|
args.preventDefault();
|
|
470
484
|
canvasContext.restore();
|
|
@@ -147,11 +147,11 @@ export function mutationCliffsWidget(table: DG.DataFrame, model: PeptidesModel):
|
|
|
147
147
|
|
|
148
148
|
const gridCols = model.analysisView.grid.columns;
|
|
149
149
|
const originalGridColCount = gridCols.length;
|
|
150
|
-
const positionColumns = model.
|
|
150
|
+
const positionColumns = model.positionColumns.toArray().map((col) => col.name);
|
|
151
151
|
const columnNames: string[] = [];
|
|
152
152
|
for (let colIdx = 1; colIdx < originalGridColCount; colIdx++) {
|
|
153
153
|
const gridCol = gridCols.byIndex(colIdx);
|
|
154
|
-
if (gridCol?.name === model.settings.sequenceColumnName || (gridCol?.visible === true && !positionColumns.
|
|
154
|
+
if (gridCol?.name === model.settings.sequenceColumnName || (gridCol?.visible === true && !positionColumns.includes(gridCol.name)))
|
|
155
155
|
columnNames.push(gridCol!.name);
|
|
156
156
|
}
|
|
157
157
|
|
package/src/widgets/peptides.ts
CHANGED
|
@@ -122,7 +122,7 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>): {h
|
|
|
122
122
|
bitsetChanged.unsubscribe();
|
|
123
123
|
if (sequencesCol) {
|
|
124
124
|
const model = await startAnalysis(activityColumnChoice.value!, sequencesCol, clustersColumnChoice.value, df,
|
|
125
|
-
scaledCol, activityScalingMethod.value ?? C.SCALING_METHODS.NONE, targetColumnChoice.value);
|
|
125
|
+
scaledCol, activityScalingMethod.value ?? C.SCALING_METHODS.NONE, targetColumnChoice.value, {addSequenceSpace: true});
|
|
126
126
|
return model !== null;
|
|
127
127
|
}
|
|
128
128
|
return false;
|
|
@@ -155,9 +155,11 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>): {h
|
|
|
155
155
|
return {host: mainHost, callback: startAnalysisCallback};
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
type AnalysisOptions = {addSequenceSpace?: boolean};
|
|
159
|
+
|
|
158
160
|
export async function startAnalysis(activityColumn: DG.Column<number>, peptidesCol: DG.Column<string>,
|
|
159
161
|
clustersColumn: DG.Column | null, currentDf: DG.DataFrame, scaledCol: DG.Column<number>, scaling: C.SCALING_METHODS,
|
|
160
|
-
targetColumn: DG.Column<string> | null = null): Promise<PeptidesModel | null> {
|
|
162
|
+
targetColumn: DG.Column<string> | null = null, options: AnalysisOptions = {}): Promise<PeptidesModel | null> {
|
|
161
163
|
const progress = DG.TaskBarProgressIndicator.create('Loading SAR...');
|
|
162
164
|
let model = null;
|
|
163
165
|
if (activityColumn.type === DG.COLUMN_TYPE.FLOAT || activityColumn.type === DG.COLUMN_TYPE.INT) {
|
|
@@ -165,15 +167,19 @@ export async function startAnalysis(activityColumn: DG.Column<number>, peptidesC
|
|
|
165
167
|
const newDf = DG.DataFrame.create(currentDf.rowCount);
|
|
166
168
|
const newDfCols = newDf.columns;
|
|
167
169
|
newDfCols.add(scaledCol);
|
|
168
|
-
for (const col of currentDf.columns)
|
|
169
|
-
|
|
170
|
+
for (const col of currentDf.columns) {
|
|
171
|
+
if (col.getTag(C.TAGS.ANALYSIS_COL) !== `${true}`) {
|
|
172
|
+
if (col.name === scaledCol.name)
|
|
173
|
+
col.name = currentDf.columns.getUnusedName(col.name);
|
|
174
|
+
newDfCols.add(col);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
170
177
|
|
|
171
178
|
newDf.name = 'Peptides analysis';
|
|
172
179
|
const settings: type.PeptidesSettings = {
|
|
173
180
|
sequenceColumnName: peptidesCol.name,
|
|
174
181
|
activityColumnName: activityColumn.name,
|
|
175
182
|
scaling: scaling,
|
|
176
|
-
isBidirectional: false,
|
|
177
183
|
columns: {},
|
|
178
184
|
maxMutations: 1,
|
|
179
185
|
minActivityDelta: 0,
|
|
@@ -212,6 +218,10 @@ export async function startAnalysis(activityColumn: DG.Column<number>, peptidesC
|
|
|
212
218
|
await model.addLogoSummaryTable();
|
|
213
219
|
await model.addMonomerPosition();
|
|
214
220
|
await model.addMostPotentResidues();
|
|
221
|
+
|
|
222
|
+
// FIXME: enable by default for tests
|
|
223
|
+
if (options.addSequenceSpace ?? false)
|
|
224
|
+
model.addSequenceSpace();
|
|
215
225
|
} else
|
|
216
226
|
grok.shell.error('The activity column must be of numeric type!');
|
|
217
227
|
progress.close();
|
package/src/widgets/selection.ts
CHANGED
|
@@ -33,6 +33,7 @@ export function getSelectionWidget(table: DG.DataFrame, model: PeptidesModel): H
|
|
|
33
33
|
}
|
|
34
34
|
const grid = newTable.plot.grid();
|
|
35
35
|
grid.props.showRowHeader = false;
|
|
36
|
+
grid.root.style.maxWidth = '100%';
|
|
36
37
|
|
|
37
38
|
DG.debounce(ui.onSizeChanged(grid.root), 50).subscribe((_) => {
|
|
38
39
|
const panel = grid.root.parentElement;
|
|
@@ -49,5 +50,13 @@ export function getSelectionWidget(table: DG.DataFrame, model: PeptidesModel): H
|
|
|
49
50
|
|
|
50
51
|
const gridHost = ui.box(grid.root);
|
|
51
52
|
gridHost.style.marginLeft = '0px';
|
|
53
|
+
setTimeout(() => {
|
|
54
|
+
for (let gridColIdx = 1; gridColIdx < sourceGrid.columns.length; gridColIdx++) {
|
|
55
|
+
const gridCol = sourceGrid.columns.byIndex(gridColIdx)!;
|
|
56
|
+
if (!gridCol.visible)
|
|
57
|
+
continue;
|
|
58
|
+
grid.col(gridCol.name)!.width = gridCol.width;
|
|
59
|
+
}
|
|
60
|
+
}, 500);
|
|
52
61
|
return gridHost;
|
|
53
62
|
}
|
package/src/widgets/settings.ts
CHANGED
|
@@ -22,7 +22,6 @@ export enum SETTINGS_PANES {
|
|
|
22
22
|
export enum GENERAL_INPUTS {
|
|
23
23
|
ACTIVITY = 'Activity',
|
|
24
24
|
ACTIVITY_SCALING = 'Activity scaling',
|
|
25
|
-
BIDIRECTIONAL_ANALYSIS = 'Bidirectional analysis',
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
export enum VIEWERS_INPUTS {
|
|
@@ -51,7 +50,7 @@ export function getSettingsDialog(model: PeptidesModel): SettingsElements {
|
|
|
51
50
|
const accordion = ui.accordion();
|
|
52
51
|
const settings = model.settings;
|
|
53
52
|
const currentScaling = settings.scaling ?? C.SCALING_METHODS.NONE;
|
|
54
|
-
const currentBidirectional = settings.isBidirectional ?? false;
|
|
53
|
+
// const currentBidirectional = settings.isBidirectional ?? false;
|
|
55
54
|
const currentMaxMutations = settings.maxMutations ?? 1;
|
|
56
55
|
const currentMinActivityDelta = settings.minActivityDelta ?? 0;
|
|
57
56
|
const currentColumns = settings.columns ?? {};
|
|
@@ -69,13 +68,9 @@ export function getSettingsDialog(model: PeptidesModel): SettingsElements {
|
|
|
69
68
|
ui.choiceInput(GENERAL_INPUTS.ACTIVITY_SCALING, currentScaling, Object.values(C.SCALING_METHODS),
|
|
70
69
|
() => result.scaling = activityScaling.value as C.SCALING_METHODS) as DG.InputBase<C.SCALING_METHODS>;
|
|
71
70
|
activityScaling.setTooltip('Activity column transformation method');
|
|
72
|
-
const bidirectionalAnalysis = ui.boolInput(GENERAL_INPUTS.BIDIRECTIONAL_ANALYSIS, currentBidirectional,
|
|
73
|
-
() => result.isBidirectional = bidirectionalAnalysis.value) as DG.InputBase<boolean>;
|
|
74
|
-
bidirectionalAnalysis.setTooltip('Distinguish between positive and negative mean activity difference in ' +
|
|
75
|
-
'Monomer-Position and Most Potent Residues viewers');
|
|
76
71
|
|
|
77
|
-
accordion.addPane(SETTINGS_PANES.GENERAL, () => ui.inputs([activityCol, activityScaling
|
|
78
|
-
inputs[SETTINGS_PANES.GENERAL] = [activityCol, activityScaling
|
|
72
|
+
accordion.addPane(SETTINGS_PANES.GENERAL, () => ui.inputs([activityCol, activityScaling]), true);
|
|
73
|
+
inputs[SETTINGS_PANES.GENERAL] = [activityCol, activityScaling];
|
|
79
74
|
|
|
80
75
|
// Viewers pane options
|
|
81
76
|
/* FIXME: combinations of adding and deleting viewers are not working properly
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
|
|
2
|
+
onmessage = async (event): Promise<void> => {
|
|
3
|
+
const {startIdx, endIdx, activityArray, monomerInfoArray, settings, currentTargetIdx, targetOptions} = event.data;
|
|
4
|
+
// const monomers1: string[] = [];
|
|
5
|
+
// const monomers2: string[] = [];
|
|
6
|
+
const pos: string[] = [];
|
|
7
|
+
const seq1Idxs: number[] = [];
|
|
8
|
+
const seq2Idxs: number[] = [];
|
|
9
|
+
const chunkSize = endIdx - startIdx;
|
|
10
|
+
//const mi = startRow;
|
|
11
|
+
//const mj = startCol;
|
|
12
|
+
let cnt = 0;
|
|
13
|
+
const startRow = activityArray.length - 2 - Math.floor(
|
|
14
|
+
Math.sqrt(-8 * startIdx + 4 * activityArray.length * (activityArray.length - 1) - 7) / 2 - 0.5);
|
|
15
|
+
const startCol = startIdx - activityArray.length * startRow + Math.floor((startRow + 1) * (startRow + 2) / 2);
|
|
16
|
+
let seq1Idx = startRow;
|
|
17
|
+
let seq2Idx = startCol;
|
|
18
|
+
const tempData = new Array(monomerInfoArray.length);
|
|
19
|
+
while (cnt < chunkSize) {
|
|
20
|
+
if (!(currentTargetIdx !== -1 && (targetOptions.targetCol?.rawData[seq1Idx] !== currentTargetIdx ||
|
|
21
|
+
targetOptions.targetCol?.rawData[seq2Idx] !== currentTargetIdx))) {
|
|
22
|
+
let substCounter = 0;
|
|
23
|
+
const activityValSeq1 = activityArray[seq1Idx];
|
|
24
|
+
const activityValSeq2 = activityArray[seq2Idx];
|
|
25
|
+
const delta = activityValSeq1 - activityValSeq2;
|
|
26
|
+
if (Math.abs(delta) >= settings.minActivityDelta) {
|
|
27
|
+
let substCounterFlag = false;
|
|
28
|
+
let tempDataIdx = 0;
|
|
29
|
+
for (const monomerInfo of monomerInfoArray) {
|
|
30
|
+
const seq1categoryIdx = monomerInfo.rawData[seq1Idx];
|
|
31
|
+
const seq2categoryIdx = monomerInfo.rawData[seq2Idx];
|
|
32
|
+
if (seq1categoryIdx === seq2categoryIdx)
|
|
33
|
+
continue;
|
|
34
|
+
|
|
35
|
+
substCounter++;
|
|
36
|
+
substCounterFlag = substCounter > settings.maxMutations;
|
|
37
|
+
if (substCounterFlag)
|
|
38
|
+
break;
|
|
39
|
+
|
|
40
|
+
tempData[tempDataIdx++] = {
|
|
41
|
+
pos: monomerInfo.name,
|
|
42
|
+
// seq1monomer: monomerInfo.cat![seq1categoryIdx],
|
|
43
|
+
// seq2monomer: monomerInfo.cat![seq2categoryIdx],
|
|
44
|
+
seq1Idx: seq1Idx,
|
|
45
|
+
seq2Idx: seq2Idx,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (!(substCounterFlag || substCounter === 0)) {
|
|
49
|
+
for (let i = 0; i < tempDataIdx; i++) {
|
|
50
|
+
const tempDataElement = tempData[i];
|
|
51
|
+
const position = tempDataElement.pos;
|
|
52
|
+
// const seq1monomer = tempDataElement.seq1monomer;
|
|
53
|
+
// const seq2monomer = tempDataElement.seq2monomer;
|
|
54
|
+
// monomers1.push(seq1monomer);
|
|
55
|
+
// monomers2.push(seq2monomer);
|
|
56
|
+
pos.push(position);
|
|
57
|
+
seq1Idxs.push(seq1Idx);
|
|
58
|
+
seq2Idxs.push(seq2Idx);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
cnt++;
|
|
64
|
+
seq2Idx++;
|
|
65
|
+
if (seq2Idx === activityArray.length) {
|
|
66
|
+
seq1Idx++;
|
|
67
|
+
seq2Idx = seq1Idx + 1;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
postMessage({
|
|
71
|
+
// monomers1: monomers1,
|
|
72
|
+
// monomers2: monomers2,
|
|
73
|
+
pos: pos,
|
|
74
|
+
seq1Idxs: new Uint32Array(seq1Idxs),
|
|
75
|
+
seq2Idxs: new Uint32Array(seq2Idxs),
|
|
76
|
+
});
|
|
77
|
+
};
|