@datagrok/peptides 1.7.2 → 1.8.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/.eslintrc.json +2 -2
- package/README.md +3 -3
- package/dist/563.js +2 -0
- package/dist/611.js +2 -0
- package/dist/802.js +2 -0
- package/dist/96.js +2 -0
- package/dist/package-test.js +2 -29778
- package/dist/package.js +2 -28285
- package/files/icons/logo-summary-viewer.svg +13 -0
- package/files/icons/peptide-sar-vertical-viewer.svg +13 -0
- package/files/icons/peptide-sar-viewer.svg +19 -0
- package/files/icons/peptide-space-viewer.svg +40 -0
- package/files/tests/HELM_small.csv +12 -0
- package/package.json +7 -8
- package/src/demo/fasta.ts +24 -0
- package/src/model.ts +381 -325
- package/src/package-test.ts +3 -0
- package/src/package.ts +54 -30
- package/src/tests/algorithms.ts +1 -1
- package/src/tests/core.ts +13 -8
- package/src/tests/model.ts +152 -0
- package/src/tests/peptide-space-test.ts +1 -2
- package/src/tests/table-view.ts +158 -0
- package/src/tests/viewers.ts +142 -4
- package/src/tests/widgets.ts +135 -0
- package/src/utils/algorithms.ts +2 -2
- package/src/utils/cell-renderer.ts +2 -2
- package/src/utils/constants.ts +8 -0
- package/src/utils/distance-matrix.worker.ts +16 -0
- package/src/utils/misc.ts +4 -4
- package/src/utils/peptide-similarity-space.ts +0 -1
- package/src/utils/statistics.ts +14 -10
- package/src/utils/types.ts +8 -4
- package/src/utils/worker-creator.ts +11 -0
- package/src/viewers/logo-summary.ts +246 -168
- package/src/viewers/peptide-space-viewer.ts +6 -6
- package/src/viewers/sar-viewer.ts +108 -110
- package/src/widgets/distribution.ts +95 -128
- package/src/widgets/mutation-cliffs.ts +2 -2
- package/src/widgets/peptides.ts +11 -3
- package/src/widgets/settings.ts +94 -24
- package/tsconfig.json +1 -1
- package/dist/vendors-node_modules_datagrok-libraries_ml_src_workers_dimensionality-reducer_js.js +0 -9077
package/src/model.ts
CHANGED
|
@@ -2,27 +2,36 @@ import * as ui from 'datagrok-api/ui';
|
|
|
2
2
|
import * as grok from 'datagrok-api/grok';
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
4
|
|
|
5
|
+
import {splitAlignedSequences} from '@datagrok-libraries/bio/src/utils/splitter';
|
|
6
|
+
import {IMonomerLib} from '@datagrok-libraries/bio/src/types';
|
|
7
|
+
import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
|
|
8
|
+
import {MonomerWorks} from '@datagrok-libraries/bio/src/monomer-works/monomer-works';
|
|
9
|
+
import {pickUpPalette, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
10
|
+
import {StringDictionary} from '@datagrok-libraries/utils/src/type-declarations';
|
|
11
|
+
import {DistanceMatrix} from '@datagrok-libraries/bio/src/trees/distance-matrix';
|
|
12
|
+
import {StringMetricsNames} from '@datagrok-libraries/ml/src/typed-metrics';
|
|
13
|
+
import {ITreeHelper} from '@datagrok-libraries/bio/src/trees/tree-helper';
|
|
14
|
+
import {TAGS as treeTAGS} from '@datagrok-libraries/bio/src/trees';
|
|
15
|
+
|
|
5
16
|
import wu from 'wu';
|
|
6
17
|
import * as rxjs from 'rxjs';
|
|
7
18
|
import * as uuid from 'uuid';
|
|
8
19
|
|
|
9
20
|
import * as C from './utils/constants';
|
|
10
21
|
import * as type from './utils/types';
|
|
11
|
-
import {calculateSelected, extractMonomerInfo, scaleActivity,
|
|
22
|
+
import {calculateSelected, extractMonomerInfo, scaleActivity, getStatsSummary} from './utils/misc';
|
|
12
23
|
import {MonomerPosition, MostPotentResiduesViewer} from './viewers/sar-viewer';
|
|
13
24
|
import * as CR from './utils/cell-renderer';
|
|
14
25
|
import {mutationCliffsWidget} from './widgets/mutation-cliffs';
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
import {
|
|
26
|
+
import {getActivityDistribution, getDistributionLegend, getDistributionWidget, getStatsTableMap,
|
|
27
|
+
} from './widgets/distribution';
|
|
28
|
+
import {getAggregatedValue, getStats, Stats} from './utils/statistics';
|
|
29
|
+
import {LogoSummaryTable} from './viewers/logo-summary';
|
|
18
30
|
import {getSettingsDialog} from './widgets/settings';
|
|
19
|
-
import {
|
|
31
|
+
import {_package, getMonomerWorksInstance, getTreeHelperInstance} from './package';
|
|
20
32
|
import {findMutations} from './utils/algorithms';
|
|
21
|
-
import {
|
|
22
|
-
import
|
|
23
|
-
import {SeqPalette} from '@datagrok-libraries/bio/src/seq-palettes';
|
|
24
|
-
import {MonomerWorks} from '@datagrok-libraries/bio/src/monomer-works/monomer-works';
|
|
25
|
-
import {pickUpPalette, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
33
|
+
import {createDistanceMatrixWorker} from './utils/worker-creator';
|
|
34
|
+
import BitArray from '@datagrok-libraries/utils/src/bit-array';
|
|
26
35
|
|
|
27
36
|
export type SummaryStats = {
|
|
28
37
|
minCount: number, maxCount: number,
|
|
@@ -32,35 +41,47 @@ export type SummaryStats = {
|
|
|
32
41
|
};
|
|
33
42
|
export type PositionStats = { [monomer: string]: Stats } & { general: SummaryStats };
|
|
34
43
|
export type MonomerPositionStats = { [position: string]: PositionStats } & { general: SummaryStats };
|
|
44
|
+
export type ClusterStats = {[cluster: string]: Stats};
|
|
45
|
+
export enum CLUSTER_TYPE {
|
|
46
|
+
ORIGINAL = 'original',
|
|
47
|
+
CUSTOM = 'custom',
|
|
48
|
+
};
|
|
49
|
+
export type ClusterType = `${CLUSTER_TYPE}`;
|
|
50
|
+
export type ClusterTypeStats = {[clusterType in ClusterType]: ClusterStats};
|
|
51
|
+
export enum VIEWER_TYPE {
|
|
52
|
+
MONOMER_POSITION = 'Monomer-Position',
|
|
53
|
+
MOST_POTENT_RESIDUES = 'Most Potent Residues',
|
|
54
|
+
LOGO_SUMMARY_TABLE = 'Logo Summary Table',
|
|
55
|
+
DENDROGRAM = 'Dendrogram',
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const getAggregatedColName = (aggF: string, colName: string): string => `${aggF}(${colName})`;
|
|
35
59
|
|
|
36
60
|
export class PeptidesModel {
|
|
37
61
|
static modelName = 'peptidesModel';
|
|
38
62
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
_newClusterSubject: rxjs.Subject<undefined>
|
|
42
|
-
_removeClusterSubject: rxjs.Subject<undefined>
|
|
43
|
-
_filterChangedSubject: rxjs.Subject<undefined>
|
|
63
|
+
_settingsSubject: rxjs.Subject<type.PeptidesSettings> = new rxjs.Subject();
|
|
64
|
+
_monomerPositionSelectionSubject: rxjs.Subject<undefined> = new rxjs.Subject();
|
|
65
|
+
_newClusterSubject: rxjs.Subject<undefined> = new rxjs.Subject();
|
|
66
|
+
_removeClusterSubject: rxjs.Subject<undefined> = new rxjs.Subject();
|
|
67
|
+
_filterChangedSubject: rxjs.Subject<undefined> = new rxjs.Subject();
|
|
44
68
|
|
|
45
69
|
_isUpdating: boolean = false;
|
|
46
70
|
isBitsetChangedInitialized = false;
|
|
47
71
|
isCellChanging = false;
|
|
72
|
+
isUserChangedSelection = true;
|
|
48
73
|
|
|
49
74
|
df: DG.DataFrame;
|
|
50
75
|
splitCol!: DG.Column<boolean>;
|
|
51
|
-
edf: DG.DataFrame | null = null;
|
|
52
76
|
_monomerPositionStats?: MonomerPositionStats;
|
|
53
|
-
_clusterStats?:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
77
|
+
_clusterStats?: ClusterTypeStats;
|
|
78
|
+
_monomerPositionSelection!: type.PositionToAARList;
|
|
79
|
+
_monomerPositionFilter!: type.PositionToAARList;
|
|
80
|
+
_clusterSelection!: string[];
|
|
81
|
+
_mutationCliffs?: type.MutationCliffs;
|
|
58
82
|
isInitialized = false;
|
|
59
83
|
_analysisView?: DG.TableView;
|
|
60
84
|
|
|
61
|
-
isPeptideSpaceChangingBitset = false;
|
|
62
|
-
isChangingEdfBitset = false;
|
|
63
|
-
|
|
64
85
|
monomerMap: { [key: string]: { molfile: string, fullName: string } } = {};
|
|
65
86
|
monomerLib: IMonomerLib | null = null; // To get monomers from lib(s)
|
|
66
87
|
monomerWorks: MonomerWorks | null = null; // To get processed monomers
|
|
@@ -79,6 +100,9 @@ export class PeptidesModel {
|
|
|
79
100
|
_mostPotentResiduesDf?: DG.DataFrame;
|
|
80
101
|
_matrixDf?: DG.DataFrame;
|
|
81
102
|
_splitSeqDf?: DG.DataFrame;
|
|
103
|
+
_distanceMatrix!: DistanceMatrix;
|
|
104
|
+
_treeHelper!: ITreeHelper;
|
|
105
|
+
_dm!: DistanceMatrix;
|
|
82
106
|
|
|
83
107
|
private constructor(dataFrame: DG.DataFrame) {
|
|
84
108
|
this.df = dataFrame;
|
|
@@ -91,6 +115,11 @@ export class PeptidesModel {
|
|
|
91
115
|
return dataFrame.temp[PeptidesModel.modelName] as PeptidesModel;
|
|
92
116
|
}
|
|
93
117
|
|
|
118
|
+
get treeHelper(): ITreeHelper {
|
|
119
|
+
this._treeHelper ??= getTreeHelperInstance();
|
|
120
|
+
return this._treeHelper;
|
|
121
|
+
}
|
|
122
|
+
|
|
94
123
|
get monomerPositionDf(): DG.DataFrame {
|
|
95
124
|
this._monomerPositionDf ??= this.createMonomerPositionDf();
|
|
96
125
|
return this._monomerPositionDf;
|
|
@@ -141,27 +170,27 @@ export class PeptidesModel {
|
|
|
141
170
|
return col.getTag(bioTAGS.alphabet);
|
|
142
171
|
}
|
|
143
172
|
|
|
144
|
-
get
|
|
145
|
-
if (this.
|
|
146
|
-
return this.
|
|
173
|
+
get mutationCliffs(): type.MutationCliffs {
|
|
174
|
+
if (this._mutationCliffs)
|
|
175
|
+
return this._mutationCliffs;
|
|
147
176
|
|
|
148
177
|
const scaledActivityCol: DG.Column<number> = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
149
178
|
//TODO: set categories ordering the same to share compare indexes instead of strings
|
|
150
179
|
const monomerColumns: type.RawColumn[] = this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER).map(extractMonomerInfo);
|
|
151
|
-
this.
|
|
152
|
-
return this.
|
|
180
|
+
this._mutationCliffs = findMutations(scaledActivityCol.getRawData(), monomerColumns, this.settings);
|
|
181
|
+
return this._mutationCliffs;
|
|
153
182
|
}
|
|
154
183
|
|
|
155
|
-
set
|
|
156
|
-
this.
|
|
184
|
+
set mutationCliffs(si: type.MutationCliffs) {
|
|
185
|
+
this._mutationCliffs = si;
|
|
157
186
|
}
|
|
158
187
|
|
|
159
|
-
get clusterStats():
|
|
188
|
+
get clusterStats(): ClusterTypeStats {
|
|
160
189
|
this._clusterStats ??= this.calculateClusterStatistics();
|
|
161
190
|
return this._clusterStats;
|
|
162
191
|
}
|
|
163
192
|
|
|
164
|
-
set clusterStats(clusterStats:
|
|
193
|
+
set clusterStats(clusterStats: ClusterTypeStats) {
|
|
165
194
|
this._clusterStats = clusterStats;
|
|
166
195
|
}
|
|
167
196
|
|
|
@@ -184,12 +213,12 @@ export class PeptidesModel {
|
|
|
184
213
|
return this._analysisView;
|
|
185
214
|
}
|
|
186
215
|
|
|
187
|
-
get
|
|
188
|
-
return this.
|
|
216
|
+
get onMonomerPositionSelectionChanged(): rxjs.Observable<undefined> {
|
|
217
|
+
return this._monomerPositionSelectionSubject.asObservable();
|
|
189
218
|
}
|
|
190
219
|
|
|
191
220
|
get onSettingsChanged(): rxjs.Observable<type.PeptidesSettings> {
|
|
192
|
-
return this.
|
|
221
|
+
return this._settingsSubject.asObservable();
|
|
193
222
|
}
|
|
194
223
|
|
|
195
224
|
get onNewCluster(): rxjs.Observable<undefined> {
|
|
@@ -204,40 +233,40 @@ export class PeptidesModel {
|
|
|
204
233
|
return this._filterChangedSubject.asObservable();
|
|
205
234
|
}
|
|
206
235
|
|
|
207
|
-
get
|
|
208
|
-
this.
|
|
209
|
-
return this.
|
|
236
|
+
get monomerPositionSelection(): type.PositionToAARList {
|
|
237
|
+
this._monomerPositionSelection ??= JSON.parse(this.df.tags[C.TAGS.SELECTION] || '{}');
|
|
238
|
+
return this._monomerPositionSelection;
|
|
210
239
|
}
|
|
211
240
|
|
|
212
|
-
set
|
|
213
|
-
this.
|
|
241
|
+
set monomerPositionSelection(selection: type.PositionToAARList) {
|
|
242
|
+
this._monomerPositionSelection = selection;
|
|
214
243
|
this.df.tags[C.TAGS.SELECTION] = JSON.stringify(selection);
|
|
215
244
|
this.fireBitsetChanged();
|
|
216
|
-
this.
|
|
245
|
+
this._monomerPositionSelectionSubject.next();
|
|
217
246
|
this.analysisView.grid.invalidate();
|
|
218
247
|
}
|
|
219
248
|
|
|
220
|
-
get
|
|
221
|
-
this.
|
|
222
|
-
return this.
|
|
249
|
+
get monomerPositionFilter(): type.PositionToAARList {
|
|
250
|
+
this._monomerPositionFilter ??= JSON.parse(this.df.tags[C.TAGS.FILTER] || '{}');
|
|
251
|
+
return this._monomerPositionFilter;
|
|
223
252
|
}
|
|
224
253
|
|
|
225
|
-
set
|
|
226
|
-
this.
|
|
254
|
+
set monomerPositionFilter(selection: type.PositionToAARList) {
|
|
255
|
+
this._monomerPositionFilter = selection;
|
|
227
256
|
this.df.tags[C.TAGS.FILTER] = JSON.stringify(selection);
|
|
228
257
|
this.isInvariantMapTrigger = true;
|
|
229
|
-
this.fireBitsetChanged(
|
|
258
|
+
this.fireBitsetChanged(true);
|
|
230
259
|
this.isInvariantMapTrigger = false;
|
|
231
260
|
this.analysisView.grid.invalidate();
|
|
232
261
|
}
|
|
233
262
|
|
|
234
|
-
get
|
|
235
|
-
this.
|
|
236
|
-
return this.
|
|
263
|
+
get clusterSelection(): string[] {
|
|
264
|
+
this._clusterSelection ??= JSON.parse(this.df.tags[C.TAGS.CLUSTER_SELECTION] || '[]');
|
|
265
|
+
return this._clusterSelection;
|
|
237
266
|
}
|
|
238
267
|
|
|
239
|
-
set
|
|
240
|
-
this.
|
|
268
|
+
set clusterSelection(selection: string[]) {
|
|
269
|
+
this._clusterSelection = selection;
|
|
241
270
|
this.df.tags[C.TAGS.CLUSTER_SELECTION] = JSON.stringify(selection);
|
|
242
271
|
this.fireBitsetChanged();
|
|
243
272
|
this.analysisView.grid.invalidate();
|
|
@@ -263,24 +292,16 @@ export class PeptidesModel {
|
|
|
263
292
|
this.df.tags['distributionSplit'] = `${splitByAARFlag}${flag ? 1 : 0}`;
|
|
264
293
|
}
|
|
265
294
|
|
|
266
|
-
get
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
set isInvariantMap(x: boolean) {
|
|
271
|
-
this.df.setTag('isInvariantMap', x ? '1' : '0');
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
get isMutationCliffSelectionEmpty(): boolean {
|
|
275
|
-
for (const aarList of Object.values(this.mutationCliffsSelection)) {
|
|
295
|
+
get isMonomerPositionSelectionEmpty(): boolean {
|
|
296
|
+
for (const aarList of Object.values(this.monomerPositionSelection)) {
|
|
276
297
|
if (aarList.length !== 0)
|
|
277
298
|
return false;
|
|
278
299
|
}
|
|
279
300
|
return true;
|
|
280
301
|
}
|
|
281
302
|
|
|
282
|
-
get
|
|
283
|
-
return this.
|
|
303
|
+
get isClusterSelectionEmpty(): boolean {
|
|
304
|
+
return this.clusterSelection.length === 0;
|
|
284
305
|
}
|
|
285
306
|
|
|
286
307
|
get customClusters(): Iterable<DG.Column<boolean>> {
|
|
@@ -290,7 +311,7 @@ export class PeptidesModel {
|
|
|
290
311
|
}
|
|
291
312
|
|
|
292
313
|
get settings(): type.PeptidesSettings {
|
|
293
|
-
this._settings ??= JSON.parse(this.df.getTag('settings')
|
|
314
|
+
this._settings ??= JSON.parse(this.df.getTag('settings')!);
|
|
294
315
|
return this._settings;
|
|
295
316
|
}
|
|
296
317
|
|
|
@@ -305,17 +326,28 @@ export class PeptidesModel {
|
|
|
305
326
|
updateVars.add('mutationCliffs');
|
|
306
327
|
updateVars.add('stats');
|
|
307
328
|
break;
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
329
|
+
case 'columns':
|
|
330
|
+
updateVars.add('grid');
|
|
331
|
+
break;
|
|
311
332
|
case 'maxMutations':
|
|
312
333
|
case 'minActivityDelta':
|
|
313
334
|
updateVars.add('mutationCliffs');
|
|
314
335
|
break;
|
|
336
|
+
case 'showDendrogram':
|
|
337
|
+
updateVars.add('dendrogram');
|
|
338
|
+
break;
|
|
339
|
+
case 'showLogoSummaryTable':
|
|
340
|
+
updateVars.add('logoSummaryTable');
|
|
341
|
+
break;
|
|
342
|
+
case 'showMonomerPosition':
|
|
343
|
+
updateVars.add('monomerPosition');
|
|
344
|
+
break;
|
|
345
|
+
case 'showMostPotentResidues':
|
|
346
|
+
updateVars.add('mostPotentResidues');
|
|
347
|
+
break;
|
|
315
348
|
}
|
|
316
349
|
}
|
|
317
350
|
this.df.setTag('settings', JSON.stringify(this._settings));
|
|
318
|
-
// this.updateDefault();
|
|
319
351
|
for (const variable of updateVars) {
|
|
320
352
|
switch (variable) {
|
|
321
353
|
case 'activity':
|
|
@@ -324,8 +356,8 @@ export class PeptidesModel {
|
|
|
324
356
|
case 'mutationCliffs':
|
|
325
357
|
const scaledActivityCol: DG.Column<number> = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
326
358
|
//TODO: set categories ordering the same to share compare indexes instead of strings
|
|
327
|
-
const
|
|
328
|
-
this.
|
|
359
|
+
const monomerCols: type.RawColumn[] = this.df.columns.bySemTypeAll(C.SEM_TYPES.MONOMER).map(extractMonomerInfo);
|
|
360
|
+
this.mutationCliffs = findMutations(scaledActivityCol.getRawData(), monomerCols, this.settings);
|
|
329
361
|
break;
|
|
330
362
|
case 'stats':
|
|
331
363
|
this.monomerPositionStats = this.calculateMonomerPositionStatistics();
|
|
@@ -334,22 +366,34 @@ export class PeptidesModel {
|
|
|
334
366
|
this.clusterStats = this.calculateClusterStatistics();
|
|
335
367
|
break;
|
|
336
368
|
case 'grid':
|
|
337
|
-
this.
|
|
369
|
+
this.postProcessGrids();
|
|
370
|
+
break;
|
|
371
|
+
case 'dendrogram':
|
|
372
|
+
this.settings.showDendrogram ? this.addDendrogram() : this.closeViewer(VIEWER_TYPE.DENDROGRAM);
|
|
373
|
+
break;
|
|
374
|
+
case 'logoSummaryTable':
|
|
375
|
+
this.settings.showLogoSummaryTable ? this.addLogoSummaryTable() :
|
|
376
|
+
this.closeViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE);
|
|
377
|
+
break;
|
|
378
|
+
case 'monomerPosition':
|
|
379
|
+
this.settings.showMonomerPosition ? this.addMonomerPosition() :
|
|
380
|
+
this.closeViewer(VIEWER_TYPE.MONOMER_POSITION);
|
|
381
|
+
break;
|
|
382
|
+
case 'mostPotentResidues':
|
|
383
|
+
this.settings.showMostPotentResidues ? this.addMostPotentResidues() :
|
|
384
|
+
this.closeViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES);
|
|
338
385
|
break;
|
|
339
386
|
}
|
|
340
387
|
}
|
|
341
388
|
|
|
342
389
|
//TODO: handle settings change
|
|
343
|
-
this.
|
|
390
|
+
this._settingsSubject.next(this.settings);
|
|
344
391
|
}
|
|
345
392
|
|
|
346
393
|
createMonomerPositionDf(): DG.DataFrame {
|
|
347
394
|
const positions = this.splitSeqDf.columns.names();
|
|
348
395
|
const matrixDf = this.matrixDf
|
|
349
396
|
.groupBy([C.COLUMNS_NAMES.MONOMER])
|
|
350
|
-
// .pivot(C.COLUMNS_NAMES.POSITION)
|
|
351
|
-
// .add('values')
|
|
352
|
-
// .add('first', C.COLUMNS_NAMES.MEAN_DIFFERENCE, '')
|
|
353
397
|
.aggregate();
|
|
354
398
|
for (const pos of positions)
|
|
355
399
|
matrixDf.columns.addNewString(pos);
|
|
@@ -384,7 +428,7 @@ export class PeptidesModel {
|
|
|
384
428
|
return splitSeqDf;
|
|
385
429
|
}
|
|
386
430
|
|
|
387
|
-
|
|
431
|
+
getCompoundBitset(): DG.BitSet {
|
|
388
432
|
return this.df.selection.clone().and(this.df.filter);
|
|
389
433
|
}
|
|
390
434
|
|
|
@@ -395,17 +439,19 @@ export class PeptidesModel {
|
|
|
395
439
|
|
|
396
440
|
const acc = ui.accordion();
|
|
397
441
|
acc.root.style.width = '100%';
|
|
398
|
-
const filterAndSelectionBs = trueModel.
|
|
442
|
+
const filterAndSelectionBs = trueModel.getCompoundBitset();
|
|
399
443
|
const filteredTitlePart = trueModel.df.filter.anyFalse ? ` among ${trueModel.df.filter.trueCount} filtered` : '';
|
|
400
444
|
acc.addTitle(ui.h1(`${filterAndSelectionBs.trueCount} selected rows${filteredTitlePart}`));
|
|
401
445
|
if (filterAndSelectionBs.anyTrue) {
|
|
402
446
|
acc.addPane('Actions', () => {
|
|
403
|
-
const newViewButton = ui.button('New view',
|
|
447
|
+
const newViewButton = ui.button('New view', () => trueModel.createNewView(),
|
|
404
448
|
'Creates a new view from current selection');
|
|
405
449
|
const newCluster = ui.button('New cluster', () => trueModel._newClusterSubject.next(),
|
|
406
450
|
'Creates a new cluster from selection');
|
|
407
451
|
const removeCluster = ui.button('Remove cluster', () => trueModel._removeClusterSubject.next(),
|
|
408
452
|
'Removes currently selected custom cluster');
|
|
453
|
+
removeCluster.disabled = trueModel.clusterSelection.length === 0 ||
|
|
454
|
+
!wu(this.customClusters).some((c) => trueModel.clusterSelection.includes(c.name));
|
|
409
455
|
return ui.divV([newViewButton, newCluster, removeCluster]);
|
|
410
456
|
});
|
|
411
457
|
}
|
|
@@ -423,7 +469,8 @@ export class PeptidesModel {
|
|
|
423
469
|
|
|
424
470
|
this.createScaledCol();
|
|
425
471
|
|
|
426
|
-
this.
|
|
472
|
+
this.initMonomerPositionFilter({notify: false});
|
|
473
|
+
this.initMonomerPositionSelection({notify: false});
|
|
427
474
|
|
|
428
475
|
this.setWebLogoInteraction();
|
|
429
476
|
this.webLogoBounds = {};
|
|
@@ -437,16 +484,38 @@ export class PeptidesModel {
|
|
|
437
484
|
this.postProcessGrids();
|
|
438
485
|
}
|
|
439
486
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
487
|
+
initMonomerPositionFilter(options: {cleanInit?: boolean, notify?: boolean} = {}): void {
|
|
488
|
+
options.cleanInit ??= false;
|
|
489
|
+
options.notify ??= true;
|
|
490
|
+
|
|
491
|
+
const tempFilter: type.PositionToAARList = this.monomerPositionFilter;
|
|
443
492
|
const positionColumns = this.splitSeqDf.columns.names();
|
|
444
493
|
for (const pos of positionColumns) {
|
|
445
|
-
|
|
446
|
-
|
|
494
|
+
if (options.cleanInit || !tempFilter.hasOwnProperty(pos))
|
|
495
|
+
tempFilter[pos] = [];
|
|
447
496
|
}
|
|
448
|
-
|
|
449
|
-
|
|
497
|
+
|
|
498
|
+
if (options.notify)
|
|
499
|
+
this.monomerPositionFilter = tempFilter;
|
|
500
|
+
else
|
|
501
|
+
this._monomerPositionFilter = tempFilter;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
initMonomerPositionSelection(options: {cleanInit?: boolean, notify?: boolean} = {}): void {
|
|
505
|
+
options.cleanInit ??= false;
|
|
506
|
+
options.notify ??= true;
|
|
507
|
+
|
|
508
|
+
const tempSelection: type.PositionToAARList = this.monomerPositionSelection;
|
|
509
|
+
const positionColumns = this.splitSeqDf.columns.names();
|
|
510
|
+
for (const pos of positionColumns) {
|
|
511
|
+
if (options.cleanInit || !tempSelection.hasOwnProperty(pos))
|
|
512
|
+
tempSelection[pos] = [];
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (options.notify)
|
|
516
|
+
this.monomerPositionSelection = tempSelection;
|
|
517
|
+
else
|
|
518
|
+
this._monomerPositionSelection = tempSelection;
|
|
450
519
|
}
|
|
451
520
|
|
|
452
521
|
joinDataFrames(): void {
|
|
@@ -505,31 +574,17 @@ export class PeptidesModel {
|
|
|
505
574
|
|
|
506
575
|
for (const posCol of positionColumns) {
|
|
507
576
|
const posColData = posCol.getRawData();
|
|
508
|
-
const
|
|
577
|
+
const posColCateogries = posCol.categories;
|
|
509
578
|
const currentPositionObject = {general: {}} as PositionStats & { general: SummaryStats };
|
|
510
579
|
|
|
511
|
-
for (
|
|
580
|
+
for (let categoryIndex = 0; categoryIndex < posColCateogries.length; ++categoryIndex) {
|
|
581
|
+
const monomer = posColCateogries[categoryIndex];
|
|
512
582
|
if (monomer == '')
|
|
513
583
|
continue;
|
|
514
584
|
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
for (let j = 0; j < sourceDfLen; ++j) {
|
|
518
|
-
mask[j] = posColData[j] == categoryIndex;
|
|
519
|
-
|
|
520
|
-
if (mask[j])
|
|
521
|
-
++trueCount;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
const maskInfo = {
|
|
525
|
-
trueCount: trueCount,
|
|
526
|
-
falseCount: sourceDfLen - trueCount,
|
|
527
|
-
mask: mask,
|
|
528
|
-
};
|
|
529
|
-
|
|
530
|
-
const stats = getStats(activityColData, maskInfo);
|
|
585
|
+
const bitArray = BitArray.fromSeq(sourceDfLen, (i: number) => posColData[i] === categoryIndex);
|
|
586
|
+
const stats = getStats(activityColData, bitArray);
|
|
531
587
|
currentPositionObject[monomer] = stats;
|
|
532
|
-
|
|
533
588
|
this.getSummaryStats(currentPositionObject.general, stats);
|
|
534
589
|
}
|
|
535
590
|
monomerPositionObject[posCol.name] = currentPositionObject;
|
|
@@ -538,93 +593,87 @@ export class PeptidesModel {
|
|
|
538
593
|
return monomerPositionObject;
|
|
539
594
|
}
|
|
540
595
|
|
|
541
|
-
getSummaryStats(
|
|
596
|
+
getSummaryStats(genObj: SummaryStats, stats: Stats | null = null, summaryStats: SummaryStats | null = null): void {
|
|
542
597
|
if (stats == null && summaryStats == null)
|
|
543
598
|
throw new Error(`MonomerPositionStatsError: either stats or summaryStats must be present`);
|
|
544
599
|
|
|
545
600
|
const possibleMaxCount = stats?.count ?? summaryStats!.maxCount;
|
|
546
|
-
|
|
547
|
-
if (
|
|
548
|
-
|
|
601
|
+
genObj.maxCount ??= possibleMaxCount;
|
|
602
|
+
if (genObj.maxCount < possibleMaxCount)
|
|
603
|
+
genObj.maxCount = possibleMaxCount;
|
|
549
604
|
|
|
550
605
|
const possibleMinCount = stats?.count ?? summaryStats!.minCount;
|
|
551
|
-
|
|
552
|
-
if (
|
|
553
|
-
|
|
606
|
+
genObj.minCount ??= possibleMinCount;
|
|
607
|
+
if (genObj.minCount > possibleMinCount)
|
|
608
|
+
genObj.minCount = possibleMinCount;
|
|
554
609
|
|
|
555
610
|
const possibleMaxMeanDifference = stats?.meanDifference ?? summaryStats!.maxMeanDifference;
|
|
556
|
-
|
|
557
|
-
if (
|
|
558
|
-
|
|
611
|
+
genObj.maxMeanDifference ??= possibleMaxMeanDifference;
|
|
612
|
+
if (genObj.maxMeanDifference < possibleMaxMeanDifference)
|
|
613
|
+
genObj.maxMeanDifference = possibleMaxMeanDifference;
|
|
559
614
|
|
|
560
615
|
const possibleMinMeanDifference = stats?.meanDifference ?? summaryStats!.minMeanDifference;
|
|
561
|
-
|
|
562
|
-
if (
|
|
563
|
-
|
|
616
|
+
genObj.minMeanDifference ??= possibleMinMeanDifference;
|
|
617
|
+
if (genObj.minMeanDifference > possibleMinMeanDifference)
|
|
618
|
+
genObj.minMeanDifference = possibleMinMeanDifference;
|
|
564
619
|
|
|
565
620
|
const possibleMaxPValue = stats?.pValue ?? summaryStats!.maxPValue;
|
|
566
|
-
|
|
567
|
-
if (
|
|
568
|
-
|
|
621
|
+
genObj.maxPValue ??= possibleMaxPValue;
|
|
622
|
+
if (genObj.maxPValue < possibleMaxPValue)
|
|
623
|
+
genObj.maxPValue = possibleMaxPValue;
|
|
569
624
|
|
|
570
625
|
const possibleMinPValue = stats?.pValue ?? summaryStats!.minPValue;
|
|
571
|
-
|
|
572
|
-
if (
|
|
573
|
-
|
|
626
|
+
genObj.minPValue ??= possibleMinPValue;
|
|
627
|
+
if (genObj.minPValue > possibleMinPValue)
|
|
628
|
+
genObj.minPValue = possibleMinPValue;
|
|
574
629
|
|
|
575
630
|
const possibleMaxRatio = stats?.ratio ?? summaryStats!.maxRatio;
|
|
576
|
-
|
|
577
|
-
if (
|
|
578
|
-
|
|
631
|
+
genObj.maxRatio ??= possibleMaxRatio;
|
|
632
|
+
if (genObj.maxRatio < possibleMaxRatio)
|
|
633
|
+
genObj.maxRatio = possibleMaxRatio;
|
|
579
634
|
|
|
580
635
|
const possibleMinRatio = stats?.ratio ?? summaryStats!.minRatio;
|
|
581
|
-
|
|
582
|
-
if (
|
|
583
|
-
|
|
636
|
+
genObj.minRatio ??= possibleMinRatio;
|
|
637
|
+
if (genObj.minRatio > possibleMinRatio)
|
|
638
|
+
genObj.minRatio = possibleMinRatio;
|
|
584
639
|
}
|
|
585
640
|
|
|
586
|
-
calculateClusterStatistics():
|
|
587
|
-
const
|
|
588
|
-
const originalClustersColData = originalClustersCol.getRawData();
|
|
589
|
-
const originalClustersColCategories = originalClustersCol.categories;
|
|
641
|
+
calculateClusterStatistics(): ClusterTypeStats {
|
|
642
|
+
const rowCount = this.df.rowCount;
|
|
590
643
|
|
|
591
|
-
const
|
|
644
|
+
const origClustCol = this.df.getCol(this.settings.clustersColumnName!);
|
|
645
|
+
const origClustColData = origClustCol.getRawData();
|
|
646
|
+
const origClustColCat = origClustCol.categories;
|
|
647
|
+
const origClustMasks: BitArray[] = Array.from({length: origClustColCat.length},
|
|
648
|
+
() => BitArray.fromSeq(rowCount, (_: number) => false));
|
|
649
|
+
for (let rowIdx = 0; rowIdx < rowCount; ++rowIdx)
|
|
650
|
+
origClustMasks[origClustColData[rowIdx]].setTrue(rowIdx);
|
|
592
651
|
|
|
593
|
-
const activityColData: type.RawData = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).getRawData();
|
|
594
|
-
const activityColLen = activityColData.length;
|
|
595
|
-
|
|
596
|
-
// const resultStats: Stats[] = new Array(originalClustersColCategories.length + customClustersColumnsList.length);
|
|
597
|
-
const resultStats: {[cluster: string]: Stats} = {};
|
|
598
|
-
|
|
599
|
-
const clusterCount = originalClustersColCategories.length + customClustersColumnsList.length;
|
|
600
|
-
for (let clusterIdx = 0; clusterIdx < clusterCount; ++clusterIdx) {
|
|
601
|
-
const customClusterIdx = clusterIdx - originalClustersColCategories.length;
|
|
602
|
-
const customClusterColData = customClustersColumnsList[customClusterIdx]?.toList();
|
|
603
|
-
const isAcitvityIdxValid = customClusterIdx < 0 ?
|
|
604
|
-
(i: number) => clusterIdx == originalClustersColData[i] :
|
|
605
|
-
(i: number) => customClusterColData[i];
|
|
606
|
-
|
|
607
|
-
const mask: boolean[] = new Array(activityColLen);
|
|
608
|
-
let trueCount = 0;
|
|
609
|
-
for (let maskIdx = 0; maskIdx < activityColLen; ++maskIdx) {
|
|
610
|
-
mask[maskIdx] = isAcitvityIdxValid(maskIdx);
|
|
611
|
-
|
|
612
|
-
if (mask[maskIdx])
|
|
613
|
-
++trueCount;
|
|
614
|
-
}
|
|
615
652
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
653
|
+
const customClustColList = wu(this.customClusters).toArray();
|
|
654
|
+
const customClustMasks = customClustColList.map(
|
|
655
|
+
(v) => BitArray.fromUint32Array(rowCount, v.getRawData() as Uint32Array));
|
|
656
|
+
const customClustColNamesList = customClustColList.map((v) => v.name);
|
|
657
|
+
|
|
658
|
+
const activityColData: type.RawData = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).getRawData();
|
|
621
659
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
660
|
+
const origClustStats: ClusterStats = {};
|
|
661
|
+
const customClustStats: ClusterStats = {};
|
|
662
|
+
|
|
663
|
+
for (let clustType = 0; clustType < 2; ++clustType) {
|
|
664
|
+
const masks = clustType == 0 ? origClustMasks : customClustMasks;
|
|
665
|
+
const clustNames = clustType == 0 ? origClustColCat : customClustColNamesList;
|
|
666
|
+
const resultStats = clustType == 0 ? origClustStats : customClustStats;
|
|
667
|
+
for (let maskIdx = 0; maskIdx < masks.length; ++maskIdx) {
|
|
668
|
+
const mask = masks[maskIdx];
|
|
669
|
+
const stats = getStats(activityColData, mask);
|
|
670
|
+
resultStats[clustNames[maskIdx]] = stats;
|
|
671
|
+
}
|
|
626
672
|
}
|
|
627
673
|
|
|
674
|
+
const resultStats = {} as ClusterTypeStats;
|
|
675
|
+
resultStats[CLUSTER_TYPE.ORIGINAL] = origClustStats;
|
|
676
|
+
resultStats[CLUSTER_TYPE.CUSTOM] = customClustStats;
|
|
628
677
|
return resultStats;
|
|
629
678
|
}
|
|
630
679
|
|
|
@@ -671,18 +720,23 @@ export class PeptidesModel {
|
|
|
671
720
|
}
|
|
672
721
|
|
|
673
722
|
modifyClusterSelection(cluster: string): void {
|
|
674
|
-
const tempSelection = this.
|
|
723
|
+
const tempSelection = this.clusterSelection;
|
|
675
724
|
const idx = tempSelection.indexOf(cluster);
|
|
676
725
|
if (idx !== -1)
|
|
677
726
|
tempSelection.splice(idx, 1);
|
|
678
727
|
else
|
|
679
728
|
tempSelection.push(cluster);
|
|
680
729
|
|
|
681
|
-
this.
|
|
730
|
+
this.clusterSelection = tempSelection;
|
|
682
731
|
}
|
|
683
732
|
|
|
684
|
-
initClusterSelection(
|
|
685
|
-
|
|
733
|
+
initClusterSelection(options: {notify?: boolean} = {}): void {
|
|
734
|
+
options.notify ??= true;
|
|
735
|
+
|
|
736
|
+
if (options.notify)
|
|
737
|
+
this.clusterSelection = [];
|
|
738
|
+
else
|
|
739
|
+
this._clusterSelection = [];
|
|
686
740
|
}
|
|
687
741
|
|
|
688
742
|
setWebLogoInteraction(): void {
|
|
@@ -714,14 +768,16 @@ export class PeptidesModel {
|
|
|
714
768
|
return null;
|
|
715
769
|
}
|
|
716
770
|
|
|
717
|
-
requestBarchartAction(ev: MouseEvent, barPart: {
|
|
771
|
+
requestBarchartAction(ev: MouseEvent, barPart: {position: string, monomer: string} | null): void {
|
|
718
772
|
if (!barPart)
|
|
719
773
|
return;
|
|
720
774
|
const monomer = barPart.monomer;
|
|
721
775
|
const position = barPart.position;
|
|
722
776
|
if (ev.type === 'click') {
|
|
723
|
-
ev.shiftKey
|
|
724
|
-
this.initMonomerPositionSelection(
|
|
777
|
+
if (!ev.shiftKey)
|
|
778
|
+
this.initMonomerPositionSelection({cleanInit: true, notify: false});
|
|
779
|
+
|
|
780
|
+
this.modifyMonomerPositionSelection(monomer, position, false);
|
|
725
781
|
} else {
|
|
726
782
|
const bar = `${position} = ${monomer}`;
|
|
727
783
|
if (this.cachedWebLogoTooltip.bar == bar)
|
|
@@ -760,12 +816,13 @@ export class PeptidesModel {
|
|
|
760
816
|
return 0;
|
|
761
817
|
}).filter((v) => v != 'general');
|
|
762
818
|
|
|
763
|
-
this.webLogoBounds[col.name] =
|
|
764
|
-
|
|
819
|
+
this.webLogoBounds[col.name] = CR.drawLogoInBounds(ctx, bounds, stats, sortedStatsOrder, this.df.rowCount,
|
|
820
|
+
this.cp, this.headerSelectedMonomers[col.name]);
|
|
765
821
|
gcArgs.preventDefault();
|
|
766
822
|
}
|
|
767
823
|
} catch (e) {
|
|
768
|
-
console.warn(`PeptidesHeaderLogoError: couldn't render WebLogo for column \`${col!.name}\`.
|
|
824
|
+
console.warn(`PeptidesHeaderLogoError: couldn't render WebLogo for column \`${col!.name}\`. ` +
|
|
825
|
+
`See original error below.`);
|
|
769
826
|
console.warn(e);
|
|
770
827
|
} finally {
|
|
771
828
|
ctx.restore();
|
|
@@ -783,12 +840,12 @@ export class PeptidesModel {
|
|
|
783
840
|
});
|
|
784
841
|
}
|
|
785
842
|
|
|
786
|
-
showMonomerTooltip(aar: string, x: number, y: number):
|
|
843
|
+
showMonomerTooltip(aar: string, x: number, y: number): boolean {
|
|
787
844
|
const tooltipElements: HTMLDivElement[] = [];
|
|
788
845
|
const monomerName = aar.toLowerCase();
|
|
789
846
|
|
|
790
|
-
const mw =
|
|
791
|
-
const mol = mw
|
|
847
|
+
const mw = getMonomerWorksInstance();
|
|
848
|
+
const mol = mw.getCappedRotatedMonomer('PEPTIDE', aar);
|
|
792
849
|
|
|
793
850
|
if (mol) {
|
|
794
851
|
tooltipElements.push(ui.div(monomerName));
|
|
@@ -798,6 +855,8 @@ export class PeptidesModel {
|
|
|
798
855
|
tooltipElements.push(ui.div(aar));
|
|
799
856
|
|
|
800
857
|
ui.tooltip.show(ui.divV(tooltipElements), x, y);
|
|
858
|
+
|
|
859
|
+
return mol !== null;
|
|
801
860
|
}
|
|
802
861
|
|
|
803
862
|
//TODO: move out to viewer code
|
|
@@ -807,92 +866,44 @@ export class PeptidesModel {
|
|
|
807
866
|
return null;
|
|
808
867
|
|
|
809
868
|
const activityCol = this.df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
810
|
-
|
|
811
|
-
const
|
|
812
|
-
const
|
|
813
|
-
const
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
});
|
|
821
|
-
const
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
const tempCol = DG.Column.float('', indexes.length);
|
|
826
|
-
tempCol.init((i) => currentColData[indexes[i]]);
|
|
827
|
-
colResults[`${agg}(${col})`] = tempCol.stats[agg as keyof DG.Stats] as number;
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
|
|
831
|
-
const das = getDistributionAndStats(distributionTable, stats, `${position} : ${aar}`, 'Other', true);
|
|
832
|
-
const resultMap: { [key: string]: any } = {...das.tableMap, ...colResults};
|
|
833
|
-
const distroStatsElem = wrapDistroAndStatsDefault(das.labels, das.histRoot, resultMap);
|
|
869
|
+
const posCol = this.df.getCol(position);
|
|
870
|
+
const posColCategories = posCol.categories;
|
|
871
|
+
const aarCategoryIndex = posColCategories.indexOf(aar);
|
|
872
|
+
const posColData = posCol.getRawData();
|
|
873
|
+
const mask = DG.BitSet.create(activityCol.length, (i) => posColData[i] === aarCategoryIndex);
|
|
874
|
+
|
|
875
|
+
const distributionTable = DG.DataFrame.fromColumns(
|
|
876
|
+
[activityCol, DG.Column.fromBitSet(C.COLUMNS_NAMES.SPLIT_COL, mask)]);
|
|
877
|
+
const labels = getDistributionLegend(`${position} : ${aar}`, 'Other');
|
|
878
|
+
const hist = getActivityDistribution(distributionTable, true);
|
|
879
|
+
const tableMap = getStatsTableMap(stats, {fractionDigits: 2});
|
|
880
|
+
const aggregatedColMap = this.getAggregatedColumnValues({mask: mask, fractionDigits: 2});
|
|
881
|
+
|
|
882
|
+
const resultMap = {...tableMap, ...aggregatedColMap};
|
|
883
|
+
const distroStatsElem = getStatsSummary(labels, hist, resultMap, true);
|
|
834
884
|
|
|
835
885
|
ui.tooltip.show(distroStatsElem, x, y);
|
|
836
886
|
|
|
837
887
|
return distroStatsElem;
|
|
838
888
|
}
|
|
839
889
|
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
const activityCol = filteredDf.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
845
|
-
const activityColData = activityCol.getRawData();
|
|
846
|
-
//TODO: use bitset instead of splitCol
|
|
847
|
-
const clusterCol = filteredDf.getCol(this.settings.clustersColumnName!);
|
|
848
|
-
const clusterColData = clusterCol.getRawData();
|
|
849
|
-
let splitCol = DG.Column.bool(C.COLUMNS_NAMES.SPLIT_COL, activityCol.length);
|
|
850
|
-
const indexes: number[] = [];
|
|
851
|
-
splitCol.init((i) => {
|
|
852
|
-
const result = clusterColData[i] == cluster;
|
|
853
|
-
if (result)
|
|
854
|
-
indexes.push(i);
|
|
855
|
-
return result;
|
|
856
|
-
});
|
|
857
|
-
if (splitCol.max == 0)
|
|
858
|
-
splitCol = filteredDf.getCol(clusterName);
|
|
859
|
-
const distDf = DG.DataFrame.fromColumns([activityCol, splitCol]);
|
|
860
|
-
|
|
861
|
-
let stats: Stats;
|
|
862
|
-
if (bs.anyFalse) {
|
|
863
|
-
const trueCount = splitCol.stats.sum;
|
|
864
|
-
const maskInfo = {
|
|
865
|
-
trueCount: trueCount,
|
|
866
|
-
falseCount: activityColData.length - trueCount,
|
|
867
|
-
mask: splitCol.toList() as boolean[],
|
|
868
|
-
};
|
|
869
|
-
stats = getStats(activityColData, maskInfo);
|
|
870
|
-
} else
|
|
871
|
-
stats = this.clusterStats[clusterName];
|
|
890
|
+
getAggregatedColumnValues(options: {filterDf?: boolean, mask?: DG.BitSet, fractionDigits?: number} = {},
|
|
891
|
+
): StringDictionary {
|
|
892
|
+
options.filterDf ??= false;
|
|
872
893
|
|
|
873
|
-
|
|
874
|
-
return null;
|
|
894
|
+
const filteredDf = options.filterDf && this.df.filter.anyFalse ? this.df.clone(this.df.filter) : this.df;
|
|
875
895
|
|
|
876
|
-
const colResults:
|
|
877
|
-
for (const [
|
|
878
|
-
const
|
|
879
|
-
const
|
|
880
|
-
|
|
881
|
-
tempCol.init((i) => currentColData[indexes[i]]);
|
|
882
|
-
colResults[`${agg}(${col})`] = tempCol.stats[agg as keyof DG.Stats] as number;
|
|
896
|
+
const colResults: StringDictionary = {};
|
|
897
|
+
for (const [colName, aggFn] of Object.entries(this.settings.columns!)) {
|
|
898
|
+
const newColName = getAggregatedColName(aggFn, colName);
|
|
899
|
+
const value = getAggregatedValue(filteredDf.getCol(colName), aggFn, options.mask);
|
|
900
|
+
colResults[newColName] = value.toFixed(options.fractionDigits);
|
|
883
901
|
}
|
|
884
|
-
|
|
885
|
-
const das = getDistributionAndStats(distDf, stats, `Cluster: ${clusterName}`, 'Other', true, splitCol.name);
|
|
886
|
-
const resultMap: {[key: string]: any} = {...das.tableMap, ...colResults};
|
|
887
|
-
const tooltip = wrapDistroAndStatsDefault(das.labels, das.histRoot, resultMap, true);
|
|
888
|
-
|
|
889
|
-
ui.tooltip.show(tooltip, x, y);
|
|
890
|
-
|
|
891
|
-
return tooltip;
|
|
902
|
+
return colResults;
|
|
892
903
|
}
|
|
893
904
|
|
|
894
|
-
modifyMonomerPositionSelection(aar: string, position: string,
|
|
895
|
-
const tempSelection =
|
|
905
|
+
modifyMonomerPositionSelection(aar: string, position: string, isFilter: boolean): void {
|
|
906
|
+
const tempSelection = isFilter ? this.monomerPositionFilter : this.monomerPositionSelection;
|
|
896
907
|
const tempSelectionAt = tempSelection[position];
|
|
897
908
|
const aarIndex = tempSelectionAt.indexOf(aar);
|
|
898
909
|
if (aarIndex === -1)
|
|
@@ -900,22 +911,10 @@ export class PeptidesModel {
|
|
|
900
911
|
else
|
|
901
912
|
tempSelectionAt.splice(aarIndex, 1);
|
|
902
913
|
|
|
903
|
-
if (
|
|
904
|
-
this.
|
|
914
|
+
if (isFilter)
|
|
915
|
+
this.monomerPositionFilter = tempSelection;
|
|
905
916
|
else
|
|
906
|
-
this.
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
initMonomerPositionSelection(aar: string, position: string, isInvariantMapSelection: boolean): void {
|
|
910
|
-
const tempSelection = isInvariantMapSelection ? this.invariantMapSelection : this.mutationCliffsSelection;
|
|
911
|
-
for (const key of Object.keys(tempSelection))
|
|
912
|
-
tempSelection[key] = [];
|
|
913
|
-
tempSelection[position] = [aar];
|
|
914
|
-
|
|
915
|
-
if (isInvariantMapSelection)
|
|
916
|
-
this.invariantMapSelection = tempSelection;
|
|
917
|
-
else
|
|
918
|
-
this.mutationCliffsSelection = tempSelection;
|
|
917
|
+
this.monomerPositionSelection = tempSelection;
|
|
919
918
|
}
|
|
920
919
|
|
|
921
920
|
setBitsetCallback(): void {
|
|
@@ -925,51 +924,56 @@ export class PeptidesModel {
|
|
|
925
924
|
const filter = this.df.filter;
|
|
926
925
|
const clusterCol = this.df.col(this.settings.clustersColumnName!);
|
|
927
926
|
|
|
928
|
-
const changeSelectionBitset = (currentBitset: DG.BitSet
|
|
929
|
-
|
|
930
|
-
const
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
return;
|
|
936
|
-
|
|
937
|
-
currentBitset.init((i) => edfSelection.get(i) || false, false);
|
|
938
|
-
return;
|
|
939
|
-
}
|
|
927
|
+
const changeSelectionBitset = (currentBitset: DG.BitSet, posList: type.RawColumn[], clustColCat: string[],
|
|
928
|
+
clustColData: type.RawData, customClust: {[key: string]: BitArray}): void => {
|
|
929
|
+
const getBitAt = (i: number): boolean => {
|
|
930
|
+
for (const posRawCol of posList) {
|
|
931
|
+
if (this.monomerPositionSelection[posRawCol.name].includes(posRawCol.cat![posRawCol.rawData[i]]))
|
|
932
|
+
return true;
|
|
933
|
+
}
|
|
940
934
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
this.isChangingEdfBitset = false;
|
|
945
|
-
};
|
|
935
|
+
const currentOrigClust = clustColCat[clustColData[i]];
|
|
936
|
+
if (typeof currentOrigClust === undefined)
|
|
937
|
+
return false;
|
|
946
938
|
|
|
947
|
-
|
|
939
|
+
for (const clust of this.clusterSelection) {
|
|
940
|
+
if (clust === currentOrigClust)
|
|
941
|
+
return true;
|
|
948
942
|
|
|
949
|
-
|
|
950
|
-
const getBitAt = (i: number): boolean => {
|
|
951
|
-
for (const position of positionList) {
|
|
952
|
-
const positionCol: DG.Column<string> = this.df.getCol(position);
|
|
953
|
-
if (this.mutationCliffsSelection[position].includes(positionCol.get(i)!))
|
|
943
|
+
if (Object.hasOwn(customClust, clust) && customClust[clust].getBit(i))
|
|
954
944
|
return true;
|
|
955
945
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
this.logoSummarySelection.some((cluster) => this.df.columns.contains(cluster) && this.df.get(cluster, i));
|
|
946
|
+
|
|
947
|
+
return false;
|
|
959
948
|
};
|
|
960
949
|
currentBitset.init((i) => getBitAt(i), false);
|
|
961
|
-
|
|
962
|
-
updateEdfSelection();
|
|
963
950
|
};
|
|
964
951
|
|
|
965
|
-
selection.onChanged.subscribe(() =>
|
|
952
|
+
selection.onChanged.subscribe(() => {
|
|
953
|
+
if (this.isUserChangedSelection)
|
|
954
|
+
return;
|
|
955
|
+
|
|
956
|
+
const positionList: type.RawColumn[] = Object.keys(this.monomerPositionSelection).map((pos) => {
|
|
957
|
+
const posCol = this.df.getCol(pos);
|
|
958
|
+
return {name: pos, cat: posCol.categories, rawData: posCol.getRawData()};
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
const clustColCat = clusterCol?.categories ?? [];
|
|
962
|
+
const clustColData = clusterCol?.getRawData() ?? new Int32Array(0);
|
|
963
|
+
const customClust: {[key: string]: BitArray} = {};
|
|
964
|
+
const rowCount = this.df.rowCount;
|
|
965
|
+
for (const clust of this.customClusters)
|
|
966
|
+
customClust[clust.name] = BitArray.fromUint32Array(rowCount, clust.getRawData() as Uint32Array);
|
|
967
|
+
|
|
968
|
+
changeSelectionBitset(selection, positionList, clustColCat, clustColData, customClust);
|
|
969
|
+
});
|
|
966
970
|
|
|
967
971
|
filter.onChanged.subscribe(() => {
|
|
968
|
-
const positionList = Object.keys(this.
|
|
972
|
+
const positionList = Object.keys(this.monomerPositionFilter);
|
|
969
973
|
const invariantMapBitset = DG.BitSet.create(filter.length, (index) => {
|
|
970
974
|
let result = true;
|
|
971
975
|
for (const position of positionList) {
|
|
972
|
-
const aarList = this.
|
|
976
|
+
const aarList = this.monomerPositionFilter[position];
|
|
973
977
|
result &&= aarList.length === 0 || aarList.includes(this.df.get(position, index));
|
|
974
978
|
if (!result)
|
|
975
979
|
return result;
|
|
@@ -988,8 +992,8 @@ export class PeptidesModel {
|
|
|
988
992
|
this.isBitsetChangedInitialized = true;
|
|
989
993
|
}
|
|
990
994
|
|
|
991
|
-
fireBitsetChanged(
|
|
992
|
-
this.
|
|
995
|
+
fireBitsetChanged(fireFilterChanged: boolean = false): void {
|
|
996
|
+
this.isUserChangedSelection = false;
|
|
993
997
|
this.df.selection.fireChanged();
|
|
994
998
|
if (fireFilterChanged)
|
|
995
999
|
this.df.filter.fireChanged();
|
|
@@ -1002,8 +1006,7 @@ export class PeptidesModel {
|
|
|
1002
1006
|
for (const pane of acc.panes)
|
|
1003
1007
|
pane.expanded = true;
|
|
1004
1008
|
}
|
|
1005
|
-
|
|
1006
|
-
this.isPeptideSpaceChangingBitset = false;
|
|
1009
|
+
this.isUserChangedSelection = true;
|
|
1007
1010
|
}
|
|
1008
1011
|
|
|
1009
1012
|
postProcessGrids(): void {
|
|
@@ -1015,7 +1018,6 @@ export class PeptidesModel {
|
|
|
1015
1018
|
const sourceGridProps = sourceGrid.props;
|
|
1016
1019
|
sourceGridProps.allowColSelection = false;
|
|
1017
1020
|
sourceGridProps.allowEdit = false;
|
|
1018
|
-
sourceGridProps.allowRowResizing = false;
|
|
1019
1021
|
sourceGridProps.showCurrentRowIndicator = false;
|
|
1020
1022
|
this.df.temp[C.EMBEDDING_STATUS] = false;
|
|
1021
1023
|
for (let colIdx = 1; colIdx < sourceGridColsLen; ++colIdx) {
|
|
@@ -1023,7 +1025,41 @@ export class PeptidesModel {
|
|
|
1023
1025
|
const tableColName = gridCol.column!.name;
|
|
1024
1026
|
gridCol.visible = posCols.includes(tableColName) || (tableColName === C.COLUMNS_NAMES.ACTIVITY_SCALED) ||
|
|
1025
1027
|
visibleColumns.includes(tableColName);
|
|
1026
|
-
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
closeViewer(viewerType: VIEWER_TYPE): void {
|
|
1032
|
+
const viewer = this.findViewer(viewerType);
|
|
1033
|
+
viewer?.detach();
|
|
1034
|
+
viewer?.close();
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
findViewerNode(viewerType: VIEWER_TYPE): DG.DockNode | null {
|
|
1038
|
+
for (const node of this.analysisView.dockManager.rootNode.children) {
|
|
1039
|
+
if (node.container.containerElement.innerHTML.includes(viewerType))
|
|
1040
|
+
return node;
|
|
1041
|
+
}
|
|
1042
|
+
return null;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
async addDendrogram(): Promise<void> {
|
|
1046
|
+
const pi = DG.TaskBarProgressIndicator.create('Calculating distance matrix...');
|
|
1047
|
+
try {
|
|
1048
|
+
const pepColValues: string[] = this.df.getCol(this.settings.sequenceColumnName!).toList();
|
|
1049
|
+
this._dm ??= new DistanceMatrix(await createDistanceMatrixWorker(pepColValues, StringMetricsNames.Levenshtein));
|
|
1050
|
+
const leafCol = this.df.col('~leaf-id') ?? this.df.columns.addNewString('~leaf-id').init((i) => i.toString());
|
|
1051
|
+
const treeNode = await this.treeHelper.hierarchicalClusteringByDistance(this._dm, 'ward');
|
|
1052
|
+
|
|
1053
|
+
this.df.setTag(treeTAGS.NEWICK, this.treeHelper.toNewick(treeNode));
|
|
1054
|
+
const leafOrdering = this.treeHelper.getLeafList(treeNode).map((leaf) => parseInt(leaf.name));
|
|
1055
|
+
this.analysisView.grid.setRowOrder(leafOrdering);
|
|
1056
|
+
const dendrogramViewer = await this.df.plot.fromType('Dendrogram', {nodeColumnName: leafCol.name}) as DG.JsViewer;
|
|
1057
|
+
|
|
1058
|
+
this.analysisView.dockManager.dock(dendrogramViewer, DG.DOCK_TYPE.LEFT, null, 'Dendrogram', 0.25);
|
|
1059
|
+
} catch (e) {
|
|
1060
|
+
_package.logger.error(e as string);
|
|
1061
|
+
} finally {
|
|
1062
|
+
pi.close();
|
|
1027
1063
|
}
|
|
1028
1064
|
}
|
|
1029
1065
|
|
|
@@ -1051,41 +1087,61 @@ export class PeptidesModel {
|
|
|
1051
1087
|
const settingsButton = ui.iconFA('wrench', () => getSettingsDialog(this), 'Peptides analysis settings');
|
|
1052
1088
|
this.analysisView.setRibbonPanels([[settingsButton]], false);
|
|
1053
1089
|
this.isRibbonSet = true;
|
|
1090
|
+
grok.events.onResetFilterRequest.subscribe(() => {
|
|
1091
|
+
this.isInvariantMapTrigger = true;
|
|
1092
|
+
this.initMonomerPositionFilter({cleanInit: true});
|
|
1093
|
+
this.isInvariantMapTrigger = false;
|
|
1094
|
+
});
|
|
1054
1095
|
}
|
|
1055
1096
|
|
|
1056
1097
|
this.updateGrid();
|
|
1057
|
-
this.fireBitsetChanged(
|
|
1098
|
+
this.fireBitsetChanged(true);
|
|
1058
1099
|
this.analysisView.grid.invalidate();
|
|
1059
1100
|
}
|
|
1060
1101
|
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
const mutationCliffsViewer = await dfPlt.fromType('peptide-sar-viewer') as MonomerPosition;
|
|
1066
|
-
const mostPotentResiduesViewer = await dfPlt.fromType('peptide-sar-viewer-vertical') as MostPotentResiduesViewer;
|
|
1067
|
-
if (this.settings.clustersColumnName)
|
|
1068
|
-
await this.addLogoSummaryTableViewer();
|
|
1102
|
+
findViewer(viewerType: VIEWER_TYPE): DG.Viewer | null {
|
|
1103
|
+
return wu(this.analysisView.viewers).find((v) => v.type === viewerType) || null;
|
|
1104
|
+
}
|
|
1069
1105
|
|
|
1070
|
-
|
|
1106
|
+
async addLogoSummaryTable(): Promise<void> {
|
|
1107
|
+
this.closeViewer(VIEWER_TYPE.MONOMER_POSITION);
|
|
1108
|
+
this.closeViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES);
|
|
1109
|
+
const logoSummaryTable = await this.df.plot.fromType(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable;
|
|
1110
|
+
this.analysisView.dockManager.dock(logoSummaryTable, DG.DOCK_TYPE.RIGHT, null, VIEWER_TYPE.LOGO_SUMMARY_TABLE);
|
|
1111
|
+
if (this.settings.showMonomerPosition)
|
|
1112
|
+
await this.addMonomerPosition();
|
|
1113
|
+
if (this.settings.showMostPotentResidues)
|
|
1114
|
+
await this.addMostPotentResidues();
|
|
1115
|
+
}
|
|
1071
1116
|
|
|
1072
|
-
|
|
1117
|
+
async addMonomerPosition(): Promise<void> {
|
|
1118
|
+
const monomerPosition = await this.df.plot.fromType(VIEWER_TYPE.MONOMER_POSITION) as MonomerPosition;
|
|
1119
|
+
const mostPotentResidues = this.findViewer(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResiduesViewer | null;
|
|
1120
|
+
const dm = this.analysisView.dockManager;
|
|
1121
|
+
const [dockType, refNode, ratio] = mostPotentResidues === null ? [DG.DOCK_TYPE.DOWN, null, undefined] :
|
|
1122
|
+
[DG.DOCK_TYPE.LEFT, this.findViewerNode(VIEWER_TYPE.MOST_POTENT_RESIDUES), 0.7];
|
|
1123
|
+
dm.dock(monomerPosition, dockType, refNode, VIEWER_TYPE.MONOMER_POSITION, ratio);
|
|
1073
1124
|
}
|
|
1074
1125
|
|
|
1075
|
-
async
|
|
1076
|
-
const
|
|
1077
|
-
|
|
1126
|
+
async addMostPotentResidues(): Promise<void> {
|
|
1127
|
+
const mostPotentResidues =
|
|
1128
|
+
await this.df.plot.fromType(VIEWER_TYPE.MOST_POTENT_RESIDUES) as MostPotentResiduesViewer;
|
|
1129
|
+
const monomerPosition = this.findViewer(VIEWER_TYPE.MONOMER_POSITION) as MonomerPosition | null;
|
|
1130
|
+
const dm = this.analysisView.dockManager;
|
|
1131
|
+
const [dockType, refNode, ratio] = monomerPosition === null ? [DG.DOCK_TYPE.DOWN, null, undefined] :
|
|
1132
|
+
[DG.DOCK_TYPE.RIGHT, this.findViewerNode(VIEWER_TYPE.MONOMER_POSITION), 0.3];
|
|
1133
|
+
dm.dock(mostPotentResidues, dockType, refNode, VIEWER_TYPE.MOST_POTENT_RESIDUES, ratio);
|
|
1078
1134
|
}
|
|
1079
1135
|
|
|
1080
1136
|
addNewCluster(clusterName: string): void {
|
|
1081
|
-
const newClusterCol = DG.Column.fromBitSet(clusterName, this.
|
|
1137
|
+
const newClusterCol = DG.Column.fromBitSet(clusterName, this.getCompoundBitset());
|
|
1082
1138
|
newClusterCol.setTag(C.TAGS.CUSTOM_CLUSTER, '1');
|
|
1083
1139
|
this.df.columns.add(newClusterCol);
|
|
1084
1140
|
this.analysisView.grid.col(newClusterCol.name)!.visible = false;
|
|
1085
1141
|
}
|
|
1086
1142
|
|
|
1087
|
-
|
|
1088
|
-
const rowMask = this.
|
|
1143
|
+
createNewView(): void {
|
|
1144
|
+
const rowMask = this.getCompoundBitset();
|
|
1089
1145
|
if (!rowMask.anyTrue)
|
|
1090
1146
|
return grok.shell.warning('Cannot create a new view, there are no visible selected rows in your dataset');
|
|
1091
1147
|
|