@datagrok/peptides 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package-test.js +113 -116
- package/dist/package.js +274 -121
- package/package.json +1 -5
- package/src/__jest__/remote.test.ts +3 -2
- package/src/model.ts +44 -58
- package/src/package.ts +8 -0
- package/src/utils/cell-renderer.ts +3 -3
- package/src/utils/constants.ts +1 -1
- package/src/utils/invariant-map.ts +163 -0
- package/src/utils/misc.ts +5 -0
- package/src/utils/{filtering-statistics.ts → statistics.ts} +0 -0
- package/src/viewers/sar-viewer.ts +3 -17
- package/src/widgets/analyze-peptides.ts +1 -1
- package/src/widgets/distribution.ts +1 -1
- package/src/widgets/{subst-table.ts → mutation-cliffs.ts} +5 -5
- package/helm/JSDraw/Pistoia.HELM.js +0 -27
- package/logojs-react/index.d.ts +0 -1
- package/test-Peptides-eb4783c07294-f4162403.html +0 -277
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datagrok/peptides",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Volodymyr Dyma",
|
|
6
6
|
"email": "vdyma@datagrok.ai"
|
|
@@ -43,10 +43,6 @@
|
|
|
43
43
|
"@types/node-fetch": "^2.6.2",
|
|
44
44
|
"node-fetch": "^2.6.7"
|
|
45
45
|
},
|
|
46
|
-
"sources": [
|
|
47
|
-
"common/ngl_viewer/ngl.js",
|
|
48
|
-
"helm/JSDraw/Pistoia.HELM.js"
|
|
49
|
-
],
|
|
50
46
|
"scripts": {
|
|
51
47
|
"link-api": "npm link datagrok-api",
|
|
52
48
|
"link-utils": "npm link @datagrok-libraries/utils",
|
|
@@ -46,15 +46,16 @@ it('TEST', async () => {
|
|
|
46
46
|
const cMessage = df.columns.byName('result');
|
|
47
47
|
const cCat = df.columns.byName('category');
|
|
48
48
|
const cName = df.columns.byName('name');
|
|
49
|
+
const cTime = df.columns.byName('ms');
|
|
49
50
|
let failed = false;
|
|
50
51
|
let passReport = '';
|
|
51
52
|
let failReport = '';
|
|
52
53
|
for (let i = 0; i < df.rowCount; i++) {
|
|
53
54
|
if (cStatus.get(i))
|
|
54
|
-
passReport += `Test result : Success : ${targetPackage}.${cCat.get(i)}.${cName.get(i)} : ${cMessage.get(i)}\n`;
|
|
55
|
+
passReport += `Test result : Success : ${cTime.get(i)} : ${targetPackage}.${cCat.get(i)}.${cName.get(i)} : ${cMessage.get(i)}\n`;
|
|
55
56
|
else {
|
|
56
57
|
failed = true;
|
|
57
|
-
failReport += `Test result : Failed : ${targetPackage}.${cCat.get(i)}.${cName.get(i)} : ${cMessage.get(i)}\n`;
|
|
58
|
+
failReport += `Test result : Failed : ${cTime.get(i)} : ${targetPackage}.${cCat.get(i)}.${cName.get(i)} : ${cMessage.get(i)}\n`;
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
resolve({failReport, passReport, failed});
|
package/src/model.ts
CHANGED
|
@@ -4,29 +4,25 @@ import * as DG from 'datagrok-api/dg';
|
|
|
4
4
|
|
|
5
5
|
import {splitAlignedSequences} from '@datagrok-libraries/bio/src/utils/splitter';
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import * as rxjs from 'rxjs';
|
|
8
|
+
|
|
8
9
|
import * as C from './utils/constants';
|
|
9
10
|
import * as type from './utils/types';
|
|
10
|
-
import {calculateBarsData, getTypedArrayConstructor, scaleActivity} from './utils/misc';
|
|
11
|
+
import {calculateBarsData, getTypedArrayConstructor, isGridCellInvalid, scaleActivity} from './utils/misc';
|
|
11
12
|
import {SARViewer, SARViewerBase, SARViewerVertical} from './viewers/sar-viewer';
|
|
12
13
|
import {PeptideSpaceViewer} from './viewers/peptide-space-viewer';
|
|
13
14
|
import {renderBarchart, renderSARCell, setAARRenderer} from './utils/cell-renderer';
|
|
14
|
-
import {
|
|
15
|
+
import {mutationCliffsWidget} from './widgets/mutation-cliffs';
|
|
15
16
|
import {getDistributionAndStats, getDistributionWidget} from './widgets/distribution';
|
|
16
|
-
import {getStats, Stats} from './utils/
|
|
17
|
-
import * as rxjs from 'rxjs';
|
|
18
|
-
|
|
17
|
+
import {getStats, Stats} from './utils/statistics';
|
|
19
18
|
|
|
20
19
|
export class PeptidesModel {
|
|
21
20
|
static modelName = 'peptidesModel';
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
_sarVGridSubject = new Subject<DG.Grid>();
|
|
26
|
-
_substitutionTableSubject = new Subject<type.SubstitutionsInfo>();
|
|
22
|
+
_sarGridSubject = new rxjs.Subject<DG.Grid>();
|
|
23
|
+
_sarVGridSubject = new rxjs.Subject<DG.Grid>();
|
|
27
24
|
|
|
28
25
|
_isUpdating: boolean = false;
|
|
29
|
-
_isSubstInitialized = false;
|
|
30
26
|
isBitsetChangedInitialized = false;
|
|
31
27
|
isCellChanging = false;
|
|
32
28
|
|
|
@@ -65,13 +61,9 @@ export class PeptidesModel {
|
|
|
65
61
|
return dataFrame.temp[PeptidesModel.modelName] as PeptidesModel;
|
|
66
62
|
}
|
|
67
63
|
|
|
68
|
-
get
|
|
69
|
-
|
|
70
|
-
get onSARGridChanged(): Observable<DG.Grid> {return this._sarGridSubject.asObservable();}
|
|
71
|
-
|
|
72
|
-
get onSARVGridChanged(): Observable<DG.Grid> {return this._sarVGridSubject.asObservable();}
|
|
64
|
+
get onSARGridChanged(): rxjs.Observable<DG.Grid> {return this._sarGridSubject.asObservable();}
|
|
73
65
|
|
|
74
|
-
get
|
|
66
|
+
get onSARVGridChanged(): rxjs.Observable<DG.Grid> {return this._sarVGridSubject.asObservable();}
|
|
75
67
|
|
|
76
68
|
get currentSelection(): type.SelectionObject {
|
|
77
69
|
this._currentSelection ??= JSON.parse(this.df.tags[C.TAGS.SELECTION] || '{}');
|
|
@@ -120,7 +112,7 @@ export class PeptidesModel {
|
|
|
120
112
|
const acc = ui.accordion();
|
|
121
113
|
acc.root.style.width = '100%';
|
|
122
114
|
acc.addTitle(ui.h1(`${this.df.selection.trueCount} selected rows`));
|
|
123
|
-
acc.addPane('Substitutions', () =>
|
|
115
|
+
acc.addPane('Substitutions', () => mutationCliffsWidget(this.df, this).root, true);
|
|
124
116
|
acc.addPane('Distribtution', () => getDistributionWidget(this.df, this).root, true);
|
|
125
117
|
|
|
126
118
|
return acc;
|
|
@@ -158,23 +150,17 @@ export class PeptidesModel {
|
|
|
158
150
|
if ((this._sourceGrid && !this._isUpdating && proprtyChanged) || !this.isInitialized) {
|
|
159
151
|
this.isInitialized = true;
|
|
160
152
|
this._isUpdating = true;
|
|
161
|
-
const [viewerGrid, viewerVGrid
|
|
153
|
+
const [viewerGrid, viewerVGrid] = this.initializeViewersComponents();
|
|
162
154
|
//FIXME: modify during the initializeViewersComponents stages
|
|
163
|
-
this._statsDataFrameSubject.next(statsDf);
|
|
164
155
|
this._sarGridSubject.next(viewerGrid);
|
|
165
156
|
this._sarVGridSubject.next(viewerVGrid);
|
|
166
|
-
if (viewer.showSubstitution) {
|
|
167
|
-
this._substitutionTableSubject.next(this.substitutionsInfo);
|
|
168
|
-
this._isSubstInitialized = true;
|
|
169
|
-
}
|
|
170
157
|
|
|
171
158
|
this.invalidateSelection();
|
|
172
|
-
|
|
173
159
|
this._isUpdating = false;
|
|
174
160
|
}
|
|
175
161
|
}
|
|
176
162
|
|
|
177
|
-
initializeViewersComponents(): [DG.Grid, DG.Grid
|
|
163
|
+
initializeViewersComponents(): [DG.Grid, DG.Grid] {
|
|
178
164
|
if (this._sourceGrid === null)
|
|
179
165
|
throw new Error(`Source grid is not initialized`);
|
|
180
166
|
|
|
@@ -207,14 +193,14 @@ export class PeptidesModel {
|
|
|
207
193
|
//unpivot a table and handle duplicates
|
|
208
194
|
let matrixDf = splitSeqDf.groupBy(positionColumns).aggregate();
|
|
209
195
|
|
|
210
|
-
matrixDf = matrixDf.unpivot([], positionColumns, C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.
|
|
196
|
+
matrixDf = matrixDf.unpivot([], positionColumns, C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER);
|
|
211
197
|
|
|
212
198
|
//statistics for specific AAR at a specific position
|
|
213
199
|
this.statsDf = this.calculateStatistics(matrixDf);
|
|
214
200
|
|
|
215
201
|
// SAR matrix table
|
|
216
202
|
//pivot a table to make it matrix-like
|
|
217
|
-
matrixDf = this.statsDf.groupBy([C.COLUMNS_NAMES.
|
|
203
|
+
matrixDf = this.statsDf.groupBy([C.COLUMNS_NAMES.MONOMER])
|
|
218
204
|
.pivot(C.COLUMNS_NAMES.POSITION)
|
|
219
205
|
.add('first', C.COLUMNS_NAMES.MEAN_DIFFERENCE, '')
|
|
220
206
|
.aggregate();
|
|
@@ -226,8 +212,7 @@ export class PeptidesModel {
|
|
|
226
212
|
// SAR vertical table (naive, choose best Mean difference from pVals <= 0.01)
|
|
227
213
|
const sequenceDf = this.createVerticalTable();
|
|
228
214
|
|
|
229
|
-
|
|
230
|
-
this.calcSubstitutions();
|
|
215
|
+
this.calcSubstitutions();
|
|
231
216
|
|
|
232
217
|
const [sarGrid, sarVGrid] = this.createGrids(matrixDf, positionColumns, sequenceDf, alphabet);
|
|
233
218
|
|
|
@@ -250,7 +235,7 @@ export class PeptidesModel {
|
|
|
250
235
|
this.postProcessGrids(sarGrid, sarVGrid);
|
|
251
236
|
|
|
252
237
|
//TODO: return class instead
|
|
253
|
-
return [sarGrid, sarVGrid
|
|
238
|
+
return [sarGrid, sarVGrid];
|
|
254
239
|
}
|
|
255
240
|
|
|
256
241
|
calcSubstitutions(): void {
|
|
@@ -387,7 +372,7 @@ export class PeptidesModel {
|
|
|
387
372
|
}
|
|
388
373
|
|
|
389
374
|
calculateStatistics(matrixDf: DG.DataFrame): DG.DataFrame {
|
|
390
|
-
matrixDf = matrixDf.groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.
|
|
375
|
+
matrixDf = matrixDf.groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER]).aggregate();
|
|
391
376
|
|
|
392
377
|
//calculate p-values based on t-test
|
|
393
378
|
const matrixCols = matrixDf.columns;
|
|
@@ -395,7 +380,7 @@ export class PeptidesModel {
|
|
|
395
380
|
const pValCol = matrixCols.addNewFloat(C.COLUMNS_NAMES.P_VALUE);
|
|
396
381
|
const countCol = matrixCols.addNewInt(C.COLUMNS_NAMES.COUNT);
|
|
397
382
|
const ratioCol = matrixCols.addNewFloat(C.COLUMNS_NAMES.RATIO);
|
|
398
|
-
const aarCol = matrixDf.getCol(C.COLUMNS_NAMES.
|
|
383
|
+
const aarCol = matrixDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
399
384
|
const posCol = matrixDf.getCol(C.COLUMNS_NAMES.POSITION);
|
|
400
385
|
const activityCol: number[] = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).toList();
|
|
401
386
|
const sourceDfLen = activityCol.length;
|
|
@@ -428,22 +413,22 @@ export class PeptidesModel {
|
|
|
428
413
|
absMDCol.init((i) => Math.abs(mdCol.get(i)));
|
|
429
414
|
}
|
|
430
415
|
|
|
431
|
-
const aarWeightsDf = this.statsDf.groupBy([C.COLUMNS_NAMES.
|
|
416
|
+
const aarWeightsDf = this.statsDf.groupBy([C.COLUMNS_NAMES.MONOMER]).sum(sortArgument, 'weight')
|
|
432
417
|
.aggregate();
|
|
433
|
-
const aarList = aarWeightsDf.getCol(C.COLUMNS_NAMES.
|
|
418
|
+
const aarList = aarWeightsDf.getCol(C.COLUMNS_NAMES.MONOMER).toList();
|
|
434
419
|
const getWeight = (aar: string): number => aarWeightsDf
|
|
435
420
|
.groupBy(['weight'])
|
|
436
|
-
.where(`${C.COLUMNS_NAMES.
|
|
421
|
+
.where(`${C.COLUMNS_NAMES.MONOMER} = ${aar}`)
|
|
437
422
|
.aggregate()
|
|
438
423
|
.get('weight', 0) as number;
|
|
439
424
|
aarList.sort((first, second) => getWeight(second) - getWeight(first));
|
|
440
425
|
|
|
441
|
-
matrixDf.getCol(C.COLUMNS_NAMES.
|
|
426
|
+
matrixDf.getCol(C.COLUMNS_NAMES.MONOMER).setCategoryOrder(aarList);
|
|
442
427
|
}
|
|
443
428
|
|
|
444
429
|
createVerticalTable(): DG.DataFrame {
|
|
445
430
|
// TODO: aquire ALL of the positions
|
|
446
|
-
const columns = [C.COLUMNS_NAMES.MEAN_DIFFERENCE, C.COLUMNS_NAMES.
|
|
431
|
+
const columns = [C.COLUMNS_NAMES.MEAN_DIFFERENCE, C.COLUMNS_NAMES.MONOMER, C.COLUMNS_NAMES.POSITION,
|
|
447
432
|
'Count', 'Ratio', C.COLUMNS_NAMES.P_VALUE];
|
|
448
433
|
let sequenceDf = this.statsDf.groupBy(columns)
|
|
449
434
|
.where('pValue <= 0.1')
|
|
@@ -469,8 +454,8 @@ export class PeptidesModel {
|
|
|
469
454
|
createGrids(
|
|
470
455
|
matrixDf: DG.DataFrame, positionColumns: string[], sequenceDf: DG.DataFrame, alphabet: string): DG.Grid[] {
|
|
471
456
|
const sarGrid = matrixDf.plot.grid();
|
|
472
|
-
sarGrid.sort([C.COLUMNS_NAMES.
|
|
473
|
-
sarGrid.columns.setOrder([C.COLUMNS_NAMES.
|
|
457
|
+
sarGrid.sort([C.COLUMNS_NAMES.MONOMER]);
|
|
458
|
+
sarGrid.columns.setOrder([C.COLUMNS_NAMES.MONOMER].concat(positionColumns as C.COLUMNS_NAMES[]));
|
|
474
459
|
|
|
475
460
|
const sarVGrid = sequenceDf.plot.grid();
|
|
476
461
|
sarVGrid.sort([C.COLUMNS_NAMES.POSITION]);
|
|
@@ -478,11 +463,11 @@ export class PeptidesModel {
|
|
|
478
463
|
pValGridCol.format = '#.000';
|
|
479
464
|
pValGridCol.name = 'P-value';
|
|
480
465
|
|
|
481
|
-
let tempCol = matrixDf.getCol(C.COLUMNS_NAMES.
|
|
466
|
+
let tempCol = matrixDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
482
467
|
if (tempCol)
|
|
483
468
|
setAARRenderer(tempCol, alphabet, sarGrid);
|
|
484
469
|
|
|
485
|
-
tempCol = sequenceDf.getCol(C.COLUMNS_NAMES.
|
|
470
|
+
tempCol = sequenceDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
486
471
|
if (tempCol)
|
|
487
472
|
setAARRenderer(tempCol, alphabet, sarGrid);
|
|
488
473
|
|
|
@@ -563,11 +548,11 @@ export class PeptidesModel {
|
|
|
563
548
|
const gridTable = cell.grid.table;
|
|
564
549
|
const currentPosition: string = tableColName !== C.COLUMNS_NAMES.MEAN_DIFFERENCE ?
|
|
565
550
|
tableColName : gridTable.get(C.COLUMNS_NAMES.POSITION, tableRowIndex);
|
|
566
|
-
const currentAAR: string = gridTable.get(C.COLUMNS_NAMES.
|
|
551
|
+
const currentAAR: string = gridTable.get(C.COLUMNS_NAMES.MONOMER, tableRowIndex);
|
|
567
552
|
|
|
568
553
|
const viewer = this.getViewer();
|
|
569
554
|
renderSARCell(canvasContext, currentAAR, currentPosition, this.statsDf, viewer.bidirectionalAnalysis, mdCol,
|
|
570
|
-
bound, cellValue, this.currentSelection,
|
|
555
|
+
bound, cellValue, this.currentSelection, this.substitutionsInfo);
|
|
571
556
|
}
|
|
572
557
|
args.preventDefault();
|
|
573
558
|
}
|
|
@@ -605,7 +590,7 @@ export class PeptidesModel {
|
|
|
605
590
|
|
|
606
591
|
if (!cell.isRowHeader && !cell.isColHeader && tableCol && tableRowIndex != null) {
|
|
607
592
|
const table = cell.grid.table;
|
|
608
|
-
const currentAAR = table.get(C.COLUMNS_NAMES.
|
|
593
|
+
const currentAAR = table.get(C.COLUMNS_NAMES.MONOMER, tableRowIndex);
|
|
609
594
|
|
|
610
595
|
if (tableCol.semType == C.SEM_TYPES.MONOMER)
|
|
611
596
|
this.showMonomerTooltip(currentAAR, x, y);
|
|
@@ -676,26 +661,24 @@ export class PeptidesModel {
|
|
|
676
661
|
const chooseAction = (aar: string, position: string, isShiftPressed: boolean): void =>
|
|
677
662
|
isShiftPressed ? this.modifyCurrentSelection(aar, position) : this.initCurrentSelection(aar, position);
|
|
678
663
|
|
|
679
|
-
const gridCellValidation = (gc: DG.GridCell | null): boolean => !gc || !gc.cell.value || !gc.tableColumn ||
|
|
680
|
-
gc.tableRowIndex == null || gc.tableRowIndex == -1;
|
|
681
664
|
this._sarGrid.root.addEventListener('click', (ev) => {
|
|
682
665
|
const gridCell = this._sarGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
683
|
-
if (
|
|
666
|
+
if (isGridCellInvalid(gridCell) || gridCell!.tableColumn!.name == C.COLUMNS_NAMES.MONOMER)
|
|
684
667
|
return;
|
|
685
668
|
|
|
686
669
|
const position = gridCell!.tableColumn!.name;
|
|
687
|
-
const aar = sarDf.get(C.COLUMNS_NAMES.
|
|
670
|
+
const aar = sarDf.get(C.COLUMNS_NAMES.MONOMER, gridCell!.tableRowIndex!);
|
|
688
671
|
chooseAction(aar, position, ev.shiftKey);
|
|
689
672
|
});
|
|
690
673
|
|
|
691
674
|
this._sarVGrid.root.addEventListener('click', (ev) => {
|
|
692
675
|
const gridCell = this._sarVGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
693
|
-
if (
|
|
676
|
+
if (isGridCellInvalid(gridCell) || gridCell!.tableColumn!.name != C.COLUMNS_NAMES.MEAN_DIFFERENCE)
|
|
694
677
|
return;
|
|
695
678
|
|
|
696
679
|
const tableRowIdx = gridCell!.tableRowIndex!;
|
|
697
680
|
const position = sarVDf.get(C.COLUMNS_NAMES.POSITION, tableRowIdx);
|
|
698
|
-
const aar = sarVDf.get(C.COLUMNS_NAMES.
|
|
681
|
+
const aar = sarVDf.get(C.COLUMNS_NAMES.MONOMER, tableRowIdx);
|
|
699
682
|
chooseAction(aar, position, ev.shiftKey);
|
|
700
683
|
});
|
|
701
684
|
|
|
@@ -805,7 +788,7 @@ export class PeptidesModel {
|
|
|
805
788
|
for (let i = 0; i < colNum; ++i) {
|
|
806
789
|
const col = girdCols.byIndex(i)!;
|
|
807
790
|
const colName = col.name;
|
|
808
|
-
if (grid == sarVGrid && colName !== 'Diff' && colName !== C.COLUMNS_NAMES.
|
|
791
|
+
if (grid == sarVGrid && colName !== 'Diff' && colName !== C.COLUMNS_NAMES.MONOMER)
|
|
809
792
|
col.width = 50;
|
|
810
793
|
else
|
|
811
794
|
col.width = gridProps.rowHeight + 10;
|
|
@@ -899,15 +882,18 @@ export class PeptidesModel {
|
|
|
899
882
|
|
|
900
883
|
const sarViewersGroup: viewerTypes[] = [this.sarViewer, this.sarViewerVertical];
|
|
901
884
|
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
885
|
+
// TODO: completely remove this viewer?
|
|
886
|
+
// if (this.df.rowCount <= 10000) {
|
|
887
|
+
// const peptideSpaceViewerOptions = {method: 'UMAP', measure: 'Levenshtein', cyclesCount: 100};
|
|
888
|
+
// const peptideSpaceViewer =
|
|
889
|
+
// await this.df.plot.fromType('peptide-space-viewer', peptideSpaceViewerOptions) as PeptideSpaceViewer;
|
|
890
|
+
// dockManager.dock(peptideSpaceViewer, DG.DOCK_TYPE.RIGHT, null, 'Peptide Space Viewer');
|
|
891
|
+
// }
|
|
908
892
|
|
|
909
893
|
this.updateDefault();
|
|
910
894
|
|
|
895
|
+
this.currentView.filters({filters: [{type: 'Peptides:invariantMapFilter'}]});
|
|
896
|
+
|
|
911
897
|
dockViewers(sarViewersGroup, DG.DOCK_TYPE.RIGHT, dockManager, DG.DOCK_TYPE.DOWN);
|
|
912
898
|
|
|
913
899
|
this._sourceGrid.props.allowEdit = false;
|
package/src/package.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {manualAlignmentWidget} from './widgets/manual-alignment';
|
|
|
11
11
|
import {SARViewer, SARViewerVertical} from './viewers/sar-viewer';
|
|
12
12
|
|
|
13
13
|
import {PeptideSpaceViewer} from './viewers/peptide-space-viewer';
|
|
14
|
+
import {InvariantMap} from './utils/invariant-map';
|
|
14
15
|
|
|
15
16
|
export const _package = new DG.Package();
|
|
16
17
|
let currentTable: DG.DataFrame;
|
|
@@ -153,3 +154,10 @@ function getOrDefine(dataframe?: DG.DataFrame, column?: DG.Column | null): [DG.D
|
|
|
153
154
|
|
|
154
155
|
return [dataframe, column];
|
|
155
156
|
}
|
|
157
|
+
|
|
158
|
+
//name: Invariant Map Filter
|
|
159
|
+
//tags: filter
|
|
160
|
+
//output: filter result
|
|
161
|
+
export function invariantMapFilter() {
|
|
162
|
+
return new InvariantMap();
|
|
163
|
+
}
|
|
@@ -63,8 +63,8 @@ export function measureAAR(s: string): number {
|
|
|
63
63
|
|
|
64
64
|
export function renderSARCell(canvasContext: CanvasRenderingContext2D, currentAAR: string, currentPosition: string,
|
|
65
65
|
statsDf: DG.DataFrame, twoColorMode: boolean, mdCol: DG.Column<number>, bound: DG.Rect, cellValue: number,
|
|
66
|
-
currentSelection: types.SelectionObject, substitutionsInfo: types.SubstitutionsInfo
|
|
67
|
-
const queryAAR = `${C.COLUMNS_NAMES.
|
|
66
|
+
currentSelection: types.SelectionObject, substitutionsInfo: types.SubstitutionsInfo): void {
|
|
67
|
+
const queryAAR = `${C.COLUMNS_NAMES.MONOMER} = ${currentAAR}`;
|
|
68
68
|
const query = `${queryAAR} and ${C.COLUMNS_NAMES.POSITION} = ${currentPosition}`;
|
|
69
69
|
const pVal: number = statsDf
|
|
70
70
|
.groupBy([C.COLUMNS_NAMES.P_VALUE])
|
|
@@ -101,7 +101,7 @@ export function renderSARCell(canvasContext: CanvasRenderingContext2D, currentAA
|
|
|
101
101
|
canvasContext.closePath();
|
|
102
102
|
|
|
103
103
|
canvasContext.fill();
|
|
104
|
-
if (substitutionsInfo) {
|
|
104
|
+
if (substitutionsInfo.size > 0) {
|
|
105
105
|
canvasContext.textBaseline = 'middle';
|
|
106
106
|
canvasContext.textAlign = 'center';
|
|
107
107
|
canvasContext.fillStyle = DG.Color.toHtml(DG.Color.getContrastColor(DG.Color.fromHtml(coef)));
|
package/src/utils/constants.ts
CHANGED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import * as ui from 'datagrok-api/ui';
|
|
2
|
+
import * as DG from 'datagrok-api/dg';
|
|
3
|
+
|
|
4
|
+
import * as C from './constants';
|
|
5
|
+
import {PeptidesModel} from '../model';
|
|
6
|
+
import {isGridCellInvalid} from './misc';
|
|
7
|
+
|
|
8
|
+
const CELL_SIZE = 20; // 20px cell height and width
|
|
9
|
+
|
|
10
|
+
export class InvariantMap extends DG.Filter {
|
|
11
|
+
model: PeptidesModel | null = null;
|
|
12
|
+
chosenCells: {[position: string]: string[]} = {};
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
super();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get caption(): string {return 'Invariant Map';}
|
|
19
|
+
|
|
20
|
+
get filterSummary(): string {
|
|
21
|
+
let summary = '';
|
|
22
|
+
for (const [pos, aarList] of Object.entries(this.chosenCells))
|
|
23
|
+
if (aarList.length > 0)
|
|
24
|
+
summary += `${pos}: ${aarList}\n`;
|
|
25
|
+
|
|
26
|
+
return summary;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get isFiltering(): boolean {return true && super.isFiltering;}
|
|
30
|
+
|
|
31
|
+
get isReadyToApplyFilter(): boolean {return this.model != null;}
|
|
32
|
+
|
|
33
|
+
async attach(df: DG.DataFrame): Promise<void> {
|
|
34
|
+
super.attach(df);
|
|
35
|
+
this.model = await PeptidesModel.getInstance(df);
|
|
36
|
+
this.render(true);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
saveState(): any {
|
|
40
|
+
const state = super.saveState();
|
|
41
|
+
state.chosenCells = JSON.stringify(this.chosenCells);
|
|
42
|
+
return state;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
applyState(state: any): void {
|
|
46
|
+
super.applyState(state);
|
|
47
|
+
if (state.chosenCells) {
|
|
48
|
+
this.chosenCells = JSON.parse(state.chosenCells);
|
|
49
|
+
this.render();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
applyFilter(): void {
|
|
54
|
+
this.dataFrame?.filter.init((bitsetIndex) => {
|
|
55
|
+
for (const [position, aarList] of Object.entries(this.chosenCells)) {
|
|
56
|
+
if (aarList.length != 0 && !aarList.includes(this.dataFrame!.get(position, bitsetIndex)))
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
render(initChosenCells: boolean = false): void {
|
|
64
|
+
if (this.model == null)
|
|
65
|
+
return;
|
|
66
|
+
|
|
67
|
+
const invariantDf = this.model.statsDf.groupBy([C.COLUMNS_NAMES.MONOMER])
|
|
68
|
+
.pivot(C.COLUMNS_NAMES.POSITION)
|
|
69
|
+
.add('first', C.COLUMNS_NAMES.COUNT, '')
|
|
70
|
+
.aggregate();
|
|
71
|
+
invariantDf.getCol(C.COLUMNS_NAMES.MONOMER).semType = C.SEM_TYPES.MONOMER;
|
|
72
|
+
const orderedColNames = invariantDf.columns.names().sort((a, b) => {
|
|
73
|
+
const aInt = parseInt(a);
|
|
74
|
+
const bInt = parseInt(b);
|
|
75
|
+
if (isNaN(aInt))
|
|
76
|
+
return -1;
|
|
77
|
+
else if (isNaN(bInt))
|
|
78
|
+
return 1;
|
|
79
|
+
return aInt - bInt;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Create grid and set properties
|
|
83
|
+
const invariantGrid = invariantDf.plot.grid();
|
|
84
|
+
const gridCols = invariantGrid.columns;
|
|
85
|
+
gridCols.rowHeader!.visible = false;
|
|
86
|
+
gridCols.setOrder(orderedColNames);
|
|
87
|
+
|
|
88
|
+
for (let gridColIndex = 0; gridColIndex < gridCols.length; ++gridColIndex) {
|
|
89
|
+
const gridCol = gridCols.byIndex(gridColIndex)!
|
|
90
|
+
if (gridCol.name != C.COLUMNS_NAMES.MONOMER)
|
|
91
|
+
gridCol.width = CELL_SIZE;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (initChosenCells) {
|
|
95
|
+
this.chosenCells = {};
|
|
96
|
+
for (const col of invariantDf.columns)
|
|
97
|
+
if (col.name != C.COLUMNS_NAMES.MONOMER)
|
|
98
|
+
this.chosenCells[col.name] = [];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
invariantGrid.root.addEventListener('click', (ev) => {
|
|
102
|
+
invariantDf.currentRowIdx = -1;
|
|
103
|
+
const gridCell = invariantGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
104
|
+
if (isGridCellInvalid(gridCell) || gridCell!.tableColumn!.name == C.COLUMNS_NAMES.MONOMER)
|
|
105
|
+
return;
|
|
106
|
+
|
|
107
|
+
const position = gridCell!.tableColumn!.name;
|
|
108
|
+
const aar = invariantDf.get(C.COLUMNS_NAMES.MONOMER, gridCell!.tableRowIndex!);
|
|
109
|
+
const aarList = this.chosenCells[position];
|
|
110
|
+
const aarIndex = aarList.indexOf(aar);
|
|
111
|
+
|
|
112
|
+
if (aarIndex != -1)
|
|
113
|
+
aarList.splice(aarIndex, 1);
|
|
114
|
+
else
|
|
115
|
+
aarList.push(aar);
|
|
116
|
+
|
|
117
|
+
invariantGrid.invalidate();
|
|
118
|
+
this.applyFilter();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
invariantGrid.onCellRender.subscribe((args) => {
|
|
122
|
+
//FIXME: for some reason it doesn't work when I set right away
|
|
123
|
+
const gridProps = invariantGrid.props;
|
|
124
|
+
gridProps.allowBlockSelection = false;
|
|
125
|
+
gridProps.allowColSelection = false;
|
|
126
|
+
gridProps.allowRowSelection = false;
|
|
127
|
+
gridProps.allowEdit = false;
|
|
128
|
+
gridProps.rowHeight = CELL_SIZE;
|
|
129
|
+
|
|
130
|
+
const gc = args.cell;
|
|
131
|
+
const tableColName = gc.tableColumn?.name;
|
|
132
|
+
const tableRowIndex = gc.tableRowIndex;
|
|
133
|
+
|
|
134
|
+
if (isGridCellInvalid(gc) || tableColName == C.COLUMNS_NAMES.MONOMER ||
|
|
135
|
+
tableRowIndex == null || tableRowIndex == -1)
|
|
136
|
+
return;
|
|
137
|
+
|
|
138
|
+
const currentPosition: string = tableColName !== C.COLUMNS_NAMES.MEAN_DIFFERENCE ?
|
|
139
|
+
tableColName : invariantDf.get(C.COLUMNS_NAMES.POSITION, tableRowIndex);
|
|
140
|
+
const currentAAR: string = invariantDf.get(C.COLUMNS_NAMES.MONOMER, tableRowIndex);
|
|
141
|
+
const canvasContext = args.g;
|
|
142
|
+
const bound = args.bounds;
|
|
143
|
+
|
|
144
|
+
canvasContext.font = '10px Roboto';
|
|
145
|
+
canvasContext.textAlign = 'center';
|
|
146
|
+
canvasContext.textBaseline = 'middle';
|
|
147
|
+
canvasContext.fillStyle = '#000';
|
|
148
|
+
canvasContext.fillText(gc.cell.value, bound.x + (bound.width / 2), bound.y + (bound.height / 2), bound.width);
|
|
149
|
+
|
|
150
|
+
const aarSelection = this.chosenCells[currentPosition];
|
|
151
|
+
if (aarSelection.includes(currentAAR)) {
|
|
152
|
+
canvasContext.strokeStyle = '#000';
|
|
153
|
+
canvasContext.lineWidth = 1;
|
|
154
|
+
canvasContext.strokeRect(bound.x + 1, bound.y + 1, bound.width - 1, bound.height - 1);
|
|
155
|
+
}
|
|
156
|
+
args.preventDefault();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const gridHost = ui.box(invariantGrid.root);
|
|
160
|
+
gridHost.style.width = '100%';
|
|
161
|
+
this.root.appendChild(gridHost);
|
|
162
|
+
}
|
|
163
|
+
}
|
package/src/utils/misc.ts
CHANGED
|
@@ -91,3 +91,8 @@ export function calculateSingleBarData(col: DG.Column<string>, selection: DG.Bit
|
|
|
91
91
|
|
|
92
92
|
return colStats;
|
|
93
93
|
}
|
|
94
|
+
|
|
95
|
+
export function isGridCellInvalid(gc: DG.GridCell | null): boolean {
|
|
96
|
+
return !gc || !gc.cell.value || !gc.tableColumn || gc.tableRowIndex == null || gc.tableRowIndex == -1 ||
|
|
97
|
+
gc.cell.value == DG.INT_NULL || gc.cell.value == DG.FLOAT_NULL;
|
|
98
|
+
}
|
|
File without changes
|
|
@@ -15,7 +15,6 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
15
15
|
model!: PeptidesModel;
|
|
16
16
|
scaling: string;
|
|
17
17
|
bidirectionalAnalysis: boolean;
|
|
18
|
-
showSubstitution: boolean;
|
|
19
18
|
maxSubstitutions: number;
|
|
20
19
|
minActivityDelta: number;
|
|
21
20
|
_titleHost = ui.divText('SAR Viewer', {id: 'pep-viewer-title'});
|
|
@@ -27,18 +26,15 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
27
26
|
|
|
28
27
|
this.scaling = this.string('scaling', 'none', {choices: ['none', 'lg', '-lg']});
|
|
29
28
|
this.bidirectionalAnalysis = this.bool('bidirectionalAnalysis', false);
|
|
30
|
-
this.
|
|
31
|
-
this.
|
|
32
|
-
this.minActivityDelta = this.float('minActivityDelta', 1);
|
|
29
|
+
this.maxSubstitutions = this.int('maxSubstitutions', 1);
|
|
30
|
+
this.minActivityDelta = this.float('minActivityDelta', 0);
|
|
33
31
|
}
|
|
34
32
|
|
|
35
33
|
async onTableAttached(): Promise<void> {
|
|
36
34
|
super.onTableAttached();
|
|
37
35
|
this.sourceGrid = this.view?.grid ?? (grok.shell.v as DG.TableView).grid;
|
|
38
36
|
this.model = await PeptidesModel.getInstance(this.dataFrame);
|
|
39
|
-
// this.model.init(this.dataFrame);
|
|
40
37
|
this.helpUrl = '/help/domains/bio/peptides.md';
|
|
41
|
-
// await this.requestDataUpdate();
|
|
42
38
|
|
|
43
39
|
this.initProperties();
|
|
44
40
|
}
|
|
@@ -65,11 +61,6 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
65
61
|
this.viewerGrid?.invalidate();
|
|
66
62
|
}
|
|
67
63
|
|
|
68
|
-
// async requestDataUpdate(): Promise<void> {
|
|
69
|
-
// await this.model.updateData(this.scaling, this.sourceGrid, this.bidirectionalAnalysis,
|
|
70
|
-
// this.minActivityDelta, this.maxSubstitutions, this.showSubstitution);
|
|
71
|
-
// }
|
|
72
|
-
|
|
73
64
|
onPropertyChanged(property: DG.Property): void {
|
|
74
65
|
super.onPropertyChanged(property);
|
|
75
66
|
this.dataFrame.tags[property.name] = `${property.get(this)}`;
|
|
@@ -89,10 +80,6 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
89
80
|
}
|
|
90
81
|
}
|
|
91
82
|
|
|
92
|
-
if (!this.showSubstitution && ['maxSubstitutions', 'activityLimit'].includes(propName))
|
|
93
|
-
return;
|
|
94
|
-
|
|
95
|
-
// await this.requestDataUpdate();
|
|
96
83
|
this.model.updateDefault();
|
|
97
84
|
this.render(true);
|
|
98
85
|
}
|
|
@@ -102,7 +89,7 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
102
89
|
* Structure-activity relationship viewer.
|
|
103
90
|
*/
|
|
104
91
|
export class SARViewer extends SARViewerBase {
|
|
105
|
-
_titleHost = ui.divText('
|
|
92
|
+
_titleHost = ui.divText('Mutation Cliffs', {id: 'pep-viewer-title'});
|
|
106
93
|
_name = 'Structure-Activity Relationship';
|
|
107
94
|
|
|
108
95
|
constructor() {super();}
|
|
@@ -112,7 +99,6 @@ export class SARViewer extends SARViewerBase {
|
|
|
112
99
|
async onTableAttached(): Promise<void> {
|
|
113
100
|
await super.onTableAttached();
|
|
114
101
|
this.model.sarViewer ??= this;
|
|
115
|
-
// this.dataFrame.temp['sarViewer'] = this;
|
|
116
102
|
|
|
117
103
|
this.subs.push(this.model.onSARGridChanged.subscribe((data) => {
|
|
118
104
|
this.viewerGrid = data;
|
|
@@ -22,7 +22,7 @@ export async function analyzePeptidesWidget(currentDf: DG.DataFrame, col: DG.Col
|
|
|
22
22
|
let funcs = DG.Func.find({package: 'Bio', name: 'webLogoViewer'});
|
|
23
23
|
if (funcs.length == 0)
|
|
24
24
|
return new DG.Widget(ui.label('Bio package is missing or out of date. Please install the latest version.'));
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
funcs = DG.Func.find({package: 'Helm', name: 'getMonomerLib'});
|
|
27
27
|
if (funcs.length == 0)
|
|
28
28
|
return new DG.Widget(ui.label('Helm package is missing or out of date. Please install the latest version.'));
|
|
@@ -5,7 +5,7 @@ import {StringDictionary} from '@datagrok-libraries/utils/src/type-declarations'
|
|
|
5
5
|
import $ from 'cash-dom';
|
|
6
6
|
|
|
7
7
|
import * as C from '../utils/constants';
|
|
8
|
-
import {getStats, Stats} from '../utils/
|
|
8
|
+
import {getStats, Stats} from '../utils/statistics';
|
|
9
9
|
import {PeptidesModel} from '../model';
|
|
10
10
|
|
|
11
11
|
const allConst = 'All';
|
|
@@ -5,13 +5,13 @@ import * as type from '../utils/types';
|
|
|
5
5
|
import {PeptidesModel} from '../model';
|
|
6
6
|
import {getSeparator} from '../utils/misc';
|
|
7
7
|
|
|
8
|
-
export function
|
|
8
|
+
export function mutationCliffsWidget(table: DG.DataFrame, model: PeptidesModel): DG.Widget {
|
|
9
9
|
const substInfo = model.substitutionsInfo;
|
|
10
10
|
const currentCell = model.currentSelection;
|
|
11
11
|
const positions = Object.keys(currentCell);
|
|
12
12
|
|
|
13
13
|
if (!positions.length)
|
|
14
|
-
return new DG.Widget(ui.label('No
|
|
14
|
+
return new DG.Widget(ui.label('No mutations table generated'));
|
|
15
15
|
|
|
16
16
|
const substitutionsArray: string[] = [];
|
|
17
17
|
const deltaArray: number[] = [];
|
|
@@ -49,9 +49,9 @@ export function substitutionsWidget(table: DG.DataFrame, model: PeptidesModel):
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
if (!substitutionsArray.length)
|
|
52
|
-
return new DG.Widget(ui.label('No
|
|
52
|
+
return new DG.Widget(ui.label('No mutations table generated'));
|
|
53
53
|
|
|
54
|
-
const substCol = DG.Column.fromStrings('
|
|
54
|
+
const substCol = DG.Column.fromStrings('Mutation', substitutionsArray);
|
|
55
55
|
substCol.semType = C.SEM_TYPES.MACROMOLECULE_DIFFERENCE;
|
|
56
56
|
substCol.tags[C.TAGS.SEPARATOR] = getSeparator(alignedSeqCol);
|
|
57
57
|
substCol.tags[DG.TAGS.UNITS] = alignedSeqCol.tags[DG.TAGS.UNITS];
|
|
@@ -61,7 +61,7 @@ export function substitutionsWidget(table: DG.DataFrame, model: PeptidesModel):
|
|
|
61
61
|
const substTable =
|
|
62
62
|
DG.DataFrame.fromColumns([substCol, DG.Column.fromList('double', 'Delta', deltaArray), hiddenSubstToAarCol]);
|
|
63
63
|
|
|
64
|
-
const aminoToInput = ui.stringInput('
|
|
64
|
+
const aminoToInput = ui.stringInput('Mutated to:', '', () => {
|
|
65
65
|
const substitutedToAar = aminoToInput.stringValue;
|
|
66
66
|
if (substitutedToAar != '')
|
|
67
67
|
substTable.filter.init((idx) => hiddenSubstToAarCol.get(idx) === substitutedToAar);
|