@datagrok/peptides 1.9.2 → 1.11.3
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/CHANGELOG.md +81 -0
- package/dist/209.js +2 -0
- package/dist/521.js +2 -0
- package/dist/535.js +2 -0
- package/dist/729.js +2 -0
- package/dist/959.js +2 -0
- package/dist/package-test.js +2 -2
- package/dist/package.js +2 -2
- package/package.json +13 -11
- package/setup-unlink-clean.sh +0 -0
- package/src/model.ts +302 -232
- package/src/package-test.ts +2 -1
- package/src/package.ts +14 -16
- package/src/tests/algorithms.ts +4 -1
- package/src/tests/core.ts +100 -14
- package/src/tests/model.ts +16 -14
- package/src/tests/peptide-space-test.ts +7 -7
- package/src/tests/table-view.ts +63 -54
- package/src/tests/utils.ts +1 -1
- package/src/tests/viewers.ts +40 -6
- package/src/tests/widgets.ts +100 -18
- package/src/utils/cell-renderer.ts +38 -20
- package/src/utils/constants.ts +3 -0
- package/src/utils/misc.ts +44 -12
- package/src/utils/peptide-similarity-space.ts +3 -4
- package/src/utils/statistics.ts +2 -2
- package/src/utils/types.ts +3 -1
- package/src/viewers/logo-summary.ts +132 -140
- package/src/viewers/peptide-space-viewer.ts +2 -1
- package/src/viewers/sar-viewer.ts +46 -39
- package/src/widgets/distribution.ts +17 -17
- package/src/widgets/mutation-cliffs.ts +91 -19
- package/src/widgets/peptides.ts +12 -13
- package/src/widgets/settings.ts +47 -25
- package/src/widgets/similarity.ts +39 -0
- package/dist/168.js +0 -2
- package/dist/478.js +0 -2
- package/dist/802.js +0 -2
- package/dist/951.js +0 -2
|
@@ -6,14 +6,11 @@ import $ from 'cash-dom';
|
|
|
6
6
|
import {ClusterType, CLUSTER_TYPE, PeptidesModel, VIEWER_TYPE} from '../model';
|
|
7
7
|
import * as C from '../utils/constants';
|
|
8
8
|
import * as CR from '../utils/cell-renderer';
|
|
9
|
-
import {
|
|
10
|
-
import {getSplitterForColumn} from '@datagrok-libraries/bio/src/utils/macromolecule/utils';
|
|
11
|
-
import {HorizontalAlignments, PositionHeight} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
9
|
+
import {HorizontalAlignments, IWebLogoViewer, PositionHeight} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
12
10
|
import {getAggregatedValue, getStats, Stats} from '../utils/statistics';
|
|
13
11
|
import wu from 'wu';
|
|
14
|
-
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
15
12
|
import {getActivityDistribution, getDistributionLegend, getStatsTableMap} from '../widgets/distribution';
|
|
16
|
-
import {getStatsSummary} from '../utils/misc';
|
|
13
|
+
import {getStatsSummary, prepareTableForHistogram} from '../utils/misc';
|
|
17
14
|
import BitArray from '@datagrok-libraries/utils/src/bit-array';
|
|
18
15
|
|
|
19
16
|
const getAggregatedColName = (aggF: string, colName: string): string => `${aggF}(${colName})`;
|
|
@@ -30,8 +27,7 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
30
27
|
initialized: boolean = false;
|
|
31
28
|
webLogoMode: string;
|
|
32
29
|
membersRatioThreshold: number;
|
|
33
|
-
|
|
34
|
-
distributionDfPlot: DG.DataFrame[] = [];
|
|
30
|
+
bitsets: DG.BitSet[] = [];
|
|
35
31
|
|
|
36
32
|
constructor() {
|
|
37
33
|
super();
|
|
@@ -43,20 +39,8 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
43
39
|
|
|
44
40
|
onTableAttached(): void {
|
|
45
41
|
super.onTableAttached();
|
|
46
|
-
|
|
47
42
|
this.model = PeptidesModel.getInstance(this.dataFrame);
|
|
48
|
-
this.
|
|
49
|
-
this.createLogoSummaryGrid();
|
|
50
|
-
this.render();
|
|
51
|
-
}));
|
|
52
|
-
this.subs.push(this.model.onNewCluster.subscribe(() => this.clusterFromSelection()));
|
|
53
|
-
this.subs.push(this.model.onRemoveCluster.subscribe(() => this.removeCluster()));
|
|
54
|
-
this.subs.push(this.model.onFilterChanged.subscribe(() => {
|
|
55
|
-
this.createLogoSummaryGrid();
|
|
56
|
-
this.render();
|
|
57
|
-
}));
|
|
58
|
-
|
|
59
|
-
this.createLogoSummaryGrid();
|
|
43
|
+
this.createLogoSummaryTableGrid();
|
|
60
44
|
this.initialized = true;
|
|
61
45
|
this.render();
|
|
62
46
|
}
|
|
@@ -64,19 +48,30 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
64
48
|
detach(): void {this.subs.forEach((sub) => sub.unsubscribe());}
|
|
65
49
|
|
|
66
50
|
render(): void {
|
|
67
|
-
if (this.initialized)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
this.viewerGrid.root.style.width = 'auto';
|
|
77
|
-
this.root.appendChild(ui.divV([this._titleHost, this.viewerGrid.root]));
|
|
78
|
-
this.viewerGrid.invalidate();
|
|
51
|
+
if (!this.initialized)
|
|
52
|
+
return;
|
|
53
|
+
$(this.root).empty();
|
|
54
|
+
const df = this.viewerGrid.dataFrame;
|
|
55
|
+
if (!df.filter.anyTrue) {
|
|
56
|
+
const emptyDf = ui.divText('No clusters to satisfy the threshold. ' +
|
|
57
|
+
'Please, lower the threshold in viewer proeperties to include clusters');
|
|
58
|
+
this.root.appendChild(ui.divV([this._titleHost, emptyDf]));
|
|
59
|
+
return;
|
|
79
60
|
}
|
|
61
|
+
const expand = ui.iconFA('expand-alt', () => {
|
|
62
|
+
const dialog = ui.dialog('Logo Summary Table');
|
|
63
|
+
dialog.add(this.viewerGrid.root);
|
|
64
|
+
dialog.onCancel(() => this.render());
|
|
65
|
+
dialog.showModal(true);
|
|
66
|
+
this.viewerGrid.invalidate();
|
|
67
|
+
}, 'Show Logo Summary Table in full screen');
|
|
68
|
+
$(expand).addClass('pep-help-icon');
|
|
69
|
+
this.viewerGrid.root.style.width = 'auto';
|
|
70
|
+
this.root.appendChild(ui.divV([
|
|
71
|
+
ui.divH([this._titleHost, expand], {style: {alignSelf: 'center', lineHeight: 'normal'}}),
|
|
72
|
+
this.viewerGrid.root,
|
|
73
|
+
]));
|
|
74
|
+
this.viewerGrid.invalidate();
|
|
80
75
|
}
|
|
81
76
|
|
|
82
77
|
onPropertyChanged(property: DG.Property): void {
|
|
@@ -86,7 +81,7 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
86
81
|
this.render();
|
|
87
82
|
}
|
|
88
83
|
|
|
89
|
-
|
|
84
|
+
createLogoSummaryTableGrid(): DG.Grid {
|
|
90
85
|
const clustersColName = this.model.settings.clustersColumnName!;
|
|
91
86
|
const isDfFiltered = this.dataFrame.filter.anyFalse;
|
|
92
87
|
const filteredDf = isDfFiltered ? this.dataFrame.clone(this.dataFrame.filter) : this.dataFrame;
|
|
@@ -132,21 +127,19 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
132
127
|
}
|
|
133
128
|
|
|
134
129
|
// BEGIN: fill LST part with custom clusters
|
|
135
|
-
const
|
|
136
|
-
const customDistTables: DG.DataFrame[] = new Array(customClustColList.length);
|
|
130
|
+
const customBitsets: DG.BitSet[] = new Array(customClustColList.length);
|
|
137
131
|
|
|
138
132
|
for (let rowIdx = 0; rowIdx < customClustColList.length; ++rowIdx) {
|
|
139
133
|
const customClustCol = customClustColList[rowIdx];
|
|
140
134
|
customLSTClustCol.set(rowIdx, customClustCol.name);
|
|
141
135
|
const bitArray = BitArray.fromUint32Array(filteredDfRowCount, customClustCol.getRawData() as Uint32Array);
|
|
142
|
-
const bsMask = DG.BitSet.
|
|
136
|
+
const bsMask = DG.BitSet.fromBytes(bitArray.buffer.buffer, filteredDfRowCount);
|
|
143
137
|
|
|
144
138
|
const stats: Stats = isDfFiltered ? getStats(activityColData, bitArray) :
|
|
145
139
|
this.model.clusterStats[CLUSTER_TYPE.CUSTOM][customClustCol.name];
|
|
146
140
|
|
|
147
141
|
customMembersColData[rowIdx] = stats.count;
|
|
148
|
-
|
|
149
|
-
customDistTables[rowIdx] = this.createDistributionPlot(activityCol, customClustColList[rowIdx]);
|
|
142
|
+
customBitsets[rowIdx] = bsMask;
|
|
150
143
|
customMDColData[rowIdx] = stats.meanDifference;
|
|
151
144
|
customPValColData[rowIdx] = stats.pValue;
|
|
152
145
|
customRatioColData[rowIdx] = stats.ratio;
|
|
@@ -176,12 +169,10 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
176
169
|
const origMDColData = origLSTCols.addNewFloat(C.LST_COLUMN_NAMES.MEAN_DIFFERENCE).getRawData();
|
|
177
170
|
const origPValColData = origLSTCols.addNewFloat(C.LST_COLUMN_NAMES.P_VALUE).getRawData();
|
|
178
171
|
const origRatioColData = origLSTCols.addNewFloat(C.LST_COLUMN_NAMES.RATIO).getRawData();
|
|
179
|
-
|
|
180
|
-
const origWebLogoTables: DG.DataFrame[] = new Array(origLSTLen);
|
|
181
|
-
const origDistTables: DG.DataFrame[] = new Array(origLSTLen);
|
|
172
|
+
const origBitsets: DG.BitSet[] = new Array(origLSTLen);
|
|
182
173
|
|
|
183
174
|
const origClustMasks = Array.from({length: origLSTLen},
|
|
184
|
-
() => BitArray
|
|
175
|
+
() => new BitArray(filteredDfRowCount, false));
|
|
185
176
|
|
|
186
177
|
for (let rowIdx = 0; rowIdx < filteredDfRowCount; ++rowIdx) {
|
|
187
178
|
const filteredClustName = filteredDfClustColCat[filteredDfClustColData[rowIdx]];
|
|
@@ -191,15 +182,13 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
191
182
|
|
|
192
183
|
for (let rowIdx = 0; rowIdx < origLSTLen; ++rowIdx) {
|
|
193
184
|
const mask = origClustMasks[rowIdx];
|
|
194
|
-
const bsMask = DG.BitSet.
|
|
185
|
+
const bsMask = DG.BitSet.fromBytes(mask.buffer.buffer, filteredDfRowCount);
|
|
195
186
|
|
|
196
187
|
const stats = isDfFiltered ? getStats(activityColData, mask) :
|
|
197
188
|
this.model.clusterStats[CLUSTER_TYPE.ORIGINAL][origLSTClustColCat[rowIdx]];
|
|
198
189
|
|
|
199
190
|
origMembersColData[rowIdx] = stats.count;
|
|
200
|
-
|
|
201
|
-
origDistTables[rowIdx] = this.createDistributionPlot(activityCol,
|
|
202
|
-
DG.Column.fromBitSet(C.COLUMNS_NAMES.SPLIT_COL, bsMask));
|
|
191
|
+
origBitsets[rowIdx] = bsMask;
|
|
203
192
|
origMDColData[rowIdx] = stats.meanDifference;
|
|
204
193
|
origPValColData[rowIdx] = stats.pValue;
|
|
205
194
|
origRatioColData[rowIdx] = stats.ratio;
|
|
@@ -211,8 +200,7 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
211
200
|
|
|
212
201
|
// combine LSTs and create a grid
|
|
213
202
|
const summaryTable = origLST.append(customLST);
|
|
214
|
-
this.
|
|
215
|
-
this.distributionDfPlot = origDistTables.concat(customDistTables);
|
|
203
|
+
this.bitsets = origBitsets.concat(customBitsets);
|
|
216
204
|
|
|
217
205
|
this.viewerGrid = summaryTable.plot.grid();
|
|
218
206
|
this.viewerGrid.sort([C.LST_COLUMN_NAMES.MEMBERS], [false]);
|
|
@@ -225,46 +213,85 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
225
213
|
C.LST_COLUMN_NAMES.P_VALUE, C.LST_COLUMN_NAMES.RATIO, ...aggColNames]);
|
|
226
214
|
this.viewerGrid.columns.rowHeader!.visible = false;
|
|
227
215
|
this.viewerGrid.props.rowHeight = 55;
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
216
|
+
|
|
217
|
+
const webLogoCache = new DG.LruCache<number, DG.Viewer & IWebLogoViewer>();
|
|
218
|
+
const distCache = new DG.LruCache<number, DG.Viewer<DG.IHistogramLookSettings>>();
|
|
219
|
+
const maxSequenceLen = this.model.splitSeqDf.columns.length;
|
|
220
|
+
const webLogoGridCol = this.viewerGrid.columns.byName(C.LST_COLUMN_NAMES.WEB_LOGO)!;
|
|
221
|
+
webLogoGridCol.cellType = 'html';
|
|
222
|
+
webLogoGridCol.width = 350;
|
|
223
|
+
|
|
224
|
+
this.viewerGrid.onCellRender.subscribe(async (gridCellArgs) => {
|
|
225
|
+
const gridCell = gridCellArgs.cell;
|
|
226
|
+
const currentRowIdx = gridCell.tableRowIndex;
|
|
227
|
+
if (!gridCell.isTableCell || currentRowIdx === null || currentRowIdx === -1)
|
|
231
228
|
return;
|
|
232
229
|
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
230
|
+
const canvasContext = gridCellArgs.g;
|
|
231
|
+
const bound = gridCellArgs.bounds;
|
|
232
|
+
canvasContext.save();
|
|
233
|
+
canvasContext.beginPath();
|
|
234
|
+
canvasContext.rect(bound.x, bound.y, bound.width, bound.height);
|
|
235
|
+
canvasContext.clip();
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const height = Math.max(gridCell.bounds.height - 2, 0);
|
|
239
|
+
const clusterBitSet = this.bitsets[currentRowIdx];
|
|
240
|
+
|
|
241
|
+
if (gridCell.tableColumn?.name === C.LST_COLUMN_NAMES.CLUSTER) {
|
|
242
|
+
CR.renderLogoSummaryCell(canvasContext, gridCell.cell.value, this.model.clusterSelection, bound);
|
|
243
|
+
gridCellArgs.preventDefault();
|
|
244
|
+
} else if (gridCell.tableColumn?.name === C.LST_COLUMN_NAMES.WEB_LOGO) {
|
|
245
|
+
const positionWidth = Math.floor((gridCell.bounds.width - 2 - (4 * (maxSequenceLen - 1))) / maxSequenceLen);
|
|
246
|
+
|
|
247
|
+
let viewer = webLogoCache.get(currentRowIdx);
|
|
248
|
+
if (viewer !== undefined) {
|
|
249
|
+
const viewerProps = viewer.getProperties();
|
|
250
|
+
|
|
251
|
+
for (const prop of viewerProps) {
|
|
252
|
+
if (prop.name === 'positionHeight' && prop.get(viewer) !== this.webLogoMode)
|
|
253
|
+
prop.set(viewer, this.webLogoMode);
|
|
254
|
+
else if (prop.name === 'positionWidth' && prop.get(viewer) !== positionWidth)
|
|
255
|
+
prop.set(viewer, positionWidth);
|
|
256
|
+
else if (prop.name === 'minHeight' && prop.get(viewer) !== height)
|
|
257
|
+
prop.set(viewer, height);
|
|
258
|
+
}
|
|
259
|
+
const viewerRoot = $(viewer.root).css('height', `${height}px`);//;
|
|
260
|
+
viewerRoot.children().first().css('overflow-y', 'hidden !important');
|
|
261
|
+
} else {
|
|
262
|
+
const webLogoTable = this.createWebLogoDf(pepCol, clusterBitSet);
|
|
263
|
+
viewer = await webLogoTable.plot
|
|
264
|
+
.fromType('WebLogo', {positionHeight: this.webLogoMode, horizontalAlignment: HorizontalAlignments.LEFT,
|
|
265
|
+
maxHeight: 1000, minHeight: height, positionWidth: positionWidth});
|
|
266
|
+
webLogoCache.set(currentRowIdx, viewer);
|
|
267
|
+
}
|
|
268
|
+
gridCell.element = viewer.root;
|
|
269
|
+
gridCellArgs.preventDefault();
|
|
270
|
+
} else if (gridCell.tableColumn?.name === C.LST_COLUMN_NAMES.DISTRIBUTION) {
|
|
271
|
+
let viewer = distCache.get(currentRowIdx);
|
|
272
|
+
if (viewer === undefined) {
|
|
273
|
+
const distributionDf = this.createDistributionDf(activityCol, clusterBitSet);
|
|
274
|
+
viewer = distributionDf.plot.histogram({
|
|
275
|
+
filteringEnabled: false,
|
|
276
|
+
valueColumnName: C.COLUMNS_NAMES.ACTIVITY_SCALED,
|
|
277
|
+
splitColumnName: C.COLUMNS_NAMES.SPLIT_COL,
|
|
278
|
+
legendVisibility: 'Never',
|
|
279
|
+
showXAxis: false,
|
|
280
|
+
showColumnSelector: false,
|
|
281
|
+
showRangeSlider: false,
|
|
282
|
+
showBinSelector: false,
|
|
283
|
+
backColor: DG.Color.toHtml(DG.Color.white),
|
|
284
|
+
xAxisHeight: 1,
|
|
285
|
+
});
|
|
286
|
+
viewer.root.style.width = 'auto';
|
|
287
|
+
distCache.set(currentRowIdx, viewer);
|
|
288
|
+
}
|
|
289
|
+
viewer.root.style.height = `${height}px`;
|
|
290
|
+
gridCell.element = viewer.root;
|
|
291
|
+
gridCellArgs.preventDefault();
|
|
245
292
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
.fromType('WebLogo', {positionHeight: this.webLogoMode, horizontalAlignment: HorizontalAlignments.LEFT,
|
|
249
|
-
maxHeight: 1000, minHeight: height - 2, positionWidth: positionWidth})
|
|
250
|
-
.then((viewer) => cell.element = viewer.root);
|
|
251
|
-
} else if (cell.tableColumn?.name === C.LST_COLUMN_NAMES.DISTRIBUTION) {
|
|
252
|
-
const viewerRoot = this.distributionDfPlot[currentRowIdx].plot.histogram({
|
|
253
|
-
filteringEnabled: false,
|
|
254
|
-
valueColumnName: C.COLUMNS_NAMES.ACTIVITY_SCALED,
|
|
255
|
-
splitColumnName: C.COLUMNS_NAMES.SPLIT_COL,
|
|
256
|
-
legendVisibility: 'Never',
|
|
257
|
-
showXAxis: false,
|
|
258
|
-
showColumnSelector: false,
|
|
259
|
-
showRangeSlider: false,
|
|
260
|
-
showBinSelector: false,
|
|
261
|
-
backColor: DG.Color.toHtml(DG.Color.white),
|
|
262
|
-
xAxisHeight: 1,
|
|
263
|
-
}).root;
|
|
264
|
-
|
|
265
|
-
viewerRoot.style.width = 'auto';
|
|
266
|
-
viewerRoot.style.height = `${height-2}px`;
|
|
267
|
-
cell.element = viewerRoot;
|
|
293
|
+
} finally {
|
|
294
|
+
canvasContext.restore();
|
|
268
295
|
}
|
|
269
296
|
});
|
|
270
297
|
this.viewerGrid.root.addEventListener('click', (ev) => {
|
|
@@ -279,20 +306,6 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
279
306
|
this.model.modifyClusterSelection(cell.cell.value);
|
|
280
307
|
this.viewerGrid.invalidate();
|
|
281
308
|
});
|
|
282
|
-
this.viewerGrid.onCellRender.subscribe((gridCellArgs) => {
|
|
283
|
-
const gc = gridCellArgs.cell;
|
|
284
|
-
if (gc.tableColumn?.name !== clustersColName || gc.isColHeader)
|
|
285
|
-
return;
|
|
286
|
-
const canvasContext = gridCellArgs.g;
|
|
287
|
-
const bound = gridCellArgs.bounds;
|
|
288
|
-
canvasContext.save();
|
|
289
|
-
canvasContext.beginPath();
|
|
290
|
-
canvasContext.rect(bound.x, bound.y, bound.width, bound.height);
|
|
291
|
-
canvasContext.clip();
|
|
292
|
-
CR.renderLogoSummaryCell(canvasContext, gc.cell.value, this.model.clusterSelection, bound);
|
|
293
|
-
gridCellArgs.preventDefault();
|
|
294
|
-
canvasContext.restore();
|
|
295
|
-
});
|
|
296
309
|
this.viewerGrid.onCellTooltip((cell, x, y) => {
|
|
297
310
|
if (!cell.isColHeader && cell.tableColumn?.name === clustersColName) {
|
|
298
311
|
const clustName = cell.cell.value;
|
|
@@ -302,9 +315,6 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
302
315
|
}
|
|
303
316
|
return true;
|
|
304
317
|
});
|
|
305
|
-
const webLogoGridCol = this.viewerGrid.columns.byName('WebLogo')!;
|
|
306
|
-
webLogoGridCol.cellType = 'html';
|
|
307
|
-
webLogoGridCol.width = 350;
|
|
308
318
|
|
|
309
319
|
const gridProps = this.viewerGrid.props;
|
|
310
320
|
gridProps.allowEdit = false;
|
|
@@ -336,27 +346,8 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
336
346
|
const activityScaledCol = filteredDf.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
337
347
|
const bitArray = BitArray.fromString(selection.toBinaryString());
|
|
338
348
|
const stats = getStats(activityScaledCol.getRawData(), bitArray);
|
|
339
|
-
const distributionTable =
|
|
340
|
-
DG.DataFrame.fromColumns([activityScaledCol, filteredDf.getCol(this.model.splitCol.name)]);
|
|
341
|
-
|
|
342
|
-
const peptideCol: DG.Column<string> = filteredDf.getCol(this.model.settings.sequenceColumnName!);
|
|
343
|
-
const peptideColData = peptideCol.getRawData();
|
|
344
|
-
const peptideColCategories = peptideCol.categories;
|
|
345
|
-
const peptideColTags = peptideCol.tags;
|
|
346
|
-
const selectedIndexes = selection.getSelectedIndexes();
|
|
347
|
-
const tCol = DG.Column.string('peptides', selectedIndexes.length);
|
|
348
|
-
|
|
349
|
-
for (let i = 0; i < selectedIndexes.length; ++i)
|
|
350
|
-
tCol.set(i, peptideColCategories[peptideColData[selectedIndexes[i]]]);
|
|
351
|
-
for (const tag of peptideColTags)
|
|
352
|
-
tCol.setTag(tag[0], tag[1]);
|
|
353
|
-
|
|
354
|
-
const uh = new UnitsHandler(tCol);
|
|
355
|
-
tCol.setTag(bioTAGS.alphabetSize, uh.getAlphabetSize().toString());
|
|
356
349
|
|
|
357
|
-
|
|
358
|
-
this.webLogoDfPlot.push(webLogoTable);
|
|
359
|
-
this.distributionDfPlot.push(distributionTable);
|
|
350
|
+
this.bitsets.push(selection.clone());
|
|
360
351
|
|
|
361
352
|
const newClusterName = viewerDfCols.getUnusedName('New Cluster');
|
|
362
353
|
|
|
@@ -407,8 +398,7 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
407
398
|
delete this.model.clusterStats[CLUSTER_TYPE.CUSTOM][cluster];
|
|
408
399
|
const clustIdx = clustColCat.indexOf(cluster);
|
|
409
400
|
viewerDfRows.removeAt(clustIdx);
|
|
410
|
-
this.
|
|
411
|
-
this.distributionDfPlot.splice(clustIdx, 1);
|
|
401
|
+
this.bitsets.splice(clustIdx, 1);
|
|
412
402
|
}
|
|
413
403
|
|
|
414
404
|
clustCol.compact();
|
|
@@ -421,20 +411,21 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
421
411
|
const bs = this.dataFrame.filter;
|
|
422
412
|
const filteredDf = bs.anyFalse ? this.dataFrame.clone(bs) : this.dataFrame;
|
|
423
413
|
const rowCount = filteredDf.rowCount;
|
|
424
|
-
|
|
425
|
-
const bitArray = new BitArray(rowCount);
|
|
414
|
+
const bitArray = new BitArray(rowCount, false);
|
|
426
415
|
const activityCol = filteredDf.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
427
416
|
const activityColData = activityCol.getRawData();
|
|
428
417
|
|
|
429
|
-
|
|
430
418
|
if (clustType === CLUSTER_TYPE.ORIGINAL) {
|
|
431
419
|
const origClustCol = filteredDf.getCol(C.LST_COLUMN_NAMES.CLUSTER);
|
|
432
420
|
const origClustColData = origClustCol.getRawData();
|
|
433
421
|
const origClustColCategories = origClustCol.categories;
|
|
434
422
|
const seekValue = origClustColCategories.indexOf(clustName);
|
|
435
423
|
|
|
436
|
-
for (let i = 0; i < rowCount; ++i)
|
|
437
|
-
|
|
424
|
+
for (let i = 0; i < rowCount; ++i) {
|
|
425
|
+
if (origClustColData[i] === seekValue)
|
|
426
|
+
bitArray.setTrue(i);
|
|
427
|
+
}
|
|
428
|
+
bitArray.incrementVersion();
|
|
438
429
|
} else {
|
|
439
430
|
const clustCol: DG.Column<boolean> = filteredDf.getCol(clustName);
|
|
440
431
|
bitArray.buffer = clustCol.getRawData() as Uint32Array;
|
|
@@ -445,27 +436,28 @@ export class LogoSummaryTable extends DG.JsViewer {
|
|
|
445
436
|
if (!stats.count)
|
|
446
437
|
return null;
|
|
447
438
|
|
|
448
|
-
const mask = DG.BitSet.
|
|
449
|
-
const distributionTable =
|
|
450
|
-
[activityCol, DG.Column.fromBitSet(C.COLUMNS_NAMES.SPLIT_COL, mask)]);
|
|
439
|
+
const mask = DG.BitSet.fromBytes(bitArray.buffer.buffer, rowCount);
|
|
440
|
+
const distributionTable = this.createDistributionDf(activityCol, mask);
|
|
451
441
|
const labels = getDistributionLegend(`Cluster: ${clustName}`, 'Other');
|
|
452
442
|
const hist = getActivityDistribution(distributionTable, true);
|
|
453
|
-
const tableMap = getStatsTableMap(stats
|
|
454
|
-
const aggregatedColMap = this.model.getAggregatedColumnValues({filterDf: true, mask: mask
|
|
455
|
-
|
|
443
|
+
const tableMap = getStatsTableMap(stats);
|
|
444
|
+
const aggregatedColMap = this.model.getAggregatedColumnValues({filterDf: true, mask: mask});
|
|
456
445
|
const resultMap: {[key: string]: any} = {...tableMap, ...aggregatedColMap};
|
|
457
|
-
const tooltip = getStatsSummary(labels, hist, resultMap
|
|
446
|
+
const tooltip = getStatsSummary(labels, hist, resultMap);
|
|
458
447
|
|
|
459
448
|
ui.tooltip.show(tooltip, x, y);
|
|
460
449
|
|
|
461
450
|
return tooltip;
|
|
462
451
|
}
|
|
463
452
|
|
|
464
|
-
|
|
465
|
-
|
|
453
|
+
createWebLogoDf(pepCol: DG.Column<string>, mask: DG.BitSet): DG.DataFrame {
|
|
454
|
+
const newDf = DG.DataFrame.fromColumns([pepCol]);
|
|
455
|
+
newDf.filter.copyFrom(mask);
|
|
456
|
+
return newDf;
|
|
466
457
|
}
|
|
467
458
|
|
|
468
|
-
|
|
469
|
-
|
|
459
|
+
createDistributionDf(activityCol: DG.Column<number>, splitMask: DG.BitSet): DG.DataFrame {
|
|
460
|
+
const table = DG.DataFrame.fromColumns([activityCol, DG.Column.fromBitSet(C.COLUMNS_NAMES.SPLIT_COL, splitMask)]);
|
|
461
|
+
return prepareTableForHistogram(table);
|
|
470
462
|
}
|
|
471
463
|
}
|
|
@@ -6,11 +6,12 @@ import $ from 'cash-dom';
|
|
|
6
6
|
|
|
7
7
|
import {getSequenceMolecularWeight} from '../utils/molecular-measure';
|
|
8
8
|
import {AlignedSequenceEncoder} from '@datagrok-libraries/bio/src/sequence-encoder';
|
|
9
|
-
import {createDimensinalityReducingWorker,
|
|
9
|
+
import {createDimensinalityReducingWorker,
|
|
10
10
|
} from '@datagrok-libraries/ml/src/workers/dimensionality-reducing-worker-creator';
|
|
11
11
|
import {StringMetrics} from '@datagrok-libraries/ml/src/typed-metrics';
|
|
12
12
|
import * as C from '../utils/constants';
|
|
13
13
|
import {PeptidesModel} from '../model';
|
|
14
|
+
import {IReduceDimensionalityResult} from '@datagrok-libraries/ml/src/reduce-dimensionality';
|
|
14
15
|
|
|
15
16
|
export class PeptideSpaceViewer extends DG.JsViewer {
|
|
16
17
|
method: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as grok from 'datagrok-api/grok';
|
|
1
2
|
import * as ui from 'datagrok-api/ui';
|
|
2
3
|
import * as DG from 'datagrok-api/dg';
|
|
3
4
|
|
|
@@ -5,6 +6,7 @@ import $ from 'cash-dom';
|
|
|
5
6
|
import * as C from '../utils/constants';
|
|
6
7
|
import * as CR from '../utils/cell-renderer';
|
|
7
8
|
import {PeptidesModel, VIEWER_TYPE} from '../model';
|
|
9
|
+
import wu from 'wu';
|
|
8
10
|
|
|
9
11
|
export enum MONOMER_POSITION_MODE {
|
|
10
12
|
MUTATION_CLIFFS = 'Mutation Cliffs',
|
|
@@ -12,7 +14,7 @@ export enum MONOMER_POSITION_MODE {
|
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export enum MONOMER_POSITION_PROPERTIES {
|
|
15
|
-
COLOR_COLUMN_NAME = '
|
|
17
|
+
COLOR_COLUMN_NAME = 'color',
|
|
16
18
|
AGGREGATION = 'aggregation',
|
|
17
19
|
TARGET = 'target',
|
|
18
20
|
};
|
|
@@ -22,7 +24,7 @@ export class MonomerPosition extends DG.JsViewer {
|
|
|
22
24
|
_titleHost = ui.divText(MONOMER_POSITION_MODE.MUTATION_CLIFFS, {id: 'pep-viewer-title'});
|
|
23
25
|
_viewerGrid!: DG.Grid;
|
|
24
26
|
_model!: PeptidesModel;
|
|
25
|
-
|
|
27
|
+
colorCol: string;
|
|
26
28
|
aggregation: string;
|
|
27
29
|
target: string;
|
|
28
30
|
|
|
@@ -30,10 +32,13 @@ export class MonomerPosition extends DG.JsViewer {
|
|
|
30
32
|
super();
|
|
31
33
|
this.target = this.string(MONOMER_POSITION_PROPERTIES.TARGET, null,
|
|
32
34
|
{category: MONOMER_POSITION_MODE.MUTATION_CLIFFS, choices: []});
|
|
33
|
-
this.
|
|
34
|
-
{category: MONOMER_POSITION_MODE.INVARIANT_MAP
|
|
35
|
+
this.colorCol = this.string(MONOMER_POSITION_PROPERTIES.COLOR_COLUMN_NAME, C.COLUMNS_NAMES.ACTIVITY_SCALED,
|
|
36
|
+
{category: MONOMER_POSITION_MODE.INVARIANT_MAP,
|
|
37
|
+
choices: wu(grok.shell.t.columns.numerical).toArray().map((col) => col.name)});
|
|
35
38
|
this.aggregation = this.string(MONOMER_POSITION_PROPERTIES.AGGREGATION, DG.AGG.AVG,
|
|
36
|
-
{category: MONOMER_POSITION_MODE.INVARIANT_MAP,
|
|
39
|
+
{category: MONOMER_POSITION_MODE.INVARIANT_MAP,
|
|
40
|
+
choices: Object.values(DG.AGG)
|
|
41
|
+
.filter((agg) => ![DG.AGG.KEY, DG.AGG.PIVOT, DG.AGG.SELECTED_ROWS_COUNT].includes(agg))});
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
get name(): string {return VIEWER_TYPE.MONOMER_POSITION;}
|
|
@@ -65,12 +70,7 @@ export class MonomerPosition extends DG.JsViewer {
|
|
|
65
70
|
|
|
66
71
|
onTableAttached(): void {
|
|
67
72
|
super.onTableAttached();
|
|
68
|
-
this.subs.push(this.model.onMonomerPositionSelectionChanged.subscribe(() => this.viewerGrid.invalidate()));
|
|
69
73
|
this.helpUrl = '/help/domains/bio/peptides.md';
|
|
70
|
-
this.subs.push(this.model.onSettingsChanged.subscribe(() => {
|
|
71
|
-
this.createMonomerPositionGrid();
|
|
72
|
-
this.render();
|
|
73
|
-
}));
|
|
74
74
|
this.render();
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -89,7 +89,7 @@ export class MonomerPosition extends DG.JsViewer {
|
|
|
89
89
|
const monomerCol = this.model.monomerPositionDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
90
90
|
CR.setAARRenderer(monomerCol, this.model.alphabet);
|
|
91
91
|
this.viewerGrid.onCellRender.subscribe((args: DG.GridCellRenderArgs) => renderCell(args, this.model,
|
|
92
|
-
this.mode === MONOMER_POSITION_MODE.INVARIANT_MAP, this.dataFrame.getCol(this.
|
|
92
|
+
this.mode === MONOMER_POSITION_MODE.INVARIANT_MAP, this.dataFrame.getCol(this.colorCol),
|
|
93
93
|
this.aggregation as DG.AggregationType));
|
|
94
94
|
this.viewerGrid.onCellTooltip((cell: DG.GridCell, x: number, y: number) => showTooltip(cell, x, y, this.model));
|
|
95
95
|
this.viewerGrid.root.addEventListener('click', (ev) => {
|
|
@@ -101,9 +101,11 @@ export class MonomerPosition extends DG.JsViewer {
|
|
|
101
101
|
const aar = monomerCol.get(gridCell!.tableRowIndex!);
|
|
102
102
|
chooseAction(aar, position, ev.shiftKey, this.mode === MONOMER_POSITION_MODE.INVARIANT_MAP, this.model);
|
|
103
103
|
this.viewerGrid.invalidate();
|
|
104
|
-
// this.model.fireBitsetChanged();
|
|
105
104
|
});
|
|
106
|
-
this.viewerGrid.onCurrentCellChanged.subscribe((_gc) =>
|
|
105
|
+
this.viewerGrid.onCurrentCellChanged.subscribe((_gc) => {
|
|
106
|
+
cellChanged(this.model.monomerPositionDf, this.model);
|
|
107
|
+
this.viewerGrid.invalidate();
|
|
108
|
+
});
|
|
107
109
|
|
|
108
110
|
setViewerGridProps(this.viewerGrid, false);
|
|
109
111
|
}
|
|
@@ -129,18 +131,20 @@ export class MonomerPosition extends DG.JsViewer {
|
|
|
129
131
|
invariantMapMode.addPostfix(MONOMER_POSITION_MODE.INVARIANT_MAP);
|
|
130
132
|
const setDefaultProperties = (input: DG.InputBase): void => {
|
|
131
133
|
$(input.root).find('.ui-input-editor').css('margin', '0px').attr('type', 'radio');
|
|
132
|
-
$(input.root).find('.ui-input-description').css('padding', '0px').css('padding-
|
|
134
|
+
$(input.root).find('.ui-input-description').css('padding', '0px').css('padding-right', '16px');
|
|
133
135
|
};
|
|
134
136
|
setDefaultProperties(mutationCliffsMode);
|
|
135
137
|
setDefaultProperties(invariantMapMode);
|
|
136
|
-
$(mutationCliffsMode.root).css('padding-right', '10px').css('padding-left', '5px');
|
|
137
138
|
|
|
138
139
|
switchHost = ui.divH([mutationCliffsMode.root, invariantMapMode.root], {id: 'pep-viewer-title'});
|
|
139
140
|
$(switchHost).css('width', 'auto').css('align-self', 'center');
|
|
140
141
|
}
|
|
141
142
|
const tips: HTMLElement = ui.iconFA('question');
|
|
142
|
-
ui.tooltip.bind(tips,
|
|
143
|
-
|
|
143
|
+
ui.tooltip.bind(tips, () => ui.divV([
|
|
144
|
+
ui.divText('Color intensity - p-value'),
|
|
145
|
+
ui.divText('Circle size - Mean difference'),
|
|
146
|
+
ui.divText('Number - # of unique sequences that form mutation cliffs pairs'),
|
|
147
|
+
]));
|
|
144
148
|
|
|
145
149
|
$(tips).addClass('pep-help-icon');
|
|
146
150
|
|
|
@@ -154,7 +158,7 @@ export class MonomerPosition extends DG.JsViewer {
|
|
|
154
158
|
}
|
|
155
159
|
|
|
156
160
|
/** Vertical structure activity relationship viewer */
|
|
157
|
-
export class
|
|
161
|
+
export class MostPotentResidues extends DG.JsViewer {
|
|
158
162
|
_titleHost = ui.divText(VIEWER_TYPE.MOST_POTENT_RESIDUES, {id: 'pep-viewer-title'});
|
|
159
163
|
_viewerGrid!: DG.Grid;
|
|
160
164
|
_model!: PeptidesModel;
|
|
@@ -183,12 +187,7 @@ export class MostPotentResiduesViewer extends DG.JsViewer {
|
|
|
183
187
|
|
|
184
188
|
onTableAttached(): void {
|
|
185
189
|
super.onTableAttached();
|
|
186
|
-
this.subs.push(this.model.onMonomerPositionSelectionChanged.subscribe(() => this.viewerGrid.invalidate()));
|
|
187
190
|
this.helpUrl = '/help/domains/bio/peptides.md';
|
|
188
|
-
this.subs.push(this.model.onSettingsChanged.subscribe(() => {
|
|
189
|
-
this.createMostPotentResiduesGrid();
|
|
190
|
-
this.render();
|
|
191
|
-
}));
|
|
192
191
|
this.render();
|
|
193
192
|
}
|
|
194
193
|
|
|
@@ -208,7 +207,8 @@ export class MostPotentResiduesViewer extends DG.JsViewer {
|
|
|
208
207
|
|
|
209
208
|
// Setting Monomer column renderer
|
|
210
209
|
CR.setAARRenderer(monomerCol, this.model.alphabet);
|
|
211
|
-
this.viewerGrid.onCellRender.subscribe(
|
|
210
|
+
this.viewerGrid.onCellRender.subscribe(
|
|
211
|
+
(args: DG.GridCellRenderArgs) => renderCell(args, this.model, false, undefined, undefined, false));
|
|
212
212
|
this.viewerGrid.onCellTooltip((cell: DG.GridCell, x: number, y: number) => showTooltip(cell, x, y, this.model));
|
|
213
213
|
this.viewerGrid.root.addEventListener('click', (ev) => {
|
|
214
214
|
const gridCell = this.viewerGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
@@ -222,7 +222,10 @@ export class MostPotentResiduesViewer extends DG.JsViewer {
|
|
|
222
222
|
this.viewerGrid.invalidate();
|
|
223
223
|
// this.model.fireBitsetChanged();
|
|
224
224
|
});
|
|
225
|
-
this.viewerGrid.onCurrentCellChanged.subscribe((_gc) =>
|
|
225
|
+
this.viewerGrid.onCurrentCellChanged.subscribe((_gc) => {
|
|
226
|
+
cellChanged(this.model.mostPotentResiduesDf, this.model);
|
|
227
|
+
this.viewerGrid.invalidate();
|
|
228
|
+
});
|
|
226
229
|
const mdCol: DG.GridColumn = this.viewerGrid.col(C.COLUMNS_NAMES.MEAN_DIFFERENCE)!;
|
|
227
230
|
mdCol.name = 'Diff';
|
|
228
231
|
setViewerGridProps(this.viewerGrid, true);
|
|
@@ -233,8 +236,11 @@ export class MostPotentResiduesViewer extends DG.JsViewer {
|
|
|
233
236
|
$(this.root).empty();
|
|
234
237
|
const switchHost = ui.divText(VIEWER_TYPE.MOST_POTENT_RESIDUES, {id: 'pep-viewer-title'});
|
|
235
238
|
const tips: HTMLElement = ui.iconFA('question');
|
|
236
|
-
ui.tooltip.bind(tips,
|
|
237
|
-
|
|
239
|
+
ui.tooltip.bind(tips, () => ui.divV([
|
|
240
|
+
ui.divText('Color intensity - p-value'),
|
|
241
|
+
ui.divText('Circle size - Mean difference'),
|
|
242
|
+
ui.divText('Number - # of unique sequences that form mutation cliffs pairs'),
|
|
243
|
+
]));
|
|
238
244
|
|
|
239
245
|
$(tips).addClass('pep-help-icon');
|
|
240
246
|
|
|
@@ -248,7 +254,7 @@ export class MostPotentResiduesViewer extends DG.JsViewer {
|
|
|
248
254
|
}
|
|
249
255
|
|
|
250
256
|
function renderCell(args: DG.GridCellRenderArgs, model: PeptidesModel, isInvariantMap?: boolean,
|
|
251
|
-
colorCol?: DG.Column<number>, colorAgg?: DG.AggregationType): void {
|
|
257
|
+
colorCol?: DG.Column<number>, colorAgg?: DG.AggregationType, renderNums?: boolean): void {
|
|
252
258
|
const renderColNames = [...model.splitSeqDf.columns.names(), C.COLUMNS_NAMES.MEAN_DIFFERENCE];
|
|
253
259
|
const canvasContext = args.g;
|
|
254
260
|
const bound = args.bounds;
|
|
@@ -293,20 +299,21 @@ function renderCell(args: DG.GridCellRenderArgs, model: PeptidesModel, isInvaria
|
|
|
293
299
|
const positionColCategories = positionCol.categories;
|
|
294
300
|
|
|
295
301
|
const colorColData = colorCol!.getRawData();
|
|
296
|
-
const
|
|
297
|
-
for (let i = 0; i <
|
|
302
|
+
const colorValuesIndexes: number[] = [];
|
|
303
|
+
for (let i = 0; i < positionCol.length; ++i) {
|
|
298
304
|
if (positionColCategories[positionColData[i]] === currentMonomer)
|
|
299
|
-
|
|
305
|
+
colorValuesIndexes.push(i);
|
|
300
306
|
}
|
|
301
|
-
const cellColorDataCol = DG.Column.
|
|
307
|
+
const cellColorDataCol = DG.Column.float('color', colorValuesIndexes.length)
|
|
308
|
+
.init((i) => colorColData[colorValuesIndexes[i]]);
|
|
302
309
|
const colorColStats = colorCol!.stats;
|
|
303
310
|
|
|
304
311
|
const color = DG.Color.scaleColor(cellColorDataCol.aggregate(colorAgg!), colorColStats.min, colorColStats.max);
|
|
305
312
|
CR.renderInvaraintMapCell(
|
|
306
|
-
canvasContext, currentMonomer, currentPosition, model.
|
|
313
|
+
canvasContext, currentMonomer, currentPosition, model.invariantMapSelection, value, bound, color);
|
|
307
314
|
} else {
|
|
308
315
|
CR.renderMutationCliffCell(canvasContext, currentMonomer, currentPosition, model.monomerPositionStats, bound,
|
|
309
|
-
model.
|
|
316
|
+
model.mutationCliffsSelection, model.mutationCliffs, model.settings.isBidirectional, renderNums);
|
|
310
317
|
}
|
|
311
318
|
args.preventDefault();
|
|
312
319
|
canvasContext.restore();
|
|
@@ -334,16 +341,16 @@ export function showTooltip(cell: DG.GridCell, x: number, y: number, model: Pept
|
|
|
334
341
|
return true;
|
|
335
342
|
}
|
|
336
343
|
|
|
337
|
-
function chooseAction(aar: string, position: string, isShiftPressed: boolean,
|
|
344
|
+
function chooseAction(aar: string, position: string, isShiftPressed: boolean, isInvariantMap: boolean,
|
|
338
345
|
model: PeptidesModel): void {
|
|
339
346
|
if (!isShiftPressed) {
|
|
340
|
-
if (
|
|
341
|
-
model.
|
|
347
|
+
if (isInvariantMap)
|
|
348
|
+
model.initInvariantMapSelection({cleanInit: true, notify: false});
|
|
342
349
|
else
|
|
343
|
-
model.
|
|
350
|
+
model.initMutationCliffsSelection({cleanInit: true, notify: false});
|
|
344
351
|
}
|
|
345
352
|
|
|
346
|
-
model.modifyMonomerPositionSelection(aar, position,
|
|
353
|
+
model.modifyMonomerPositionSelection(aar, position, isInvariantMap);
|
|
347
354
|
}
|
|
348
355
|
|
|
349
356
|
function cellChanged(table: DG.DataFrame, model: PeptidesModel): void {
|