@datagrok/peptides 0.8.13 → 1.0.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.
Files changed (39) hide show
  1. package/.eslintrc.json +5 -2
  2. package/dist/package-test.js +1268 -1766
  3. package/dist/package.js +1097 -1622
  4. package/dist/vendors-node_modules_datagrok-libraries_ml_src_workers_dimensionality-reducer_js.js +120 -62
  5. package/package.json +13 -17
  6. package/package.png +0 -0
  7. package/src/model.ts +504 -448
  8. package/src/monomer-library.ts +31 -30
  9. package/src/package-test.ts +5 -6
  10. package/src/package.ts +52 -70
  11. package/src/tests/core.ts +67 -0
  12. package/src/tests/msa-tests.ts +3 -3
  13. package/src/tests/peptide-space-test.ts +65 -45
  14. package/src/tests/utils.ts +20 -50
  15. package/src/utils/cell-renderer.ts +25 -151
  16. package/src/utils/chem-palette.ts +3 -14
  17. package/src/utils/constants.ts +5 -0
  18. package/src/utils/filtering-statistics.ts +2 -2
  19. package/src/utils/misc.ts +29 -0
  20. package/src/utils/multiple-sequence-alignment.ts +5 -18
  21. package/src/utils/multivariate-analysis.ts +5 -8
  22. package/src/utils/peptide-similarity-space.ts +12 -9
  23. package/src/utils/types.ts +5 -2
  24. package/src/viewers/peptide-space-viewer.ts +67 -39
  25. package/src/viewers/sar-viewer.ts +34 -37
  26. package/src/viewers/stacked-barchart-viewer.ts +38 -61
  27. package/src/widgets/analyze-peptides.ts +53 -75
  28. package/src/widgets/distribution.ts +34 -18
  29. package/src/widgets/manual-alignment.ts +8 -12
  30. package/src/widgets/peptide-molecule.ts +48 -25
  31. package/src/widgets/subst-table.ts +53 -52
  32. package/src/workers/dimensionality-reducer.ts +8 -13
  33. package/{test-Peptides-f8114def7953-4bf59d70.html → test-Peptides-69a4761f6044-40ac3a0c.html} +2 -2
  34. package/src/peptides.ts +0 -327
  35. package/src/semantics.ts +0 -5
  36. package/src/tests/peptides-tests.ts +0 -60
  37. package/src/utils/SAR-multiple-filter.ts +0 -439
  38. package/src/utils/SAR-multiple-selection.ts +0 -177
  39. package/src/viewers/logo-viewer.ts +0 -195
package/src/model.ts CHANGED
@@ -4,7 +4,6 @@ import * as DG from 'datagrok-api/dg';
4
4
 
5
5
  import {Subject, Observable} from 'rxjs';
6
6
  import {addViewerToHeader, StackedBarChart} from './viewers/stacked-barchart-viewer';
7
- import {PeptidesController} from './peptides';
8
7
  import {tTest} from '@datagrok-libraries/statistics/src/tests';
9
8
  import {fdrcorrection} from '@datagrok-libraries/statistics/src/multiple-tests';
10
9
  import {ChemPalette} from './utils/chem-palette';
@@ -12,22 +11,28 @@ import {MonomerLibrary} from './monomer-library';
12
11
  import * as C from './utils/constants';
13
12
  import * as type from './utils/types';
14
13
  import {FilteringStatistics} from './utils/filtering-statistics';
14
+ import {getSeparator, getTypedArrayConstructor, stringToBool} from './utils/misc';
15
+ import {_package} from './package';
16
+ import {SARViewer, SARViewerVertical} from './viewers/sar-viewer';
17
+ import {PeptideSpaceViewer} from './viewers/peptide-space-viewer';
18
+ import {setAARRenderer} from './utils/cell-renderer';
19
+ import {substitutionsWidget} from './widgets/subst-table';
20
+ import {getDistributionWidget} from './widgets/distribution';
15
21
 
16
22
  export class PeptidesModel {
17
23
  static _modelName = 'peptidesModel';
18
-
24
+
19
25
  _statsDataFrameSubject = new Subject<DG.DataFrame>();
20
26
  _sarGridSubject = new Subject<DG.Grid>();
21
27
  _sarVGridSubject = new Subject<DG.Grid>();
22
- // _groupMappingSubject = new Subject<StringDictionary>();
23
- _substitutionTableSubject = new Subject<DG.DataFrame>();
28
+ _substitutionTableSubject = new Subject<type.SubstitutionsInfo>();
24
29
 
25
30
  _isUpdating: boolean = false;
26
31
  _isSubstInitialized = false;
27
32
  isBitsetChangedInitialized = false;
33
+ isCellChanging = false;
28
34
 
29
35
  //viewer properties
30
- // _grouping!: boolean;
31
36
  _filterMode!: boolean;
32
37
  _twoColorMode!: boolean;
33
38
  _activityScaling!: string;
@@ -39,79 +44,107 @@ export class PeptidesModel {
39
44
  _sarVGrid!: DG.Grid;
40
45
  _sourceGrid!: DG.Grid;
41
46
  _dataFrame: DG.DataFrame;
42
- _substitutionTable!: DG.DataFrame;
43
- splitCol!: DG.Column;
47
+ splitCol!: DG.Column<boolean>;
44
48
  stackedBarchart!: StackedBarChart;
49
+ edf: DG.DataFrame | null = null;
50
+
51
+ substitutionsInfo: type.SubstitutionsInfo = new Map();
52
+ isInitialized: boolean = false;
53
+ currentView!: DG.TableView;
45
54
 
46
- // _substTableTooltipData!: { [aar: string]: number[][][]; };
47
- _casesTable!: type.SubstitutionCases;
48
- _substTableTooltipData!: type.SubstitutionTooltips;
55
+ _currentSelection!: type.SelectionObject;
56
+ isPeptideSpaceChangingBitset: boolean = false;
57
+ isChangingEdfBitset: boolean = false;
49
58
 
50
59
  private constructor(dataFrame: DG.DataFrame) {
51
60
  this._dataFrame = dataFrame;
52
- this._dataFrame.temp[C.PEPTIDES_ANALYSIS] = true;
53
-
54
61
  this.updateProperties();
55
62
  }
56
63
 
57
- static getInstance(dataFrame: DG.DataFrame): PeptidesModel {
64
+ static async getInstance(dataFrame: DG.DataFrame, dgPackage?: DG.Package): Promise<PeptidesModel> {
65
+ if (dataFrame.temp[MonomerLibrary.id] === null) {
66
+ const sdf = await (dgPackage ?? _package).files.readAsText('HELMMonomers_June10.sdf');
67
+ dataFrame.temp[MonomerLibrary.id] ??= new MonomerLibrary(sdf);
68
+ }
58
69
  dataFrame.temp[PeptidesModel.modelName] ??= new PeptidesModel(dataFrame);
59
- return dataFrame.temp[PeptidesModel.modelName];
70
+ await (dataFrame.temp[PeptidesModel.modelName] as PeptidesModel).init();
71
+ return dataFrame.temp[PeptidesModel.modelName] as PeptidesModel;
72
+ }
73
+
74
+ static get modelName(): string {return PeptidesModel._modelName;}
75
+
76
+ static get chemPalette(): typeof ChemPalette {return ChemPalette;}
77
+
78
+ get onStatsDataFrameChanged(): Observable<DG.DataFrame> {return this._statsDataFrameSubject.asObservable();}
79
+
80
+ get onSARGridChanged(): Observable<DG.Grid> {return this._sarGridSubject.asObservable();}
81
+
82
+ get onSARVGridChanged(): Observable<DG.Grid> {return this._sarVGridSubject.asObservable();}
83
+
84
+ get onSubstTableChanged(): Observable<type.SubstitutionsInfo> {return this._substitutionTableSubject.asObservable();}
85
+
86
+ get currentSelection(): type.SelectionObject {
87
+ this._currentSelection ??= JSON.parse(this._dataFrame.tags[C.TAGS.SELECTION] || '{}');
88
+ return this._currentSelection;
89
+ }
90
+ set currentSelection(selection: type.SelectionObject) {
91
+ this._currentSelection = selection;
92
+ this._dataFrame.tags[C.TAGS.SELECTION] = JSON.stringify(selection);
93
+ this.invalidateSelection();
94
+ }
95
+
96
+ invalidateSelection(): void {
97
+ this.fireBitsetChanged();
98
+ this.invalidateGrids();
60
99
  }
61
100
 
62
- updateProperties() {
101
+ createAccordion() {
102
+ const acc = ui.accordion('Selection info');
103
+ acc.root.style.width = '100%';
104
+ acc.addTitle(ui.h1('Selection info'));
105
+ acc.addPane('Substitutions', () => substitutionsWidget(this._dataFrame, this).root, true);
106
+ acc.addPane('Distribtution', () => getDistributionWidget(this._dataFrame).root, true);
107
+
108
+ return acc;
109
+ }
110
+
111
+ updateProperties(): void {
63
112
  this._activityScaling = this._dataFrame.tags['scaling'];
64
- this._filterMode = this.stringToBool(this._dataFrame.tags['filterMode']);
65
- this._twoColorMode = this.stringToBool(this._dataFrame.tags['bidirectionalAnalysis']);
66
- // this._grouping = this.stringToBool(this._dataFrame.tags['grouping']);
67
- this._isSubstitutionOn = this.stringToBool(this._dataFrame.tags['showSubstitution']);
113
+ this._filterMode = stringToBool(this._dataFrame.tags['filterMode']);
114
+ this._twoColorMode = stringToBool(this._dataFrame.tags['bidirectionalAnalysis']);
115
+ this._isSubstitutionOn = stringToBool(this._dataFrame.tags['showSubstitution']);
68
116
  this._maxSubstitutions = parseInt(this._dataFrame.tags['maxSubstitutions']);
69
117
  this._activityLimit = parseFloat(this._dataFrame.tags['activityLimit']);
70
118
  }
71
119
 
72
- stringToBool(str: string) {
73
- return str === 'true' ? true : false;
74
- }
75
-
76
120
  setProperties(
77
121
  activityScaling: string, filterMode: boolean, twoColorMode: boolean, isSubstitutionOn: boolean,
78
122
  maxSubstitutions: number, activityLimit: number, forceUpdate = false,
79
- ) {
80
- const chooseAction = (value: string, defaultValue: any) => forceUpdate ? value : defaultValue ?? value;
123
+ ): void {
124
+ const chooseAction =
125
+ (value: string, defaultValue: string | boolean | number): string | boolean | number =>
126
+ forceUpdate ? value : defaultValue ?? value;
81
127
  this._dataFrame.tags['scaling'] = chooseAction(`${activityScaling}`, this._dataFrame.tags['scaling']);
82
128
  this._dataFrame.tags['filterMode'] = chooseAction(`${filterMode}`, this._dataFrame.tags['filterMode']);
83
- this._dataFrame.tags['bidirectionalAnalysis'] = chooseAction(`${twoColorMode}`, this._dataFrame.tags['bidirectionalAnalysis']);
84
- // this._dataFrame.tags['grouping'] = chooseAction(`${grouping}`, this._dataFrame.tags['grouping']);
85
- this._dataFrame.tags['showSubstitution'] = chooseAction(`${isSubstitutionOn}`, this._dataFrame.tags['showSubstitution']);
86
- this._dataFrame.tags['maxSubstitutions'] = chooseAction(`${maxSubstitutions}`, this._dataFrame.tags['maxSubstitutions']);
129
+ this._dataFrame.tags['bidirectionalAnalysis'] =
130
+ chooseAction(`${twoColorMode}`, this._dataFrame.tags['bidirectionalAnalysis']);
131
+ this._dataFrame.tags['showSubstitution'] =
132
+ chooseAction(`${isSubstitutionOn}`, this._dataFrame.tags['showSubstitution']);
133
+ this._dataFrame.tags['maxSubstitutions'] =
134
+ chooseAction(`${maxSubstitutions}`, this._dataFrame.tags['maxSubstitutions']);
87
135
  this._dataFrame.tags['activityLimit'] = chooseAction(`${activityLimit}`, this._dataFrame.tags['activityLimit']);
88
-
136
+
89
137
  this.updateProperties();
90
138
  }
91
139
 
92
- get dataFrame(): DG.DataFrame {return this._dataFrame;}
93
-
94
- get onStatsDataFrameChanged(): Observable<DG.DataFrame> {return this._statsDataFrameSubject.asObservable();}
95
-
96
- get onSARGridChanged(): Observable<DG.Grid> {return this._sarGridSubject.asObservable();}
97
-
98
- get onSARVGridChanged(): Observable<DG.Grid> {return this._sarVGridSubject.asObservable();}
99
-
100
- // get onGroupMappingChanged(): Observable<StringDictionary> {return this._groupMappingSubject.asObservable();}
101
-
102
- get onSubstTableChanged(): Observable<DG.DataFrame> {return this._substitutionTableSubject.asObservable();}
103
-
104
- get substTooltipData(): type.SubstitutionTooltips {return this._substTableTooltipData!;}
105
-
106
140
  async updateData(
107
- activityScaling?: string, sourceGrid?: DG.Grid, twoColorMode?: boolean, activityLimit?: number,
141
+ activityScaling?: string, sourceGrid?: DG.Grid, twoColorMode?: boolean, activityLimit?: number,
108
142
  maxSubstitutions?: number, isSubstitutionOn?: boolean, filterMode?: boolean,
109
- ) {
143
+ ): Promise<void> {
110
144
  //FIXME: threre are too many assignments, some are duplicating
111
145
  this._activityScaling = activityScaling ?? this._activityScaling;
112
146
  this._sourceGrid = sourceGrid ?? this._sourceGrid;
113
147
  this._twoColorMode = twoColorMode ?? this._twoColorMode;
114
- // this._grouping = grouping ?? this._grouping;
115
148
  this._activityLimit = activityLimit ?? this._activityLimit;
116
149
  this._maxSubstitutions = maxSubstitutions ?? this._maxSubstitutions;
117
150
  this._isSubstitutionOn = isSubstitutionOn ?? this._isSubstitutionOn;
@@ -122,81 +155,68 @@ export class PeptidesModel {
122
155
  await this.updateDefault();
123
156
  }
124
157
 
125
- async updateDefault() {
158
+ async updateDefault(): Promise<void> {
126
159
  if (this._activityScaling && this._sourceGrid && this._twoColorMode !== null && !this._isUpdating) {
127
160
  this._isUpdating = true;
128
- const [viewerGrid, viewerVGrid, statsDf, substTable] = await this.initializeViewersComponents();
161
+ const [viewerGrid, viewerVGrid, statsDf] = await this.initializeViewersComponents();
129
162
  //FIXME: modify during the initializeViewersComponents stages
130
163
  this._statsDataFrameSubject.next(statsDf);
131
- // this._groupMappingSubject.next(groupMapping);
132
164
  this._sarGridSubject.next(viewerGrid);
133
165
  this._sarVGridSubject.next(viewerVGrid);
134
166
  if (this._isSubstitutionOn) {
135
- this._substitutionTableSubject.next(substTable);
167
+ this._substitutionTableSubject.next(this.substitutionsInfo);
136
168
  this._isSubstInitialized = true;
137
169
  }
138
170
  }
139
171
  await this.updateBarchart();
140
- this.invalidateGrids();
172
+ this.invalidateSelection();
141
173
 
142
174
  this._isUpdating = false;
143
175
  }
144
176
 
145
- async updateBarchart() {
177
+ async updateBarchart(): Promise<void> {
146
178
  this.stackedBarchart ??= await this._dataFrame?.plot.fromType('StackedBarChartAA') as StackedBarChart;
147
179
  if (this.stackedBarchart && this._sourceGrid)
148
180
  addViewerToHeader(this._sourceGrid, this.stackedBarchart);
149
181
  }
150
182
 
151
- static get modelName() { return PeptidesModel._modelName; }
152
-
153
- async initializeViewersComponents(): Promise<[DG.Grid, DG.Grid, DG.DataFrame, DG.DataFrame]> {
183
+ async initializeViewersComponents(): Promise<[DG.Grid, DG.Grid, DG.DataFrame]> {
154
184
  if (this._sourceGrid === null)
155
185
  throw new Error(`Source grid is not initialized`);
156
186
 
157
187
  //Split the aligned sequence into separate AARs
158
188
  let splitSeqDf: DG.DataFrame | undefined;
159
189
  let invalidIndexes: number[];
160
- const col: DG.Column = (this._dataFrame.columns as DG.ColumnList).bySemType(C.SEM_TYPES.ALIGNED_SEQUENCE)!;
161
- [splitSeqDf, invalidIndexes] = PeptidesController.splitAlignedPeptides(col);
190
+ const col: DG.Column = this._dataFrame.columns.bySemType(C.SEM_TYPES.ALIGNED_SEQUENCE)!;
191
+ [splitSeqDf, invalidIndexes] = PeptidesModel.splitAlignedPeptides(col);
162
192
 
163
- const positionColumns = (splitSeqDf.columns as DG.ColumnList).names();
164
- const renderColNames: string[] = (splitSeqDf.columns as DG.ColumnList).names();
193
+ const positionColumns = splitSeqDf.columns.names();
194
+ const renderColNames: string[] = splitSeqDf.columns.names();
165
195
 
166
- (splitSeqDf.columns as DG.ColumnList).add(this._dataFrame.getCol(C.COLUMNS_NAMES.ACTIVITY));
196
+ const activityCol = this._dataFrame.columns.bySemType(C.SEM_TYPES.ACTIVITY)!;
197
+ splitSeqDf.columns.add(activityCol);
167
198
 
168
- this.joinDataFrames(this._dataFrame, positionColumns, splitSeqDf);
199
+ this.joinDataFrames(positionColumns, splitSeqDf);
169
200
 
170
- for (const dfCol of (this._dataFrame.columns as DG.ColumnList)) {
171
- if (splitSeqDf.col(dfCol.name) && dfCol.name != C.COLUMNS_NAMES.ACTIVITY)
172
- PeptidesController.setAARRenderer(dfCol, this._sourceGrid);
201
+ for (const dfCol of this._dataFrame.columns) {
202
+ if (positionColumns.includes(dfCol.name))
203
+ setAARRenderer(dfCol, this._sourceGrid);
173
204
  }
174
205
 
175
206
  this.sortSourceGrid(this._sourceGrid);
176
207
 
177
- await this.createScaledCol(this._activityScaling!, this._dataFrame, this._sourceGrid, splitSeqDf);
208
+ await this.createScaledCol(this._activityScaling, this._dataFrame, this._sourceGrid, splitSeqDf);
178
209
 
179
210
  //unpivot a table and handle duplicates
180
211
  splitSeqDf = splitSeqDf.groupBy(positionColumns)
181
212
  .add('med', C.COLUMNS_NAMES.ACTIVITY_SCALED, C.COLUMNS_NAMES.ACTIVITY_SCALED)
182
213
  .aggregate();
183
214
 
184
- const peptidesCount = splitSeqDf.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).length;
215
+ const peptidesCount = splitSeqDf.rowCount;
185
216
 
186
217
  let matrixDf = splitSeqDf.unpivot(
187
218
  [C.COLUMNS_NAMES.ACTIVITY_SCALED], positionColumns, C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.AMINO_ACID_RESIDUE);
188
219
 
189
- //TODO: move to chem palette
190
- // let groupMapping: StringDictionary = {};
191
- // if (this._grouping) {
192
- // groupMapping = C.aarGroups;
193
- // const aarCol = matrixDf.getCol(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE);
194
- // aarCol.init((index) => groupMapping[aarCol.get(index)[0]] ?? '-');
195
- // aarCol.compact();
196
- // } else
197
- // Object.keys(C.aarGroups).forEach((value) => groupMapping[value] = value);
198
-
199
-
200
220
  //FIXME: for some reason Mean difference is not calculated for all the AARs
201
221
  //statistics for specific AAR at a specific position
202
222
  const statsDf = await this.calculateStatistics(matrixDf, peptidesCount, splitSeqDf);
@@ -210,15 +230,15 @@ export class PeptidesModel {
210
230
  matrixDf.name = 'SAR';
211
231
 
212
232
  // Setting category order
213
- await this.setCategoryOrder(this._twoColorMode!, statsDf, matrixDf);
233
+ await this.setCategoryOrder(this._twoColorMode, statsDf, matrixDf);
214
234
 
215
235
  // SAR vertical table (naive, choose best Mean difference from pVals <= 0.01)
216
- const sequenceDf = this.createVerticalTable(statsDf, this._twoColorMode!);
236
+ const sequenceDf = this.createVerticalTable(statsDf, this._twoColorMode);
217
237
  renderColNames.push(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
218
238
 
219
- let substTable: DG.DataFrame | null = null;
239
+
220
240
  if (this._isSubstitutionOn || !this._isSubstInitialized)
221
- substTable = this.calcSubstitutions();
241
+ this.calcSubstitutions();
222
242
 
223
243
  //TODO: move everything below out to controller
224
244
  const [sarGrid, sarVGrid] = this.createGrids(matrixDf, positionColumns, sequenceDf);
@@ -234,165 +254,112 @@ export class PeptidesModel {
234
254
 
235
255
  this.setInteractionCallback();
236
256
 
237
- this.modifyOrCreateSplitCol(C.CATEGORIES.ALL, C.CATEGORIES.ALL);
238
-
239
257
  this.setBitsetCallback();
240
258
 
241
259
  this.postProcessGrids(this._sourceGrid, invalidIndexes, sarGrid, sarVGrid);
242
260
 
243
- if (this.dataFrame.tags[C.TAGS.AAR] && this.dataFrame.tags[C.TAGS.POSITION]) {
244
- const sarDf = sarGrid.dataFrame;
245
- const rowCount = sarDf.rowCount;
246
- let index = -1;
247
- for (let i = 0; i < rowCount; i++) {
248
- if (sarDf.get(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE, i) === this.dataFrame.tags[C.TAGS.AAR]) {
249
- index = i;
250
- break;
251
- }
252
- }
253
- sarDf.currentCell = sarDf.cell(index, this.dataFrame.tags[C.TAGS.POSITION]);
254
- }
255
-
256
261
  //TODO: return class instead
257
- return [sarGrid, sarVGrid, statsDf, substTable!];
262
+ return [sarGrid, sarVGrid, statsDf];
258
263
  }
259
264
 
260
- calcSubstitutions() {
261
- // const col: DG.Column = this.dataFrame.columns.bySemType(C.SEM_TYPES.ALIGNED_SEQUENCE);
262
- // const values: number[] = this.dataFrame.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).toList();
263
- const activityValues = this.dataFrame.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
264
- // const splitedMatrix = this.split(col);
265
- const columnList = (this.dataFrame.columns as DG.ColumnList).toList()
266
- .filter(col => col.semType === C.SEM_TYPES.AMINO_ACIDS);
267
- if (columnList.length === 0)
268
- throw new Error(`Couldn't find any column of semType '${C.SEM_TYPES.AMINO_ACIDS}'`);
269
-
270
- const tableValues: { [aar: string]: number[]; } = {};
271
- const tableTooltips: { [aar: string]: {}[][]; } = {};
272
- const tableCases: { [aar: string]: number[][][]; } = {};
273
-
274
- // const nRows = splitedMatrix.length;
275
- // const nCols = splitedMatrix[0].length;
276
- // const nColsArray = Array(nCols);
265
+ //TODO: move to controller?
266
+ calcSubstitutions(): void {
267
+ const activityValues: DG.Column<number> = this._dataFrame.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
268
+ const columnList: DG.Column<string>[] = this._dataFrame.columns.bySemTypeAll(C.SEM_TYPES.AMINO_ACIDS);
277
269
  const nCols = columnList.length;
278
- const nRows = this.dataFrame.rowCount;
270
+ if (nCols == 0)
271
+ throw new Error(`Couldn't find any column of semType '${C.SEM_TYPES.AMINO_ACIDS}'`);
279
272
 
280
- //TODO: this looks **very** expensive
281
- for (let i = 0; i < nRows - 1; i++) {
282
- for (let j = i + 1; j < nRows; j++) {
273
+ this.substitutionsInfo = new Map();
274
+ const nRows = this._dataFrame.rowCount;
275
+ for (let seq1Idx = 0; seq1Idx < nRows - 1; seq1Idx++) {
276
+ for (let seq2Idx = seq1Idx + 1; seq2Idx < nRows; seq2Idx++) {
283
277
  let substCounter = 0;
284
- const subst1: { [pos: number]: [string, {key: string, value: string, diff: number}] } = {};
285
- const subst2: { [pos: number]: [string, {key: string, value: string, diff: number}] } = {};
286
- const activityValI = activityValues.get(i) as number;
287
- const activityValJ = activityValues.get(j) as number;
288
- const strActivityValI = activityValI.toFixed(2);
289
- const strActivityValJ = activityValJ.toFixed(2);
290
- const delta = activityValI - activityValJ;
291
-
278
+ const activityValSeq1 = activityValues.get(seq1Idx);
279
+ const activityValSeq2 = activityValues.get(seq2Idx);
280
+ const delta = activityValSeq1 - activityValSeq2;
292
281
  if (Math.abs(delta) < this._activityLimit)
293
282
  continue;
294
283
 
295
- for (let k = 0; k < nCols; k++) {
296
- // const smik = splitedMatrix[i][k];
297
- // const smjk = splitedMatrix[j][k];
298
- const smik = columnList[k].get(i) as string;
299
- const smjk = columnList[k].get(j) as string;
300
-
301
- if (smik === smjk)
284
+ let substCounterFlag = false;
285
+ const tempData: {pos: string, seq1monomer: string, seq2monomer: string, seq1Idx: number, seq2Idx: number}[] =
286
+ [];
287
+ for (const currentPosCol of columnList) {
288
+ const seq1monomer = currentPosCol.get(seq1Idx);
289
+ const seq2monomer = currentPosCol.get(seq2Idx);
290
+ if (seq1monomer == seq2monomer)
302
291
  continue;
303
292
 
304
293
  substCounter++;
305
- subst1[k] = [
306
- smik,
307
- {
308
- key: `${smik === '-' ? 'Empty' : smik} → ${smjk === '-' ? 'Empty' : smjk}`,
309
- value: `${strActivityValI} → ${strActivityValJ}`,
310
- diff: -delta,
311
- },
312
- ];
313
- subst2[k] = [
314
- smjk,
315
- {
316
- key: `${smjk === '-' ? 'Empty' : smjk} → ${smik === '-' ? 'Empty' : smik}`,
317
- value: `${strActivityValJ} → ${strActivityValI}`,
318
- diff: delta,
319
- },
320
- ];
294
+ substCounterFlag = substCounter > this._maxSubstitutions;
295
+ if (substCounterFlag)
296
+ break;
297
+
298
+ tempData.push({
299
+ pos: currentPosCol.name,
300
+ seq1monomer: seq1monomer,
301
+ seq2monomer: seq2monomer,
302
+ seq1Idx: seq1Idx,
303
+ seq2Idx: seq2Idx,
304
+ });
321
305
  }
322
306
 
323
- if (substCounter > this._maxSubstitutions || substCounter === 0)
307
+ if (substCounterFlag || substCounter == 0)
324
308
  continue;
325
309
 
326
- for (const subst of [subst1, subst2]) {
327
- Object.keys(subst).forEach((pos) => {
328
- const posInt = parseInt(pos);
329
- const aar = subst[posInt][0];
330
- if (!Object.keys(tableValues).includes(aar)) {
331
- // tableValues[aar] = Array(...nColsArray).map(() => DG.INT_NULL);
332
- // tableTooltips[aar] = Array(...nColsArray).map(() => []);
333
- // tableCases[aar] = Array(...nColsArray).map(() => []);
334
- tableValues[aar] = Array(nCols).fill(DG.INT_NULL);
335
- tableTooltips[aar] = Array(nCols).fill([]);
336
- tableCases[aar] = Array(nCols).fill([]);
337
- }
338
-
339
- tableValues[aar][posInt] = tableValues[aar][posInt] === DG.INT_NULL ? 1 : tableValues[aar][posInt] + 1;
340
- tableTooltips[aar][posInt] = !tableTooltips[aar][posInt].length ?
341
- [{key: 'Substitution', value: 'Values'}] : tableTooltips[aar][posInt];
342
- tableTooltips[aar][posInt].push(subst[posInt][1]);
343
- tableCases[aar][posInt].push([i, j, subst == subst1 ? delta : -delta]);
344
- });
345
- }
346
- }
347
- }
310
+ for (const tempDataElement of tempData) {
311
+ const position = tempDataElement.pos;
348
312
 
349
- const tableValuesKeys = Object.keys(tableValues);
350
- const dfLength = tableValuesKeys.length;
351
- const cols = columnList.map(col => {
352
- const newCol = DG.Column.int(`${col.name}`, dfLength);
353
- newCol.semType = 'Substitution';
354
- return newCol;
355
- });
356
- const aarCol = DG.Column.string(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE, dfLength);
357
- cols.splice(0, 0, aarCol);
358
- const table = DG.DataFrame.fromColumns(cols);
359
-
360
- for (let i = 0; i < dfLength; i++) {
361
- const aar = tableValuesKeys[i];
362
- // tableValues[aar].splice(0, 1);
363
- table.rows.setValues(i, [aar, ...tableValues[aar]]);
364
- }
313
+ //Working with seq1monomer
314
+ const seq1monomer = tempDataElement.seq1monomer;
315
+ if (!this.substitutionsInfo.has(seq1monomer))
316
+ this.substitutionsInfo.set(seq1monomer, new Map());
365
317
 
366
- // let groupMapping: { [key: string]: string } = {};
318
+ let positionsMap = this.substitutionsInfo.get(seq1monomer)!;
319
+ if (!positionsMap.has(position))
320
+ positionsMap.set(position, new Map());
367
321
 
368
- //TODO: enable grouping
369
- // Object.keys(aarGroups).forEach((value) => groupMapping[value] = value);
370
- this._substTableTooltipData = tableTooltips;
371
- this._casesTable = tableCases;
372
- this.substitutionTable = table;
322
+ let indexes = positionsMap.get(position)!;
373
323
 
374
- return table;
375
- }
324
+ !indexes.has(seq1Idx) ? indexes.set(seq1Idx, [seq2Idx]) : (indexes.get(seq1Idx)! as number[]).push(seq2Idx);
325
+
326
+ //Working with seq2monomer
327
+ const seq2monomer = tempDataElement.seq2monomer;
328
+ if (!this.substitutionsInfo.has(seq2monomer))
329
+ this.substitutionsInfo.set(seq2monomer, new Map());
330
+
331
+ positionsMap = this.substitutionsInfo.get(seq2monomer)!;
332
+ if (!positionsMap.has(position))
333
+ positionsMap.set(position, new Map());
334
+
335
+ indexes = positionsMap.get(position)!;
336
+ !indexes.has(seq2Idx) ? indexes.set(seq2Idx, [seq1Idx]) : (indexes.get(seq2Idx)! as number[]).push(seq1Idx);
337
+ }
338
+ }
339
+ }
376
340
 
377
- get substitutionTable() { return this._substitutionTable; }
378
- set substitutionTable(table: DG.DataFrame) {
379
- if (!table)
380
- throw new Error(`Substitution table cannot be set to null`);
381
- this._substitutionTable = table;
382
- this._substitutionTableSubject.next(table);
341
+ const TypedArray = getTypedArrayConstructor(nRows);
342
+ for (const positionMap of this.substitutionsInfo.values()) {
343
+ for (const indexMap of positionMap.values()) {
344
+ for (const [index, indexArray] of indexMap.entries())
345
+ indexMap.set(index, new TypedArray(indexArray));
346
+ }
347
+ }
383
348
  }
384
349
 
385
- joinDataFrames(df: DG.DataFrame, positionColumns: string[], splitSeqDf: DG.DataFrame) {
350
+ joinDataFrames(positionColumns: string[], splitSeqDf: DG.DataFrame): void {
386
351
  // append splitSeqDf columns to source table and make sure columns are not added more than once
387
- const dfColsSet = new Set((df.columns as DG.ColumnList).names());
352
+ const name = this._dataFrame.name;
353
+ const dfColsSet = new Set(this._dataFrame.columns.names());
388
354
  if (!positionColumns.every((col: string) => dfColsSet.has(col))) {
389
- df.join(
390
- splitSeqDf, [C.COLUMNS_NAMES.ACTIVITY], [C.COLUMNS_NAMES.ACTIVITY], (df.columns as DG.ColumnList).names(),
391
- positionColumns, 'inner', true);
355
+ this._dataFrame.join(splitSeqDf, [C.COLUMNS_NAMES.ACTIVITY], [C.COLUMNS_NAMES.ACTIVITY],
356
+ this._dataFrame.columns.names(), positionColumns, 'inner', true);
392
357
  }
358
+ this._dataFrame.name = name;
359
+ this.currentView.name = name;
393
360
  }
394
361
 
395
- sortSourceGrid(sourceGrid: DG.Grid) {
362
+ sortSourceGrid(sourceGrid: DG.Grid): void {
396
363
  if (sourceGrid) {
397
364
  const colNames: DG.GridColumn[] = [];
398
365
  for (let i = 1; i < sourceGrid.columns.length; i++)
@@ -412,54 +379,57 @@ export class PeptidesModel {
412
379
  }
413
380
  }
414
381
 
382
+ //TODO: move out, merge with controller's scaleActivity
415
383
  async createScaledCol(
416
384
  activityScaling: string, df: DG.DataFrame, sourceGrid: DG.Grid, splitSeqDf: DG.DataFrame,
417
- ) {
418
- const [scaledDf, newColName] = await PeptidesController.scaleActivity(
419
- activityScaling, df, df.temp[C.COLUMNS_NAMES.ACTIVITY]);
385
+ ): Promise<void> {
386
+ const [scaledDf, newColName] = await PeptidesModel.scaleActivity(
387
+ activityScaling, df, df.tags[C.COLUMNS_NAMES.ACTIVITY]);
420
388
  //TODO: make another func
421
389
  const scaledCol = scaledDf.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
422
- (splitSeqDf.columns as DG.ColumnList).add(scaledCol);
390
+ scaledCol.semType = C.SEM_TYPES.ACTIVITY_SCALED;
391
+ splitSeqDf.columns.add(scaledCol);
423
392
  const oldScaledCol = df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
424
- (df.columns as DG.ColumnList).replace(oldScaledCol, scaledCol);
393
+ df.columns.replace(oldScaledCol, scaledCol);
425
394
  const gridCol = sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED);
426
395
  if (gridCol !== null) {
427
396
  gridCol.name = newColName;
428
- df.temp[C.COLUMNS_NAMES.ACTIVITY_SCALED] = newColName;
397
+ df.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED] = newColName;
429
398
  }
430
399
 
431
400
  sourceGrid.columns.setOrder([newColName]);
432
401
  }
433
402
 
434
- async calculateStatistics(matrixDf: DG.DataFrame, peptidesCount: number, splitSeqDf: DG.DataFrame) {
403
+ //TODO: move out
404
+ async calculateStatistics(
405
+ matrixDf: DG.DataFrame, peptidesCount: number, splitSeqDf: DG.DataFrame): Promise<DG.DataFrame> {
435
406
  matrixDf = matrixDf.groupBy([C.COLUMNS_NAMES.POSITION, C.COLUMNS_NAMES.AMINO_ACID_RESIDUE])
436
407
  .add('count', C.COLUMNS_NAMES.ACTIVITY_SCALED, 'Count')
437
408
  .aggregate();
438
409
 
439
410
  const countThreshold = 4;
440
- //@ts-ignore: never gets old
441
411
  matrixDf.rows.filter((row) => row.Count >= countThreshold && row.Count <= peptidesCount - countThreshold);
442
412
  matrixDf = matrixDf.clone(matrixDf.filter);
443
413
 
444
414
  // calculate additional stats
445
- await (matrixDf.columns as DG.ColumnList).addNewCalculated('Ratio', '${count}/'.concat(`${peptidesCount}`));
415
+ await matrixDf.columns.addNewCalculated('Ratio', '${count}/'.concat(`${peptidesCount}`));
446
416
 
447
417
  //calculate p-values based on t-test
448
418
  let pvalues: Float32Array = new Float32Array(matrixDf.rowCount).fill(1);
449
- const mdCol: DG.Column = (matrixDf.columns as DG.ColumnList).addNewFloat(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
450
- const pValCol: DG.Column = (matrixDf.columns as DG.ColumnList).addNewFloat(C.COLUMNS_NAMES.P_VALUE);
419
+ const mdCol: DG.Column = matrixDf.columns.addNewFloat(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
420
+ const pValCol: DG.Column = matrixDf.columns.addNewFloat(C.COLUMNS_NAMES.P_VALUE);
421
+ const aarCol = matrixDf.getCol(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE);
422
+ const posCol = matrixDf.getCol(C.COLUMNS_NAMES.POSITION);
451
423
  for (let i = 0; i < matrixDf.rowCount; i++) {
452
- const position = matrixDf.get(C.COLUMNS_NAMES.POSITION, i);
453
- const aar = matrixDf.get(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE, i);
424
+ const position = posCol.get(i);
425
+ const aar = aarCol.get(i);
454
426
 
455
- //@ts-ignore
456
427
  splitSeqDf.rows.select((row) => row[position] === aar);
457
428
  const currentActivity: number[] = splitSeqDf
458
429
  .clone(splitSeqDf.selection, [C.COLUMNS_NAMES.ACTIVITY_SCALED])
459
430
  .getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED)
460
431
  .toList();
461
432
 
462
- //@ts-ignore
463
433
  splitSeqDf.rows.select((row) => row[position] !== aar);
464
434
  const otherActivity: number[] = splitSeqDf
465
435
  .clone(splitSeqDf.selection, [C.COLUMNS_NAMES.ACTIVITY_SCALED])
@@ -467,7 +437,6 @@ export class PeptidesModel {
467
437
  .toList();
468
438
 
469
439
  const testResult = tTest(currentActivity, otherActivity);
470
- // testResult = uTest(currentActivity, otherActivity);
471
440
  const currentMeanDiff = testResult[C.COLUMNS_NAMES.MEAN_DIFFERENCE]!;
472
441
  const pvalue = testResult[currentMeanDiff >= 0 ? 'p-value more' : 'p-value less'];
473
442
 
@@ -483,25 +452,25 @@ export class PeptidesModel {
483
452
  return matrixDf.clone();
484
453
  }
485
454
 
486
- async setCategoryOrder(twoColorMode: boolean, statsDf: DG.DataFrame, matrixDf: DG.DataFrame) {
455
+ async setCategoryOrder(twoColorMode: boolean, statsDf: DG.DataFrame, matrixDf: DG.DataFrame): Promise<void> {
487
456
  const absMD = 'Absolute Mean difference';
488
457
  const sortArgument = twoColorMode ? absMD : C.COLUMNS_NAMES.MEAN_DIFFERENCE;
489
458
  if (twoColorMode)
490
- await (statsDf.columns as DG.ColumnList).addNewCalculated(absMD, 'Abs(${Mean difference})');
459
+ await statsDf.columns.addNewCalculated(absMD, 'Abs(${Mean difference})');
491
460
 
492
461
  const aarWeightsDf = statsDf.groupBy([C.COLUMNS_NAMES.AMINO_ACID_RESIDUE]).sum(sortArgument, 'weight').aggregate();
493
462
  const aarList = aarWeightsDf.getCol(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE).toList();
494
- const getWeight = (aar: string) => aarWeightsDf
463
+ const getWeight = (aar: string): number => aarWeightsDf
495
464
  .groupBy(['weight'])
496
465
  .where(`${C.COLUMNS_NAMES.AMINO_ACID_RESIDUE} = ${aar}`)
497
466
  .aggregate()
498
- .get('weight', 0);
467
+ .get('weight', 0) as number;
499
468
  aarList.sort((first, second) => getWeight(second) - getWeight(first));
500
469
 
501
470
  matrixDf.getCol(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE).setCategoryOrder(aarList);
502
471
  }
503
472
 
504
- createVerticalTable(statsDf: DG.DataFrame, twoColorMode: boolean) {
473
+ createVerticalTable(statsDf: DG.DataFrame, twoColorMode: boolean): DG.DataFrame {
505
474
  // TODO: aquire ALL of the positions
506
475
  const columns = [C.COLUMNS_NAMES.MEAN_DIFFERENCE, C.COLUMNS_NAMES.AMINO_ACID_RESIDUE, C.COLUMNS_NAMES.POSITION,
507
476
  'Count', 'Ratio', C.COLUMNS_NAMES.P_VALUE];
@@ -511,47 +480,51 @@ export class PeptidesModel {
511
480
 
512
481
  let tempStats: DG.Stats;
513
482
  const maxAtPos: {[index: string]: number} = {};
514
- for (const pos of sequenceDf.getCol(C.COLUMNS_NAMES.POSITION).categories) {
515
- tempStats = DG.Stats.fromColumn(
516
- sequenceDf.getCol(C.COLUMNS_NAMES.MEAN_DIFFERENCE),
517
- DG.BitSet.create(sequenceDf.rowCount, (i) => sequenceDf.get(C.COLUMNS_NAMES.POSITION, i) === pos),
518
- );
483
+ const posColCategories = sequenceDf.getCol(C.COLUMNS_NAMES.POSITION).categories;
484
+ const mdCol = sequenceDf.getCol(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
485
+ const posCol = sequenceDf.getCol(C.COLUMNS_NAMES.POSITION);
486
+ const rowCount = sequenceDf.rowCount;
487
+ for (const pos of posColCategories) {
488
+ tempStats = DG.Stats.fromColumn(mdCol, DG.BitSet.create(rowCount, (i) => posCol.get(i) === pos));
519
489
  maxAtPos[pos] = twoColorMode ?
520
- (tempStats.max > Math.abs(tempStats.min) ? tempStats.max : tempStats.min) : tempStats.max;
490
+ (tempStats.max > Math.abs(tempStats.min) ? tempStats.max : tempStats.min) :
491
+ tempStats.max;
521
492
  }
522
- sequenceDf = sequenceDf.clone(DG.BitSet.create(sequenceDf.rowCount, (i) =>
523
- sequenceDf.get(C.COLUMNS_NAMES.MEAN_DIFFERENCE, i) === maxAtPos[sequenceDf.get(C.COLUMNS_NAMES.POSITION, i)]));
493
+ sequenceDf = sequenceDf.clone(DG.BitSet.create(rowCount, (i) => mdCol.get(i) === maxAtPos[posCol.get(i)]));
524
494
 
525
495
  return sequenceDf;
526
496
  }
527
497
 
528
- createGrids(matrixDf: DG.DataFrame, positionColumns: string[], sequenceDf: DG.DataFrame) {
498
+ createGrids(matrixDf: DG.DataFrame, positionColumns: string[], sequenceDf: DG.DataFrame): DG.Grid[] {
529
499
  const sarGrid = matrixDf.plot.grid();
530
500
  sarGrid.sort([C.COLUMNS_NAMES.AMINO_ACID_RESIDUE]);
531
501
  sarGrid.columns.setOrder([C.COLUMNS_NAMES.AMINO_ACID_RESIDUE].concat(positionColumns as C.COLUMNS_NAMES[]));
532
502
 
533
503
  const sarVGrid = sequenceDf.plot.grid();
534
504
  sarVGrid.sort([C.COLUMNS_NAMES.POSITION]);
535
- sarVGrid.col(C.COLUMNS_NAMES.P_VALUE)!.format = 'four digits after comma';
536
- sarVGrid.col(C.COLUMNS_NAMES.P_VALUE)!.name = 'P-Value';
505
+ const pValGridCol = sarVGrid.col(C.COLUMNS_NAMES.P_VALUE)!;
506
+ pValGridCol.format = '#.000';
507
+ pValGridCol.name = 'P-value';
537
508
 
538
- let tempCol = (matrixDf.columns as DG.ColumnList).byName(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE);
509
+ let tempCol = matrixDf.getCol(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE);
539
510
  if (tempCol)
540
- PeptidesController.setAARRenderer(tempCol, sarGrid);
511
+ setAARRenderer(tempCol, sarGrid);
541
512
 
542
- tempCol = (sequenceDf.columns as DG.ColumnList).byName(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE);
513
+ tempCol = sequenceDf.getCol(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE);
543
514
  if (tempCol)
544
- PeptidesController.setAARRenderer(tempCol, sarGrid);
515
+ setAARRenderer(tempCol, sarGrid);
545
516
 
546
517
  return [sarGrid, sarVGrid];
547
518
  }
548
519
 
520
+ //TODO: move out
549
521
  setCellRenderers(
550
522
  renderColNames: string[], statsDf: DG.DataFrame, twoColorMode: boolean, sarGrid: DG.Grid, sarVGrid: DG.Grid,
551
523
  isSubstitutionOn: boolean,
552
- ) {
524
+ ): void {
553
525
  const mdCol = statsDf.getCol(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
554
- const cellRendererAction = (args: DG.GridCellRenderArgs) => {
526
+ //decompose into two different renering funcs
527
+ const cellRendererAction = (args: DG.GridCellRenderArgs): void => {
555
528
  const canvasContext = args.g;
556
529
  const bound = args.bounds;
557
530
  const cell = args.cell;
@@ -574,12 +547,10 @@ export class PeptidesModel {
574
547
 
575
548
  if (cell.isTableCell && tableColName && tableRowIndex !== null && renderColNames.indexOf(tableColName) !== -1) {
576
549
  const gridTable = cell.grid.table;
577
- const currentPosition = tableColName !== C.COLUMNS_NAMES.MEAN_DIFFERENCE ?
550
+ const currentPosition: string = tableColName !== C.COLUMNS_NAMES.MEAN_DIFFERENCE ?
578
551
  tableColName : gridTable.get(C.COLUMNS_NAMES.POSITION, tableRowIndex);
579
- const currentAAR = gridTable.get(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE, tableRowIndex);
580
- if (currentAAR === 'Aib' && currentPosition === '02')
581
- console.log('stop');
582
-
552
+ const currentAAR: string = gridTable.get(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE, tableRowIndex);
553
+
583
554
  const queryAAR = `${C.COLUMNS_NAMES.AMINO_ACID_RESIDUE} = ${currentAAR}`;
584
555
  if (cellValue) {
585
556
  const query = `${queryAAR} and ${C.COLUMNS_NAMES.POSITION} = ${currentPosition}`;
@@ -601,9 +572,9 @@ export class PeptidesModel {
601
572
  coef = DG.Color.toHtml(DG.Color.lightLightGray);
602
573
 
603
574
 
604
- const chooseMin = () => twoColorMode ? 0 : mdCol.min;
605
- const chooseMax = () => twoColorMode ? Math.max(Math.abs(mdCol.min), mdCol.max) : mdCol.max;
606
- const chooseCurrent = () => twoColorMode ? Math.abs(cellValue) : cellValue;
575
+ const chooseMin = (): number => twoColorMode ? 0 : mdCol.min;
576
+ const chooseMax = (): number => twoColorMode ? Math.max(Math.abs(mdCol.min), mdCol.max) : mdCol.max;
577
+ const chooseCurrent = (): any => twoColorMode ? Math.abs(cellValue) : cellValue;
607
578
 
608
579
  const rCoef = (chooseCurrent() - chooseMin()) / (chooseMax() - chooseMin());
609
580
 
@@ -619,15 +590,20 @@ export class PeptidesModel {
619
590
  if (isSubstitutionOn) {
620
591
  canvasContext.textBaseline = 'middle';
621
592
  canvasContext.textAlign = 'center';
622
- canvasContext.fillStyle = DG.Color.toHtml(DG.Color.black);
623
- // DG.Color.getContrastColor()
593
+ canvasContext.fillStyle = DG.Color.toHtml(DG.Color.getContrastColor(DG.Color.fromHtml(coef)));
624
594
  canvasContext.font = '13px Roboto, Roboto Local, sans-serif';
625
- const substValue = this.substitutionTable.groupBy([currentPosition])
626
- .where(queryAAR)
627
- .aggregate()
628
- .get(currentPosition, 0);
629
- if (substValue && substValue !== DG.INT_NULL)
630
- canvasContext.fillText(substValue, midX, midY);
595
+ let substValue = 0;
596
+ this.substitutionsInfo.get(currentAAR)?.get(currentPosition)?.forEach((idxs) => substValue += idxs.length);
597
+ if (substValue && substValue != 0)
598
+ canvasContext.fillText(substValue.toString(), midX, midY);
599
+ }
600
+
601
+ //TODO: frame based on currentSelection
602
+ const aarSelection = this.currentSelection[currentPosition];
603
+ if (aarSelection && aarSelection.includes(currentAAR)) {
604
+ canvasContext.strokeStyle = '#000';
605
+ canvasContext.lineWidth = 1;
606
+ canvasContext.strokeRect(bound.x + 1, bound.y + 1, bound.width - 1, bound.height - 1);
631
607
  }
632
608
  }
633
609
  args.preventDefault();
@@ -638,17 +614,18 @@ export class PeptidesModel {
638
614
  sarVGrid.onCellRender.subscribe(cellRendererAction);
639
615
  }
640
616
 
617
+ //FIXME: doesn't work at all
641
618
  setTooltips(
642
619
  renderColNames: string[], statsDf: DG.DataFrame, peptidesCount: number, sarGrid: DG.Grid, sarVGrid: DG.Grid,
643
620
  sourceDf: DG.DataFrame,
644
- ) {
645
- const onCellTooltipAction = async (cell: DG.GridCell, x: number, y: number) => {
621
+ ): void {
622
+ const onCellTooltipAction = async (cell: DG.GridCell, x: number, y: number): Promise<boolean> => {
646
623
  if (
647
624
  !cell.isRowHeader && !cell.isColHeader && cell.tableColumn !== null && cell.cell.value !== null &&
648
625
  cell.tableRowIndex !== null && renderColNames.indexOf(cell.tableColumn.name) !== -1) {
649
626
  const tooltipMap: { [index: string]: string } = {};
650
627
 
651
- for (const col of (statsDf.columns as DG.ColumnList).names()) {
628
+ for (const col of statsDf.columns.names()) {
652
629
  if (col !== C.COLUMNS_NAMES.AMINO_ACID_RESIDUE && col !== C.COLUMNS_NAMES.POSITION) {
653
630
  const currentPosition = cell.tableColumn.name !== C.COLUMNS_NAMES.MEAN_DIFFERENCE ?
654
631
  cell.tableColumn.name : cell.grid.table.get(C.COLUMNS_NAMES.POSITION, cell.tableRowIndex);
@@ -672,11 +649,6 @@ export class PeptidesModel {
672
649
  ui.tooltip.show(ui.tableFromMap(tooltipMap), x, y);
673
650
  }
674
651
  if (!cell.isColHeader && cell.tableColumn?.name == C.COLUMNS_NAMES.AMINO_ACID_RESIDUE) {
675
- // if (grouping) {
676
- // const currentGroup = C.groupDescription[cell.cell.value];
677
- // const divText = ui.divText('Amino Acids in this group: ' + currentGroup[C.SEM_TYPES.AMINO_ACIDS].join(', '));
678
- // ui.tooltip.show(ui.divV([ui.h3(currentGroup['description']), divText]), x, y);
679
- // } else {
680
652
  const monomerLib = sourceDf.temp[MonomerLibrary.id];
681
653
  ChemPalette.showTooltip(cell, x, y, monomerLib);
682
654
  }
@@ -686,53 +658,71 @@ export class PeptidesModel {
686
658
  sarVGrid.onCellTooltip(onCellTooltipAction);
687
659
  }
688
660
 
689
- setInteractionCallback() {
661
+ //TODO: think about it, move out?
662
+ setInteractionCallback(): void {
690
663
  const sarDf = this._sarGrid.dataFrame;
691
664
  const sarVDf = this._sarVGrid.dataFrame;
692
665
 
693
- const getAARandPosition = (isVertical = false): [string, string] => {
694
- let aar : string;
695
- let position: string;
696
- if (isVertical) {
697
- const currentRowIdx = sarVDf.currentRowIdx;
698
- aar = sarVDf.get(C.COLUMNS_NAMES.MEAN_DIFFERENCE, currentRowIdx);
699
- position = sarVDf.get(C.COLUMNS_NAMES.POSITION, currentRowIdx);
700
- } else {
701
- aar = sarDf.get(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE, sarDf.currentRowIdx);
702
- position = sarDf.currentCol.name;
703
- }
704
- return [aar, position];
666
+ const chooseAction = (aar: string, position: string, isShiftPressed: boolean) => {
667
+ isShiftPressed ? this.modifyCurrentSelection(aar, position) : this.initCurrentSelection(aar, position);
705
668
  };
706
669
 
707
- this._sarGrid.onCurrentCellChanged.subscribe((gc) => {
708
- const isNegativeRowIndex = sarDf.currentRowIdx === -1;
709
- if (!sarDf.currentCol || (!sarDf.currentCell.value && !isNegativeRowIndex))
670
+ const gridCellValidation = (gc: DG.GridCell | null) => !gc || !gc.cell.value || !gc.tableColumn ||
671
+ gc.tableRowIndex == null || gc.tableRowIndex == -1;
672
+ this._sarGrid.root.addEventListener('click', (ev) => {
673
+ const gridCell = this._sarGrid.hitTest(ev.offsetX, ev.offsetY);
674
+ if (gridCellValidation(gridCell) || gridCell!.tableColumn!.name == C.COLUMNS_NAMES.AMINO_ACID_RESIDUE)
710
675
  return;
711
- this.syncGrids(false, sarDf, sarVDf);
712
- let aar: string = C.CATEGORIES.ALL;
713
- let position: string = C.CATEGORIES.ALL;
714
- if (!isNegativeRowIndex) {
715
- [aar, position] = getAARandPosition();
716
- this.dataFrame.tags[C.TAGS.AAR] = aar;
717
- this.dataFrame.tags[C.TAGS.POSITION] = position;
718
- } else {
719
- this.dataFrame.tags[C.TAGS.AAR] = this.dataFrame.tags[C.TAGS.POSITION] = null;
720
- }
721
- this.dataFrame.temp['substTable'] = this.getSubstitutionTable();
722
- this.modifyOrCreateSplitCol(aar, position);
723
- this.fireBitsetChanged();
724
- this.invalidateGrids();
725
- grok.shell.o = this.dataFrame;
676
+
677
+ const position = gridCell!.tableColumn!.name;
678
+ const aar = sarDf.get(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE, gridCell!.tableRowIndex!);
679
+ chooseAction(aar, position, ev.shiftKey);
726
680
  });
727
681
 
728
- this._sarVGrid.onCurrentCellChanged.subscribe((gc) => {
729
- if (!sarVDf.currentCol || sarVDf.currentRowIdx === -1)
682
+ this._sarVGrid.root.addEventListener('click', (ev) => {
683
+ const gridCell = this._sarVGrid.hitTest(ev.offsetX, ev.offsetY);
684
+ if (gridCellValidation(gridCell) || gridCell!.tableColumn!.name != C.COLUMNS_NAMES.MEAN_DIFFERENCE)
730
685
  return;
731
- this.syncGrids(true, sarDf, sarVDf);
686
+
687
+ const tableRowIdx = gridCell!.tableRowIndex!;
688
+ const position = sarVDf.get(C.COLUMNS_NAMES.POSITION, tableRowIdx);
689
+ const aar = sarVDf.get(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE, tableRowIdx);
690
+ chooseAction(aar, position, ev.shiftKey);
732
691
  });
692
+
693
+ const cellChanged = (table: DG.DataFrame) => {
694
+ if (this.isCellChanging)
695
+ return;
696
+ this.isCellChanging = true;
697
+ table.currentRowIdx = -1;
698
+ this.isCellChanging = false;
699
+ };
700
+ this._sarGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(sarDf));
701
+ this._sarVGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(sarVDf));
702
+ }
703
+
704
+ modifyCurrentSelection(aar: string, position: string): void {
705
+ const tempSelection = this.currentSelection;
706
+ if (!tempSelection.hasOwnProperty(position))
707
+ tempSelection[position] = [aar];
708
+ else {
709
+ const tempSelectionAt = tempSelection[position];
710
+ const aarIndex = tempSelectionAt.indexOf(aar);
711
+ aarIndex == -1 ? tempSelectionAt.push(aar) :
712
+ tempSelectionAt.length == 1 ? delete tempSelection[position] :
713
+ tempSelectionAt.splice(aarIndex, 1);
714
+ }
715
+
716
+ this.currentSelection = tempSelection;
717
+ }
718
+
719
+ initCurrentSelection(aar: string, position: string): void {
720
+ const tempSelection: type.SelectionObject = {};
721
+ tempSelection[position] = [aar];
722
+ this.currentSelection = tempSelection;
733
723
  }
734
724
 
735
- invalidateGrids() {
725
+ invalidateGrids(): void {
736
726
  this.stackedBarchart?.computeData();
737
727
  this._sarGrid.invalidate();
738
728
  this._sarVGrid.invalidate();
@@ -740,22 +730,53 @@ export class PeptidesModel {
740
730
  //TODO: this.peptideSpaceGrid.invalidate();
741
731
  }
742
732
 
743
- setBitsetCallback() {
733
+ setBitsetCallback(): void {
744
734
  if (this.isBitsetChangedInitialized)
745
735
  return;
746
- const filter = this.dataFrame.filter;
747
- const selection = this.dataFrame.selection;
736
+ const filter = this._dataFrame.filter;
737
+ const selection = this._dataFrame.selection;
748
738
 
749
- const changeBitset = (currentBitset: DG.BitSet, previousBitset: DG.BitSet) => {
739
+ const changeBitset = (currentBitset: DG.BitSet, previousBitset: DG.BitSet): void => {
750
740
  previousBitset.setAll(!this._filterMode, false);
751
- currentBitset.init((i) => {
752
- const currentCategory = this.splitCol.get(i);
753
- return currentCategory !== C.CATEGORIES.OTHER && currentCategory !== C.CATEGORIES.ALL;
754
- }, false);
741
+
742
+ const edfSelection = this.edf?.selection;
743
+ if (this.isPeptideSpaceChangingBitset) {
744
+ if (edfSelection == null)
745
+ return;
746
+
747
+ currentBitset.init((i) => edfSelection.get(i) ?? false, false);
748
+ return;
749
+ }
750
+
751
+ const updateEdfSelection = () => {
752
+ this.isChangingEdfBitset = true;
753
+ edfSelection?.copyFrom(currentBitset);
754
+ this.isChangingEdfBitset = false;
755
+ };
756
+
757
+ const positionList = Object.keys(this.currentSelection);
758
+ if (positionList.length == 0) {
759
+ currentBitset.init(() => false, false);
760
+ updateEdfSelection();
761
+ return;
762
+ }
763
+
764
+ //TODO: move out
765
+ const getBitAt = (i: number) => {
766
+ for (const position of positionList) {
767
+ const positionCol: DG.Column<string> = this._dataFrame.getCol(position);
768
+ if (this._currentSelection[position].includes(positionCol.get(i)))
769
+ return true;
770
+ }
771
+ return false;
772
+ };
773
+ currentBitset.init(getBitAt, false);
774
+
775
+ updateEdfSelection();
755
776
  };
756
777
 
757
778
  const recalculateStatistics =
758
- (bitset: DG.BitSet) => (this.dataFrame.temp[C.STATS] as FilteringStatistics).setMask(bitset);
779
+ (bitset: DG.BitSet): void => (this._dataFrame.temp[C.STATS] as FilteringStatistics).setMask(bitset);
759
780
 
760
781
  filter.onChanged.subscribe(() => {
761
782
  changeBitset(filter, selection);
@@ -768,9 +789,18 @@ export class PeptidesModel {
768
789
  this.isBitsetChangedInitialized = true;
769
790
  }
770
791
 
771
- fireBitsetChanged() {(this._filterMode ? this._dataFrame.filter : this._dataFrame.selection).fireChanged();}
792
+ fireBitsetChanged(isPeptideSpaceSource: boolean = false): void {
793
+ this.isPeptideSpaceChangingBitset = isPeptideSpaceSource;
794
+ this.getBiteset().fireChanged();
795
+ this.isPeptideSpaceChangingBitset = false;
796
+ this.modifyOrCreateSplitCol();
797
+ grok.shell.o = this.createAccordion().root;
798
+ }
799
+
800
+ getBiteset(): DG.BitSet {return this._filterMode ? this._dataFrame.filter : this._dataFrame.selection;}
772
801
 
773
- postProcessGrids(sourceGrid: DG.Grid, invalidIndexes: number[], sarGrid: DG.Grid, sarVGrid: DG.Grid) {
802
+ //TODO: move out
803
+ postProcessGrids(sourceGrid: DG.Grid, invalidIndexes: number[], sarGrid: DG.Grid, sarVGrid: DG.Grid): void {
774
804
  sourceGrid.onCellPrepare((cell: DG.GridCell) => {
775
805
  const currentRowIndex = cell.tableRowIndex;
776
806
  if (currentRowIndex && invalidIndexes.includes(currentRowIndex) && !cell.isRowHeader)
@@ -781,27 +811,77 @@ export class PeptidesModel {
781
811
  mdCol.name = 'Diff';
782
812
 
783
813
  for (const grid of [sarGrid, sarVGrid]) {
784
- grid.props.rowHeight = 20;
785
- grid.columns.rowHeader!.width = 20;
786
- for (let i = 0; i < grid.columns.length; ++i) {
787
- const col = grid.columns.byIndex(i)!;
788
- if (grid == sarVGrid && col.name !== 'Diff' && col.name !== C.COLUMNS_NAMES.AMINO_ACID_RESIDUE)
789
- col.width = 45;
814
+ const gridProps = grid.props;
815
+ gridProps.rowHeight = 20;
816
+ const girdCols = grid.columns;
817
+ const colNum = girdCols.length;
818
+ for (let i = 0; i < colNum; ++i) {
819
+ const col = girdCols.byIndex(i)!;
820
+ const colName = col.name;
821
+ if (grid == sarVGrid && colName !== 'Diff' && colName !== C.COLUMNS_NAMES.AMINO_ACID_RESIDUE)
822
+ col.width = 50;
790
823
  else
791
- col.width = grid.props.rowHeight;
824
+ col.width = gridProps.rowHeight + 10;
792
825
  }
793
826
  }
794
827
 
795
- // if (grouping) {
796
- // sarGrid.col(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE)!.name = 'Groups';
797
- // sarVGrid.col(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE)!.name = 'Groups';
798
- // }
828
+ const setViewerGridProps = (grid: DG.Grid) => {
829
+ grid.props.allowEdit = false;
830
+ grid.props.allowRowSelection = false;
831
+ grid.props.allowBlockSelection = false;
832
+ };
833
+
834
+ setViewerGridProps(sarGrid);
835
+ setViewerGridProps(sarVGrid);
836
+ }
837
+
838
+ getSplitColValueAt(index: number, aar: string, position: string, aarLabel: string): string {
839
+ const currentAAR = this._dataFrame.get(position, index) as string;
840
+ return currentAAR === aar ? aarLabel : C.CATEGORIES.OTHER;
841
+ }
842
+
843
+ modifyOrCreateSplitCol(): void {
844
+ const bs = this.getBiteset();
845
+ this.splitCol = this._dataFrame.col(C.COLUMNS_NAMES.SPLIT_COL) ??
846
+ this._dataFrame.columns.addNewBool(C.COLUMNS_NAMES.SPLIT_COL);
847
+ this.splitCol.init((i) => bs.get(i));
848
+ this.splitCol.compact();
849
+ }
850
+
851
+ static async scaleActivity(
852
+ activityScaling: string, df: DG.DataFrame, originalActivityName?: string, cloneBitset = false,
853
+ ): Promise<[DG.DataFrame, string]> {
854
+ let currentActivityColName = originalActivityName ?? C.COLUMNS_NAMES.ACTIVITY;
855
+ const flag = df.columns.names().includes(currentActivityColName) &&
856
+ currentActivityColName === originalActivityName;
857
+ currentActivityColName = flag ? currentActivityColName : C.COLUMNS_NAMES.ACTIVITY;
858
+ const tempDf = df.clone(cloneBitset ? df.filter : null, [currentActivityColName]);
859
+
860
+ let formula = '${' + currentActivityColName + '}';
861
+ let newColName = 'activity';
862
+ switch (activityScaling) {
863
+ case 'none':
864
+ break;
865
+ case 'lg':
866
+ formula = `Log10(${formula})`;
867
+ newColName = `Log10(${newColName})`;
868
+ break;
869
+ case '-lg':
870
+ formula = `-1*Log10(${formula})`;
871
+ newColName = `-Log10(${newColName})`;
872
+ break;
873
+ default:
874
+ throw new Error(`ScalingError: method \`${activityScaling}\` is not available.`);
875
+ }
876
+
877
+ await tempDf.columns.addNewCalculated(C.COLUMNS_NAMES.ACTIVITY_SCALED, formula);
878
+ df.tags['scaling'] = activityScaling;
799
879
 
800
- sarGrid.props.allowEdit = false;
801
- sarVGrid.props.allowEdit = false;
880
+ return [tempDf, newColName];
802
881
  }
803
882
 
804
- split(peptideColumn: DG.Column, filter: boolean = true): string[][] {
883
+ static splitAlignedPeptides(peptideColumn: DG.Column<string>, filter: boolean = true): [DG.DataFrame, number[]] {
884
+ const separator = peptideColumn.tags[C.TAGS.SEPARATOR] ?? getSeparator(peptideColumn);
805
885
  const splitPeptidesArray: string[][] = [];
806
886
  let currentSplitPeptide: string[];
807
887
  let modeMonomerCount = 0;
@@ -809,16 +889,16 @@ export class PeptidesModel {
809
889
  const colLength = peptideColumn.length;
810
890
 
811
891
  // splitting data
812
- const monomerLengths: { [index: string]: number } = {};
892
+ const monomerLengths: {[index: string]: number} = {};
813
893
  for (let i = 0; i < colLength; i++) {
814
- currentSplitPeptide = peptideColumn.get(i).split('-').map((value: string) => value ? value : '-');
894
+ currentSplitPeptide = peptideColumn.get(i).split(separator).map((value: string) => value ? value : '-');
815
895
  splitPeptidesArray.push(currentSplitPeptide);
816
896
  currentLength = currentSplitPeptide.length;
817
897
  monomerLengths[currentLength + ''] =
818
898
  monomerLengths[currentLength + ''] ? monomerLengths[currentLength + ''] + 1 : 1;
819
899
  }
820
- //@ts-ignore: what I do here is converting string to number the most effective way I could find. parseInt is slow
821
- modeMonomerCount = 1 * Object.keys(monomerLengths).reduce((a, b) => monomerLengths[a] > monomerLengths[b] ? a : b);
900
+ modeMonomerCount =
901
+ parseInt(Object.keys(monomerLengths).reduce((a, b) => monomerLengths[a] > monomerLengths[b] ? a : b));
822
902
 
823
903
  // making sure all of the sequences are of the same size
824
904
  // and marking invalid sequences
@@ -841,9 +921,9 @@ export class PeptidesModel {
841
921
  modeMonomerCount--; // minus C-terminal
842
922
 
843
923
  //create column names list
844
- const columnNames = Array.from({length: modeMonomerCount}, (_, index) => `${index + 1 < 10 ? 0 : ''}${index + 1}`);
845
- columnNames.splice(0, 0, 'N-terminal');
846
- columnNames.push('C-terminal');
924
+ const columnNames = Array.from({length: modeMonomerCount}, (_, index) => `${index + 1 < 10 ? 0 : ''}${index + 1 }`);
925
+ columnNames.splice(0, 0, 'N');
926
+ columnNames.push('C');
847
927
 
848
928
  // filter out the columns with the same values
849
929
  if (filter) {
@@ -856,134 +936,110 @@ export class PeptidesModel {
856
936
  });
857
937
  }
858
938
 
859
- return splitPeptidesArray;
939
+ return [
940
+ DG.DataFrame.fromColumns(splitColumns.map((positionArray, index) => {
941
+ return DG.Column.fromList('string', columnNames[index], positionArray);
942
+ })),
943
+ invalidIndexes,
944
+ ];
860
945
  }
861
946
 
862
- getSubstitutionTable() {
863
- if (!this._casesTable)
864
- this.calcSubstitutions();
865
- const sarDf = this._sarGrid.dataFrame;
866
- const sourceDf = this._sourceGrid.dataFrame;
867
- if (sarDf.currentRowIdx === -1)
868
- return null;
869
- const currentColName = sarDf.currentCol.name;
870
- if (currentColName !== C.COLUMNS_NAMES.AMINO_ACID_RESIDUE) {
871
- const col: DG.Column = sourceDf.columns.bySemType(C.SEM_TYPES.ALIGNED_SEQUENCE);
872
- const aar = sarDf.get(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE, sarDf.currentRowIdx);
873
- const pos = parseInt(currentColName);
874
- const substitutionsCount = this.substitutionTable.groupBy([currentColName])
875
- .where(`${C.COLUMNS_NAMES.AMINO_ACID_RESIDUE} = ${aar}`)
876
- .aggregate()
877
- .get(currentColName, 0);
878
- if (substitutionsCount === DG.INT_NULL)
879
- return null;
880
- const currentCase = this._casesTable[aar][pos];
881
- const tempDfLength = currentCase.length;
882
- const initCol = DG.Column.string('Initial', tempDfLength);
883
- const subsCol = DG.Column.string('Substituted', tempDfLength);
884
-
885
- const tempDf = DG.DataFrame.fromColumns([
886
- initCol,
887
- subsCol,
888
- DG.Column.float('Difference', tempDfLength),
889
- ]);
890
-
891
- for (let i = 0; i < tempDfLength; i++) {
892
- const row = currentCase[i];
893
- tempDf.rows.setValues(i, [col.get(row[0]), col.get(row[1]), row[2]]);
894
- }
947
+ syncProperties(isSourceSAR = true): void {
948
+ const sarViewer: SARViewer = this._dataFrame.temp['sarViewer'];
949
+ const sarViewerVertical: SARViewerVertical = this._dataFrame.temp['sarViewerVertical'];
950
+ const sourceViewer = isSourceSAR ? sarViewer : sarViewerVertical;
951
+ const targetViewer = isSourceSAR ? sarViewerVertical : sarViewer;
952
+ const properties = sourceViewer.props.getProperties();
953
+ for (const property of properties)
954
+ targetViewer.props.set(property.name, property.get(sourceViewer));
955
+ }
956
+
957
+ /** Class initializer */
958
+ async init(): Promise<void> {
959
+ if (this.isInitialized)
960
+ return;
961
+ this.isInitialized = true;
962
+ //calculate initial stats
963
+ const stats = new FilteringStatistics();
964
+ const activityScaledCol = this._dataFrame.columns.bySemType(C.SEM_TYPES.ACTIVITY_SCALED)!;
965
+ stats.setData(activityScaledCol.getRawData() as Float32Array);
966
+ stats.setMask(this._dataFrame.selection);
967
+ this._dataFrame.temp[C.STATS] = stats;
968
+
969
+ this.currentView = this._dataFrame.tags[C.PEPTIDES_ANALYSIS] == 'true' ? grok.shell.v as DG.TableView :
970
+ grok.shell.addTableView(this._dataFrame);
971
+ const sourceGrid = this.currentView.grid;
972
+ if (this._dataFrame.tags[C.PEPTIDES_ANALYSIS] == 'true')
973
+ return;
974
+
975
+ this._dataFrame.tags[C.PEPTIDES_ANALYSIS] = 'true';
976
+ sourceGrid.col(C.COLUMNS_NAMES.ACTIVITY_SCALED)!.name = this._dataFrame.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED];
977
+ sourceGrid.columns.setOrder([this._dataFrame.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED]]);
895
978
 
896
- // tempDf.temp['isReal'] = true;
897
979
 
898
- initCol.semType = C.SEM_TYPES.ALIGNED_SEQUENCE;
899
- initCol.temp['isAnalysisApplicable'] = false;
900
- subsCol.semType = C.SEM_TYPES.ALIGNED_SEQUENCE;
901
- subsCol.temp['isAnalysisApplicable'] = false;
980
+ this._dataFrame.temp[C.EMBEDDING_STATUS] = false;
981
+ const adjustCellSize = (grid: DG.Grid): void => {
982
+ const colNum = grid.columns.length;
983
+ for (let i = 0; i < colNum; ++i) {
984
+ const iCol = grid.columns.byIndex(i)!;
985
+ iCol.width = isNaN(parseInt(iCol.name)) ? 50 : 40;
986
+ }
987
+ grid.props.rowHeight = 20;
988
+ };
902
989
 
903
- // grok.shell.o = DG.SemanticValue.fromValueType(tempDf, 'Substitution');
904
- return tempDf;
990
+ for (let i = 0; i < sourceGrid.columns.length; i++) {
991
+ const aarCol = sourceGrid.columns.byIndex(i);
992
+ if (aarCol && aarCol.name && aarCol.column?.semType !== C.SEM_TYPES.AMINO_ACIDS &&
993
+ aarCol.name !== this._dataFrame.tags[C.COLUMNS_NAMES.ACTIVITY_SCALED])
994
+ aarCol.visible = false;
905
995
  }
906
- return null;
907
- }
908
996
 
909
- //TODO: refactor, use this.sarDf and accept aar & position as parameters
910
- syncGrids(sourceVertical: boolean, sarDf: DG.DataFrame, sarVDf: DG.DataFrame) {
911
- let otherColName: string;
912
- let otherRowIndex: number;
913
- const otherDf = sourceVertical ? sarDf : sarVDf;
997
+ const options = {scaling: this._dataFrame.tags['scaling']};
998
+ await this.updateData(this._dataFrame.tags['scaling'], sourceGrid, false, 1, 2, false, false);
914
999
 
915
- if (otherDf.temp[C.FLAGS.CELL_CHANGING])
916
- return;
1000
+ const dockManager = this.currentView.dockManager;
917
1001
 
918
- //on vertical SAR viewer click
919
- if (sourceVertical) {
920
- const currentRowIdx = sarVDf.currentRowIdx;
921
- const currentColName = sarVDf.currentCol.name;
922
- if (currentColName !== C.COLUMNS_NAMES.MEAN_DIFFERENCE)
923
- return;
1002
+ const sarViewer = await this._dataFrame.plot.fromType('peptide-sar-viewer', options) as SARViewer;
924
1003
 
925
- otherColName = sarVDf.get(C.COLUMNS_NAMES.POSITION, currentRowIdx);
926
- const otherRowName: string = sarVDf.get(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE, currentRowIdx);
927
- otherRowIndex = -1;
928
- const rows = otherDf.rowCount;
929
- for (let i = 0; i < rows; i++) {
930
- if (otherDf.get(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE, i) === otherRowName) {
931
- otherRowIndex = i;
932
- break;
933
- }
934
- }
935
- //on SAR viewer click
936
- } else {
937
- otherColName = C.COLUMNS_NAMES.MEAN_DIFFERENCE;
938
- const otherPos: string = sarDf.currentCol.name;
939
- if (otherPos === C.COLUMNS_NAMES.AMINO_ACID_RESIDUE)
940
- return;
1004
+ const sarViewerVertical =
1005
+ await this._dataFrame.plot.fromType('peptide-sar-viewer-vertical', options) as SARViewerVertical;
941
1006
 
942
- const otherAAR: string =
943
- sarDf.get(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE, sarDf.currentRowIdx);
944
- otherRowIndex = -1;
945
- for (let i = 0; i < sarVDf.rowCount; i++) {
946
- if (
947
- sarVDf.get(C.COLUMNS_NAMES.AMINO_ACID_RESIDUE, i) === otherAAR &&
948
- sarVDf.get(C.COLUMNS_NAMES.POSITION, i) === otherPos
949
- ) {
950
- otherRowIndex = i;
951
- break;
952
- }
953
- }
1007
+ const sarViewersGroup: viewerTypes[] = [sarViewer, sarViewerVertical];
1008
+
1009
+ if (this._dataFrame.rowCount <= 10000) {
1010
+ const peptideSpaceViewerOptions = {method: 'UMAP', measure: 'Levenshtein', cyclesCount: 100};
1011
+ const peptideSpaceViewer =
1012
+ await this._dataFrame.plot.fromType('peptide-space-viewer', peptideSpaceViewerOptions) as PeptideSpaceViewer;
1013
+ dockManager.dock(peptideSpaceViewer, DG.DOCK_TYPE.RIGHT, null, 'Peptide Space Viewer');
954
1014
  }
955
- otherDf.temp[C.FLAGS.CELL_CHANGING] = true;
956
- otherDf.currentCell = otherDf.cell(otherRowIndex, otherColName);
957
- otherDf.temp[C.FLAGS.CELL_CHANGING] = false;
958
- }
959
1015
 
960
- getSplitColValueAt(index: number, aar: string, position: string, aarLabel: string): string {
961
- const currentAAR = this.dataFrame.get(position, index) as string;
962
- return currentAAR === aar ? aarLabel : C.CATEGORIES.OTHER;
963
- }
1016
+ dockViewers(sarViewersGroup, DG.DOCK_TYPE.RIGHT, dockManager, DG.DOCK_TYPE.DOWN);
964
1017
 
965
- modifyOrCreateSplitCol(aar: string, position: string): void {
966
- const df = this.dataFrame;
967
- this.splitCol = df.col(C.COLUMNS_NAMES.SPLIT_COL) ??
968
- df.columns.addNew(C.COLUMNS_NAMES.SPLIT_COL, 'string') as DG.Column;
1018
+ sourceGrid.props.allowEdit = false;
1019
+ adjustCellSize(sourceGrid);
969
1020
 
970
- if (aar === C.CATEGORIES.ALL && position === C.CATEGORIES.ALL) {
971
- this.splitCol.init(() => C.CATEGORIES.ALL);
972
- return;
973
- }
1021
+ this.invalidateGrids();
1022
+ }
974
1023
 
975
- const aarLabel = `${aar === '-' ? 'Gap' : aar} : ${position}`;
976
- this.splitCol.init((i) => this.getSplitColValueAt(i, aar, position, aarLabel));
1024
+ invalidateSourceGrid(): void {this._sourceGrid.invalidate();}
1025
+ }
977
1026
 
978
- // splitCol.init((i) => bitset.get(i) ? aarLabel : C.CATEGORY_OTHER);
979
- this.splitCol.setCategoryOrder([aarLabel]);
980
- this.splitCol.compact();
1027
+ type viewerTypes = SARViewer | SARViewerVertical;
1028
+
1029
+ function dockViewers(
1030
+ viewerList: viewerTypes[], attachDirection: DG.DockType, dockManager: DG.DockManager,
1031
+ initialAttachDirection?: DG.DockType): DG.DockNode[] | null {
1032
+ const viewerListLength = viewerList.length;
1033
+ if (viewerListLength === 0)
1034
+ return null;
981
1035
 
982
- const colorMap: {[index: string]: string | number} = {};
1036
+ let currentViewer = viewerList[0];
1037
+ const nodeList = [dockManager.dock(currentViewer, initialAttachDirection, null, currentViewer.name ?? '')];
1038
+ const ratio = 1 / viewerListLength;
983
1039
 
984
- colorMap[C.CATEGORIES.OTHER] = DG.Color.blue;
985
- colorMap[aarLabel] = DG.Color.orange;
986
- // colorMap[currentAAR] = cp.getColor(currentAAR);
987
- df.getCol(C.COLUMNS_NAMES.SPLIT_COL).colors.setCategorical(colorMap);
1040
+ for (let i = 1; i < viewerListLength; i++) {
1041
+ currentViewer = viewerList[i];
1042
+ nodeList.push(dockManager.dock(currentViewer, attachDirection, nodeList[i - 1], currentViewer.name ?? '', ratio));
988
1043
  }
1044
+ return nodeList;
989
1045
  }