@datagrok/peptides 1.16.0 → 1.17.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 +17 -6
- package/CHANGELOG.md +33 -8
- package/README.md +12 -7
- package/dist/196.js +2 -3
- package/dist/23.js +2 -0
- package/dist/282.js +2 -0
- package/dist/361.js +2 -2
- package/dist/40.js +2 -0
- package/dist/436.js +2 -2
- package/dist/65.js +2 -0
- package/dist/704.js +2 -0
- package/dist/package-test.js +2 -3
- package/dist/package.js +2 -3
- package/package.json +13 -13
- package/setup-unlink-clean.cmd +6 -0
- package/setup.cmd +2 -2
- package/src/demo/fasta.ts +8 -2
- package/src/model.ts +857 -560
- package/src/package-test.ts +1 -3
- package/src/package.ts +28 -50
- package/src/tests/benchmarks.ts +31 -11
- package/src/tests/core.ts +11 -6
- package/src/tests/misc.ts +6 -6
- package/src/tests/model.ts +80 -45
- package/src/tests/table-view.ts +49 -39
- package/src/tests/utils.ts +0 -76
- package/src/tests/viewers.ts +30 -12
- package/src/tests/widgets.ts +30 -11
- package/src/utils/algorithms.ts +115 -38
- package/src/utils/cell-renderer.ts +217 -96
- package/src/utils/constants.ts +37 -7
- package/src/utils/misc.ts +285 -30
- package/src/utils/parallel-mutation-cliffs.ts +18 -15
- package/src/utils/statistics.ts +70 -14
- package/src/utils/tooltips.ts +46 -25
- package/src/utils/types.ts +29 -26
- package/src/utils/worker-creator.ts +5 -0
- package/src/viewers/logo-summary.ts +597 -135
- package/src/viewers/sar-viewer.ts +946 -249
- package/src/widgets/distribution.ts +291 -196
- package/src/widgets/manual-alignment.ts +18 -11
- package/src/widgets/mutation-cliffs.ts +45 -21
- package/src/widgets/peptides.ts +86 -91
- package/src/widgets/selection.ts +56 -22
- package/src/widgets/settings.ts +94 -44
- package/src/workers/dimensionality-reducer.ts +5 -6
- package/src/workers/mutation-cliffs-worker.ts +3 -16
- package/dist/196.js.LICENSE.txt +0 -51
- package/dist/209.js +0 -2
- package/dist/381.js +0 -2
- package/dist/694.js +0 -2
- package/dist/831.js +0 -2
- package/dist/868.js +0 -2
- package/dist/package-test.js.LICENSE.txt +0 -51
- package/dist/package.js.LICENSE.txt +0 -51
- package/src/tests/peptide-space-test.ts +0 -48
- package/src/tests/test-data.ts +0 -649
- package/src/utils/molecular-measure.ts +0 -174
- package/src/utils/peptide-similarity-space.ts +0 -216
- package/src/viewers/peptide-space-viewer.ts +0 -150
|
@@ -3,60 +3,319 @@ import * as grok from 'datagrok-api/grok';
|
|
|
3
3
|
import * as DG from 'datagrok-api/dg';
|
|
4
4
|
|
|
5
5
|
import $ from 'cash-dom';
|
|
6
|
-
import {
|
|
6
|
+
import {PeptidesModel, VIEWER_TYPE} from '../model';
|
|
7
7
|
import * as C from '../utils/constants';
|
|
8
|
+
import {COLUMN_NAME, SCALING_METHODS} from '../utils/constants';
|
|
8
9
|
import * as CR from '../utils/cell-renderer';
|
|
9
10
|
import {HorizontalAlignments, IWebLogoViewer, PositionHeight} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
10
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
AggregationColumns,
|
|
13
|
+
ClusterTypeStats,
|
|
14
|
+
getAggregatedColumnValues,
|
|
15
|
+
getAggregatedValue,
|
|
16
|
+
getStats,
|
|
17
|
+
StatsItem,
|
|
18
|
+
} from '../utils/statistics';
|
|
11
19
|
import wu from 'wu';
|
|
12
|
-
import {getActivityDistribution,
|
|
13
|
-
import {
|
|
20
|
+
import {getActivityDistribution, getStatsTableMap} from '../widgets/distribution';
|
|
21
|
+
import {
|
|
22
|
+
getDistributionPanel,
|
|
23
|
+
getDistributionTable,
|
|
24
|
+
getTotalAggColumns,
|
|
25
|
+
isApplicableDataframe,
|
|
26
|
+
modifySelection,
|
|
27
|
+
scaleActivity,
|
|
28
|
+
} from '../utils/misc';
|
|
14
29
|
import BitArray from '@datagrok-libraries/utils/src/bit-array';
|
|
30
|
+
import * as type from '../utils/types';
|
|
15
31
|
import {SelectionItem} from '../utils/types';
|
|
16
32
|
import {_package} from '../package';
|
|
33
|
+
import {calculateClusterStatistics} from '../utils/algorithms';
|
|
34
|
+
import {splitAlignedSequences} from '@datagrok-libraries/bio/src/utils/splitter';
|
|
35
|
+
import {SARViewer} from './sar-viewer';
|
|
17
36
|
|
|
18
37
|
const getAggregatedColName = (aggF: string, colName: string): string => `${aggF}(${colName})`;
|
|
19
38
|
|
|
20
|
-
export enum
|
|
39
|
+
export enum CLUSTER_TYPE {
|
|
40
|
+
ORIGINAL = 'original',
|
|
41
|
+
CUSTOM = 'custom',
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type ClusterType = `${CLUSTER_TYPE}`;
|
|
45
|
+
|
|
46
|
+
export const enum LST_PROPERTIES {
|
|
21
47
|
WEB_LOGO_MODE = 'webLogoMode',
|
|
22
48
|
MEMBERS_RATIO_THRESHOLD = 'membersRatioThreshold',
|
|
23
|
-
|
|
49
|
+
SEQUENCE = 'sequence',
|
|
50
|
+
CLUSTERS = 'clusters',
|
|
51
|
+
COLUMNS = 'columns',
|
|
52
|
+
AGGREGATION = 'aggregation',
|
|
53
|
+
ACTIVITY_SCALING = 'activityScaling',
|
|
54
|
+
ACTIVITY = 'activity',
|
|
55
|
+
}
|
|
24
56
|
|
|
25
|
-
|
|
57
|
+
enum LST_CATEGORIES {
|
|
58
|
+
GENERAL = 'General',
|
|
59
|
+
STYLE = 'WebLogo',
|
|
60
|
+
AGGREGATION = 'Aggregation',
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface ILogoSummaryTable {
|
|
64
|
+
sequenceColumnName: string;
|
|
65
|
+
clustersColumnName: string;
|
|
66
|
+
activityColumnName: string;
|
|
67
|
+
activityScaling: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** LogoSummaryTable viewer shows per cluster information. */
|
|
71
|
+
export class LogoSummaryTable extends DG.JsViewer implements ILogoSummaryTable {
|
|
26
72
|
_titleHost = ui.divText(VIEWER_TYPE.LOGO_SUMMARY_TABLE, {id: 'pep-viewer-title'});
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
initialized: boolean = false;
|
|
73
|
+
sequenceColumnName: string;
|
|
74
|
+
clustersColumnName: string;
|
|
30
75
|
webLogoMode: string;
|
|
31
76
|
membersRatioThreshold: number;
|
|
32
77
|
bitsets: DG.BitSet[] = [];
|
|
33
78
|
keyPress: boolean = false;
|
|
34
79
|
currentRowIndex: number | null = null;
|
|
80
|
+
columns: string[];
|
|
81
|
+
aggregation: string;
|
|
82
|
+
activityColumnName: string;
|
|
83
|
+
activityScaling: SCALING_METHODS;
|
|
84
|
+
_scaledActivityColumn: DG.Column | null = null;
|
|
35
85
|
|
|
86
|
+
/** Creates LogoSummaryTable properties. */
|
|
36
87
|
constructor() {
|
|
37
88
|
super();
|
|
38
89
|
|
|
90
|
+
this.sequenceColumnName = this.column(LST_PROPERTIES.SEQUENCE,
|
|
91
|
+
{
|
|
92
|
+
category: LST_CATEGORIES.GENERAL,
|
|
93
|
+
nullable: false,
|
|
94
|
+
});
|
|
95
|
+
this.clustersColumnName = this.column(LST_PROPERTIES.CLUSTERS,
|
|
96
|
+
{
|
|
97
|
+
category: LST_CATEGORIES.GENERAL,
|
|
98
|
+
nullable: false,
|
|
99
|
+
});
|
|
100
|
+
this.activityColumnName = this.column(LST_PROPERTIES.ACTIVITY, {category: LST_CATEGORIES.GENERAL, nullable: false});
|
|
101
|
+
this.activityScaling = this.string(LST_PROPERTIES.ACTIVITY_SCALING, C.SCALING_METHODS.NONE,
|
|
102
|
+
{
|
|
103
|
+
category: LST_CATEGORIES.GENERAL,
|
|
104
|
+
choices: Object.values(C.SCALING_METHODS),
|
|
105
|
+
}) as SCALING_METHODS;
|
|
106
|
+
|
|
39
107
|
this.webLogoMode = this.string(LST_PROPERTIES.WEB_LOGO_MODE, PositionHeight.Entropy,
|
|
40
|
-
{
|
|
41
|
-
|
|
108
|
+
{
|
|
109
|
+
choices: [PositionHeight.full, PositionHeight.Entropy],
|
|
110
|
+
category: LST_CATEGORIES.STYLE,
|
|
111
|
+
});
|
|
112
|
+
this.membersRatioThreshold = this.float(LST_PROPERTIES.MEMBERS_RATIO_THRESHOLD, 0.3,
|
|
113
|
+
{
|
|
114
|
+
min: 0,
|
|
115
|
+
max: 1.0,
|
|
116
|
+
category: LST_CATEGORIES.STYLE,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
this.columns = this.columnList(LST_PROPERTIES.COLUMNS, [], {category: LST_CATEGORIES.AGGREGATION});
|
|
120
|
+
this.aggregation = this.string(LST_PROPERTIES.AGGREGATION, DG.AGG.AVG,
|
|
121
|
+
{
|
|
122
|
+
category: LST_CATEGORIES.AGGREGATION,
|
|
123
|
+
choices: C.AGGREGATION_TYPES,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Returns PeptidesModel instance that belongs to the attached dataframe.
|
|
129
|
+
* @return - PeptidesModel instance.
|
|
130
|
+
*/
|
|
131
|
+
get model(): PeptidesModel {
|
|
132
|
+
return PeptidesModel.getInstance(this.dataFrame);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
_viewerGrid: DG.Grid | null = null;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Returns LogoSummaryTable grid. Creates a new one if it is null.
|
|
139
|
+
* @return - LogoSummaryTable grid.
|
|
140
|
+
*/
|
|
141
|
+
get viewerGrid(): DG.Grid {
|
|
142
|
+
this._viewerGrid ??= this.createLogoSummaryTableGrid();
|
|
143
|
+
return this._viewerGrid;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
_clusterStats: ClusterTypeStats | null = null;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Returns cluster statistics. Calculates it if it is null.
|
|
150
|
+
* @return - cluster statistics.
|
|
151
|
+
*/
|
|
152
|
+
get clusterStats(): ClusterTypeStats {
|
|
153
|
+
this._clusterStats ??= calculateClusterStatistics(this.dataFrame, this.clustersColumnName,
|
|
154
|
+
this.customClusters.toArray(), this.getScaledActivityColumn());
|
|
155
|
+
return this._clusterStats;
|
|
42
156
|
}
|
|
43
157
|
|
|
158
|
+
_clusterSelection: type.Selection | null = null;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Returns cluster selection. Initializes it if it is null.
|
|
162
|
+
* @return - cluster selection.
|
|
163
|
+
*/
|
|
164
|
+
get clusterSelection(): type.Selection {
|
|
165
|
+
const tagSelection = this.dataFrame.getTag(C.TAGS.CLUSTER_SELECTION);
|
|
166
|
+
this._clusterSelection ??= tagSelection === null ? this.initClusterSelection({notify: false}) :
|
|
167
|
+
JSON.parse(tagSelection);
|
|
168
|
+
return this._clusterSelection!;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Sets cluster selection.
|
|
173
|
+
* @param selection - cluster selection.
|
|
174
|
+
*/
|
|
175
|
+
set clusterSelection(selection: type.Selection) {
|
|
176
|
+
this._clusterSelection = selection;
|
|
177
|
+
this.dataFrame.setTag(C.TAGS.CLUSTER_SELECTION, JSON.stringify(selection));
|
|
178
|
+
this.model.fireBitsetChanged(VIEWER_TYPE.LOGO_SUMMARY_TABLE);
|
|
179
|
+
this.model.analysisView.grid.invalidate();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
_logoSummaryTable: DG.DataFrame | null = null;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Returns LogoSummaryTable dataframe. Creates a new one if it is null.
|
|
186
|
+
* @return - LogoSummaryTable dataframe.
|
|
187
|
+
*/
|
|
188
|
+
get logoSummaryTable(): DG.DataFrame {
|
|
189
|
+
this._logoSummaryTable ??= this.createLogoSummaryTable();
|
|
190
|
+
return this._logoSummaryTable;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Sets LogoSummaryTable dataframe.
|
|
195
|
+
* @param df - LogoSummaryTable dataframe.
|
|
196
|
+
*/
|
|
197
|
+
set logoSummaryTable(df: DG.DataFrame) {
|
|
198
|
+
this._logoSummaryTable = df;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
_positionColumns: DG.Column<string>[] | null = null;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Returns position columns. If position columns are not attached to LogoSummaryTable, it tries to get them from
|
|
205
|
+
* other viewers or analysis (given that relevant parameters are the same), or creates own position columns.
|
|
206
|
+
* @return - position columns.
|
|
207
|
+
*/
|
|
208
|
+
get positionColumns(): DG.Column<string>[] {
|
|
209
|
+
if (this._positionColumns != null) {
|
|
210
|
+
return this._positionColumns;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
const getSharedPositionColumns = (viewerType: VIEWER_TYPE): DG.Column<string>[] | null => {
|
|
215
|
+
const viewer = this.model.findViewer(viewerType) as SARViewer | null;
|
|
216
|
+
if (this.sequenceColumnName === viewer?.sequenceColumnName) {
|
|
217
|
+
return viewer._positionColumns;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
return null;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
if (this.model.positionColumns != null && this.sequenceColumnName === this.model.settings?.sequenceColumnName) {
|
|
225
|
+
this._positionColumns = this.model.positionColumns;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
this._positionColumns ??= getSharedPositionColumns(VIEWER_TYPE.MONOMER_POSITION) ??
|
|
230
|
+
getSharedPositionColumns(VIEWER_TYPE.MOST_POTENT_RESIDUES) ??
|
|
231
|
+
splitAlignedSequences(this.dataFrame.getCol(this.sequenceColumnName)).columns.toList();
|
|
232
|
+
return this._positionColumns!;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Checks if cluster selection is empty.
|
|
237
|
+
* @return - flag indicating if cluster selection is empty.
|
|
238
|
+
*/
|
|
239
|
+
get isClusterSelectionEmpty(): boolean {
|
|
240
|
+
const clusterSelectionCount =
|
|
241
|
+
this.clusterSelection[CLUSTER_TYPE.ORIGINAL].length + this.clusterSelection[CLUSTER_TYPE.CUSTOM].length;
|
|
242
|
+
return clusterSelectionCount === 0;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Gets iterable over custom clusters columns.
|
|
247
|
+
* @return - iterable over custom clusters columns.
|
|
248
|
+
*/
|
|
249
|
+
get customClusters(): wu.WuIterable<DG.Column<boolean>> {
|
|
250
|
+
const query: {
|
|
251
|
+
[key: string]: string
|
|
252
|
+
} = {};
|
|
253
|
+
query[C.TAGS.CUSTOM_CLUSTER] = '1';
|
|
254
|
+
return wu(this.dataFrame.columns.byTags(query));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Gets scaled activity column.
|
|
259
|
+
* @param isFiltered - flag indicating if only filtered rows should be taken into account.
|
|
260
|
+
* @return - scaled activity column.
|
|
261
|
+
*/
|
|
262
|
+
getScaledActivityColumn(isFiltered: boolean = false): DG.Column<number> {
|
|
263
|
+
if (this.model.settings?.activityColumnName === this.activityColumnName &&
|
|
264
|
+
this.model.settings?.activityScaling === this.activityScaling) {
|
|
265
|
+
this._scaledActivityColumn = this.model.getScaledActivityColumn(isFiltered);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
this._scaledActivityColumn ??= scaleActivity(this.dataFrame.getCol(this.activityColumnName),
|
|
270
|
+
this.activityScaling);
|
|
271
|
+
if (isFiltered) {
|
|
272
|
+
return DG.DataFrame.fromColumns([this._scaledActivityColumn]).clone(this.dataFrame.filter)
|
|
273
|
+
.getCol(this._scaledActivityColumn.name) as DG.Column<number>;
|
|
274
|
+
}
|
|
275
|
+
return this._scaledActivityColumn as DG.Column<number>;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Gets a map of columns and aggregations.
|
|
280
|
+
* @return - map of columns and aggregations.
|
|
281
|
+
*/
|
|
282
|
+
getAggregationColumns(): AggregationColumns {
|
|
283
|
+
return Object.fromEntries(this.columns.map((colName) => [colName, this.aggregation] as [string, DG.AGG]));
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** Processes attached table and sets viewer properties. */
|
|
44
287
|
onTableAttached(): void {
|
|
45
288
|
super.onTableAttached();
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
289
|
+
if (isApplicableDataframe(this.dataFrame)) {
|
|
290
|
+
this.getProperty(`${LST_PROPERTIES.SEQUENCE}${COLUMN_NAME}`)
|
|
291
|
+
?.set(this, this.dataFrame.columns.bySemType(DG.SEMTYPE.MACROMOLECULE)!.name);
|
|
292
|
+
this.getProperty(`${LST_PROPERTIES.ACTIVITY}${COLUMN_NAME}`)
|
|
293
|
+
?.set(this, wu(this.dataFrame.columns.numerical).next().value.name);
|
|
294
|
+
this.getProperty(`${LST_PROPERTIES.CLUSTERS}${COLUMN_NAME}`)
|
|
295
|
+
?.set(this, wu(this.dataFrame.columns.categorical).next().value.name);
|
|
296
|
+
} else {
|
|
297
|
+
const msg = 'PeptidesError: dataframe is missing Macromolecule or numeric columns';
|
|
298
|
+
grok.log.error(msg);
|
|
299
|
+
grok.shell.warning(msg);
|
|
300
|
+
}
|
|
49
301
|
this.render();
|
|
50
302
|
}
|
|
51
303
|
|
|
52
|
-
|
|
304
|
+
/** Removes all the active subscriptions. */
|
|
305
|
+
detach(): void {
|
|
306
|
+
this.subs.forEach((sub) => sub.unsubscribe());
|
|
307
|
+
}
|
|
53
308
|
|
|
309
|
+
/** Renders Logo Summary Table body. */
|
|
54
310
|
render(): void {
|
|
55
|
-
if (!this.initialized)
|
|
56
|
-
return;
|
|
57
311
|
$(this.root).empty();
|
|
58
|
-
|
|
59
|
-
|
|
312
|
+
if (this.clustersColumnName == null || this.sequenceColumnName == null || this.activityColumnName == null) {
|
|
313
|
+
this.root.appendChild(
|
|
314
|
+
ui.divText('Please, select a sequence, cluster and activity columns in the viewer properties'));
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (!this.logoSummaryTable.filter.anyTrue) {
|
|
60
319
|
const emptyDf = ui.divText('No clusters to satisfy the threshold. ' +
|
|
61
320
|
'Please, lower the threshold in viewer proeperties to include clusters');
|
|
62
321
|
this.root.appendChild(ui.divV([this._titleHost, emptyDf]));
|
|
@@ -72,37 +331,115 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
72
331
|
$(expand).addClass('pep-help-icon');
|
|
73
332
|
this.viewerGrid.root.style.width = 'auto';
|
|
74
333
|
this.root.appendChild(ui.divV([
|
|
75
|
-
ui.divH([this._titleHost, expand], {
|
|
334
|
+
ui.divH([this._titleHost, expand], {
|
|
335
|
+
style: {
|
|
336
|
+
alignSelf: 'center',
|
|
337
|
+
lineHeight: 'normal',
|
|
338
|
+
},
|
|
339
|
+
}),
|
|
76
340
|
this.viewerGrid.root,
|
|
77
341
|
]));
|
|
78
342
|
this.viewerGrid.invalidate();
|
|
79
343
|
}
|
|
80
344
|
|
|
345
|
+
/**
|
|
346
|
+
* Processes property changes.
|
|
347
|
+
* @param property - changed property.
|
|
348
|
+
*/
|
|
81
349
|
onPropertyChanged(property: DG.Property): void {
|
|
82
350
|
super.onPropertyChanged(property);
|
|
83
|
-
|
|
351
|
+
let doRender = false;
|
|
352
|
+
switch (property.name) {
|
|
353
|
+
case LST_PROPERTIES.MEMBERS_RATIO_THRESHOLD:
|
|
84
354
|
this.updateFilter();
|
|
85
|
-
|
|
355
|
+
break;
|
|
356
|
+
case `${LST_PROPERTIES.SEQUENCE}${COLUMN_NAME}`:
|
|
357
|
+
this._viewerGrid = null;
|
|
358
|
+
this._logoSummaryTable = null;
|
|
359
|
+
doRender = true;
|
|
360
|
+
break;
|
|
361
|
+
case `${LST_PROPERTIES.CLUSTERS}${COLUMN_NAME}`:
|
|
362
|
+
this._clusterStats = null;
|
|
363
|
+
this._clusterSelection = null;
|
|
364
|
+
this._viewerGrid = null;
|
|
365
|
+
this._logoSummaryTable = null;
|
|
366
|
+
doRender = true;
|
|
367
|
+
break;
|
|
368
|
+
case `${LST_PROPERTIES.ACTIVITY}${COLUMN_NAME}`:
|
|
369
|
+
case LST_PROPERTIES.ACTIVITY_SCALING:
|
|
370
|
+
this._scaledActivityColumn = null;
|
|
371
|
+
this._viewerGrid = null;
|
|
372
|
+
this._clusterStats = null;
|
|
373
|
+
this._logoSummaryTable = null;
|
|
374
|
+
doRender = true;
|
|
375
|
+
break;
|
|
376
|
+
case LST_PROPERTIES.COLUMNS:
|
|
377
|
+
case LST_PROPERTIES.AGGREGATION:
|
|
378
|
+
this._viewerGrid = null;
|
|
379
|
+
this._logoSummaryTable = null;
|
|
380
|
+
doRender = true;
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
if (doRender) {
|
|
384
|
+
this.render();
|
|
385
|
+
}
|
|
86
386
|
}
|
|
87
387
|
|
|
88
|
-
|
|
89
|
-
|
|
388
|
+
/**
|
|
389
|
+
* Initializes cluster selection.
|
|
390
|
+
* @param options - initializatiion options.
|
|
391
|
+
* @param options.notify - flag that indicates if bitset changed event should fire.
|
|
392
|
+
* @return - cluster selection.
|
|
393
|
+
*/
|
|
394
|
+
initClusterSelection(options: {
|
|
395
|
+
notify?: boolean
|
|
396
|
+
} = {}): type.Selection {
|
|
397
|
+
options.notify ??= true;
|
|
398
|
+
|
|
399
|
+
const newClusterSelection = {} as type.Selection;
|
|
400
|
+
newClusterSelection[CLUSTER_TYPE.ORIGINAL] = [];
|
|
401
|
+
newClusterSelection[CLUSTER_TYPE.CUSTOM] = [];
|
|
402
|
+
if (options.notify) {
|
|
403
|
+
this.clusterSelection = newClusterSelection;
|
|
404
|
+
} else {
|
|
405
|
+
this._clusterSelection = newClusterSelection;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
return this.clusterSelection;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Gets total viewer aggregated columns from viewer properties and analysis settings if applicable.
|
|
414
|
+
* @return - total viewer aggregated columns.
|
|
415
|
+
*/
|
|
416
|
+
getTotalViewerAggColumns(): [string, DG.AggregationType][] {
|
|
417
|
+
const aggrCols = this.getAggregationColumns();
|
|
418
|
+
return getTotalAggColumns(this.columns, aggrCols, this.model?.settings?.columns);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Creates LogoSummaryTable dataframe to be used in LogoSummaryTable grid.
|
|
423
|
+
* @return - LogoSummaryTable dataframe.
|
|
424
|
+
*/
|
|
425
|
+
createLogoSummaryTable(): DG.DataFrame {
|
|
426
|
+
const clustersColName = this.clustersColumnName;
|
|
90
427
|
const isDfFiltered = this.dataFrame.filter.anyFalse;
|
|
91
428
|
const filteredDf = isDfFiltered ? this.dataFrame.clone(this.dataFrame.filter) : this.dataFrame;
|
|
92
429
|
const filteredDfCols = filteredDf.columns;
|
|
93
430
|
const filteredDfRowCount = filteredDf.rowCount;
|
|
94
|
-
const
|
|
95
|
-
const activityColData = activityCol.getRawData();
|
|
431
|
+
const activityColData = this.getScaledActivityColumn(isDfFiltered).getRawData();
|
|
96
432
|
|
|
97
433
|
const filteredDfClustCol = filteredDf.getCol(clustersColName);
|
|
98
434
|
const filteredDfClustColData = filteredDfClustCol.getRawData();
|
|
99
435
|
const filteredDfClustColCat = filteredDfClustCol.categories;
|
|
100
436
|
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
437
|
+
const query: {
|
|
438
|
+
[key: string]: string
|
|
439
|
+
} = {};
|
|
104
440
|
query[C.TAGS.CUSTOM_CLUSTER] = '1';
|
|
105
|
-
const customClustColList: DG.Column<boolean>[] = wu(filteredDfCols.byTags(query))
|
|
441
|
+
const customClustColList: DG.Column<boolean>[] = wu(filteredDfCols.byTags(query))
|
|
442
|
+
.filter((c) => c.max > 0).toArray();
|
|
106
443
|
|
|
107
444
|
const customLST = DG.DataFrame.create(customClustColList.length);
|
|
108
445
|
const customLSTCols = customLST.columns;
|
|
@@ -116,7 +453,7 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
116
453
|
const customRatioColData = customLSTCols.addNewFloat(C.LST_COLUMN_NAMES.RATIO).getRawData();
|
|
117
454
|
|
|
118
455
|
let origLSTBuilder = filteredDf.groupBy([clustersColName]);
|
|
119
|
-
const aggColsEntries =
|
|
456
|
+
const aggColsEntries = this.getTotalViewerAggColumns();
|
|
120
457
|
const aggColNames = aggColsEntries.map(([colName, aggFn]) => getAggregatedColName(aggFn, colName));
|
|
121
458
|
const customAggRawCols = new Array(aggColNames.length);
|
|
122
459
|
const colAggEntries = aggColsEntries.map(
|
|
@@ -136,12 +473,15 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
136
473
|
const customClustCol = customClustColList[rowIdx];
|
|
137
474
|
customLSTClustCol.set(rowIdx, customClustCol.name);
|
|
138
475
|
const bitArray = BitArray.fromUint32Array(filteredDfRowCount, customClustCol.getRawData() as Uint32Array);
|
|
139
|
-
if (bitArray.allFalse || bitArray.allTrue)
|
|
476
|
+
if (bitArray.allFalse || bitArray.allTrue) {
|
|
140
477
|
continue;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
|
|
141
481
|
const bsMask = DG.BitSet.fromBytes(bitArray.buffer.buffer, filteredDfRowCount);
|
|
142
482
|
|
|
143
|
-
const stats:
|
|
144
|
-
this.
|
|
483
|
+
const stats: StatsItem = isDfFiltered ? getStats(activityColData, bitArray) :
|
|
484
|
+
this.clusterStats[CLUSTER_TYPE.CUSTOM][customClustCol.name];
|
|
145
485
|
|
|
146
486
|
customMembersColData[rowIdx] = stats.count;
|
|
147
487
|
customBitsets[rowIdx] = bsMask;
|
|
@@ -154,10 +494,8 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
154
494
|
customAggRawCols[aggColIdx][rowIdx] = getAggregatedValue(col, aggFn, bsMask);
|
|
155
495
|
}
|
|
156
496
|
}
|
|
157
|
-
|
|
158
497
|
customWebLogoCol.setTag(DG.TAGS.CELL_RENDERER, 'html');
|
|
159
498
|
customDistCol.setTag(DG.TAGS.CELL_RENDERER, 'html');
|
|
160
|
-
|
|
161
499
|
// END
|
|
162
500
|
|
|
163
501
|
// BEGIN: fill LST part with original clusters
|
|
@@ -166,9 +504,12 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
166
504
|
const origLSTCols = origLST.columns;
|
|
167
505
|
const origLSTClustCol: DG.Column<string> = origLST.getCol(clustersColName);
|
|
168
506
|
origLSTClustCol.name = C.LST_COLUMN_NAMES.CLUSTER;
|
|
507
|
+
if (origLSTClustCol.type !== DG.COLUMN_TYPE.STRING) {
|
|
508
|
+
origLST.columns.replace(origLSTClustCol, origLSTClustCol.convertTo(DG.COLUMN_TYPE.STRING));
|
|
509
|
+
}
|
|
169
510
|
|
|
170
|
-
const origLSTClustColCat = origLSTClustCol.categories;
|
|
171
511
|
|
|
512
|
+
const origLSTClustColCat = origLSTClustCol.categories;
|
|
172
513
|
const origMembersColData = origLSTCols.addNewInt(C.LST_COLUMN_NAMES.MEMBERS).getRawData();
|
|
173
514
|
const origWebLogoCol = origLSTCols.addNewString(C.LST_COLUMN_NAMES.WEB_LOGO);
|
|
174
515
|
const origDistCol = origLSTCols.addNewString(C.LST_COLUMN_NAMES.DISTRIBUTION);
|
|
@@ -176,7 +517,6 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
176
517
|
const origPValColData = origLSTCols.addNewFloat(C.LST_COLUMN_NAMES.P_VALUE).getRawData();
|
|
177
518
|
const origRatioColData = origLSTCols.addNewFloat(C.LST_COLUMN_NAMES.RATIO).getRawData();
|
|
178
519
|
const origBitsets: DG.BitSet[] = new Array(origLSTLen);
|
|
179
|
-
|
|
180
520
|
const origClustMasks = Array.from({length: origLSTLen},
|
|
181
521
|
() => new BitArray(filteredDfRowCount, false));
|
|
182
522
|
|
|
@@ -188,12 +528,14 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
188
528
|
|
|
189
529
|
for (let rowIdx = 0; rowIdx < origLSTLen; ++rowIdx) {
|
|
190
530
|
const mask = origClustMasks[rowIdx];
|
|
191
|
-
if (mask.allFalse || mask.allTrue)
|
|
531
|
+
if (mask.allFalse || mask.allTrue) {
|
|
192
532
|
continue;
|
|
193
|
-
|
|
533
|
+
}
|
|
194
534
|
|
|
535
|
+
|
|
536
|
+
const bsMask = DG.BitSet.fromBytes(mask.buffer.buffer, filteredDfRowCount);
|
|
195
537
|
const stats = isDfFiltered ? getStats(activityColData, mask) :
|
|
196
|
-
this.
|
|
538
|
+
this.clusterStats[CLUSTER_TYPE.ORIGINAL][origLSTClustColCat[rowIdx]];
|
|
197
539
|
|
|
198
540
|
origMembersColData[rowIdx] = stats.count;
|
|
199
541
|
origBitsets[rowIdx] = bsMask;
|
|
@@ -201,7 +543,6 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
201
543
|
origPValColData[rowIdx] = stats.pValue ?? DG.FLOAT_NULL;
|
|
202
544
|
origRatioColData[rowIdx] = stats.ratio;
|
|
203
545
|
}
|
|
204
|
-
|
|
205
546
|
origWebLogoCol.setTag(DG.TAGS.CELL_RENDERER, 'html');
|
|
206
547
|
origDistCol.setTag(DG.TAGS.CELL_RENDERER, 'html');
|
|
207
548
|
// END
|
|
@@ -209,30 +550,46 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
209
550
|
// combine LSTs and create a grid
|
|
210
551
|
const summaryTable = origLST.append(customLST);
|
|
211
552
|
this.bitsets = origBitsets.concat(customBitsets);
|
|
553
|
+
return summaryTable;
|
|
554
|
+
}
|
|
212
555
|
|
|
213
|
-
|
|
214
|
-
|
|
556
|
+
/**
|
|
557
|
+
* Builds LogoSummaryTable interactive grid.
|
|
558
|
+
* @return - LogoSummaryTable grid.
|
|
559
|
+
*/
|
|
560
|
+
createLogoSummaryTableGrid(): DG.Grid {
|
|
561
|
+
const isDfFiltered = this.dataFrame.filter.anyFalse;
|
|
562
|
+
const filteredDf = isDfFiltered ? this.dataFrame.clone(this.dataFrame.filter) : this.dataFrame;
|
|
563
|
+
const aggColsEntries = this.getTotalViewerAggColumns();
|
|
564
|
+
const aggColNames = aggColsEntries.map(([colName, aggFn]) => getAggregatedColName(aggFn, colName));
|
|
565
|
+
|
|
566
|
+
const grid = this.logoSummaryTable.plot.grid();
|
|
567
|
+
grid.sort([C.LST_COLUMN_NAMES.MEMBERS], [false]);
|
|
215
568
|
this.updateFilter();
|
|
216
|
-
const gridClustersCol =
|
|
569
|
+
const gridClustersCol = grid.col(C.LST_COLUMN_NAMES.CLUSTER)!;
|
|
217
570
|
gridClustersCol.visible = true;
|
|
218
|
-
|
|
571
|
+
grid.columns.setOrder([C.LST_COLUMN_NAMES.CLUSTER, C.LST_COLUMN_NAMES.MEMBERS,
|
|
219
572
|
C.LST_COLUMN_NAMES.WEB_LOGO, C.LST_COLUMN_NAMES.DISTRIBUTION, C.LST_COLUMN_NAMES.MEAN_DIFFERENCE,
|
|
220
573
|
C.LST_COLUMN_NAMES.P_VALUE, C.LST_COLUMN_NAMES.RATIO, ...aggColNames]);
|
|
221
|
-
|
|
222
|
-
|
|
574
|
+
grid.columns.rowHeader!.visible = false;
|
|
575
|
+
grid.props.rowHeight = 55;
|
|
223
576
|
|
|
224
577
|
const webLogoCache = new DG.LruCache<number, DG.Viewer & IWebLogoViewer>();
|
|
225
578
|
const distCache = new DG.LruCache<number, DG.Viewer<DG.IHistogramLookSettings>>();
|
|
226
|
-
const maxSequenceLen = this.
|
|
227
|
-
const webLogoGridCol =
|
|
579
|
+
const maxSequenceLen = this.positionColumns.length;
|
|
580
|
+
const webLogoGridCol = grid.columns.byName(C.LST_COLUMN_NAMES.WEB_LOGO)!;
|
|
228
581
|
webLogoGridCol.cellType = 'html';
|
|
229
582
|
webLogoGridCol.width = 350;
|
|
583
|
+
const activityCol = this.getScaledActivityColumn(isDfFiltered);
|
|
584
|
+
const pepCol: DG.Column<string> = filteredDf.getCol(this.sequenceColumnName);
|
|
230
585
|
|
|
231
|
-
|
|
586
|
+
grid.onCellRender.subscribe(async (gridCellArgs) => {
|
|
232
587
|
const gridCell = gridCellArgs.cell;
|
|
233
588
|
const currentRowIdx = gridCell.tableRowIndex;
|
|
234
|
-
if (!gridCell.isTableCell || currentRowIdx === null || currentRowIdx === -1)
|
|
589
|
+
if (!gridCell.isTableCell || currentRowIdx === null || currentRowIdx === -1) {
|
|
235
590
|
return;
|
|
591
|
+
}
|
|
592
|
+
|
|
236
593
|
|
|
237
594
|
const canvasContext = gridCellArgs.g;
|
|
238
595
|
const bound = gridCellArgs.bounds;
|
|
@@ -246,7 +603,7 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
246
603
|
const clusterBitSet = this.bitsets[currentRowIdx];
|
|
247
604
|
|
|
248
605
|
if (gridCell.tableColumn?.name === C.LST_COLUMN_NAMES.CLUSTER) {
|
|
249
|
-
CR.renderLogoSummaryCell(canvasContext, gridCell.cell.value, this.
|
|
606
|
+
CR.renderLogoSummaryCell(canvasContext, gridCell.cell.value, this.clusterSelection, bound);
|
|
250
607
|
gridCellArgs.preventDefault();
|
|
251
608
|
} else if (gridCell.tableColumn?.name === C.LST_COLUMN_NAMES.WEB_LOGO) {
|
|
252
609
|
const positionWidth = Math.floor((gridCell.bounds.width - 2 - (4 * (maxSequenceLen - 1))) / maxSequenceLen);
|
|
@@ -256,20 +613,27 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
256
613
|
const viewerProps = viewer.getProperties();
|
|
257
614
|
|
|
258
615
|
for (const prop of viewerProps) {
|
|
259
|
-
if (prop.name === 'positionHeight' && prop.get(viewer) !== this.webLogoMode)
|
|
616
|
+
if (prop.name === 'positionHeight' && prop.get(viewer) !== this.webLogoMode) {
|
|
260
617
|
prop.set(viewer, this.webLogoMode);
|
|
261
|
-
else if (prop.name === 'positionWidth' && prop.get(viewer) !== positionWidth)
|
|
618
|
+
} else if (prop.name === 'positionWidth' && prop.get(viewer) !== positionWidth) {
|
|
262
619
|
prop.set(viewer, positionWidth);
|
|
263
|
-
else if (prop.name === 'minHeight' && prop.get(viewer) !== height)
|
|
620
|
+
} else if (prop.name === 'minHeight' && prop.get(viewer) !== height) {
|
|
264
621
|
prop.set(viewer, height);
|
|
622
|
+
}
|
|
265
623
|
}
|
|
266
624
|
const viewerRoot = $(viewer.root).css('height', `${height}px`);//;
|
|
267
625
|
viewerRoot.children().first().css('overflow-y', 'hidden !important');
|
|
268
626
|
} else {
|
|
269
627
|
const webLogoTable = this.createWebLogoDf(pepCol, clusterBitSet);
|
|
270
628
|
viewer = await webLogoTable.plot
|
|
271
|
-
.fromType('WebLogo', {
|
|
272
|
-
|
|
629
|
+
.fromType('WebLogo', {
|
|
630
|
+
positionHeight: this.webLogoMode,
|
|
631
|
+
horizontalAlignment: HorizontalAlignments.LEFT,
|
|
632
|
+
maxHeight: 1000,
|
|
633
|
+
minHeight: height,
|
|
634
|
+
positionWidth: positionWidth,
|
|
635
|
+
showPositionLabels: false,
|
|
636
|
+
});
|
|
273
637
|
webLogoCache.set(currentRowIdx, viewer);
|
|
274
638
|
}
|
|
275
639
|
gridCell.element = viewer.root;
|
|
@@ -277,10 +641,10 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
277
641
|
} else if (gridCell.tableColumn?.name === C.LST_COLUMN_NAMES.DISTRIBUTION) {
|
|
278
642
|
let viewer = distCache.get(currentRowIdx);
|
|
279
643
|
if (viewer === undefined) {
|
|
280
|
-
const distributionDf =
|
|
644
|
+
const distributionDf = getDistributionTable(activityCol, clusterBitSet);
|
|
281
645
|
viewer = distributionDf.plot.histogram({
|
|
282
646
|
filteringEnabled: false,
|
|
283
|
-
valueColumnName:
|
|
647
|
+
valueColumnName: activityCol.name,
|
|
284
648
|
splitColumnName: C.COLUMNS_NAMES.SPLIT_COL,
|
|
285
649
|
legendVisibility: 'Never',
|
|
286
650
|
showXAxis: false,
|
|
@@ -301,106 +665,153 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
301
665
|
canvasContext.restore();
|
|
302
666
|
}
|
|
303
667
|
});
|
|
304
|
-
|
|
305
|
-
DG.debounce(
|
|
306
|
-
if (!gridCell.isTableCell)
|
|
668
|
+
grid.root.addEventListener('mouseleave', (_ev) => this.model.unhighlight());
|
|
669
|
+
DG.debounce(grid.onCurrentCellChanged, 500).subscribe((gridCell) => {
|
|
670
|
+
if (!gridCell.isTableCell) {
|
|
307
671
|
return;
|
|
672
|
+
}
|
|
673
|
+
|
|
308
674
|
|
|
309
675
|
try {
|
|
310
|
-
if (!this.keyPress || gridCell.tableColumn?.name !== C.LST_COLUMN_NAMES.CLUSTER)
|
|
676
|
+
if (!this.keyPress || gridCell.tableColumn?.name !== C.LST_COLUMN_NAMES.CLUSTER) {
|
|
311
677
|
return;
|
|
312
|
-
|
|
313
|
-
|
|
678
|
+
}
|
|
679
|
+
|
|
314
680
|
|
|
315
|
-
|
|
316
|
-
|
|
681
|
+
if (this.currentRowIndex !== null && this.currentRowIndex !== -1) {
|
|
682
|
+
this.modifyClusterSelection(this.getCluster(grid.cell(C.LST_COLUMN_NAMES.CLUSTER, this.currentRowIndex)),
|
|
683
|
+
{
|
|
684
|
+
shiftPressed: true,
|
|
685
|
+
ctrlPressed: true,
|
|
686
|
+
}, false);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
this.modifyClusterSelection(this.getCluster(gridCell), {
|
|
690
|
+
shiftPressed: true,
|
|
691
|
+
ctrlPressed: false,
|
|
692
|
+
});
|
|
693
|
+
grid.invalidate();
|
|
317
694
|
} finally {
|
|
318
695
|
this.keyPress = false;
|
|
319
696
|
this.currentRowIndex = gridCell.gridRow;
|
|
320
697
|
}
|
|
321
698
|
});
|
|
322
|
-
|
|
699
|
+
grid.root.addEventListener('keydown', (ev) => {
|
|
323
700
|
this.keyPress = ev.key.startsWith('Arrow');
|
|
324
|
-
if (this.keyPress)
|
|
701
|
+
if (this.keyPress) {
|
|
325
702
|
return;
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
if (ev.key === 'Escape' || (ev.code === 'KeyA' && ev.shiftKey && ev.ctrlKey)) {
|
|
707
|
+
this.initClusterSelection({notify: false});
|
|
708
|
+
} else if (ev.code === 'KeyA' && ev.ctrlKey) {
|
|
709
|
+
for (let rowIdx = 0; rowIdx < this.logoSummaryTable.rowCount; ++rowIdx) {
|
|
710
|
+
this.modifyClusterSelection(this.getCluster(grid.cell(C.LST_COLUMN_NAMES.CLUSTER, rowIdx)),
|
|
711
|
+
{
|
|
712
|
+
shiftPressed: true,
|
|
713
|
+
ctrlPressed: false,
|
|
714
|
+
}, false);
|
|
332
715
|
}
|
|
333
716
|
}
|
|
334
|
-
this.model.fireBitsetChanged();
|
|
335
|
-
|
|
717
|
+
this.model.fireBitsetChanged(VIEWER_TYPE.LOGO_SUMMARY_TABLE);
|
|
718
|
+
grid.invalidate();
|
|
336
719
|
});
|
|
337
|
-
|
|
338
|
-
const gridCell =
|
|
339
|
-
if (!gridCell || !gridCell.isTableCell || gridCell.tableColumn?.name !== C.LST_COLUMN_NAMES.CLUSTER)
|
|
720
|
+
grid.root.addEventListener('click', (ev) => {
|
|
721
|
+
const gridCell = grid.hitTest(ev.offsetX, ev.offsetY);
|
|
722
|
+
if (!gridCell || !gridCell.isTableCell || gridCell.tableColumn?.name !== C.LST_COLUMN_NAMES.CLUSTER) {
|
|
340
723
|
return;
|
|
724
|
+
}
|
|
725
|
+
|
|
341
726
|
|
|
342
727
|
const selection = this.getCluster(gridCell);
|
|
343
|
-
this.
|
|
344
|
-
|
|
728
|
+
this.modifyClusterSelection(selection, {
|
|
729
|
+
shiftPressed: ev.shiftKey,
|
|
730
|
+
ctrlPressed: ev.ctrlKey,
|
|
731
|
+
});
|
|
732
|
+
grid.invalidate();
|
|
345
733
|
|
|
346
734
|
_package.files.readAsText('help/logo-summary-table.md').then((text) => {
|
|
347
735
|
grok.shell.windows.help.showHelp(ui.markdown(text));
|
|
348
736
|
}).catch((e) => grok.log.error(e));
|
|
349
737
|
});
|
|
350
|
-
|
|
738
|
+
grid.onCellTooltip((gridCell, x, y) => {
|
|
351
739
|
if (!gridCell.isTableCell) {
|
|
352
740
|
this.model.unhighlight();
|
|
353
741
|
return true;
|
|
354
742
|
}
|
|
355
743
|
|
|
356
744
|
const cluster = this.getCluster(gridCell);
|
|
357
|
-
this.
|
|
358
|
-
if (gridCell.tableColumn?.name === C.LST_COLUMN_NAMES.CLUSTER)
|
|
745
|
+
this.highlightCluster(cluster);
|
|
746
|
+
if (gridCell.tableColumn?.name === C.LST_COLUMN_NAMES.CLUSTER) {
|
|
359
747
|
this.showTooltip(cluster, x, y);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
|
|
360
751
|
return true;
|
|
361
752
|
});
|
|
362
753
|
|
|
363
|
-
const gridProps =
|
|
754
|
+
const gridProps = grid.props;
|
|
364
755
|
gridProps.allowEdit = false;
|
|
365
756
|
gridProps.allowRowSelection = false;
|
|
366
757
|
gridProps.allowBlockSelection = false;
|
|
367
758
|
gridProps.allowColSelection = false;
|
|
368
759
|
gridProps.showCurrentRowIndicator = false;
|
|
760
|
+
gridProps.showReadOnlyNotifications = false;
|
|
369
761
|
|
|
370
|
-
return
|
|
762
|
+
return grid;
|
|
371
763
|
}
|
|
372
764
|
|
|
765
|
+
/**
|
|
766
|
+
* Highlights selected cluster.
|
|
767
|
+
* @param cluster - cluster to highlight.
|
|
768
|
+
*/
|
|
769
|
+
highlightCluster(cluster: type.SelectionItem): void {
|
|
770
|
+
const bitArray = this.clusterStats[cluster.positionOrClusterType as ClusterType][cluster.monomerOrCluster].mask;
|
|
771
|
+
this.dataFrame.rows.highlight((i) => bitArray.getBit(i));
|
|
772
|
+
this.model.isHighlighting = true;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Gets cluster from LogoSummaryTable grid cell.
|
|
777
|
+
* @param gridCell - LogoSummaryTable grid cell.
|
|
778
|
+
* @return - cluster.
|
|
779
|
+
*/
|
|
373
780
|
getCluster(gridCell: DG.GridCell): SelectionItem {
|
|
374
|
-
const clustName = this.
|
|
375
|
-
const clustColCat = this.dataFrame.getCol(this.
|
|
376
|
-
return {
|
|
377
|
-
|
|
781
|
+
const clustName = this.logoSummaryTable.get(C.LST_COLUMN_NAMES.CLUSTER, gridCell.tableRowIndex!);
|
|
782
|
+
const clustColCat = this.dataFrame.getCol(this.clustersColumnName).categories;
|
|
783
|
+
return {
|
|
784
|
+
positionOrClusterType: clustColCat.includes(clustName) ? CLUSTER_TYPE.ORIGINAL : CLUSTER_TYPE.CUSTOM,
|
|
785
|
+
monomerOrCluster: clustName,
|
|
786
|
+
};
|
|
378
787
|
}
|
|
379
788
|
|
|
789
|
+
/** Updates LogoSummaryTable filter. */
|
|
380
790
|
updateFilter(): void {
|
|
381
|
-
const
|
|
382
|
-
const memberstCol = table.getCol(C.LST_COLUMN_NAMES.MEMBERS);
|
|
791
|
+
const memberstCol = this.logoSummaryTable.getCol(C.LST_COLUMN_NAMES.MEMBERS);
|
|
383
792
|
const membersColData = memberstCol.getRawData();
|
|
384
793
|
const maxCount = memberstCol.stats.max;
|
|
385
794
|
const minMembers = Math.ceil(maxCount * this.membersRatioThreshold);
|
|
386
|
-
|
|
795
|
+
this.logoSummaryTable.filter.init((i) => membersColData[i] > minMembers);
|
|
387
796
|
}
|
|
388
797
|
|
|
798
|
+
/** Creates a new cluster from current selection and adds to Logo Summary Table. */
|
|
389
799
|
clusterFromSelection(): void {
|
|
390
|
-
const currentSelection = this.model.
|
|
391
|
-
const
|
|
392
|
-
const viewerDfCols = viewerDf.columns;
|
|
800
|
+
const currentSelection = this.model.getVisibleSelection();
|
|
801
|
+
const viewerDfCols = this.logoSummaryTable.columns;
|
|
393
802
|
const viewerDfColsLength = viewerDfCols.length;
|
|
394
803
|
const newClusterVals = new Array(viewerDfCols.length);
|
|
395
|
-
const activityScaledCol = this.
|
|
804
|
+
const activityScaledCol = this.getScaledActivityColumn();
|
|
396
805
|
const bitArray = BitArray.fromString(currentSelection.toBinaryString());
|
|
397
806
|
const stats = getStats(activityScaledCol.getRawData(), bitArray);
|
|
398
807
|
|
|
399
808
|
this.bitsets.push(currentSelection.clone());
|
|
400
809
|
|
|
401
|
-
const newClusterName = this.
|
|
402
|
-
const aggregatedValues: {
|
|
403
|
-
|
|
810
|
+
const newClusterName = this.dataFrame.columns.getUnusedName('New Cluster');
|
|
811
|
+
const aggregatedValues: {
|
|
812
|
+
[colName: string]: number
|
|
813
|
+
} = {};
|
|
814
|
+
const aggColsEntries = this.getTotalViewerAggColumns();
|
|
404
815
|
for (const [colName, aggFn] of aggColsEntries) {
|
|
405
816
|
const newColName = getAggregatedColName(aggFn, colName);
|
|
406
817
|
const col = this.dataFrame.getCol(colName);
|
|
@@ -413,35 +824,40 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
413
824
|
col.name === C.LST_COLUMN_NAMES.MEMBERS ? currentSelection.trueCount :
|
|
414
825
|
col.name === C.LST_COLUMN_NAMES.WEB_LOGO ? null :
|
|
415
826
|
col.name === C.LST_COLUMN_NAMES.DISTRIBUTION ? null :
|
|
416
|
-
col.name === C.LST_COLUMN_NAMES.MEAN_DIFFERENCE ? stats.meanDifference:
|
|
417
|
-
col.name === C.LST_COLUMN_NAMES.P_VALUE ? stats.pValue:
|
|
418
|
-
col.name === C.LST_COLUMN_NAMES.RATIO ? stats.ratio:
|
|
827
|
+
col.name === C.LST_COLUMN_NAMES.MEAN_DIFFERENCE ? stats.meanDifference :
|
|
828
|
+
col.name === C.LST_COLUMN_NAMES.P_VALUE ? stats.pValue :
|
|
829
|
+
col.name === C.LST_COLUMN_NAMES.RATIO ? stats.ratio :
|
|
419
830
|
col.name in aggregatedValues ? aggregatedValues[col.name] :
|
|
420
|
-
|
|
831
|
+
undefined;
|
|
832
|
+
if (typeof newClusterVals[i] === 'undefined') {
|
|
833
|
+
_package.logger.warning(`PeptidesLSTWarn: value for column ${col.name} is undefined`);
|
|
834
|
+
}
|
|
421
835
|
}
|
|
422
|
-
|
|
836
|
+
this.logoSummaryTable.rows.addNew(newClusterVals);
|
|
423
837
|
|
|
424
|
-
this.
|
|
425
|
-
this.
|
|
838
|
+
this.clusterStats[CLUSTER_TYPE.CUSTOM][newClusterName] = stats;
|
|
839
|
+
this.addNewCluster(newClusterName);
|
|
426
840
|
}
|
|
427
841
|
|
|
842
|
+
/** Removes selected custom clusters from Logo Summary Table. */
|
|
428
843
|
removeCluster(): void {
|
|
429
|
-
const lss = this.
|
|
844
|
+
const lss = this.clusterSelection[CLUSTER_TYPE.CUSTOM];
|
|
430
845
|
|
|
431
|
-
//
|
|
432
|
-
if (lss.length === 0)
|
|
433
|
-
|
|
846
|
+
// Names of the clusters to remove
|
|
847
|
+
if (lss.length === 0) {
|
|
848
|
+
grok.shell.warning('No custom clusters selected to be removed');
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
434
851
|
|
|
435
|
-
const
|
|
436
|
-
const
|
|
437
|
-
const clustCol = viewerDf.getCol(C.LST_COLUMN_NAMES.CLUSTER);
|
|
852
|
+
const viewerDfRows = this.logoSummaryTable.rows;
|
|
853
|
+
const clustCol = this.logoSummaryTable.getCol(C.LST_COLUMN_NAMES.CLUSTER);
|
|
438
854
|
const clustColCat = clustCol.categories;
|
|
439
855
|
const dfCols = this.dataFrame.columns;
|
|
440
856
|
|
|
441
857
|
for (const cluster of lss) {
|
|
442
858
|
lss.splice(lss.indexOf(cluster), 1);
|
|
443
859
|
dfCols.remove(cluster);
|
|
444
|
-
delete this.
|
|
860
|
+
delete this.clusterStats[CLUSTER_TYPE.CUSTOM][cluster];
|
|
445
861
|
const clustIdx = clustColCat.indexOf(cluster);
|
|
446
862
|
viewerDfRows.removeAt(clustIdx);
|
|
447
863
|
this.bitsets.splice(clustIdx, 1);
|
|
@@ -449,28 +865,66 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
449
865
|
|
|
450
866
|
clustCol.compact();
|
|
451
867
|
|
|
452
|
-
this.
|
|
453
|
-
this.model.clusterSelection = this.model.clusterSelection;
|
|
868
|
+
this.clusterSelection[CLUSTER_TYPE.CUSTOM] = lss;
|
|
454
869
|
this.render();
|
|
455
870
|
}
|
|
456
871
|
|
|
872
|
+
/**
|
|
873
|
+
* Adds new cluster to the dataframe viewer attached to.
|
|
874
|
+
* @param clusterName - cluster name.
|
|
875
|
+
*/
|
|
876
|
+
addNewCluster(clusterName: string): void {
|
|
877
|
+
const newClusterCol = DG.Column.fromBitSet(clusterName, this.model.getVisibleSelection());
|
|
878
|
+
newClusterCol.setTag(C.TAGS.CUSTOM_CLUSTER, '1');
|
|
879
|
+
newClusterCol.setTag(C.TAGS.ANALYSIS_COL, `${true}`);
|
|
880
|
+
this.dataFrame.columns.add(newClusterCol);
|
|
881
|
+
this.model.analysisView.grid.col(newClusterCol.name)!.visible = false;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* Modifies cluster selection. If shift and ctrl keys are both pressed, it removes cluster from selection.
|
|
886
|
+
* If only shift key is pressed, it adds cluster to selection. If only ctrl key is pressed, it changes cluster
|
|
887
|
+
* presence in selection. If none of the keys is pressed, it sets cluster as the only selected one.
|
|
888
|
+
* @param cluster - cluster to modify selection with.
|
|
889
|
+
* @param options - selection options.
|
|
890
|
+
* @param notify - flag indicating if bitset changed event should fire.
|
|
891
|
+
*/
|
|
892
|
+
modifyClusterSelection(cluster: type.SelectionItem, options: type.SelectionOptions = {
|
|
893
|
+
shiftPressed: false,
|
|
894
|
+
ctrlPressed: false,
|
|
895
|
+
}, notify: boolean = true): void {
|
|
896
|
+
if (notify) {
|
|
897
|
+
this.clusterSelection = modifySelection(this.clusterSelection, cluster, options);
|
|
898
|
+
} else {
|
|
899
|
+
this._clusterSelection = modifySelection(this.clusterSelection, cluster, options);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* Shows tooltip for a cluster.
|
|
905
|
+
* @param cluster - cluster to show tooltip for.
|
|
906
|
+
* @param x - x coordinate of the tooltip.
|
|
907
|
+
* @param y - y coordinate of the tooltip.
|
|
908
|
+
* @return - tooltip body.
|
|
909
|
+
*/
|
|
457
910
|
showTooltip(cluster: SelectionItem, x: number, y: number): HTMLDivElement | null {
|
|
458
911
|
const bs = this.dataFrame.filter;
|
|
459
912
|
const filteredDf = bs.anyFalse ? this.dataFrame.clone(bs) : this.dataFrame;
|
|
460
913
|
const rowCount = filteredDf.rowCount;
|
|
461
914
|
const bitArray = new BitArray(rowCount, false);
|
|
462
|
-
const activityCol =
|
|
915
|
+
const activityCol = this.getScaledActivityColumn(bs.anyFalse);
|
|
463
916
|
const activityColData = activityCol.getRawData();
|
|
464
917
|
|
|
465
918
|
if (cluster.positionOrClusterType === CLUSTER_TYPE.ORIGINAL) {
|
|
466
|
-
const origClustCol = filteredDf.getCol(this.
|
|
919
|
+
const origClustCol = filteredDf.getCol(this.clustersColumnName);
|
|
467
920
|
const origClustColData = origClustCol.getRawData();
|
|
468
921
|
const origClustColCategories = origClustCol.categories;
|
|
469
922
|
const seekValue = origClustColCategories.indexOf(cluster.monomerOrCluster);
|
|
470
923
|
|
|
471
924
|
for (let i = 0; i < rowCount; ++i) {
|
|
472
|
-
if (origClustColData[i] === seekValue)
|
|
925
|
+
if (origClustColData[i] === seekValue) {
|
|
473
926
|
bitArray.setTrue(i);
|
|
927
|
+
}
|
|
474
928
|
}
|
|
475
929
|
bitArray.incrementVersion();
|
|
476
930
|
} else {
|
|
@@ -479,33 +933,41 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
479
933
|
}
|
|
480
934
|
|
|
481
935
|
const stats = bs.anyFalse ? getStats(activityColData, bitArray) :
|
|
482
|
-
this.
|
|
936
|
+
this.clusterStats[cluster.positionOrClusterType as ClusterType][cluster.monomerOrCluster];
|
|
483
937
|
|
|
484
|
-
if (!stats.count)
|
|
938
|
+
if (!stats.count) {
|
|
485
939
|
return null;
|
|
940
|
+
}
|
|
941
|
+
|
|
486
942
|
|
|
487
943
|
const mask = DG.BitSet.fromBytes(bitArray.buffer.buffer, rowCount);
|
|
488
|
-
const distributionTable =
|
|
489
|
-
const labels = getDistributionLegend(`Cluster: ${cluster.monomerOrCluster}`, 'Other');
|
|
944
|
+
const distributionTable = getDistributionTable(activityCol, mask);
|
|
490
945
|
const hist = getActivityDistribution(distributionTable, true);
|
|
491
946
|
const tableMap = getStatsTableMap(stats);
|
|
492
|
-
const aggregatedColMap = getAggregatedColumnValues(this.
|
|
493
|
-
|
|
494
|
-
|
|
947
|
+
const aggregatedColMap = getAggregatedColumnValues(this.dataFrame,
|
|
948
|
+
this.getTotalViewerAggColumns(), {
|
|
949
|
+
filterDf: true,
|
|
950
|
+
mask: mask,
|
|
951
|
+
});
|
|
952
|
+
const resultMap: {
|
|
953
|
+
[key: string]: any
|
|
954
|
+
} = {...tableMap, ...aggregatedColMap};
|
|
955
|
+
const tooltip = getDistributionPanel(hist, resultMap);
|
|
495
956
|
|
|
496
957
|
ui.tooltip.show(tooltip, x, y);
|
|
497
958
|
|
|
498
959
|
return tooltip;
|
|
499
960
|
}
|
|
500
961
|
|
|
962
|
+
/**
|
|
963
|
+
* Creates a dataframe for WebLogo viewer.
|
|
964
|
+
* @param pepCol - column with peptides.
|
|
965
|
+
* @param mask - bitset to filter dataframe with.
|
|
966
|
+
* @return - dataframe for WebLogo viewer.
|
|
967
|
+
*/
|
|
501
968
|
createWebLogoDf(pepCol: DG.Column<string>, mask: DG.BitSet): DG.DataFrame {
|
|
502
969
|
const newDf = DG.DataFrame.fromColumns([pepCol]);
|
|
503
970
|
newDf.filter.copyFrom(mask);
|
|
504
971
|
return newDf;
|
|
505
972
|
}
|
|
506
|
-
|
|
507
|
-
createDistributionDf(activityCol: DG.Column<number>, splitMask: DG.BitSet): DG.DataFrame {
|
|
508
|
-
const table = DG.DataFrame.fromColumns([activityCol, DG.Column.fromBitSet(C.COLUMNS_NAMES.SPLIT_COL, splitMask)]);
|
|
509
|
-
return prepareTableForHistogram(table);
|
|
510
|
-
}
|
|
511
973
|
}
|