@datagrok/peptides 1.8.0 → 1.8.2

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.
@@ -1,4 +1,3 @@
1
- import * as grok from 'datagrok-api/grok';
2
1
  import * as ui from 'datagrok-api/ui';
3
2
  import * as DG from 'datagrok-api/dg';
4
3
 
@@ -7,70 +6,111 @@ import * as C from '../utils/constants';
7
6
  import * as CR from '../utils/cell-renderer';
8
7
  import {PeptidesModel, VIEWER_TYPE} from '../model';
9
8
 
10
- export class SARViewerBase extends DG.JsViewer {
11
- tempName!: string;
9
+ export enum MONOMER_POSITION_MODE {
10
+ MUTATION_CLIFFS = 'Mutation Cliffs',
11
+ INVARIANT_MAP = 'Invariant Map',
12
+ }
13
+
14
+ /** Structure-activity relationship viewer */
15
+ export class MonomerPosition extends DG.JsViewer {
16
+ _titleHost = ui.divText('Mutation Cliffs', {id: 'pep-viewer-title'});
12
17
  _viewerGrid!: DG.Grid;
13
- sourceGrid!: DG.Grid;
14
18
  _model!: PeptidesModel;
15
- isPropertyChanging: boolean = false;
16
- _isVertical = false;
19
+ colorColumnName: string;
20
+ aggregation: string;
17
21
 
18
22
  constructor() {
19
23
  super();
24
+ this.colorColumnName = this.string('colorColumnName', C.COLUMNS_NAMES.ACTIVITY_SCALED, {category: 'Invariant Map'});
25
+ this.aggregation = this.string('aggregation', 'avg', {category: 'Invariant Map', choices: Object.values(DG.AGG)});
20
26
  }
21
27
 
22
- get name(): string {return '';}
28
+ get name(): string {return VIEWER_TYPE.MONOMER_POSITION;}
23
29
 
24
30
  get viewerGrid(): DG.Grid {
31
+ if (!this._viewerGrid)
32
+ this.createMonomerPositionGrid();
25
33
  return this._viewerGrid;
26
34
  }
27
35
  set viewerGrid(grid: DG.Grid) {
28
36
  this._viewerGrid = grid;
29
37
  }
30
38
 
39
+ get mode(): MONOMER_POSITION_MODE {
40
+ return this.dataFrame.getTag(C.TAGS.MONOMER_POSITION_MODE) as MONOMER_POSITION_MODE ??
41
+ MONOMER_POSITION_MODE.MUTATION_CLIFFS;
42
+ }
43
+ set mode(mode: MONOMER_POSITION_MODE) {
44
+ this.dataFrame.setTag(C.TAGS.MONOMER_POSITION_MODE, mode);
45
+ this.viewerGrid.invalidate();
46
+ }
47
+
31
48
  get model(): PeptidesModel {
32
49
  this._model ??= PeptidesModel.getInstance(this.dataFrame);
33
50
  return this._model;
34
51
  }
35
52
 
53
+ detach(): void {this.subs.forEach((sub) => sub.unsubscribe());}
54
+
36
55
  onTableAttached(): void {
37
56
  super.onTableAttached();
38
- this.sourceGrid = this.view?.grid ?? (grok.shell.v as DG.TableView).grid;
39
- // this.model = PeptidesModel.getInstance(this.dataFrame);
40
- this.subs.push(this.model.onMutationCliffsSelectionChanged.subscribe(() => this.viewerGrid.invalidate()));
57
+ this.subs.push(this.model.onMonomerPositionSelectionChanged.subscribe(() => this.viewerGrid.invalidate()));
41
58
  this.helpUrl = '/help/domains/bio/peptides.md';
59
+ this.subs.push(this.model.onSettingsChanged.subscribe(() => {
60
+ this.createMonomerPositionGrid();
61
+ this.render();
62
+ }));
63
+ this.render();
42
64
  }
43
65
 
44
- detach(): void {this.subs.forEach((sub) => sub.unsubscribe());}
45
-
46
- get isMutationCliffsMode(): string {
47
- return this.dataFrame.getTag(C.TAGS.SAR_MODE) ?? '1';
66
+ onPropertyChanged(property: DG.Property): void {
67
+ super.onPropertyChanged(property);
68
+ this.render();
48
69
  }
49
- set isMutationCliffsMode(s: string) {
50
- this.dataFrame.setTag(C.TAGS.SAR_MODE, s);
70
+
71
+ createMonomerPositionGrid(): void {
72
+ this.viewerGrid = this.model.monomerPositionDf.plot.grid();
73
+ this.viewerGrid.sort([C.COLUMNS_NAMES.MONOMER]);
74
+ this.viewerGrid.columns.setOrder([C.COLUMNS_NAMES.MONOMER, ...this.model.splitSeqDf.columns.names()]);
75
+ const monomerCol = this.model.monomerPositionDf.getCol(C.COLUMNS_NAMES.MONOMER);
76
+ CR.setAARRenderer(monomerCol, this.model.alphabet);
77
+ this.viewerGrid.onCellRender.subscribe((args: DG.GridCellRenderArgs) => renderCell(args, this.model,
78
+ this.mode === MONOMER_POSITION_MODE.INVARIANT_MAP, this.dataFrame.getCol(this.colorColumnName),
79
+ this.aggregation as DG.AggregationType));
80
+ this.viewerGrid.onCellTooltip((cell: DG.GridCell, x: number, y: number) => showTooltip(cell, x, y, this.model));
81
+ this.viewerGrid.root.addEventListener('click', (ev) => {
82
+ const gridCell = this.viewerGrid.hitTest(ev.offsetX, ev.offsetY);
83
+ if (!gridCell?.isTableCell || gridCell?.tableColumn?.name == C.COLUMNS_NAMES.MONOMER)
84
+ return;
85
+
86
+ const position = gridCell!.tableColumn!.name;
87
+ const aar = monomerCol.get(gridCell!.tableRowIndex!);
88
+ chooseAction(aar, position, ev.shiftKey, this.mode === MONOMER_POSITION_MODE.INVARIANT_MAP, this.model);
89
+ this.viewerGrid.invalidate();
90
+ // this.model.fireBitsetChanged();
91
+ });
92
+ this.viewerGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(this.model.monomerPositionDf, this.model));
93
+
94
+ setViewerGridProps(this.viewerGrid, false);
51
95
  }
52
96
 
53
97
  render(refreshOnly = false): void {
54
98
  if (!refreshOnly) {
55
99
  $(this.root).empty();
56
100
  let switchHost = ui.divText(VIEWER_TYPE.MOST_POTENT_RESIDUES, {id: 'pep-viewer-title'});
57
- if (this.name == 'MC') {
58
- const mutationCliffsMode = ui.boolInput('', this.isMutationCliffsMode === '1');
101
+ if (this.name == VIEWER_TYPE.MONOMER_POSITION) {
102
+ const mutationCliffsMode = ui.boolInput('', this.mode === MONOMER_POSITION_MODE.MUTATION_CLIFFS);
59
103
  mutationCliffsMode.root.addEventListener('click', () => {
60
104
  invariantMapMode.value = false;
61
105
  mutationCliffsMode.value = true;
62
- this.isMutationCliffsMode = '1';
63
- this.model.isInvariantMap = false;
64
- this.viewerGrid.invalidate();
106
+ this.mode = MONOMER_POSITION_MODE.MUTATION_CLIFFS;
65
107
  });
66
108
  mutationCliffsMode.addPostfix('Mutation Cliffs');
67
- const invariantMapMode = ui.boolInput('', this.isMutationCliffsMode === '0');
109
+ const invariantMapMode = ui.boolInput('', this.mode === MONOMER_POSITION_MODE.INVARIANT_MAP);
68
110
  invariantMapMode.root.addEventListener('click', () => {
69
111
  mutationCliffsMode.value = false;
70
112
  invariantMapMode.value = true;
71
- this.isMutationCliffsMode = '0';
72
- this.model.isInvariantMap = true;
73
- this.viewerGrid.invalidate();
113
+ this.mode = MONOMER_POSITION_MODE.INVARIANT_MAP;
74
114
  });
75
115
  invariantMapMode.addPostfix('Invariant Map');
76
116
  const setDefaultProperties = (input: DG.InputBase): void => {
@@ -97,89 +137,19 @@ export class SARViewerBase extends DG.JsViewer {
97
137
  }
98
138
  this.viewerGrid?.invalidate();
99
139
  }
100
-
101
- onPropertyChanged(property: DG.Property): void {
102
- super.onPropertyChanged(property);
103
-
104
- this.render(true);
105
- }
106
- }
107
-
108
- /** Structure-activity relationship viewer */
109
- export class MonomerPosition extends SARViewerBase {
110
- _titleHost = ui.divText('Mutation Cliffs', {id: 'pep-viewer-title'});
111
- _name = 'MC';
112
- _isVertical = false;
113
- colorColumnName: string;
114
- aggregation: string;
115
-
116
- constructor() {
117
- super();
118
- this.colorColumnName = this.string('colorColumnName', C.COLUMNS_NAMES.ACTIVITY_SCALED, {category: 'Invariant Map'});
119
- this.aggregation = this.string('aggregation', 'avg', {category: 'Invariant Map', choices: Object.values(DG.AGG)});
120
- }
121
-
122
- get name(): string {return this._name;}
123
-
124
- get viewerGrid(): DG.Grid {
125
- if (!this._viewerGrid)
126
- this.createMonomerPositionGrid();
127
- return this._viewerGrid;
128
- }
129
- set viewerGrid(grid: DG.Grid) {
130
- this._viewerGrid = grid;
131
- }
132
-
133
- onTableAttached(): void {
134
- super.onTableAttached();
135
- this.subs.push(this.model.onSettingsChanged.subscribe(() => {
136
- this.createMonomerPositionGrid();
137
- this.render();
138
- }));
139
- this.render();
140
- }
141
-
142
- onPropertyChanged(property: DG.Property): void {
143
- super.onPropertyChanged(property);
144
- }
145
-
146
- createMonomerPositionGrid(): void {
147
- this.viewerGrid = this.model.monomerPositionDf.plot.grid();
148
- this.viewerGrid.sort([C.COLUMNS_NAMES.MONOMER]);
149
- this.viewerGrid.columns.setOrder([C.COLUMNS_NAMES.MONOMER, ...this.model.splitSeqDf.columns.names()]);
150
- const monomerCol = this.model.monomerPositionDf.getCol(C.COLUMNS_NAMES.MONOMER);
151
- CR.setAARRenderer(monomerCol, this.model.alphabet);
152
- this.viewerGrid.onCellRender.subscribe((args: DG.GridCellRenderArgs) => renderCell(args, this.model,
153
- this.model.isInvariantMap, this.dataFrame.getCol(this.colorColumnName), this.aggregation as DG.AggregationType));
154
- this.viewerGrid.onCellTooltip((cell: DG.GridCell, x: number, y: number) => showTooltip(cell, x, y, this.model));
155
- this.viewerGrid.root.addEventListener('click', (ev) => {
156
- const gridCell = this.viewerGrid.hitTest(ev.offsetX, ev.offsetY);
157
- if (!gridCell?.isTableCell || gridCell?.tableColumn?.name == C.COLUMNS_NAMES.MONOMER)
158
- return;
159
-
160
- const position = gridCell!.tableColumn!.name;
161
- const aar = monomerCol.get(gridCell!.tableRowIndex!);
162
- chooseAction(aar, position, ev.shiftKey, this.model.isInvariantMap, this.model);
163
- this.viewerGrid.invalidate();
164
- this.model.fireBitsetChanged();
165
- });
166
- this.viewerGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(this.model.monomerPositionDf, this.model));
167
-
168
- setViewerGridProps(this.viewerGrid, false);
169
- }
170
140
  }
171
141
 
172
142
  /** Vertical structure activity relationship viewer */
173
- export class MostPotentResiduesViewer extends SARViewerBase {
174
- _name = 'MPR';
143
+ export class MostPotentResiduesViewer extends DG.JsViewer {
175
144
  _titleHost = ui.divText(VIEWER_TYPE.MOST_POTENT_RESIDUES, {id: 'pep-viewer-title'});
176
- _isVertical = true;
145
+ _viewerGrid!: DG.Grid;
146
+ _model!: PeptidesModel;
177
147
 
178
148
  constructor() {
179
149
  super();
180
150
  }
181
151
 
182
- get name(): string {return this._name;}
152
+ get name(): string {return VIEWER_TYPE.MOST_POTENT_RESIDUES;}
183
153
 
184
154
  get viewerGrid(): DG.Grid {
185
155
  if (!this._viewerGrid)
@@ -190,8 +160,17 @@ export class MostPotentResiduesViewer extends SARViewerBase {
190
160
  this._viewerGrid = grid;
191
161
  }
192
162
 
163
+ get model(): PeptidesModel {
164
+ this._model ??= PeptidesModel.getInstance(this.dataFrame);
165
+ return this._model;
166
+ }
167
+
168
+ detach(): void {this.subs.forEach((sub) => sub.unsubscribe());}
169
+
193
170
  onTableAttached(): void {
194
171
  super.onTableAttached();
172
+ this.subs.push(this.model.onMonomerPositionSelectionChanged.subscribe(() => this.viewerGrid.invalidate()));
173
+ this.helpUrl = '/help/domains/bio/peptides.md';
195
174
  this.subs.push(this.model.onSettingsChanged.subscribe(() => {
196
175
  this.createMostPotentResiduesGrid();
197
176
  this.render();
@@ -201,6 +180,7 @@ export class MostPotentResiduesViewer extends SARViewerBase {
201
180
 
202
181
  onPropertyChanged(property: DG.Property): void {
203
182
  super.onPropertyChanged(property);
183
+ this.render();
204
184
  }
205
185
 
206
186
  createMostPotentResiduesGrid(): void {
@@ -226,13 +206,31 @@ export class MostPotentResiduesViewer extends SARViewerBase {
226
206
  const aar = monomerCol.get(tableRowIdx);
227
207
  chooseAction(aar, position!.toFixed(), ev.shiftKey, false, this.model);
228
208
  this.viewerGrid.invalidate();
229
- this.model.fireBitsetChanged();
209
+ // this.model.fireBitsetChanged();
230
210
  });
231
211
  this.viewerGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(this.model.mostPotentResiduesDf, this.model));
232
212
  const mdCol: DG.GridColumn = this.viewerGrid.col(C.COLUMNS_NAMES.MEAN_DIFFERENCE)!;
233
213
  mdCol.name = 'Diff';
234
214
  setViewerGridProps(this.viewerGrid, true);
235
215
  }
216
+
217
+ render(refreshOnly = false): void {
218
+ if (!refreshOnly) {
219
+ $(this.root).empty();
220
+ const switchHost = ui.divText(VIEWER_TYPE.MOST_POTENT_RESIDUES, {id: 'pep-viewer-title'});
221
+ const tips: HTMLElement = ui.iconFA('question');
222
+ ui.tooltip.bind(tips,
223
+ () => ui.divV([ui.divText('Color intensity - p-value'), ui.divText('Circle size - Mean difference')]));
224
+
225
+ $(tips).addClass('pep-help-icon');
226
+
227
+ const viewerRoot = this.viewerGrid.root;
228
+ viewerRoot.style.width = 'auto';
229
+ const header = ui.divH([switchHost, tips], {style: {alignSelf: 'center', lineHeight: 'normal'}});
230
+ this.root.appendChild(ui.divV([header, viewerRoot]));
231
+ }
232
+ this.viewerGrid?.invalidate();
233
+ }
236
234
  }
237
235
 
238
236
  function renderCell(args: DG.GridCellRenderArgs, model: PeptidesModel, isInvariantMap?: boolean,
@@ -292,16 +290,16 @@ function renderCell(args: DG.GridCellRenderArgs, model: PeptidesModel, isInvaria
292
290
 
293
291
  const color = DG.Color.scaleColor(cellColorDataCol.aggregate(colorAgg!), colorColStats.min, colorColStats.max);
294
292
  CR.renderInvaraintMapCell(
295
- canvasContext, currentMonomer, currentPosition, model.invariantMapSelection, value, bound, color);
293
+ canvasContext, currentMonomer, currentPosition, model.monomerPositionFilter, value, bound, color);
296
294
  } else {
297
295
  CR.renderMutationCliffCell(canvasContext, currentMonomer, currentPosition, model.monomerPositionStats, bound,
298
- model.mutationCliffsSelection, model.substitutionsInfo, model.settings.isBidirectional);
296
+ model.monomerPositionSelection, model.mutationCliffs, model.settings.isBidirectional);
299
297
  }
300
298
  args.preventDefault();
301
299
  canvasContext.restore();
302
300
  }
303
301
 
304
- function showTooltip(cell: DG.GridCell, x: number, y: number, model: PeptidesModel): boolean {
302
+ export function showTooltip(cell: DG.GridCell, x: number, y: number, model: PeptidesModel): boolean {
305
303
  const renderColNames = [...model.splitSeqDf.columns.names(), C.COLUMNS_NAMES.MEAN_DIFFERENCE];
306
304
  const tableCol = cell.tableColumn;
307
305
  const tableColName = tableCol?.name;
@@ -323,12 +321,12 @@ function showTooltip(cell: DG.GridCell, x: number, y: number, model: PeptidesMod
323
321
  return true;
324
322
  }
325
323
 
326
- function chooseAction(aar: string, position: string, isShiftPressed: boolean, isInvariantMapSelection: boolean,
324
+ function chooseAction(aar: string, position: string, isShiftPressed: boolean, isFilter: boolean,
327
325
  model: PeptidesModel): void {
328
- if (isShiftPressed)
329
- model.modifyMonomerPositionSelection(aar, position, isInvariantMapSelection);
330
- else
331
- model.initMonomerPositionSelection(aar, position, isInvariantMapSelection);
326
+ if (!isShiftPressed)
327
+ model.initMonomerPositionSelection({cleanInit: true, notify: false});
328
+
329
+ model.modifyMonomerPositionSelection(aar, position, isFilter);
332
330
  }
333
331
 
334
332
  function cellChanged(table: DG.DataFrame, model: PeptidesModel): void {
@@ -5,27 +5,27 @@ import {StringDictionary} from '@datagrok-libraries/utils/src/type-declarations'
5
5
  import $ from 'cash-dom';
6
6
 
7
7
  import * as C from '../utils/constants';
8
- import {getAggregatedValue, getStats, MaskInfo, Stats} from '../utils/statistics';
8
+ import {getStats, Stats} from '../utils/statistics';
9
9
  import {PeptidesModel} from '../model';
10
- import {wrapDistroAndStatsDefault} from '../utils/misc';
10
+ import {getStatsSummary} from '../utils/misc';
11
+ import BitArray from '@datagrok-libraries/utils/src/bit-array';
11
12
 
12
13
  const allConst = 'All';
13
14
  const otherConst = 'Other';
14
15
 
15
16
  export function getDistributionWidget(table: DG.DataFrame, model: PeptidesModel): DG.Widget {
16
- const activityScaledCol = table.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
17
- const rowCount = activityScaledCol.length;
18
- const selectionObject = model.mutationCliffsSelection;
17
+ const activityCol = table.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
18
+ const activityColData = activityCol.getRawData();
19
+ const rowCount = activityCol.length;
20
+ const selectionObject = model.monomerPositionSelection;
19
21
  const clustersColName = model.settings.clustersColumnName;
20
22
  let clustersProcessedObject: string[] = [];
21
23
  if (clustersColName)
22
- clustersProcessedObject = model.logoSummarySelection;
24
+ clustersProcessedObject = model.clusterSelection;
23
25
 
24
26
  const positions = Object.keys(selectionObject);
25
- const positionsLen = positions.length;
26
27
  let aarStr = allConst;
27
28
  let otherStr = '';
28
- // const useSelectedStr = model.isPeptideSpaceChangingBitset;
29
29
 
30
30
  const updateDistributionHost = (): void => {
31
31
  model.splitByPos = splitByPosition.value!;
@@ -34,34 +34,30 @@ export function getDistributionWidget(table: DG.DataFrame, model: PeptidesModel)
34
34
  if (splitByPosition.value && splitByAAR.value) {
35
35
  otherStr = otherConst;
36
36
  for (const position of positions) {
37
- const posCol = table.getCol(position);
38
37
  const aarList = selectionObject[position];
39
38
  if (aarList.length === 0)
40
39
  continue;
41
40
 
41
+ const posCol = table.getCol(position);
42
+ const posColCategories = posCol.categories;
43
+ const posColData = posCol.getRawData();
44
+
42
45
  for (const aar of aarList) {
43
- const indexes: number[] = [];
44
- aarStr = `${position} : ${aar}`;
45
- const splitCol = DG.Column.bool(C.COLUMNS_NAMES.SPLIT_COL, rowCount).init((i) => {
46
- const flag = posCol.get(i) == aar;
47
- if (flag)
48
- indexes.push(i);
49
- });
50
- const distributionTable = DG.DataFrame.fromColumns([activityScaledCol, splitCol]);
51
-
52
- const colResults: {[colName: string]: number} = {};
53
- for (const [col, agg] of Object.entries(model.settings.columns || {})) {
54
- const currentCol = table.getCol(col);
55
- const currentColData = currentCol.getRawData();
56
- const tempCol = DG.Column.float('', indexes.length);
57
- tempCol.init((i) => currentColData[indexes[i]]);
58
- colResults[`${agg}(${col})`] = tempCol.stats[agg as keyof DG.Stats] as number;
59
- }
46
+ const labels = getDistributionLegend(`${position} : ${aar}`, otherStr);
47
+
48
+ const aarCategoryIndex = posColCategories.indexOf(aar);
49
+ const mask = DG.BitSet.create(rowCount, (i) => posColData[i] === aarCategoryIndex);
50
+ const distributionTable = DG.DataFrame.fromColumns(
51
+ [activityCol, DG.Column.fromBitSet(C.COLUMNS_NAMES.SPLIT_COL, mask)]);
52
+ const hist = getActivityDistribution(distributionTable);
60
53
 
61
54
  const stats = model.monomerPositionStats[position][aar];
62
- const das = getDistributionAndStats(distributionTable, stats, aarStr, otherStr, true);
63
- const resultMap: {[key: string]: any} = {...das.tableMap, ...colResults};
64
- const distributionRoot = wrapDistroAndStatsDefault(das.labels, das.histRoot, resultMap);
55
+ const tableMap = getStatsTableMap(stats, {fractionDigits: 2});
56
+
57
+ const aggregatedColMap = model.getAggregatedColumnValues({filterDf: true, mask, fractionDigits: 2});
58
+
59
+ const resultMap = {...tableMap, ...aggregatedColMap};
60
+ const distributionRoot = getStatsSummary(labels, hist, resultMap);
65
61
  $(distributionRoot).addClass('d4-flex-col');
66
62
 
67
63
  res.push(distributionRoot);
@@ -69,40 +65,32 @@ export function getDistributionWidget(table: DG.DataFrame, model: PeptidesModel)
69
65
  }
70
66
  } else if (splitByPosition.value) {
71
67
  otherStr = otherConst;
72
- const activityScaledData = activityScaledCol.toList();
73
68
  for (const position of positions) {
74
- const posCol = table.getCol(position);
75
69
  const aarList = selectionObject[position];
76
70
  if (aarList.length === 0)
77
71
  continue;
78
72
 
79
73
  aarStr = `${position}: {${aarList.join(', ')}}`;
74
+ const labels = getDistributionLegend(aarStr, otherStr);
80
75
 
81
- //OPTIMIZE: don't create Bitset, use bool[]
82
- const mask = DG.BitSet.create(rowCount, (i) => aarList.includes(posCol.get(i)));
76
+ const posCol = table.getCol(position);
77
+ const posColCategories = posCol.categories;
78
+ const posColData = posCol.getRawData();
79
+ const aarIndexesList = aarList.map((aar) => posColCategories.indexOf(aar));
80
+ const mask = DG.BitSet.create(rowCount, (i) => aarIndexesList.includes(posColData[i]));
83
81
  const splitCol = DG.Column.fromBitSet(C.COLUMNS_NAMES.SPLIT_COL, mask);
84
82
 
85
- const maskInfo: MaskInfo = {
86
- mask: splitCol.toList() as boolean[],
87
- trueCount: mask.trueCount,
88
- falseCount: mask.falseCount,
89
- };
90
- const stats = getStats(activityScaledData, maskInfo);
91
- const distributionTable = DG.DataFrame.fromColumns([activityScaledCol, splitCol]);
92
-
93
- const indexes = mask.getSelectedIndexes();
94
- const colResults: {[colName: string]: number} = {};
95
- for (const [col, agg] of Object.entries(model.settings.columns || {})) {
96
- const currentCol = table.getCol(col);
97
- const currentColData = currentCol.getRawData();
98
- const tempCol = DG.Column.float('', indexes.length);
99
- tempCol.init((i) => currentColData[indexes[i]]);
100
- colResults[`${agg}(${col})`] = tempCol.stats[agg as keyof DG.Stats] as number;
101
- }
83
+ const aggregatedColMap = model.getAggregatedColumnValues({filterDf: true, mask, fractionDigits: 2});
102
84
 
103
- const das = getDistributionAndStats(distributionTable, stats, aarStr, otherStr, true);
104
- const resultMap: {[key: string]: any} = {...das.tableMap, ...colResults};
105
- const distributionRoot = wrapDistroAndStatsDefault(das.labels, das.histRoot, resultMap);
85
+ const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
86
+ const hist = getActivityDistribution(distributionTable);
87
+
88
+ const bitArray = BitArray.fromUint32Array(rowCount, splitCol.getRawData() as Uint32Array);
89
+ const stats = getStats(activityColData, bitArray);
90
+ const tableMap = getStatsTableMap(stats, {fractionDigits: 2});
91
+
92
+ const resultMap = {...tableMap, ...aggregatedColMap};
93
+ const distributionRoot = getStatsSummary(labels, hist, resultMap);
106
94
  $(distributionRoot).addClass('d4-flex-col');
107
95
 
108
96
  res.push(distributionRoot);
@@ -123,43 +111,30 @@ export function getDistributionWidget(table: DG.DataFrame, model: PeptidesModel)
123
111
  }
124
112
 
125
113
  otherStr = otherConst;
126
- const activityScaledData = activityScaledCol.getRawData();
127
114
  for (const aar of aars) {
128
115
  const posList = reversedSelectionObject[aar];
116
+ const posColList = posList.map((pos) => table.getCol(pos));
117
+ const posColCategoriesList = posColList.map((posCol) => posCol.categories);
118
+ const posColDataList = posColList.map((posCol) => posCol.getRawData());
119
+ const aarCategoryIndexList = posColCategoriesList.map((posColCategories) => posColCategories.indexOf(aar));
120
+
129
121
  aarStr = `${aar}: {${posList.join(', ')}}`;
122
+ const labels = getDistributionLegend(aarStr, otherStr);
123
+
124
+ const mask = DG.BitSet.create(rowCount,
125
+ (i) => posColDataList.some((posColData, j) => posColData[i] === aarCategoryIndexList[j]));
126
+ const aggregatedColMap = model.getAggregatedColumnValues({filterDf: true, mask, fractionDigits: 2});
130
127
 
131
- //OPTIMIZE: don't create Bitset, use bool[]
132
- const mask = DG.BitSet.create(rowCount, (i) => {
133
- const currentRow = table.row(i);
134
- for (const position of posList) {
135
- if (currentRow.get(position) == aar)
136
- return true;
137
- }
138
- return false;
139
- });
140
128
  const splitCol = DG.Column.fromBitSet(C.COLUMNS_NAMES.SPLIT_COL, mask);
129
+ const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
130
+ const hist = getActivityDistribution(distributionTable);
141
131
 
142
- const maskInfo: MaskInfo = {
143
- mask: splitCol.toList() as boolean[],
144
- trueCount: mask.trueCount,
145
- falseCount: mask.falseCount,
146
- };
147
- const stats = getStats(activityScaledData, maskInfo);
148
- const distributionTable = DG.DataFrame.fromColumns([activityScaledCol, splitCol]);
149
-
150
- const indexes = mask.getSelectedIndexes();
151
- const colResults: {[colName: string]: number} = {};
152
- for (const [col, agg] of Object.entries(model.settings.columns || {})) {
153
- const currentCol = table.getCol(col);
154
- const currentColData = currentCol.getRawData();
155
- const tempCol = DG.Column.float('', indexes.length);
156
- tempCol.init((i) => currentColData[indexes[i]]);
157
- colResults[`${agg}(${col})`] = tempCol.stats[agg as keyof DG.Stats] as number;
158
- }
132
+ const bitArray = BitArray.fromUint32Array(rowCount, splitCol.getRawData() as Uint32Array);
133
+ const stats = getStats(activityColData, bitArray);
134
+ const tableMap = getStatsTableMap(stats, {fractionDigits: 2});
159
135
 
160
- const das = getDistributionAndStats(distributionTable, stats, aarStr, otherStr, true);
161
- const resultMap: {[key: string]: any} = {...das.tableMap, ...colResults};
162
- const distributionRoot = wrapDistroAndStatsDefault(das.labels, das.histRoot, resultMap);
136
+ const resultMap: {[key: string]: any} = {...tableMap, ...aggregatedColMap};
137
+ const distributionRoot = getStatsSummary(labels, hist, resultMap);
163
138
  $(distributionRoot).addClass('d4-flex-col');
164
139
 
165
140
  res.push(distributionRoot);
@@ -170,10 +145,8 @@ export function getDistributionWidget(table: DG.DataFrame, model: PeptidesModel)
170
145
  res.push(ui.divText('No distribution'));
171
146
  else {
172
147
  otherStr = '';
173
- if (false /*useSelectedStr*/) {
174
- aarStr = 'Selected';
175
- otherStr = otherConst;
176
- } else if (positionsLen) {
148
+ if (Object.values(selectionObject).some((selectedAar) => selectedAar.length !== 0) ||
149
+ clustersProcessedObject.length !== 0) {
177
150
  aarStr = '';
178
151
  for (const position of positions) {
179
152
  const aarList = selectionObject[position];
@@ -184,25 +157,20 @@ export function getDistributionWidget(table: DG.DataFrame, model: PeptidesModel)
184
157
  aarStr += `Clusters: ${clustersProcessedObject.join(', ')}`;
185
158
  otherStr = otherConst;
186
159
  }
160
+ const labels = getDistributionLegend(aarStr, otherStr);
187
161
 
188
- const distributionTable = DG.DataFrame.fromColumns([activityScaledCol, splitCol]);
162
+ const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
163
+ const hist = getActivityDistribution(distributionTable);
189
164
 
190
- const compoundBs = model.getCompoundBitest();
191
- const colResults: {[colName: string]: number} = {};
192
- for (const [col, agg] of Object.entries(model.settings.columns || {})) {
193
- const currentCol = table.getCol(col);
194
- colResults[`${agg}(${col})`] = getAggregatedValue(currentCol, agg, compoundBs);
195
- }
165
+ const bitArray = BitArray.fromUint32Array(rowCount, splitCol.getRawData() as Uint32Array);
166
+ const mask = DG.BitSet.create(rowCount, (i) => bitArray.getBit(i));
167
+ const aggregatedColMap = model.getAggregatedColumnValues({filterDf: true, mask, fractionDigits: 2});
168
+
169
+ const stats = getStats(activityColData, bitArray);
170
+ const tableMap = getStatsTableMap(stats, {fractionDigits: 2});
196
171
 
197
- const maskInfo: MaskInfo = {
198
- mask: splitCol.toList() as boolean[],
199
- trueCount: compoundBs.trueCount,
200
- falseCount: compoundBs.falseCount,
201
- };
202
- const stats = getStats(activityScaledCol.getRawData(), maskInfo);
203
- const das = getDistributionAndStats(distributionTable, stats, aarStr, otherStr);
204
- const resultMap: {[key: string]: any} = {...das.tableMap, ...colResults};
205
- const distributionRoot = wrapDistroAndStatsDefault(das.labels, das.histRoot, resultMap);
172
+ const resultMap: {[key: string]: any} = {...tableMap, ...aggregatedColMap};
173
+ const distributionRoot = getStatsSummary(labels, hist, resultMap);
206
174
  $(distributionRoot).addClass('d4-flex-col');
207
175
 
208
176
  res.push(distributionRoot);
@@ -212,14 +180,14 @@ export function getDistributionWidget(table: DG.DataFrame, model: PeptidesModel)
212
180
  };
213
181
 
214
182
  const setDefaultProperties = (input: DG.InputBase): void => {
215
- input.enabled = !model.isMutationCliffSelectionEmpty;
183
+ input.enabled = !model.isMonomerPositionSelectionEmpty;
216
184
  $(input.root).find('.ui-input-editor').css('margin', '0px');
217
185
  $(input.root).find('.ui-input-description').css('padding', '0px').css('padding-left', '5px');
218
186
  };
219
187
 
220
188
  let defaultValuePos = model.splitByPos;
221
189
  let defaultValueAAR = model.splitByAAR;
222
- if (!model.isLogoSummarySelectionEmpty && model.isMutationCliffSelectionEmpty) {
190
+ if (!model.isClusterSelectionEmpty && model.isMonomerPositionSelectionEmpty) {
223
191
  defaultValuePos = false;
224
192
  defaultValueAAR = false;
225
193
  }
@@ -239,34 +207,36 @@ export function getDistributionWidget(table: DG.DataFrame, model: PeptidesModel)
239
207
  return new DG.Widget(ui.divV([controlsHost, distributionHost]));
240
208
  }
241
209
 
242
- type DistroAndStats = {labels: HTMLDivElement, histRoot: HTMLElement, tableMap: StringDictionary};
243
-
244
- export function getDistributionAndStats(table: DG.DataFrame, stats: Stats, thisLabel: string, otherLabel: string = '',
245
- isTooltip: boolean = false, splitColName?: string): DistroAndStats {
246
- const labels = ui.divV([
247
- ui.label(thisLabel, {style: {color: DG.Color.toHtml(otherLabel == '' ? DG.Color.blue : DG.Color.orange)}}),
248
- ui.label(otherLabel, {style: {color: DG.Color.toHtml(DG.Color.blue)}})]);
249
-
250
- const histRoot = table.plot.histogram({
210
+ export function getActivityDistribution(table: DG.DataFrame, isTooltip: boolean = false,
211
+ ): DG.Viewer<DG.IHistogramLookSettings> {
212
+ const hist = table.plot.histogram({
251
213
  filteringEnabled: false,
252
214
  valueColumnName: C.COLUMNS_NAMES.ACTIVITY_SCALED,
253
- splitColumnName: splitColName ?? C.COLUMNS_NAMES.SPLIT_COL,
215
+ splitColumnName: C.COLUMNS_NAMES.SPLIT_COL,
254
216
  legendVisibility: 'Never',
255
217
  showXAxis: true,
256
218
  showColumnSelector: false,
257
219
  showRangeSlider: false,
258
220
  showBinSelector: !isTooltip,
259
221
  backColor: isTooltip ? '#fdffe5' : '#fffff',
260
- }).root;
261
- histRoot.style.width = 'auto';
222
+ }) as DG.Viewer<DG.IHistogramLookSettings>;
223
+ hist.root.style.width = 'auto';
224
+ return hist;
225
+ }
262
226
 
263
- const tableMap: StringDictionary = {
227
+ export function getStatsTableMap(stats: Stats, options: {fractionDigits?: number} = {}): StringDictionary {
228
+ const tableMap = {
264
229
  'Statistics:': '',
265
230
  'Count': stats.count.toString(),
266
- 'Ratio': stats.ratio.toFixed(2),
267
- 'p-value': stats.pValue < 0.01 ? '<0.01' : stats.pValue.toFixed(2),
268
- 'Mean difference': stats.meanDifference.toFixed(2),
231
+ 'Ratio': stats.ratio.toFixed(options.fractionDigits),
232
+ 'p-value': stats.pValue < 0.01 ? '<0.01' : stats.pValue.toFixed(options.fractionDigits),
233
+ 'Mean difference': stats.meanDifference.toFixed(options.fractionDigits),
269
234
  };
235
+ return tableMap;
236
+ }
270
237
 
271
- return {labels: labels, histRoot: histRoot, tableMap: tableMap};
238
+ export function getDistributionLegend(thisLabel: string, otherLabel: string = ''): HTMLDivElement {
239
+ return ui.divV([
240
+ ui.label(thisLabel, {style: {color: DG.Color.toHtml(otherLabel.length === 0 ? DG.Color.blue : DG.Color.orange)}}),
241
+ ui.label(otherLabel, {style: {color: DG.Color.toHtml(DG.Color.blue)}})]);
272
242
  }
@@ -7,8 +7,8 @@ import {getSeparator} from '../utils/misc';
7
7
 
8
8
  export function mutationCliffsWidget(table: DG.DataFrame, model: PeptidesModel): DG.Widget {
9
9
  const currentFilter = table.filter.getSelectedIndexes();
10
- const substInfo = model.substitutionsInfo;
11
- const currentCell = model.mutationCliffsSelection;
10
+ const substInfo = model.mutationCliffs;
11
+ const currentCell = model.monomerPositionSelection;
12
12
  const positions = Object.keys(currentCell);
13
13
 
14
14
  if (!positions.length)