@datagrok/peptides 1.2.0 → 1.3.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/src/model.ts CHANGED
@@ -9,31 +9,36 @@ import * as rxjs from 'rxjs';
9
9
  import * as C from './utils/constants';
10
10
  import * as type from './utils/types';
11
11
  import {calculateBarsData, getTypedArrayConstructor, isGridCellInvalid, scaleActivity} from './utils/misc';
12
- import {SARViewer, SARViewerBase, SARViewerVertical} from './viewers/sar-viewer';
13
- import {PeptideSpaceViewer} from './viewers/peptide-space-viewer';
14
- import {renderBarchart, renderSARCell, setAARRenderer} from './utils/cell-renderer';
12
+ import {MutationCliffsViewer, SARViewerBase, MostPotentResiduesViewer} from './viewers/sar-viewer';
13
+ import * as CR from './utils/cell-renderer';
15
14
  import {mutationCliffsWidget} from './widgets/mutation-cliffs';
16
15
  import {getDistributionAndStats, getDistributionWidget} from './widgets/distribution';
17
16
  import {getStats, Stats} from './utils/statistics';
17
+ import {LogoSummary} from './viewers/logo-summary';
18
18
 
19
19
  export class PeptidesModel {
20
20
  static modelName = 'peptidesModel';
21
21
 
22
- _sarGridSubject = new rxjs.Subject<DG.Grid>();
23
- _sarVGridSubject = new rxjs.Subject<DG.Grid>();
22
+ mutationCliffsGridSubject = new rxjs.Subject<DG.Grid>();
23
+ mostPotentResiduesGridSubject = new rxjs.Subject<DG.Grid>();
24
+ logoSummaryGridSubject = new rxjs.Subject<DG.Grid>();
24
25
 
25
26
  _isUpdating: boolean = false;
26
27
  isBitsetChangedInitialized = false;
27
28
  isCellChanging = false;
28
29
 
29
- _sarGrid!: DG.Grid;
30
- _sarVGrid!: DG.Grid;
31
- _sourceGrid!: DG.Grid;
30
+ mutationCliffsGrid!: DG.Grid;
31
+ mostPotentResiduesGrid!: DG.Grid;
32
+ logoSummaryGrid!: DG.Grid;
33
+ sourceGrid!: DG.Grid;
32
34
  df: DG.DataFrame;
33
35
  splitCol!: DG.Column<boolean>;
34
36
  edf: DG.DataFrame | null = null;
35
- statsDf!: DG.DataFrame;
36
- _currentSelection!: type.SelectionObject;
37
+ monomerPositionStatsDf!: DG.DataFrame;
38
+ clusterStatsDf!: DG.DataFrame;
39
+ _mutationCliffsSelection: type.PositionToAARList = {};
40
+ _invariantMapSelection: type.PositionToAARList = {};
41
+ _logoSummarySelection: number[] = [];
37
42
  substitutionsInfo: type.SubstitutionsInfo = new Map();
38
43
  isInitialized = false;
39
44
  currentView!: DG.TableView;
@@ -41,8 +46,8 @@ export class PeptidesModel {
41
46
  isPeptideSpaceChangingBitset = false;
42
47
  isChangingEdfBitset = false;
43
48
 
44
- sarViewer!: SARViewer;
45
- sarViewerVertical!: SARViewerVertical;
49
+ mutationCliffsViewer!: MutationCliffsViewer;
50
+ mostPotentResiduesViewer!: MostPotentResiduesViewer;
46
51
 
47
52
  _usedProperties: {[propName: string]: string | number | boolean} = {};
48
53
  monomerMap: {[key: string]: {molfile: string, fullName: string}} = {};
@@ -61,19 +66,49 @@ export class PeptidesModel {
61
66
  return dataFrame.temp[PeptidesModel.modelName] as PeptidesModel;
62
67
  }
63
68
 
64
- get onSARGridChanged(): rxjs.Observable<DG.Grid> {return this._sarGridSubject.asObservable();}
69
+ get onMutationCliffsGridChanged(): rxjs.Observable<DG.Grid> {
70
+ return this.mutationCliffsGridSubject.asObservable();
71
+ }
72
+
73
+ get onMostPotentResiduesGridChanged(): rxjs.Observable<DG.Grid> {
74
+ return this.mostPotentResiduesGridSubject.asObservable();
75
+ }
65
76
 
66
- get onSARVGridChanged(): rxjs.Observable<DG.Grid> {return this._sarVGridSubject.asObservable();}
77
+ get onLogoSummaryGridChanged(): rxjs.Observable<DG.Grid> {
78
+ return this.logoSummaryGridSubject.asObservable();
79
+ }
67
80
 
68
- get currentSelection(): type.SelectionObject {
69
- this._currentSelection ??= JSON.parse(this.df.tags[C.TAGS.SELECTION] || '{}');
70
- return this._currentSelection;
81
+ get mutationCliffsSelection(): type.PositionToAARList {
82
+ this._mutationCliffsSelection ??= JSON.parse(this.df.tags[C.TAGS.SELECTION] || '{}');
83
+ return this._mutationCliffsSelection;
71
84
  }
72
- set currentSelection(selection: type.SelectionObject) {
73
- this._currentSelection = selection;
85
+ set mutationCliffsSelection(selection: type.PositionToAARList) {
86
+ this._mutationCliffsSelection = selection;
74
87
  this.df.tags[C.TAGS.SELECTION] = JSON.stringify(selection);
75
- this.invalidateSelection();
76
- this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
88
+ this.fireBitsetChanged();
89
+ this.invalidateGrids();
90
+ }
91
+
92
+ get invariantMapSelection(): type.PositionToAARList {
93
+ this._invariantMapSelection ??= JSON.parse(this.df.tags[C.TAGS.FILTER] || '{}');
94
+ return this._invariantMapSelection;
95
+ }
96
+ set invariantMapSelection(selection: type.PositionToAARList) {
97
+ this._invariantMapSelection = selection;
98
+ this.df.tags[C.TAGS.FILTER] = JSON.stringify(selection);
99
+ this.df.filter.fireChanged();
100
+ this.invalidateGrids();
101
+ }
102
+
103
+ get logoSummarySelection(): number[] {
104
+ this._logoSummarySelection ??= JSON.parse(this.df.tags[C.TAGS.CLUSTER_SELECTION] || '[]');
105
+ return this._logoSummarySelection;
106
+ }
107
+ set logoSummarySelection(selection: number[]) {
108
+ this._logoSummarySelection = selection;
109
+ this.df.tags[C.TAGS.CLUSTER_SELECTION] = JSON.stringify(selection);
110
+ this.fireBitsetChanged();
111
+ this.invalidateGrids();
77
112
  }
78
113
 
79
114
  get usedProperties(): {[propName: string]: string | number | boolean} {
@@ -103,23 +138,25 @@ export class PeptidesModel {
103
138
  this.df.tags['distributionSplit'] = `${splitByAARFlag}${flag ? 1 : 0}`;
104
139
  }
105
140
 
106
- invalidateSelection(): void {
107
- this.fireBitsetChanged();
108
- this.invalidateGrids();
141
+ get isInvariantMap(): boolean {
142
+ return this.df.getTag('isInvariantMap') === '1';
143
+ }
144
+ set isInvariantMap(x: boolean) {
145
+ this.df.setTag('isInvariantMap', x ? '1' : '0');
109
146
  }
110
147
 
111
148
  createAccordion(): DG.Accordion {
112
149
  const acc = ui.accordion();
113
150
  acc.root.style.width = '100%';
114
151
  acc.addTitle(ui.h1(`${this.df.selection.trueCount} selected rows`));
115
- acc.addPane('Substitutions', () => mutationCliffsWidget(this.df, this).root, true);
116
- acc.addPane('Distribtution', () => getDistributionWidget(this.df, this).root, true);
152
+ acc.addPane('Mutation Cliff pairs', () => mutationCliffsWidget(this.df, this).root, true);
153
+ acc.addPane('Distribution', () => getDistributionWidget(this.df, this).root, true);
117
154
 
118
155
  return acc;
119
156
  }
120
157
 
121
158
  getViewer(): SARViewerBase {
122
- const viewer = this.sarViewer ?? this.sarViewerVertical;
159
+ const viewer = this.mutationCliffsViewer ?? this.mostPotentResiduesViewer;
123
160
  if (!viewer)
124
161
  throw new Error('ViewerError: none of the SAR viewers is initialized');
125
162
  return viewer;
@@ -145,28 +182,30 @@ export class PeptidesModel {
145
182
  }
146
183
 
147
184
  updateDefault(): void {
148
- const viewer = this.getViewer();
149
- const proprtyChanged = this.isPropertyChanged(this.sarViewer) || this.isPropertyChanged(this.sarViewerVertical);
150
- if ((this._sourceGrid && !this._isUpdating && proprtyChanged) || !this.isInitialized) {
185
+ const proprtyChanged =
186
+ this.isPropertyChanged(this.mutationCliffsViewer) || this.isPropertyChanged(this.mostPotentResiduesViewer);
187
+ if ((this.sourceGrid && !this._isUpdating && proprtyChanged) || !this.isInitialized) {
151
188
  this.isInitialized = true;
152
189
  this._isUpdating = true;
153
- const [viewerGrid, viewerVGrid] = this.initializeViewersComponents();
190
+ this.initializeViewersComponents();
154
191
  //FIXME: modify during the initializeViewersComponents stages
155
- this._sarGridSubject.next(viewerGrid);
156
- this._sarVGridSubject.next(viewerVGrid);
192
+ this.mutationCliffsGridSubject.next(this.mutationCliffsGrid);
193
+ this.mostPotentResiduesGridSubject.next(this.mostPotentResiduesGrid);
194
+ if (this.df.getTag(C.TAGS.CLUSTERS))
195
+ this.logoSummaryGridSubject.next(this.logoSummaryGrid);
157
196
 
158
- this.invalidateSelection();
197
+ this.fireBitsetChanged();
198
+ this.invalidateGrids();
159
199
  this._isUpdating = false;
160
200
  }
161
201
  }
162
202
 
163
- initializeViewersComponents(): [DG.Grid, DG.Grid] {
164
- if (this._sourceGrid === null)
203
+ initializeViewersComponents(): void {
204
+ if (this.sourceGrid === null)
165
205
  throw new Error(`Source grid is not initialized`);
166
206
 
167
207
  //Split the aligned sequence into separate AARs
168
- const col: DG.Column<string> = this.df.columns.bySemType(C.SEM_TYPES.MACROMOLECULE)!;
169
- // const alphabet = col.tags[DG.TAGS.UNITS].split(':')[2];
208
+ const col = this.df.getCol(C.COLUMNS_NAMES.MACROMOLECULE);
170
209
  const alphabet = col.tags['alphabet'];
171
210
  const splitSeqDf = splitAlignedSequences(col);
172
211
 
@@ -177,12 +216,7 @@ export class PeptidesModel {
177
216
  const activityCol = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY)!;
178
217
  splitSeqDf.columns.add(activityCol);
179
218
 
180
- this.joinDataFrames(positionColumns, splitSeqDf);
181
-
182
- for (const dfCol of this.df.columns) {
183
- if (positionColumns.includes(dfCol.name))
184
- setAARRenderer(dfCol, alphabet, this._sourceGrid);
185
- }
219
+ this.joinDataFrames(positionColumns, splitSeqDf, alphabet);
186
220
 
187
221
  this.sortSourceGrid();
188
222
 
@@ -196,11 +230,11 @@ export class PeptidesModel {
196
230
  matrixDf = matrixDf.unpivot([], positionColumns, C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER);
197
231
 
198
232
  //statistics for specific AAR at a specific position
199
- this.statsDf = this.calculateStatistics(matrixDf);
233
+ this.monomerPositionStatsDf = this.calculateMonomerPositionStatistics(matrixDf);
200
234
 
201
235
  // SAR matrix table
202
236
  //pivot a table to make it matrix-like
203
- matrixDf = this.statsDf.groupBy([C.COLUMNS_NAMES.MONOMER])
237
+ matrixDf = this.monomerPositionStatsDf.groupBy([C.COLUMNS_NAMES.MONOMER])
204
238
  .pivot(C.COLUMNS_NAMES.POSITION)
205
239
  .add('first', C.COLUMNS_NAMES.MEAN_DIFFERENCE, '')
206
240
  .aggregate();
@@ -214,28 +248,31 @@ export class PeptidesModel {
214
248
 
215
249
  this.calcSubstitutions();
216
250
 
217
- const [sarGrid, sarVGrid] = this.createGrids(matrixDf, positionColumns, sequenceDf, alphabet);
251
+ [this.mutationCliffsGrid, this.mostPotentResiduesGrid] =
252
+ this.createGrids(matrixDf, sequenceDf, positionColumns, alphabet);
218
253
 
219
- this._sarGrid = sarGrid;
220
- this._sarVGrid = sarVGrid;
254
+ if (this.df.getTag(C.TAGS.CLUSTERS)) {
255
+ this.clusterStatsDf = this.calculateClusterStatistics();
256
+ this.logoSummaryGrid = this.createLogoSummaryGrid();
257
+ }
258
+
259
+ // init invariant map & mutation cliffs selections
260
+ this.initSelections(positionColumns);
221
261
 
222
262
  positionColumns.push(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
223
263
 
224
264
  this.setBarChartInteraction();
225
265
 
226
- this.setCellRenderers(positionColumns, sarGrid, sarVGrid);
266
+ this.setCellRenderers(positionColumns);
227
267
 
228
268
  // show all the statistics in a tooltip over cell
229
- this.setTooltips(positionColumns, sarGrid, sarVGrid);
269
+ this.setTooltips(positionColumns);
230
270
 
231
271
  this.setInteractionCallback();
232
272
 
233
273
  this.setBitsetCallback();
234
274
 
235
- this.postProcessGrids(sarGrid, sarVGrid);
236
-
237
- //TODO: return class instead
238
- return [sarGrid, sarVGrid];
275
+ this.postProcessGrids();
239
276
  }
240
277
 
241
278
  calcSubstitutions(): void {
@@ -323,13 +360,31 @@ export class PeptidesModel {
323
360
  }
324
361
  }
325
362
 
326
- joinDataFrames(positionColumns: string[], splitSeqDf: DG.DataFrame): void {
363
+ initSelections(positionColumns: string[]): void {
364
+ const tempInvariantMapSelection: type.PositionToAARList = this.invariantMapSelection;
365
+ const mutationCliffsSelection: type.PositionToAARList = this.mutationCliffsSelection;
366
+ for (const pos of positionColumns) {
367
+ tempInvariantMapSelection[pos] ??= [];
368
+ mutationCliffsSelection[pos] ??= [];
369
+ }
370
+ this.invariantMapSelection = tempInvariantMapSelection;
371
+ this.mutationCliffsSelection = mutationCliffsSelection;
372
+ this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
373
+ }
374
+
375
+ joinDataFrames(positionColumns: string[], splitSeqDf: DG.DataFrame, alphabet: string): void {
327
376
  // append splitSeqDf columns to source table and make sure columns are not added more than once
328
377
  const name = this.df.name;
329
- const dfColsSet = new Set(this.df.columns.names());
330
- if (!positionColumns.every((col: string) => dfColsSet.has(col))) {
331
- this.df.join(splitSeqDf, [C.COLUMNS_NAMES.ACTIVITY], [C.COLUMNS_NAMES.ACTIVITY],
332
- this.df.columns.names(), positionColumns, 'inner', true);
378
+ for (const colName of positionColumns) {
379
+ const col = this.df.col(colName);
380
+ const newCol = splitSeqDf.getCol(colName);
381
+ if (col === null)
382
+ this.df.columns.add(newCol);
383
+ else {
384
+ this.df.columns.remove(colName);
385
+ this.df.columns.add(newCol);
386
+ }
387
+ CR.setAARRenderer(newCol, alphabet, this.sourceGrid);
333
388
  }
334
389
  this.df.name = name;
335
390
  this.currentView.name = name;
@@ -337,8 +392,8 @@ export class PeptidesModel {
337
392
 
338
393
  sortSourceGrid(): void {
339
394
  const colNames: DG.GridColumn[] = [];
340
- for (let i = 1; i < this._sourceGrid.columns.length; i++)
341
- colNames.push(this._sourceGrid.columns.byIndex(i)!);
395
+ for (let i = 1; i < this.sourceGrid.columns.length; i++)
396
+ colNames.push(this.sourceGrid.columns.byIndex(i)!);
342
397
 
343
398
  colNames.sort((a, b)=>{
344
399
  if (a.column!.semType == C.SEM_TYPES.MONOMER) {
@@ -350,28 +405,28 @@ export class PeptidesModel {
350
405
  return 1;
351
406
  return 0;
352
407
  });
353
- this._sourceGrid.columns.setOrder(colNames.map((v) => v.name));
408
+ this.sourceGrid.columns.setOrder(colNames.map((v) => v.name));
354
409
  }
355
410
 
356
411
  createScaledCol(activityScaling: string, splitSeqDf: DG.DataFrame): void {
357
- const [scaledDf, newColName] =
358
- scaleActivity(activityScaling, this.df, this.df.tags[C.COLUMNS_NAMES.ACTIVITY]);
412
+ const [scaledDf, _, newColName] =
413
+ scaleActivity(activityScaling, this.df.getCol(C.COLUMNS_NAMES.ACTIVITY));
359
414
  //TODO: make another func
360
415
  const scaledCol = scaledDf.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
361
416
  scaledCol.semType = C.SEM_TYPES.ACTIVITY_SCALED;
362
417
  splitSeqDf.columns.add(scaledCol);
363
418
  const oldScaledCol = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
364
419
  this.df.columns.replace(oldScaledCol, scaledCol);
365
- const gridCol = this._sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED);
420
+ const gridCol = this.sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED);
366
421
  if (gridCol !== null) {
367
422
  gridCol.name = newColName;
368
423
  this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED] = newColName;
369
424
  }
370
425
 
371
- this._sourceGrid.columns.setOrder([newColName]);
426
+ this.sourceGrid.columns.setOrder([newColName]);
372
427
  }
373
428
 
374
- calculateStatistics(matrixDf: DG.DataFrame): DG.DataFrame {
429
+ calculateMonomerPositionStatistics(matrixDf: DG.DataFrame): DG.DataFrame {
375
430
  matrixDf = matrixDf.groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER]).aggregate();
376
431
 
377
432
  //calculate p-values based on t-test
@@ -388,7 +443,7 @@ export class PeptidesModel {
388
443
  for (let i = 0; i < matrixDf.rowCount; i++) {
389
444
  const position: string = posCol.get(i);
390
445
  const aar: string = aarCol.get(i);
391
- const mask = DG.BitSet.create(sourceDfLen, (j) => this.df.get(position, j) == aar);
446
+ const mask = DG.BitSet.create(sourceDfLen, (j) => this.df.get(position, j) === aar);
392
447
  const stats = getStats(activityCol, mask);
393
448
 
394
449
  mdCol.set(i, stats.meanDifference);
@@ -397,23 +452,43 @@ export class PeptidesModel {
397
452
  ratioCol.set(i, stats.ratio);
398
453
  }
399
454
 
400
- const countThreshold = 4;
401
- matrixDf = matrixDf.rows.match(`${C.COLUMNS_NAMES.COUNT} >= ${countThreshold}`).toDataFrame();
402
- matrixDf = matrixDf.rows.match(`${C.COLUMNS_NAMES.COUNT} <= ${sourceDfLen - countThreshold}`).toDataFrame();
403
-
404
455
  return matrixDf as DG.DataFrame;
405
456
  }
406
457
 
458
+ calculateClusterStatistics(): DG.DataFrame {
459
+ const originalClustersCol = this.df.getCol(C.COLUMNS_NAMES.CLUSTERS);
460
+ const statsDf = this.df.groupBy([C.COLUMNS_NAMES.CLUSTERS]).aggregate();
461
+ const clustersCol = statsDf.getCol(C.COLUMNS_NAMES.CLUSTERS);
462
+ const statsDfCols = statsDf.columns;
463
+ const mdCol= statsDfCols.addNewFloat(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
464
+ const pValCol = statsDfCols.addNewFloat(C.COLUMNS_NAMES.P_VALUE);
465
+ const countCol = statsDfCols.addNewInt(C.COLUMNS_NAMES.COUNT);
466
+ const ratioCol = statsDfCols.addNewFloat(C.COLUMNS_NAMES.RATIO);
467
+ const activityList: number[] = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).toList();
468
+
469
+ for (let rowIdx = 0; rowIdx < clustersCol.length; ++rowIdx) {
470
+ const cluster = clustersCol.get(rowIdx);
471
+ const mask = DG.BitSet.create(activityList.length, (bitIdx) => originalClustersCol.get(bitIdx) === cluster);
472
+ const stats = getStats(activityList, mask);
473
+
474
+ mdCol.set(rowIdx, stats.meanDifference);
475
+ pValCol.set(rowIdx, stats.pValue);
476
+ countCol.set(rowIdx, stats.count);
477
+ ratioCol.set(rowIdx, stats.ratio);
478
+ }
479
+ return statsDf;
480
+ }
481
+
407
482
  setCategoryOrder(matrixDf: DG.DataFrame): void {
408
483
  let sortArgument: string = C.COLUMNS_NAMES.MEAN_DIFFERENCE;
409
484
  if (this.getViewer().bidirectionalAnalysis) {
410
- const mdCol = this.statsDf.getCol(sortArgument);
485
+ const mdCol = this.monomerPositionStatsDf.getCol(sortArgument);
411
486
  sortArgument = 'Absolute Mean difference';
412
- const absMDCol = this.statsDf.columns.addNewFloat(sortArgument);
487
+ const absMDCol = this.monomerPositionStatsDf.columns.addNewFloat(sortArgument);
413
488
  absMDCol.init((i) => Math.abs(mdCol.get(i)));
414
489
  }
415
490
 
416
- const aarWeightsDf = this.statsDf.groupBy([C.COLUMNS_NAMES.MONOMER]).sum(sortArgument, 'weight')
491
+ const aarWeightsDf = this.monomerPositionStatsDf.groupBy([C.COLUMNS_NAMES.MONOMER]).sum(sortArgument, 'weight')
417
492
  .aggregate();
418
493
  const aarList = aarWeightsDf.getCol(C.COLUMNS_NAMES.MONOMER).toList();
419
494
  const getWeight = (aar: string): number => aarWeightsDf
@@ -430,7 +505,7 @@ export class PeptidesModel {
430
505
  // TODO: aquire ALL of the positions
431
506
  const columns = [C.COLUMNS_NAMES.MEAN_DIFFERENCE, C.COLUMNS_NAMES.MONOMER, C.COLUMNS_NAMES.POSITION,
432
507
  'Count', 'Ratio', C.COLUMNS_NAMES.P_VALUE];
433
- let sequenceDf = this.statsDf.groupBy(columns)
508
+ let sequenceDf = this.monomerPositionStatsDf.groupBy(columns)
434
509
  .where('pValue <= 0.1')
435
510
  .aggregate();
436
511
 
@@ -451,32 +526,125 @@ export class PeptidesModel {
451
526
  return sequenceDf;
452
527
  }
453
528
 
454
- createGrids(
455
- matrixDf: DG.DataFrame, positionColumns: string[], sequenceDf: DG.DataFrame, alphabet: string): DG.Grid[] {
456
- const sarGrid = matrixDf.plot.grid();
457
- sarGrid.sort([C.COLUMNS_NAMES.MONOMER]);
458
- sarGrid.columns.setOrder([C.COLUMNS_NAMES.MONOMER].concat(positionColumns as C.COLUMNS_NAMES[]));
459
-
460
- const sarVGrid = sequenceDf.plot.grid();
461
- sarVGrid.sort([C.COLUMNS_NAMES.POSITION]);
462
- const pValGridCol = sarVGrid.col(C.COLUMNS_NAMES.P_VALUE)!;
529
+ createGrids(mutationCliffsDf: DG.DataFrame, mostPotentResiduesDf: DG.DataFrame, positionColumns: string[],
530
+ alphabet: string): [DG.Grid, DG.Grid] {
531
+ // Creating Mutation Cliffs grid and sorting columns
532
+ const mutationCliffsGrid = mutationCliffsDf.plot.grid();
533
+ mutationCliffsGrid.sort([C.COLUMNS_NAMES.MONOMER]);
534
+ mutationCliffsGrid.columns.setOrder([C.COLUMNS_NAMES.MONOMER].concat(positionColumns as C.COLUMNS_NAMES[]));
535
+
536
+ // Creating Monomer-Position grid, sorting and setting column format
537
+ const mostPotentResiduesGrid = mostPotentResiduesDf.plot.grid();
538
+ mostPotentResiduesGrid.sort([C.COLUMNS_NAMES.POSITION]);
539
+ const pValGridCol = mostPotentResiduesGrid.col(C.COLUMNS_NAMES.P_VALUE)!;
463
540
  pValGridCol.format = '#.000';
464
541
  pValGridCol.name = 'P-value';
465
542
 
466
- let tempCol = matrixDf.getCol(C.COLUMNS_NAMES.MONOMER);
467
- if (tempCol)
468
- setAARRenderer(tempCol, alphabet, sarGrid);
543
+ // Setting Monomer column renderer
544
+ CR.setAARRenderer(mutationCliffsDf.getCol(C.COLUMNS_NAMES.MONOMER), alphabet, mutationCliffsGrid);
545
+ CR.setAARRenderer(mostPotentResiduesDf.getCol(C.COLUMNS_NAMES.MONOMER), alphabet, mostPotentResiduesGrid);
546
+
547
+ return [mutationCliffsGrid, mostPotentResiduesGrid];
548
+ }
549
+
550
+ createLogoSummaryGrid(): DG.Grid {
551
+ const summaryTable = this.df.groupBy([C.COLUMNS_NAMES.CLUSTERS]).aggregate();
552
+ const summaryTableLength = summaryTable.rowCount;
553
+ const clustersCol: DG.Column<number> = summaryTable.getCol(C.COLUMNS_NAMES.CLUSTERS);
554
+ const membersCol: DG.Column<number> = summaryTable.columns.addNewInt('Members');
555
+ const webLogoCol: DG.Column<string> = summaryTable.columns.addNew('WebLogo', DG.COLUMN_TYPE.STRING);
556
+ const tempDfList: DG.DataFrame[] = new Array(summaryTableLength);
557
+ const originalClustersCol = this.df.getCol(C.COLUMNS_NAMES.CLUSTERS);
558
+ const peptideCol: DG.Column<string> = this.df.getCol(C.COLUMNS_NAMES.MACROMOLECULE);
559
+
560
+ for (let index = 0; index < summaryTableLength; ++index) {
561
+ const indexes: number[] = [];
562
+ for (let j = 0; j < originalClustersCol.length; ++j) {
563
+ if (originalClustersCol.get(j) === clustersCol.get(index))
564
+ indexes.push(j);
565
+ }
566
+ const tCol = DG.Column.string('peptides', indexes.length);
567
+ tCol.init((i) => peptideCol.get(indexes[i]));
568
+
569
+ for (const tag of peptideCol.tags)
570
+ tCol.setTag(tag[0], tag[1]);
571
+
572
+ const dfSlice = DG.DataFrame.fromColumns([tCol]);
573
+ tempDfList[index] = dfSlice;
574
+ webLogoCol.set(index, index.toString());
575
+ membersCol.set(index, dfSlice.rowCount);
576
+ }
577
+ webLogoCol.setTag(DG.TAGS.CELL_RENDERER, 'html');
578
+
579
+ const grid = summaryTable.plot.grid();
580
+ const gridClustersCol = grid.col(C.COLUMNS_NAMES.CLUSTERS)!;
581
+ gridClustersCol.name = 'Clusters';
582
+ gridClustersCol.visible = true;
583
+ grid.columns.rowHeader!.visible = false;
584
+ grid.props.rowHeight = 55;
585
+ grid.onCellPrepare((cell) => {
586
+ if (cell.isTableCell && cell.tableColumn?.name === 'WebLogo') {
587
+ tempDfList[parseInt(cell.cell.value)].plot.fromType('WebLogo', {maxHeight: 50})
588
+ .then((viewer) => cell.element = viewer.root);
589
+ }
590
+ });
591
+ grid.root.addEventListener('click', (ev) => {
592
+ const cell = grid.hitTest(ev.offsetX, ev.offsetY);
593
+ if (!cell || !cell.isTableCell)
594
+ return;
595
+
596
+ const cluster = clustersCol.get(cell.tableRowIndex!)!;
597
+ summaryTable.currentRowIdx = -1;
598
+ if (ev.shiftKey)
599
+ this.modifyClusterSelection(cluster);
600
+ else
601
+ this.initClusterSelection(cluster);
602
+ this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
603
+ });
604
+ grid.onCellRender.subscribe((gridCellArgs) => {
605
+ const gc = gridCellArgs.cell;
606
+ if (gc.tableColumn?.name !== C.COLUMNS_NAMES.CLUSTERS || gc.isColHeader)
607
+ return;
608
+ const canvasContext = gridCellArgs.g;
609
+ const bound = gridCellArgs.bounds;
610
+ canvasContext.save();
611
+ canvasContext.beginPath();
612
+ canvasContext.rect(bound.x, bound.y, bound.width, bound.height);
613
+ canvasContext.clip();
614
+ CR.renderLogoSummaryCell(canvasContext, gc.cell.value, this.logoSummarySelection, bound);
615
+ gridCellArgs.preventDefault();
616
+ canvasContext.restore();
617
+ });
618
+ grid.onCellTooltip((cell, x, y) => {
619
+ if (!cell.isColHeader && cell.tableColumn?.name === C.COLUMNS_NAMES.CLUSTERS)
620
+ this.showTooltipCluster(cell.cell.value, x, y);
621
+ return true;
622
+ });
623
+ const webLogoGridCol = grid.columns.byName('WebLogo')!;
624
+ webLogoGridCol.cellType = 'html';
625
+ webLogoGridCol.width = 350;
626
+
627
+ return grid;
628
+ }
629
+
630
+ modifyClusterSelection(cluster: number): void {
631
+ const tempSelection = this.logoSummarySelection;
632
+ const idx = tempSelection.indexOf(cluster);
633
+ if (idx !== -1)
634
+ tempSelection.splice(idx, 1);
635
+ else
636
+ tempSelection.push(cluster);
469
637
 
470
- tempCol = sequenceDf.getCol(C.COLUMNS_NAMES.MONOMER);
471
- if (tempCol)
472
- setAARRenderer(tempCol, alphabet, sarGrid);
638
+ this.logoSummarySelection = tempSelection;
639
+ }
473
640
 
474
- return [sarGrid, sarVGrid];
641
+ initClusterSelection(cluster: number): void {
642
+ this.logoSummarySelection = [cluster];
475
643
  }
476
644
 
477
645
  setBarChartInteraction(): void {
478
646
  const eventAction = (ev: MouseEvent): void => {
479
- const cell = this._sourceGrid.hitTest(ev.offsetX, ev.offsetY);
647
+ const cell = this.sourceGrid.hitTest(ev.offsetX, ev.offsetY);
480
648
  if (cell?.isColHeader && cell.tableColumn?.semType == C.SEM_TYPES.MONOMER) {
481
649
  const newBarPart = this.findAARandPosition(cell, ev);
482
650
  this.requestBarchartAction(ev, newBarPart);
@@ -484,9 +652,9 @@ export class PeptidesModel {
484
652
  };
485
653
 
486
654
  // The following events makes the barchart interactive
487
- rxjs.fromEvent<MouseEvent>(this._sourceGrid.overlay, 'mousemove')
655
+ rxjs.fromEvent<MouseEvent>(this.sourceGrid.overlay, 'mousemove')
488
656
  .subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
489
- rxjs.fromEvent<MouseEvent>(this._sourceGrid.overlay, 'click')
657
+ rxjs.fromEvent<MouseEvent>(this.sourceGrid.overlay, 'click')
490
658
  .subscribe((mouseMove: MouseEvent) => eventAction(mouseMove));
491
659
  }
492
660
 
@@ -508,8 +676,9 @@ export class PeptidesModel {
508
676
  const monomer = barPart.monomer;
509
677
  const position = barPart.position;
510
678
  if (ev.type === 'click') {
511
- ev.shiftKey ? this.modifyCurrentSelection(monomer, position) :
512
- this.initCurrentSelection(monomer, position);
679
+ ev.shiftKey ? this.modifyMonomerPositionSelection(monomer, position, true) :
680
+ this.initMonomerPositionSelection(monomer, position, true);
681
+ this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
513
682
  } else {
514
683
  const bar = `${monomer}:${position}`;
515
684
  if (this.cachedBarchartTooltip.bar == bar)
@@ -519,8 +688,8 @@ export class PeptidesModel {
519
688
  }
520
689
  }
521
690
 
522
- setCellRenderers(renderColNames: string[], sarGrid: DG.Grid, sarVGrid: DG.Grid): void {
523
- const mdCol = this.statsDf.getCol(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
691
+ setCellRenderers(renderColNames: string[]): void {
692
+ const mdCol = this.monomerPositionStatsDf.getCol(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
524
693
  //decompose into two different renering funcs
525
694
  const renderCell = (args: DG.GridCellRenderArgs): void => {
526
695
  const canvasContext = args.g;
@@ -551,18 +720,28 @@ export class PeptidesModel {
551
720
  const currentAAR: string = gridTable.get(C.COLUMNS_NAMES.MONOMER, tableRowIndex);
552
721
 
553
722
  const viewer = this.getViewer();
554
- renderSARCell(canvasContext, currentAAR, currentPosition, this.statsDf, viewer.bidirectionalAnalysis, mdCol,
555
- bound, cellValue, this.currentSelection, this.substitutionsInfo);
723
+ if (this.isInvariantMap) {
724
+ const value: number = this.monomerPositionStatsDf
725
+ .groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.MONOMER, C.COLUMNS_NAMES.COUNT])
726
+ .where(`${C.COLUMNS_NAMES.POSITION} = ${currentPosition} and ${C.COLUMNS_NAMES.MONOMER} = ${currentAAR}`)
727
+ .aggregate().get(C.COLUMNS_NAMES.COUNT, 0);
728
+ CR.renderInvaraintMapCell(
729
+ canvasContext, currentAAR, currentPosition, this.invariantMapSelection, value, bound);
730
+ } else {
731
+ CR.renderMutationCliffCell(
732
+ canvasContext, currentAAR, currentPosition, this.monomerPositionStatsDf, viewer.bidirectionalAnalysis,
733
+ mdCol, bound, cellValue, this.mutationCliffsSelection, this.substitutionsInfo);
734
+ }
556
735
  }
557
736
  args.preventDefault();
558
737
  }
559
738
  canvasContext.restore();
560
739
  };
561
- sarGrid.onCellRender.subscribe(renderCell);
562
- sarVGrid.onCellRender.subscribe(renderCell);
740
+ this.mutationCliffsGrid.onCellRender.subscribe(renderCell);
741
+ this.mostPotentResiduesGrid.onCellRender.subscribe(renderCell);
563
742
 
564
- this._sourceGrid.setOptions({'colHeaderHeight': 130});
565
- this._sourceGrid.onCellRender.subscribe((gcArgs) => {
743
+ this.sourceGrid.setOptions({'colHeaderHeight': 130});
744
+ this.sourceGrid.onCellRender.subscribe((gcArgs) => {
566
745
  const context = gcArgs.g;
567
746
  const bounds = gcArgs.bounds;
568
747
  const col = gcArgs.cell.tableColumn;
@@ -573,7 +752,7 @@ export class PeptidesModel {
573
752
  context.clip();
574
753
 
575
754
  if (gcArgs.cell.isColHeader && col?.semType == C.SEM_TYPES.MONOMER) {
576
- const barBounds = renderBarchart(context, col, this.barData[col.name], bounds, this.df.filter.trueCount);
755
+ const barBounds = CR.renderBarchart(context, col, this.barData[col.name], bounds, this.df.filter.trueCount);
577
756
  this.barsBounds[col.name] = barBounds;
578
757
  gcArgs.preventDefault();
579
758
  }
@@ -582,7 +761,7 @@ export class PeptidesModel {
582
761
  });
583
762
  }
584
763
 
585
- setTooltips(renderColNames: string[], sarGrid: DG.Grid, sarVGrid: DG.Grid): void {
764
+ setTooltips(renderColNames: string[]): void {
586
765
  const showTooltip = (cell: DG.GridCell, x: number, y: number): boolean => {
587
766
  const tableCol = cell.tableColumn;
588
767
  const tableColName = tableCol?.name;
@@ -604,9 +783,9 @@ export class PeptidesModel {
604
783
  return true;
605
784
  };
606
785
 
607
- sarGrid.onCellTooltip(showTooltip);
608
- sarVGrid.onCellTooltip(showTooltip);
609
- this._sourceGrid.onCellTooltip((cell, x, y) => {
786
+ this.mutationCliffsGrid.onCellTooltip(showTooltip);
787
+ this.mostPotentResiduesGrid.onCellTooltip(showTooltip);
788
+ this.sourceGrid.onCellTooltip((cell, x, y) => {
610
789
  const col = cell.tableColumn;
611
790
  const cellValue = cell.cell.value;
612
791
  if (cellValue && col && col.semType === C.SEM_TYPES.MONOMER)
@@ -617,8 +796,6 @@ export class PeptidesModel {
617
796
 
618
797
  showMonomerTooltip(aar: string, x: number, y: number): void {
619
798
  const tooltipElements: HTMLDivElement[] = [];
620
- //@ts-ignore: no types for org
621
- // const monomer: type.HELMMonomer = org.helm.webeditor.monomers.getMonomer('HELM_AA', aar);
622
799
  const monomer: type.HELMMonomer = this.monomerLib[aar.toLowerCase()];
623
800
 
624
801
  if (monomer) {
@@ -632,8 +809,9 @@ export class PeptidesModel {
632
809
  }
633
810
 
634
811
  showTooltipAt(aar: string, position: string, x: number, y: number): HTMLDivElement | null {
635
- const currentStatsDf = this.statsDf.rows.match({Pos: position, AAR: aar}).toDataFrame();
812
+ const currentStatsDf = this.monomerPositionStatsDf.rows.match({Pos: position, AAR: aar}).toDataFrame();
636
813
  const activityCol = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
814
+ //TODO: use bitset instead of splitCol
637
815
  const splitCol = DG.Column.bool(C.COLUMNS_NAMES.SPLIT_COL, activityCol.length);
638
816
  const currentPosCol = this.df.getCol(position);
639
817
  splitCol.init((i) => currentPosCol.get(i) == aar);
@@ -654,32 +832,60 @@ export class PeptidesModel {
654
832
  return tooltip;
655
833
  }
656
834
 
657
- setInteractionCallback(): void {
658
- const sarDf = this._sarGrid.dataFrame;
659
- const sarVDf = this._sarVGrid.dataFrame;
835
+ showTooltipCluster(cluster: number, x: number, y: number): HTMLDivElement | null {
836
+ const currentStatsDf = this.clusterStatsDf.rows.match({clusters: cluster}).toDataFrame();
837
+ const activityCol = this.df.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
838
+ //TODO: use bitset instead of splitCol
839
+ const splitCol = DG.Column.bool(C.COLUMNS_NAMES.SPLIT_COL, activityCol.length);
840
+ const currentClusterCol = this.df.getCol(C.COLUMNS_NAMES.CLUSTERS);
841
+ splitCol.init((i) => currentClusterCol.get(i) == cluster);
842
+ const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
843
+ const stats: Stats = {
844
+ count: currentStatsDf.get(C.COLUMNS_NAMES.COUNT, 0),
845
+ ratio: currentStatsDf.get(C.COLUMNS_NAMES.RATIO, 0),
846
+ pValue: currentStatsDf.get(C.COLUMNS_NAMES.P_VALUE, 0),
847
+ meanDifference: currentStatsDf.get(C.COLUMNS_NAMES.MEAN_DIFFERENCE, 0),
848
+ };
849
+ if (!stats.count)
850
+ return null;
851
+
852
+ const tooltip = getDistributionAndStats(distributionTable, stats, `Cluster: ${cluster}`, 'Other', true);
853
+
854
+ ui.tooltip.show(tooltip, x, y);
660
855
 
661
- const chooseAction = (aar: string, position: string, isShiftPressed: boolean): void =>
662
- isShiftPressed ? this.modifyCurrentSelection(aar, position) : this.initCurrentSelection(aar, position);
856
+ return tooltip;
857
+ }
663
858
 
664
- this._sarGrid.root.addEventListener('click', (ev) => {
665
- const gridCell = this._sarGrid.hitTest(ev.offsetX, ev.offsetY);
859
+ setInteractionCallback(): void {
860
+ const mutationCliffsDf = this.mutationCliffsGrid.dataFrame;
861
+ const mostPotentResiduesDf = this.mostPotentResiduesGrid.dataFrame;
862
+
863
+ const chooseAction =
864
+ (aar: string, position: string, isShiftPressed: boolean, isInvariantMapSelection: boolean = true): void => {
865
+ isShiftPressed ? this.modifyMonomerPositionSelection(aar, position, isInvariantMapSelection) :
866
+ this.initMonomerPositionSelection(aar, position, isInvariantMapSelection);
867
+ this.barData = calculateBarsData(this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER), this.df.selection);
868
+ };
869
+
870
+ this.mutationCliffsGrid.root.addEventListener('click', (ev) => {
871
+ const gridCell = this.mutationCliffsGrid.hitTest(ev.offsetX, ev.offsetY);
666
872
  if (isGridCellInvalid(gridCell) || gridCell!.tableColumn!.name == C.COLUMNS_NAMES.MONOMER)
667
873
  return;
668
874
 
669
875
  const position = gridCell!.tableColumn!.name;
670
- const aar = sarDf.get(C.COLUMNS_NAMES.MONOMER, gridCell!.tableRowIndex!);
671
- chooseAction(aar, position, ev.shiftKey);
876
+ const aar = mutationCliffsDf.get(C.COLUMNS_NAMES.MONOMER, gridCell!.tableRowIndex!);
877
+ chooseAction(aar, position, ev.shiftKey, this.isInvariantMap);
672
878
  });
673
879
 
674
- this._sarVGrid.root.addEventListener('click', (ev) => {
675
- const gridCell = this._sarVGrid.hitTest(ev.offsetX, ev.offsetY);
880
+ this.mostPotentResiduesGrid.root.addEventListener('click', (ev) => {
881
+ const gridCell = this.mostPotentResiduesGrid.hitTest(ev.offsetX, ev.offsetY);
676
882
  if (isGridCellInvalid(gridCell) || gridCell!.tableColumn!.name != C.COLUMNS_NAMES.MEAN_DIFFERENCE)
677
883
  return;
678
884
 
679
885
  const tableRowIdx = gridCell!.tableRowIndex!;
680
- const position = sarVDf.get(C.COLUMNS_NAMES.POSITION, tableRowIdx);
681
- const aar = sarVDf.get(C.COLUMNS_NAMES.MONOMER, tableRowIdx);
682
- chooseAction(aar, position, ev.shiftKey);
886
+ const position = mostPotentResiduesDf.get(C.COLUMNS_NAMES.POSITION, tableRowIdx);
887
+ const aar = mostPotentResiduesDf.get(C.COLUMNS_NAMES.MONOMER, tableRowIdx);
888
+ chooseAction(aar, position, ev.shiftKey, false);
683
889
  });
684
890
 
685
891
  const cellChanged = (table: DG.DataFrame): void => {
@@ -689,45 +895,50 @@ export class PeptidesModel {
689
895
  table.currentRowIdx = -1;
690
896
  this.isCellChanging = false;
691
897
  };
692
- this._sarGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(sarDf));
693
- this._sarVGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(sarVDf));
694
- }
695
-
696
- modifyCurrentSelection(aar: string, position: string): void {
697
- const tempSelection = this.currentSelection;
698
- if (!tempSelection.hasOwnProperty(position))
699
- tempSelection[position] = [aar];
700
- else {
701
- const tempSelectionAt = tempSelection[position];
702
- const aarIndex = tempSelectionAt.indexOf(aar);
703
- aarIndex == -1 ? tempSelectionAt.push(aar) :
704
- tempSelectionAt.length == 1 ? delete tempSelection[position] :
705
- tempSelectionAt.splice(aarIndex, 1);
706
- }
898
+ this.mutationCliffsGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(mutationCliffsDf));
899
+ this.mostPotentResiduesGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(mostPotentResiduesDf));
900
+ }
707
901
 
708
- this.currentSelection = tempSelection;
902
+ modifyMonomerPositionSelection(aar: string, position: string, isInvariantMapSelection: boolean): void {
903
+ const tempSelection = isInvariantMapSelection ? this.invariantMapSelection : this.mutationCliffsSelection;
904
+ const tempSelectionAt = tempSelection[position];
905
+ const aarIndex = tempSelectionAt.indexOf(aar);
906
+ if (aarIndex === -1)
907
+ tempSelectionAt.push(aar);
908
+ else
909
+ tempSelectionAt.splice(aarIndex, 1);
910
+
911
+ if (isInvariantMapSelection)
912
+ this.invariantMapSelection = tempSelection;
913
+ else
914
+ this.mutationCliffsSelection = tempSelection;
709
915
  }
710
916
 
711
- initCurrentSelection(aar: string, position: string): void {
712
- const tempSelection: type.SelectionObject = {};
917
+ initMonomerPositionSelection(aar: string, position: string, isInvariantMapSelection: boolean): void {
918
+ const tempSelection = isInvariantMapSelection ? this.invariantMapSelection : this.mutationCliffsSelection;
713
919
  tempSelection[position] = [aar];
714
- this.currentSelection = tempSelection;
920
+
921
+ if (isInvariantMapSelection)
922
+ this.invariantMapSelection = tempSelection;
923
+ else
924
+ this.mutationCliffsSelection = tempSelection;
715
925
  }
716
926
 
717
927
  invalidateGrids(): void {
718
- // this.stackedBarchart?.computeData();
719
- this._sarGrid.invalidate();
720
- this._sarVGrid.invalidate();
721
- this._sourceGrid?.invalidate();
722
- //TODO: this.peptideSpaceGrid.invalidate();
928
+ this.mutationCliffsGrid.invalidate();
929
+ this.mostPotentResiduesGrid.invalidate();
930
+ this.logoSummaryGrid?.invalidate();
931
+ this.sourceGrid?.invalidate();
723
932
  }
724
933
 
725
934
  setBitsetCallback(): void {
726
935
  if (this.isBitsetChangedInitialized)
727
936
  return;
728
937
  const selection = this.df.selection;
938
+ const filter = this.df.filter;
939
+ const clusterCol = this.df.col(C.COLUMNS_NAMES.CLUSTERS);
729
940
 
730
- const changeBitset = (currentBitset: DG.BitSet): void => {
941
+ const changeSelectionBitset = (currentBitset: DG.BitSet): void => {
731
942
  const edfSelection = this.edf?.selection;
732
943
  if (this.isPeptideSpaceChangingBitset) {
733
944
  if (edfSelection == null)
@@ -743,20 +954,17 @@ export class PeptidesModel {
743
954
  this.isChangingEdfBitset = false;
744
955
  };
745
956
 
746
- const positionList = Object.keys(this.currentSelection);
747
- if (positionList.length == 0) {
748
- currentBitset.init(() => false, false);
749
- updateEdfSelection();
750
- return;
751
- }
957
+ const positionList = Object.keys(this.mutationCliffsSelection);
752
958
 
753
959
  //TODO: move out
754
960
  const getBitAt = (i: number): boolean => {
755
961
  for (const position of positionList) {
756
962
  const positionCol: DG.Column<string> = this.df.getCol(position);
757
- if (this._currentSelection[position].includes(positionCol.get(i)!))
963
+ if (this._mutationCliffsSelection[position].includes(positionCol.get(i)!))
758
964
  return true;
759
965
  }
966
+ if (this._logoSummarySelection.includes(clusterCol?.get(i)!))
967
+ return true;
760
968
  return false;
761
969
  };
762
970
  currentBitset.init(getBitAt, false);
@@ -764,7 +972,18 @@ export class PeptidesModel {
764
972
  updateEdfSelection();
765
973
  };
766
974
 
767
- selection.onChanged.subscribe(() => changeBitset(selection));
975
+ selection.onChanged.subscribe(() => changeSelectionBitset(selection));
976
+ filter.onChanged.subscribe(() => {
977
+ const positionList = Object.keys(this.invariantMapSelection);
978
+ filter.init((i) => {
979
+ let result = true;
980
+ for (const position of positionList) {
981
+ const aarList = this._invariantMapSelection[position];
982
+ result &&= aarList.length == 0 || aarList.includes(this.df.get(position, i));
983
+ }
984
+ return result;
985
+ }, false);
986
+ });
768
987
  this.isBitsetChangedInitialized = true;
769
988
  }
770
989
 
@@ -776,11 +995,11 @@ export class PeptidesModel {
776
995
  this.isPeptideSpaceChangingBitset = false;
777
996
  }
778
997
 
779
- postProcessGrids(sarGrid: DG.Grid, sarVGrid: DG.Grid): void {
780
- const mdCol: DG.GridColumn = sarVGrid.col(C.COLUMNS_NAMES.MEAN_DIFFERENCE)!;
998
+ postProcessGrids(): void {
999
+ const mdCol: DG.GridColumn = this.mostPotentResiduesGrid.col(C.COLUMNS_NAMES.MEAN_DIFFERENCE)!;
781
1000
  mdCol.name = 'Diff';
782
1001
 
783
- for (const grid of [sarGrid, sarVGrid]) {
1002
+ for (const grid of [this.mutationCliffsGrid, this.mostPotentResiduesGrid]) {
784
1003
  const gridProps = grid.props;
785
1004
  gridProps.rowHeight = 20;
786
1005
  const girdCols = grid.columns;
@@ -788,21 +1007,24 @@ export class PeptidesModel {
788
1007
  for (let i = 0; i < colNum; ++i) {
789
1008
  const col = girdCols.byIndex(i)!;
790
1009
  const colName = col.name;
791
- if (grid == sarVGrid && colName !== 'Diff' && colName !== C.COLUMNS_NAMES.MONOMER)
792
- col.width = 50;
793
- else
794
- col.width = gridProps.rowHeight + 10;
1010
+ col.width =
1011
+ grid == this.mostPotentResiduesGrid && colName !== 'Diff' && colName !== C.COLUMNS_NAMES.MONOMER ? 50 :
1012
+ gridProps.rowHeight + 10;
795
1013
  }
796
1014
  }
797
1015
 
798
1016
  const setViewerGridProps = (grid: DG.Grid): void => {
799
- grid.props.allowEdit = false;
800
- grid.props.allowRowSelection = false;
801
- grid.props.allowBlockSelection = false;
1017
+ const gridProps = grid.props;
1018
+ gridProps.allowEdit = false;
1019
+ gridProps.allowRowSelection = false;
1020
+ gridProps.allowBlockSelection = false;
1021
+ gridProps.allowColSelection = false;
802
1022
  };
803
1023
 
804
- setViewerGridProps(sarGrid);
805
- setViewerGridProps(sarVGrid);
1024
+ setViewerGridProps(this.mutationCliffsGrid);
1025
+ setViewerGridProps(this.mostPotentResiduesGrid);
1026
+ if (this.df.getTag(C.TAGS.CLUSTERS))
1027
+ setViewerGridProps(this.logoSummaryGrid);
806
1028
  }
807
1029
 
808
1030
  getSplitColValueAt(index: number, aar: string, position: string, aarLabel: string): string {
@@ -819,9 +1041,9 @@ export class PeptidesModel {
819
1041
  }
820
1042
 
821
1043
  syncProperties(isSourceSAR = true): void {
822
- if (this.sarViewer && this.sarViewerVertical) {
823
- const [sourceViewer, targetViewer] = isSourceSAR ? [this.sarViewer, this.sarViewerVertical] :
824
- [this.sarViewerVertical, this.sarViewer];
1044
+ if (this.mutationCliffsViewer && this.mostPotentResiduesViewer) {
1045
+ const [sourceViewer, targetViewer] = isSourceSAR ? [this.mutationCliffsViewer, this.mostPotentResiduesViewer] :
1046
+ [this.mostPotentResiduesViewer, this.mutationCliffsViewer];
825
1047
  const properties = sourceViewer.props.getProperties();
826
1048
  const newProps: {[propName: string]: string | number | boolean} = {};
827
1049
  for (const property of properties) {
@@ -845,14 +1067,16 @@ export class PeptidesModel {
845
1067
 
846
1068
  this.currentView = this.df.tags[C.PEPTIDES_ANALYSIS] == 'true' ? grok.shell.v as DG.TableView :
847
1069
  grok.shell.addTableView(this.df);
848
- this._sourceGrid = this.currentView.grid;
1070
+ this.sourceGrid = this.currentView.grid;
849
1071
  if (this.df.tags[C.PEPTIDES_ANALYSIS] == 'true')
850
1072
  return;
851
1073
 
852
1074
  this.df.tags[C.PEPTIDES_ANALYSIS] = 'true';
853
- this._sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED)!.name = this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED];
854
- this._sourceGrid.columns.setOrder([this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED]]);
855
- this._sourceGrid.props.allowColSelection = false;
1075
+ const scaledGridCol = this.sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED)!;
1076
+ scaledGridCol.name = this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED];
1077
+ scaledGridCol.format = '#.000';
1078
+ this.sourceGrid.columns.setOrder([this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED]]);
1079
+ this.sourceGrid.props.allowColSelection = false;
856
1080
 
857
1081
  this.df.temp[C.EMBEDDING_STATUS] = false;
858
1082
  const adjustCellSize = (grid: DG.Grid): void => {
@@ -864,63 +1088,40 @@ export class PeptidesModel {
864
1088
  grid.props.rowHeight = 20;
865
1089
  };
866
1090
 
867
- for (let i = 0; i < this._sourceGrid.columns.length; i++) {
868
- const aarCol = this._sourceGrid.columns.byIndex(i);
869
- if (aarCol && aarCol.name && aarCol.column?.semType !== C.SEM_TYPES.MONOMER &&
870
- aarCol.name !== this.df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED])
871
- aarCol.visible = false;
1091
+ for (let i = 0; i < this.sourceGrid.columns.length; i++) {
1092
+ const currentCol = this.sourceGrid.columns.byIndex(i);
1093
+ if (currentCol?.column?.getTag(C.TAGS.VISIBLE) === '0')
1094
+ currentCol.visible = false;
872
1095
  }
873
1096
 
874
1097
  const options = {scaling: this.df.tags['scaling']};
875
1098
 
876
1099
  const dockManager = this.currentView.dockManager;
877
1100
 
878
- this.sarViewer = await this.df.plot.fromType('peptide-sar-viewer', options) as SARViewer;
879
-
880
- this.sarViewerVertical =
881
- await this.df.plot.fromType('peptide-sar-viewer-vertical', options) as SARViewerVertical;
1101
+ this.mutationCliffsViewer = await this.df.plot.fromType('peptide-sar-viewer', options) as MutationCliffsViewer;
882
1102
 
883
- const sarViewersGroup: viewerTypes[] = [this.sarViewer, this.sarViewerVertical];
1103
+ this.mostPotentResiduesViewer =
1104
+ await this.df.plot.fromType('peptide-sar-viewer-vertical', options) as MostPotentResiduesViewer;
884
1105
 
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
- // }
1106
+ if (this.df.getTag(C.TAGS.CLUSTERS)) {
1107
+ const logoSummary = await this.df.plot.fromType('logo-summary-viewer') as LogoSummary;
1108
+ dockManager.dock(logoSummary, DG.DOCK_TYPE.RIGHT, null, 'Logo Summary Table');
1109
+ }
892
1110
 
893
1111
  this.updateDefault();
894
1112
 
895
- this.currentView.filters({filters: [{type: 'Peptides:invariantMapFilter'}]});
1113
+ const mcNode =
1114
+ dockManager.dock(this.mutationCliffsViewer, DG.DOCK_TYPE.DOWN, null, this.mutationCliffsViewer.name);
1115
+
1116
+ dockManager.dock(
1117
+ this.mostPotentResiduesViewer, DG.DOCK_TYPE.RIGHT, mcNode, this.mostPotentResiduesViewer.name, 0.3);
896
1118
 
897
- dockViewers(sarViewersGroup, DG.DOCK_TYPE.RIGHT, dockManager, DG.DOCK_TYPE.DOWN);
898
1119
 
899
- this._sourceGrid.props.allowEdit = false;
900
- adjustCellSize(this._sourceGrid);
1120
+ this.sourceGrid.props.allowEdit = false;
1121
+ adjustCellSize(this.sourceGrid);
901
1122
 
902
1123
  this.invalidateGrids();
903
1124
  }
904
1125
 
905
- invalidateSourceGrid(): void {this._sourceGrid.invalidate();}
906
- }
907
-
908
- type viewerTypes = SARViewer | SARViewerVertical;
909
-
910
- function dockViewers(
911
- viewerList: viewerTypes[], attachDirection: DG.DockType, dockManager: DG.DockManager,
912
- initialAttachDirection?: DG.DockType): DG.DockNode[] | null {
913
- const viewerListLength = viewerList.length;
914
- if (viewerListLength === 0)
915
- return null;
916
-
917
- let currentViewer = viewerList[0];
918
- const nodeList = [dockManager.dock(currentViewer, initialAttachDirection, null, currentViewer.name ?? '')];
919
- const ratio = 1 / viewerListLength;
920
-
921
- for (let i = 1; i < viewerListLength; i++) {
922
- currentViewer = viewerList[i];
923
- nodeList.push(dockManager.dock(currentViewer, attachDirection, nodeList[i - 1], currentViewer.name ?? '', ratio));
924
- }
925
- return nodeList;
1126
+ invalidateSourceGrid(): void {this.sourceGrid.invalidate();}
926
1127
  }