@datagrok/peptides 1.5.1 → 1.7.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.
- package/.eslintrc.json +2 -0
- package/dist/package-test.js +5231 -60293
- package/dist/package.js +5083 -60223
- package/dist/vendors-node_modules_datagrok-libraries_ml_src_workers_dimensionality-reducer_js.js +354 -265
- package/files/tests/aligned_5k.d42 +0 -0
- package/package.json +15 -22
- package/src/model.ts +373 -236
- package/src/package-test.ts +1 -0
- package/src/package.ts +23 -50
- package/src/styles.css +7 -0
- package/src/tests/core.ts +54 -13
- package/src/tests/peptide-space-test.ts +5 -53
- package/src/tests/viewers.ts +17 -0
- package/src/utils/cell-renderer.ts +28 -29
- package/src/utils/constants.ts +11 -1
- package/src/utils/misc.ts +16 -4
- package/src/utils/peptide-similarity-space.ts +1 -1
- package/src/utils/types.ts +2 -0
- package/src/viewers/logo-summary.ts +239 -74
- package/src/viewers/sar-viewer.ts +63 -49
- package/src/widgets/distribution.ts +74 -29
- package/src/widgets/manual-alignment.ts +2 -2
- package/src/widgets/mutation-cliffs.ts +6 -2
- package/src/widgets/peptides.ts +17 -10
- package/jest.config.js +0 -33
- package/src/__jest__/remote.test.ts +0 -76
- package/src/__jest__/test-node.ts +0 -97
- package/test-Peptides-4775b69ad08a-1bc1a2b4.html +0 -277
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import * as ui from 'datagrok-api/ui';
|
|
2
|
+
import * as grok from 'datagrok-api/grok';
|
|
2
3
|
import * as DG from 'datagrok-api/dg';
|
|
3
4
|
|
|
4
5
|
import $ from 'cash-dom';
|
|
5
6
|
import {PeptidesModel} from '../model';
|
|
6
7
|
import * as C from '../utils/constants';
|
|
7
8
|
import * as CR from '../utils/cell-renderer';
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
9
|
+
import {TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
10
|
+
import {PositionHeight} from '@datagrok-libraries/bio/src/viewers/web-logo';
|
|
11
|
+
import {getStats, MaskInfo, Stats} from '../utils/statistics';
|
|
12
|
+
import wu from 'wu';
|
|
13
|
+
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
10
14
|
|
|
11
15
|
export class LogoSummary extends DG.JsViewer {
|
|
12
16
|
_titleHost = ui.divText('Logo Summary Table', {id: 'pep-viewer-title'});
|
|
@@ -15,13 +19,17 @@ export class LogoSummary extends DG.JsViewer {
|
|
|
15
19
|
initialized: boolean = false;
|
|
16
20
|
webLogoMode: string;
|
|
17
21
|
membersRatioThreshold: number;
|
|
22
|
+
newClusterName: string;
|
|
23
|
+
webLogoDfPlot: DG.DataFramePlotHelper[] = [];
|
|
24
|
+
distributionDfPlot: DG.DataFramePlotHelper[] = [];
|
|
18
25
|
|
|
19
26
|
constructor() {
|
|
20
27
|
super();
|
|
21
28
|
|
|
22
|
-
this.webLogoMode = this.string('webLogoMode',
|
|
23
|
-
{choices: [
|
|
24
|
-
this.membersRatioThreshold = this.float('membersRatioThreshold', 0.7, {min: 0
|
|
29
|
+
this.webLogoMode = this.string('webLogoMode', PositionHeight.full,
|
|
30
|
+
{choices: [PositionHeight.full, PositionHeight.Entropy]});
|
|
31
|
+
this.membersRatioThreshold = this.float('membersRatioThreshold', 0.7, {min: 0, max: 1.0});
|
|
32
|
+
this.newClusterName = this.string('newClusterName', 'New cluster');
|
|
25
33
|
}
|
|
26
34
|
|
|
27
35
|
onTableAttached(): void {
|
|
@@ -32,6 +40,12 @@ export class LogoSummary extends DG.JsViewer {
|
|
|
32
40
|
this.createLogoSummaryGrid();
|
|
33
41
|
this.render();
|
|
34
42
|
}));
|
|
43
|
+
this.subs.push(this.model.onNewCluster.subscribe(() => this.clusterFromSelection()));
|
|
44
|
+
this.subs.push(this.model.onRemoveCluster.subscribe(() => this.removeCluster()));
|
|
45
|
+
this.subs.push(this.model.onFilterChanged.subscribe(() => {
|
|
46
|
+
this.createLogoSummaryGrid();
|
|
47
|
+
this.render();
|
|
48
|
+
}));
|
|
35
49
|
|
|
36
50
|
this.createLogoSummaryGrid();
|
|
37
51
|
this.initialized = true;
|
|
@@ -43,6 +57,13 @@ export class LogoSummary extends DG.JsViewer {
|
|
|
43
57
|
render(): void {
|
|
44
58
|
if (this.initialized) {
|
|
45
59
|
$(this.root).empty();
|
|
60
|
+
const df = this.viewerGrid.dataFrame;
|
|
61
|
+
if (!df.filter.anyTrue) {
|
|
62
|
+
const emptyDf = ui.divText('No clusters to satisfy the threshold. ' +
|
|
63
|
+
'Please, lower the threshold in viewer proeperties to include clusters');
|
|
64
|
+
this.root.appendChild(ui.divV([this._titleHost, emptyDf]));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
46
67
|
this.viewerGrid.root.style.width = 'auto';
|
|
47
68
|
this.root.appendChild(ui.divV([this._titleHost, this.viewerGrid.root]));
|
|
48
69
|
this.viewerGrid.invalidate();
|
|
@@ -58,76 +79,136 @@ export class LogoSummary extends DG.JsViewer {
|
|
|
58
79
|
|
|
59
80
|
createLogoSummaryGrid(): DG.Grid {
|
|
60
81
|
const clustersColName = this.model.settings.clustersColumnName!;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
82
|
+
const isDfFiltered = this.dataFrame.filter.anyFalse;
|
|
83
|
+
const filteredDf = isDfFiltered ? this.dataFrame.clone(this.dataFrame.filter) : this.dataFrame;
|
|
84
|
+
const filteredDfCols = filteredDf.columns;
|
|
85
|
+
const activityCol = filteredDf.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
86
|
+
const activityColData = activityCol.getRawData();
|
|
87
|
+
|
|
88
|
+
const filteredDfClustersCol = filteredDf.getCol(clustersColName);
|
|
89
|
+
const filteredDfClustersColData = filteredDfClustersCol.getRawData();
|
|
90
|
+
const filteredDfClustersColCategories = filteredDfClustersCol.categories;
|
|
91
|
+
const filteredDfClustersColLength = filteredDfClustersColData.length;
|
|
92
|
+
|
|
93
|
+
// const customClustersColumnsList = wu(this.model.customClusters).toArray();
|
|
94
|
+
const query: { [key: string]: string } = {};
|
|
95
|
+
query[C.TAGS.CUSTOM_CLUSTER] = '1';
|
|
96
|
+
const customClustersColumnsList = wu(filteredDfCols.byTags(query)).filter(c => c.max > 0).toArray();
|
|
97
|
+
const getAggregatedColName = (aggF: string, colName: string) => `${aggF}(${colName})`;
|
|
98
|
+
const isCustomCluster = (cluster: string) => filteredDfCols.contains(cluster);
|
|
64
99
|
|
|
65
|
-
|
|
100
|
+
let summaryTableBuilder = filteredDf.groupBy([clustersColName]);
|
|
101
|
+
const aggregateColumnsEntries = Object.entries(this.model.settings.columns ?? {});
|
|
102
|
+
for (const [colName, aggregationFunc] of aggregateColumnsEntries) {
|
|
103
|
+
summaryTableBuilder = summaryTableBuilder.add(
|
|
104
|
+
aggregationFunc as any, colName, getAggregatedColName(aggregationFunc, colName));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const tempSummaryTable = summaryTableBuilder.aggregate();
|
|
108
|
+
const tempSummaryTableLength = tempSummaryTable.rowCount;
|
|
109
|
+
const tempClustersCol: DG.Column<string> = tempSummaryTable.getCol(clustersColName);
|
|
110
|
+
const summaryTableLength = tempSummaryTableLength + customClustersColumnsList.length;
|
|
111
|
+
const summaryTable = DG.DataFrame.create(summaryTableLength);
|
|
66
112
|
const summaryTableCols = summaryTable.columns;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
113
|
+
|
|
114
|
+
const clustersCol = summaryTableCols.addNewString(clustersColName);
|
|
115
|
+
for (let i = 0; i < summaryTableLength; ++i) {
|
|
116
|
+
clustersCol.set(i, i < tempSummaryTableLength ? tempClustersCol.get(i) :
|
|
117
|
+
customClustersColumnsList[i - tempSummaryTableLength].name);
|
|
118
|
+
}
|
|
70
119
|
const clustersColData = clustersCol.getRawData();
|
|
71
120
|
const clustersColCategories = clustersCol.categories;
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
const tempWebLogoDfPlotList: DG.DataFramePlotHelper[] = new Array(summaryTableLength);
|
|
75
|
-
const tempDistributionDfPlotList: DG.DataFramePlotHelper[] = new Array(summaryTableLength);
|
|
76
|
-
const originalClustersCol = this.dataFrame.getCol(clustersColName);
|
|
77
|
-
const originalClustersColData = originalClustersCol.getRawData();
|
|
78
|
-
const originalClustersColCategories = originalClustersCol.categories;
|
|
79
|
-
const originalClustersColLength = originalClustersColData.length;
|
|
80
|
-
const peptideCol: DG.Column<string> = this.dataFrame.getCol(this.model.settings.sequenceColumnName!);
|
|
121
|
+
|
|
122
|
+
const peptideCol: DG.Column<string> = filteredDf.getCol(this.model.settings.sequenceColumnName!);
|
|
81
123
|
const peptideColData = peptideCol.getRawData();
|
|
82
124
|
const peptideColCategories = peptideCol.categories;
|
|
83
|
-
const
|
|
84
|
-
const pValColData = summaryTableCols.addNewFloat('P-Value').getRawData();
|
|
85
|
-
const ratioColData = summaryTableCols.addNewFloat('Ratio').getRawData();
|
|
86
|
-
const distributionCol = summaryTableCols.addNewString('Distribution');
|
|
87
|
-
// const distributionColData = distributionCol.getRawData();
|
|
88
|
-
|
|
89
|
-
for (let index = 0; index < summaryTableLength; ++index) {
|
|
90
|
-
const indexes: number[] = [];
|
|
91
|
-
const currentCluster = clustersColCategories[clustersColData[index]];
|
|
92
|
-
for (let j = 0; j < originalClustersColLength; ++j) {
|
|
93
|
-
if (originalClustersColCategories[originalClustersColData[j]] == currentCluster)
|
|
94
|
-
indexes.push(j);
|
|
95
|
-
}
|
|
96
|
-
const tCol = DG.Column.string('peptides', indexes.length);
|
|
97
|
-
tCol.init((i) => peptideColCategories[peptideColData[indexes[i]]]);
|
|
125
|
+
const peptideColTags = peptideCol.tags;
|
|
98
126
|
|
|
99
|
-
|
|
100
|
-
|
|
127
|
+
const membersColData = summaryTableCols.addNewInt(C.LST_COLUMN_NAMES.MEMBERS).getRawData();
|
|
128
|
+
const webLogoCol = summaryTableCols.addNewString(C.LST_COLUMN_NAMES.WEB_LOGO);
|
|
129
|
+
const distributionCol = summaryTableCols.addNewString(C.LST_COLUMN_NAMES.DISTRIBUTION);
|
|
130
|
+
const meanDifferenceColData = summaryTableCols.addNewFloat(C.LST_COLUMN_NAMES.MEAN_DIFFERENCE).getRawData();
|
|
131
|
+
const pValColData = summaryTableCols.addNewFloat(C.LST_COLUMN_NAMES.P_VALUE).getRawData();
|
|
132
|
+
const ratioColData = summaryTableCols.addNewFloat(C.LST_COLUMN_NAMES.RATIO).getRawData();
|
|
101
133
|
|
|
102
|
-
|
|
103
|
-
|
|
134
|
+
for (const [colName, aggregationFunc] of aggregateColumnsEntries) {
|
|
135
|
+
const tempSummaryTableCol = tempSummaryTable.getCol(getAggregatedColName(aggregationFunc, colName));
|
|
136
|
+
const summaryTableCol = summaryTableCols.addNew(tempSummaryTableCol.name, tempSummaryTableCol.type);
|
|
137
|
+
summaryTableCol.init((i) => i < tempSummaryTableLength ? tempSummaryTableCol.get(i) : null);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
this.webLogoDfPlot = new Array(summaryTableLength);
|
|
141
|
+
this.distributionDfPlot = new Array(summaryTableLength);
|
|
142
|
+
|
|
143
|
+
for (let summaryTableRowIndex = 0; summaryTableRowIndex < summaryTableLength; ++summaryTableRowIndex) {
|
|
144
|
+
const isOriginalCluster = summaryTableRowIndex < tempSummaryTableLength;
|
|
145
|
+
const currentClusterCategoryIndex = clustersColData[summaryTableRowIndex];
|
|
146
|
+
const currentCluster = clustersColCategories[currentClusterCategoryIndex]; // Cluster name
|
|
147
|
+
const customClusterColData = customClustersColumnsList.find((col) => col.name == currentCluster)?.toList();
|
|
148
|
+
|
|
149
|
+
const isValidIndex = isOriginalCluster ?
|
|
150
|
+
(j: number) => filteredDfClustersColCategories[filteredDfClustersColData[j]] == currentCluster :
|
|
151
|
+
(j: number) => customClusterColData![j];
|
|
104
152
|
|
|
105
|
-
|
|
106
|
-
const matcher: {[key: string]: number | string} = {};
|
|
107
|
-
matcher[this.model.settings.clustersColumnName!] = currentCluster;
|
|
108
|
-
const currentStatsDf = this.model.clusterStatsDf.rows.match(matcher).toDataFrame();
|
|
109
|
-
const activityCol = this.dataFrame.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
153
|
+
|
|
110
154
|
//TODO: use bitset instead of splitCol
|
|
111
155
|
const splitCol = DG.Column.bool(C.COLUMNS_NAMES.SPLIT_COL, activityCol.length);
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
156
|
+
const getSplitColValueAt = isOriginalCluster ?
|
|
157
|
+
(splitColIndex: number) => filteredDfClustersColData[splitColIndex] == currentClusterCategoryIndex :
|
|
158
|
+
(splitColIndex: number) => customClusterColData![splitColIndex];
|
|
159
|
+
splitCol.init((i) => getSplitColValueAt(i));
|
|
160
|
+
|
|
161
|
+
let stats: Stats;
|
|
162
|
+
if (isDfFiltered) {
|
|
163
|
+
const trueCount = splitCol.stats.sum;
|
|
164
|
+
const maskInfo = {
|
|
165
|
+
trueCount: trueCount,
|
|
166
|
+
falseCount: activityColData.length - trueCount,
|
|
167
|
+
mask: splitCol.toList() as boolean[],
|
|
168
|
+
};
|
|
169
|
+
stats = getStats(activityColData, maskInfo);
|
|
170
|
+
} else
|
|
171
|
+
stats = this.model.clusterStats[currentCluster];
|
|
121
172
|
|
|
173
|
+
const tCol = DG.Column.string('peptides', stats.count);
|
|
174
|
+
let tColIdx = 0;
|
|
175
|
+
for (let j = 0; j < filteredDfClustersColLength; ++j) {
|
|
176
|
+
if (isValidIndex(j))
|
|
177
|
+
tCol.set(tColIdx++, peptideColCategories[peptideColData[j]]);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
for (const tag of peptideColTags)
|
|
181
|
+
tCol.setTag(tag[0], tag[1]);
|
|
182
|
+
|
|
183
|
+
const uh = new UnitsHandler(tCol);
|
|
184
|
+
tCol.setTag(bioTAGS.alphabetSize, uh.getAlphabetSize().toString());
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
|
|
122
188
|
const dfSlice = DG.DataFrame.fromColumns([tCol]);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
membersColData[
|
|
128
|
-
meanDifferenceColData[
|
|
129
|
-
pValColData[
|
|
130
|
-
ratioColData[
|
|
189
|
+
|
|
190
|
+
this.webLogoDfPlot[summaryTableRowIndex] = dfSlice.plot;
|
|
191
|
+
this.distributionDfPlot[summaryTableRowIndex] = distributionTable.plot;
|
|
192
|
+
|
|
193
|
+
membersColData[summaryTableRowIndex] = stats.count;
|
|
194
|
+
meanDifferenceColData[summaryTableRowIndex] = stats.meanDifference;
|
|
195
|
+
pValColData[summaryTableRowIndex] = stats.pValue;
|
|
196
|
+
ratioColData[summaryTableRowIndex] = stats.ratio;
|
|
197
|
+
|
|
198
|
+
//Setting aggregated col values
|
|
199
|
+
if (!isOriginalCluster) {
|
|
200
|
+
for (const [colName, aggregationFunc] of aggregateColumnsEntries) {
|
|
201
|
+
const arrayBuffer = filteredDf.getCol(colName).getRawData();
|
|
202
|
+
const clusterMask = DG.BitSet.fromBytes(arrayBuffer.buffer, arrayBuffer.byteLength / 4);
|
|
203
|
+
const subDf = filteredDf.clone(clusterMask, [colName]);
|
|
204
|
+
const newColName = getAggregatedColName(aggregationFunc, colName);
|
|
205
|
+
const aggregatedDf = subDf.groupBy()
|
|
206
|
+
.add(aggregationFunc as any, colName, newColName)
|
|
207
|
+
.aggregate();
|
|
208
|
+
const value = aggregatedDf.get(newColName, 0);
|
|
209
|
+
summaryTable.set(newColName, summaryTableRowIndex, value);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
131
212
|
}
|
|
132
213
|
webLogoCol.setTag(DG.TAGS.CELL_RENDERER, 'html');
|
|
133
214
|
distributionCol.setTag(DG.TAGS.CELL_RENDERER, 'html');
|
|
@@ -145,11 +226,12 @@ export class LogoSummary extends DG.JsViewer {
|
|
|
145
226
|
return;
|
|
146
227
|
|
|
147
228
|
if (cell.tableColumn?.name == 'WebLogo') {
|
|
148
|
-
|
|
149
|
-
.fromType('WebLogo', {maxHeight: cell.grid.props.rowHeight - 5, positionHeight: this.webLogoMode
|
|
229
|
+
this.webLogoDfPlot[currentRowIdx]
|
|
230
|
+
.fromType('WebLogo', {maxHeight: cell.grid.props.rowHeight - 5, positionHeight: this.webLogoMode,
|
|
231
|
+
horizontalAlignment: 'left'})
|
|
150
232
|
.then((viewer) => cell.element = viewer.root);
|
|
151
233
|
} else if (cell.tableColumn?.name == 'Distribution') {
|
|
152
|
-
const viewerRoot =
|
|
234
|
+
const viewerRoot = this.distributionDfPlot[currentRowIdx].histogram({
|
|
153
235
|
filteringEnabled: false,
|
|
154
236
|
valueColumnName: C.COLUMNS_NAMES.ACTIVITY_SCALED,
|
|
155
237
|
splitColumnName: C.COLUMNS_NAMES.SPLIT_COL,
|
|
@@ -169,15 +251,14 @@ export class LogoSummary extends DG.JsViewer {
|
|
|
169
251
|
});
|
|
170
252
|
this.viewerGrid.root.addEventListener('click', (ev) => {
|
|
171
253
|
const cell = this.viewerGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
172
|
-
if (!cell || !cell.isTableCell)
|
|
254
|
+
if (!cell || !cell.isTableCell || cell.tableColumn?.name != clustersColName)
|
|
173
255
|
return;
|
|
174
256
|
|
|
175
|
-
const clusterIdx = clustersColData[cell.tableRowIndex!];
|
|
176
257
|
summaryTable.currentRowIdx = -1;
|
|
177
258
|
if (ev.shiftKey)
|
|
178
|
-
this.model.modifyClusterSelection(
|
|
259
|
+
this.model.modifyClusterSelection(cell.cell.value);
|
|
179
260
|
else
|
|
180
|
-
this.model.initClusterSelection(
|
|
261
|
+
this.model.initClusterSelection(cell.cell.value);
|
|
181
262
|
this.viewerGrid.invalidate();
|
|
182
263
|
});
|
|
183
264
|
this.viewerGrid.onCellRender.subscribe((gridCellArgs) => {
|
|
@@ -190,14 +271,13 @@ export class LogoSummary extends DG.JsViewer {
|
|
|
190
271
|
canvasContext.beginPath();
|
|
191
272
|
canvasContext.rect(bound.x, bound.y, bound.width, bound.height);
|
|
192
273
|
canvasContext.clip();
|
|
193
|
-
|
|
194
|
-
CR.renderLogoSummaryCell(canvasContext, gc.cell.value, cellRawData, this.model.logoSummarySelection, bound);
|
|
274
|
+
CR.renderLogoSummaryCell(canvasContext, gc.cell.value, this.model.logoSummarySelection, bound);
|
|
195
275
|
gridCellArgs.preventDefault();
|
|
196
276
|
canvasContext.restore();
|
|
197
277
|
});
|
|
198
278
|
this.viewerGrid.onCellTooltip((cell, x, y) => {
|
|
199
279
|
if (!cell.isColHeader && cell.tableColumn?.name === clustersColName)
|
|
200
|
-
this.model.showTooltipCluster(cell.cell.
|
|
280
|
+
this.model.showTooltipCluster(cell.cell.rowIndex, x, y, cell.cell.value);
|
|
201
281
|
return true;
|
|
202
282
|
});
|
|
203
283
|
const webLogoGridCol = this.viewerGrid.columns.byName('WebLogo')!;
|
|
@@ -215,9 +295,94 @@ export class LogoSummary extends DG.JsViewer {
|
|
|
215
295
|
|
|
216
296
|
updateFilter(): void {
|
|
217
297
|
const table = this.viewerGrid.table;
|
|
218
|
-
const memberstCol = table.getCol(C.
|
|
298
|
+
const memberstCol = table.getCol(C.LST_COLUMN_NAMES.MEMBERS);
|
|
219
299
|
const membersColData = memberstCol.getRawData();
|
|
220
300
|
const maxCount = memberstCol.stats.max;
|
|
221
|
-
|
|
301
|
+
const minMembers = Math.ceil(maxCount * this.membersRatioThreshold);
|
|
302
|
+
table.filter.init((i) => membersColData[i] > minMembers);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
clusterFromSelection(): void {
|
|
306
|
+
const filteredDf = this.dataFrame.filter.anyFalse ? this.dataFrame.clone(this.dataFrame.filter, null, true) :
|
|
307
|
+
this.dataFrame;
|
|
308
|
+
const selection = filteredDf.selection;
|
|
309
|
+
const viewerDf = this.viewerGrid.dataFrame;
|
|
310
|
+
const viewerDfCols = viewerDf.columns;
|
|
311
|
+
const viewerDfColsLength = viewerDfCols.length;
|
|
312
|
+
const newClusterVals = new Array(viewerDfCols.length);
|
|
313
|
+
|
|
314
|
+
const activityScaledCol = filteredDf.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
315
|
+
const maskInfo: MaskInfo = {
|
|
316
|
+
mask: selection.getBuffer(),
|
|
317
|
+
trueCount: selection.trueCount,
|
|
318
|
+
falseCount: selection.falseCount,
|
|
319
|
+
};
|
|
320
|
+
const stats = getStats(activityScaledCol.getRawData(), maskInfo);
|
|
321
|
+
const distributionTable = DG.DataFrame.fromColumns([activityScaledCol, filteredDf.getCol(this.model.splitCol.name)]);
|
|
322
|
+
|
|
323
|
+
const peptideCol: DG.Column<string> = filteredDf.getCol(this.model.settings.sequenceColumnName!);
|
|
324
|
+
const peptideColData = peptideCol.getRawData();
|
|
325
|
+
const peptideColCategories = peptideCol.categories;
|
|
326
|
+
const peptideColTags = peptideCol.tags;
|
|
327
|
+
const selectedIndexes = selection.getSelectedIndexes();
|
|
328
|
+
const tCol = DG.Column.string('peptides', selectedIndexes.length);
|
|
329
|
+
|
|
330
|
+
for (let i = 0; i < selectedIndexes.length; ++i)
|
|
331
|
+
tCol.set(i, peptideColCategories[peptideColData[selectedIndexes[i]]]);
|
|
332
|
+
for (const tag of peptideColTags)
|
|
333
|
+
tCol.setTag(tag[0], tag[1]);
|
|
334
|
+
|
|
335
|
+
const uh = new UnitsHandler(tCol);
|
|
336
|
+
tCol.setTag(bioTAGS.alphabetSize, uh.getAlphabetSize().toString());
|
|
337
|
+
|
|
338
|
+
const webLogoTable = DG.DataFrame.fromColumns([tCol]);
|
|
339
|
+
this.webLogoDfPlot.push(webLogoTable.plot);
|
|
340
|
+
this.distributionDfPlot.push(distributionTable.plot);
|
|
341
|
+
|
|
342
|
+
const colCategories = viewerDfCols.byName(this.model.settings.clustersColumnName!).categories;
|
|
343
|
+
let newClusterName = this.newClusterName;
|
|
344
|
+
let clusterNum = 1;
|
|
345
|
+
const getString = !isNaN(parseInt(newClusterName)) ? () => `${parseInt(newClusterName) + 1}` :
|
|
346
|
+
newClusterName == '' ? () => `${clusterNum++}` :
|
|
347
|
+
() => `${this.newClusterName} ${clusterNum++}`;
|
|
348
|
+
while (colCategories.includes(newClusterName))
|
|
349
|
+
newClusterName = getString();
|
|
350
|
+
|
|
351
|
+
this.getProperty('newClusterName')?.set(this, getString());
|
|
352
|
+
|
|
353
|
+
for (let i = 0; i < viewerDfColsLength; ++i) {
|
|
354
|
+
const col = viewerDfCols.byIndex(i);
|
|
355
|
+
newClusterVals[i] = col.name == this.model.settings.clustersColumnName! ? newClusterName :
|
|
356
|
+
col.name == C.LST_COLUMN_NAMES.MEMBERS ? maskInfo.trueCount :
|
|
357
|
+
col.name == C.LST_COLUMN_NAMES.WEB_LOGO ? null :
|
|
358
|
+
col.name == C.LST_COLUMN_NAMES.DISTRIBUTION ? null :
|
|
359
|
+
col.name == C.LST_COLUMN_NAMES.MEAN_DIFFERENCE ? stats.meanDifference:
|
|
360
|
+
col.name == C.LST_COLUMN_NAMES.P_VALUE ? stats.pValue:
|
|
361
|
+
col.name == C.LST_COLUMN_NAMES.RATIO ? stats.ratio:
|
|
362
|
+
console.warn(`PeptidesLSTWarn: value for column ${col.name} is undefined`)! || null;
|
|
363
|
+
}
|
|
364
|
+
viewerDf.rows.addNew(newClusterVals);
|
|
365
|
+
|
|
366
|
+
this.model.clusterStats[newClusterName] = stats;
|
|
367
|
+
this.model.addNewCluster(newClusterName);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
removeCluster(): void {
|
|
371
|
+
const lss = this.model.logoSummarySelection;
|
|
372
|
+
const dfCols = this.dataFrame.columns;
|
|
373
|
+
|
|
374
|
+
const removeClusterIndexesList = lss.filter((cluster) => dfCols.contains(cluster));
|
|
375
|
+
if (removeClusterIndexesList.length == 0)
|
|
376
|
+
return grok.shell.info('Nothing removed. Please select a created cluster to remove');
|
|
377
|
+
|
|
378
|
+
for (const cluster of removeClusterIndexesList) {
|
|
379
|
+
lss.splice(lss.indexOf(cluster), 1);
|
|
380
|
+
dfCols.remove(cluster);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
this.model.logoSummarySelection = lss;
|
|
384
|
+
this.model.clusterStats = this.model.calculateClusterStatistics();
|
|
385
|
+
this.createLogoSummaryGrid();
|
|
386
|
+
this.render();
|
|
222
387
|
}
|
|
223
388
|
}
|
|
@@ -6,13 +6,12 @@ import $ from 'cash-dom';
|
|
|
6
6
|
import * as C from '../utils/constants';
|
|
7
7
|
import * as CR from '../utils/cell-renderer';
|
|
8
8
|
import {PeptidesModel} from '../model';
|
|
9
|
-
import {isGridCellInvalid} from '../utils/misc';
|
|
10
9
|
|
|
11
10
|
export class SARViewerBase extends DG.JsViewer {
|
|
12
11
|
tempName!: string;
|
|
13
12
|
_viewerGrid!: DG.Grid;
|
|
14
13
|
sourceGrid!: DG.Grid;
|
|
15
|
-
|
|
14
|
+
_model!: PeptidesModel;
|
|
16
15
|
isPropertyChanging: boolean = false;
|
|
17
16
|
_isVertical = false;
|
|
18
17
|
|
|
@@ -29,10 +28,15 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
29
28
|
this._viewerGrid = grid;
|
|
30
29
|
}
|
|
31
30
|
|
|
31
|
+
get model(): PeptidesModel {
|
|
32
|
+
this._model ??= PeptidesModel.getInstance(this.dataFrame);
|
|
33
|
+
return this._model;
|
|
34
|
+
}
|
|
35
|
+
|
|
32
36
|
onTableAttached(): void {
|
|
33
37
|
super.onTableAttached();
|
|
34
38
|
this.sourceGrid = this.view?.grid ?? (grok.shell.v as DG.TableView).grid;
|
|
35
|
-
this.model = PeptidesModel.getInstance(this.dataFrame);
|
|
39
|
+
// this.model = PeptidesModel.getInstance(this.dataFrame);
|
|
36
40
|
this.subs.push(this.model.onMutationCliffsSelectionChanged.subscribe(() => this.viewerGrid.invalidate()));
|
|
37
41
|
this.helpUrl = '/help/domains/bio/peptides.md';
|
|
38
42
|
}
|
|
@@ -80,9 +84,16 @@ export class SARViewerBase extends DG.JsViewer {
|
|
|
80
84
|
switchHost = ui.divH([mutationCliffsMode.root, invariantMapMode.root], {id: 'pep-viewer-title'});
|
|
81
85
|
$(switchHost).css('width', 'auto').css('align-self', 'center');
|
|
82
86
|
}
|
|
87
|
+
const tips: HTMLElement = ui.iconFA('question');
|
|
88
|
+
ui.tooltip.bind(tips,
|
|
89
|
+
() => ui.divV([ui.divText('Color intensity - p-value'), ui.divText('Circle size - Mean difference')]));
|
|
90
|
+
|
|
91
|
+
$(tips).addClass('pep-help-icon');
|
|
92
|
+
|
|
83
93
|
const viewerRoot = this.viewerGrid.root;
|
|
84
94
|
viewerRoot.style.width = 'auto';
|
|
85
|
-
|
|
95
|
+
const header = ui.divH([switchHost, tips], {style: {alignSelf: 'center', lineHeight: 'normal'}});
|
|
96
|
+
this.root.appendChild(ui.divV([header, viewerRoot]));
|
|
86
97
|
}
|
|
87
98
|
this.viewerGrid?.invalidate();
|
|
88
99
|
}
|
|
@@ -143,18 +154,18 @@ export class MonomerPosition extends SARViewerBase {
|
|
|
143
154
|
this.viewerGrid.onCellTooltip((cell: DG.GridCell, x: number, y: number) => showTooltip(cell, x, y, this.model));
|
|
144
155
|
this.viewerGrid.root.addEventListener('click', (ev) => {
|
|
145
156
|
const gridCell = this.viewerGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
146
|
-
if (
|
|
157
|
+
if (!gridCell?.isTableCell || gridCell?.tableColumn?.name == C.COLUMNS_NAMES.MONOMER)
|
|
147
158
|
return;
|
|
148
159
|
|
|
149
160
|
const position = gridCell!.tableColumn!.name;
|
|
150
161
|
const aar = monomerCol.get(gridCell!.tableRowIndex!);
|
|
151
162
|
chooseAction(aar, position, ev.shiftKey, this.model.isInvariantMap, this.model);
|
|
152
163
|
this.viewerGrid.invalidate();
|
|
164
|
+
this.model.fireBitsetChanged();
|
|
153
165
|
});
|
|
154
166
|
this.viewerGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(this.model.monomerPositionDf, this.model));
|
|
155
167
|
|
|
156
168
|
setViewerGridProps(this.viewerGrid, false);
|
|
157
|
-
setTimeout(() => this.viewerGrid.col(C.COLUMNS_NAMES.MONOMER)!.width = 60, 10);
|
|
158
169
|
}
|
|
159
170
|
}
|
|
160
171
|
|
|
@@ -199,7 +210,7 @@ export class MostPotentResiduesViewer extends SARViewerBase {
|
|
|
199
210
|
pValGridCol.format = '#.000';
|
|
200
211
|
pValGridCol.name = 'P-value';
|
|
201
212
|
const monomerCol = this.model.mostPotentResiduesDf.getCol(C.COLUMNS_NAMES.MONOMER);
|
|
202
|
-
const positionCol = this.model.mostPotentResiduesDf.getCol(C.COLUMNS_NAMES.POSITION);
|
|
213
|
+
const positionCol: DG.Column<number> = this.model.mostPotentResiduesDf.getCol(C.COLUMNS_NAMES.POSITION);
|
|
203
214
|
|
|
204
215
|
// Setting Monomer column renderer
|
|
205
216
|
CR.setAARRenderer(monomerCol, this.model.alphabet);
|
|
@@ -207,27 +218,27 @@ export class MostPotentResiduesViewer extends SARViewerBase {
|
|
|
207
218
|
this.viewerGrid.onCellTooltip((cell: DG.GridCell, x: number, y: number) => showTooltip(cell, x, y, this.model));
|
|
208
219
|
this.viewerGrid.root.addEventListener('click', (ev) => {
|
|
209
220
|
const gridCell = this.viewerGrid.hitTest(ev.offsetX, ev.offsetY);
|
|
210
|
-
if (
|
|
221
|
+
if (!gridCell?.isTableCell || gridCell!.tableColumn!.name != C.COLUMNS_NAMES.MEAN_DIFFERENCE)
|
|
211
222
|
return;
|
|
212
223
|
|
|
213
224
|
const tableRowIdx = gridCell!.tableRowIndex!;
|
|
214
225
|
const position = positionCol.get(tableRowIdx);
|
|
215
226
|
const aar = monomerCol.get(tableRowIdx);
|
|
216
|
-
chooseAction(aar, position, ev.shiftKey, false, this.model);
|
|
227
|
+
chooseAction(aar, position!.toFixed(), ev.shiftKey, false, this.model);
|
|
217
228
|
this.viewerGrid.invalidate();
|
|
229
|
+
this.model.fireBitsetChanged();
|
|
218
230
|
});
|
|
219
231
|
this.viewerGrid.onCurrentCellChanged.subscribe((_gc) => cellChanged(this.model.mostPotentResiduesDf, this.model));
|
|
220
232
|
const mdCol: DG.GridColumn = this.viewerGrid.col(C.COLUMNS_NAMES.MEAN_DIFFERENCE)!;
|
|
221
233
|
mdCol.name = 'Diff';
|
|
222
234
|
setViewerGridProps(this.viewerGrid, true);
|
|
223
|
-
setTimeout(() => this.viewerGrid.col(C.COLUMNS_NAMES.MONOMER)!.width = 60, 10);
|
|
224
235
|
}
|
|
225
236
|
}
|
|
226
237
|
|
|
227
238
|
function renderCell(args: DG.GridCellRenderArgs, model: PeptidesModel, isInvariantMap?: boolean,
|
|
228
239
|
colorCol?: DG.Column<number>, colorAgg?: DG.AggregationType): void {
|
|
229
240
|
const renderColNames = [...model.splitSeqDf.columns.names(), C.COLUMNS_NAMES.MEAN_DIFFERENCE];
|
|
230
|
-
const mdCol = model.
|
|
241
|
+
// const mdCol = model.monomerPositionStats.getCol(C.COLUMNS_NAMES.MEAN_DIFFERENCE);
|
|
231
242
|
const canvasContext = args.g;
|
|
232
243
|
const bound = args.bounds;
|
|
233
244
|
|
|
@@ -241,49 +252,52 @@ function renderCell(args: DG.GridCellRenderArgs, model: PeptidesModel, isInvaria
|
|
|
241
252
|
if (cell.isRowHeader && cell.gridColumn.visible) {
|
|
242
253
|
cell.gridColumn.visible = false;
|
|
243
254
|
args.preventDefault();
|
|
255
|
+
canvasContext.restore();
|
|
244
256
|
return;
|
|
245
257
|
}
|
|
246
258
|
|
|
247
259
|
const tableColName = cell.tableColumn?.name;
|
|
248
260
|
const tableRowIndex = cell.tableRowIndex!;
|
|
249
|
-
if (cell.isTableCell
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
.where(`${C.COLUMNS_NAMES.POSITION} = ${currentPosition} and ${C.COLUMNS_NAMES.MONOMER} = ${currentAAR}`)
|
|
262
|
-
.aggregate().get(C.COLUMNS_NAMES.COUNT, 0);
|
|
263
|
-
const positionCol = model.df.getCol(currentPosition);
|
|
264
|
-
const positionColData = positionCol.getRawData();
|
|
265
|
-
const positionColCategories = positionCol.categories;
|
|
266
|
-
|
|
267
|
-
const colorColData = colorCol!.getRawData();
|
|
268
|
-
const colorDataList: number[] = [];
|
|
269
|
-
for (let i = 0; i < positionColData.length; ++i) {
|
|
270
|
-
if (positionColCategories[positionColData[i]] === currentAAR)
|
|
271
|
-
colorDataList.push(colorColData[i]);
|
|
272
|
-
}
|
|
273
|
-
const cellColorDataCol = DG.Column.fromList('double', '', colorDataList);
|
|
274
|
-
const colorColStats = colorCol!.stats;
|
|
275
|
-
|
|
276
|
-
const color = DG.Color.scaleColor(cellColorDataCol.aggregate(colorAgg!), colorColStats.min, colorColStats.max);
|
|
277
|
-
CR.renderInvaraintMapCell(
|
|
278
|
-
canvasContext, currentAAR, currentPosition, model.invariantMapSelection, value, bound, color);
|
|
279
|
-
} else {
|
|
280
|
-
CR.renderMutationCliffCell(canvasContext, currentAAR, currentPosition, model.monomerPositionStatsDf,
|
|
281
|
-
mdCol, bound, cellValue, model.mutationCliffsSelection, model.substitutionsInfo,
|
|
282
|
-
model.settings.isBidirectional);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
261
|
+
if (!cell.isTableCell || renderColNames.indexOf(tableColName!) == -1) {
|
|
262
|
+
canvasContext.restore();
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const gridTable = cell.grid.table;
|
|
267
|
+
const currentMonomer: string = gridTable.get(C.COLUMNS_NAMES.MONOMER, tableRowIndex);
|
|
268
|
+
const currentPosition: string = tableColName !== C.COLUMNS_NAMES.MEAN_DIFFERENCE ? tableColName :
|
|
269
|
+
gridTable.get(C.COLUMNS_NAMES.POSITION, tableRowIndex).toFixed();
|
|
270
|
+
const currentPosStats = model.monomerPositionStats[currentPosition];
|
|
271
|
+
|
|
272
|
+
if (!currentPosStats[currentMonomer]) {
|
|
285
273
|
args.preventDefault();
|
|
274
|
+
canvasContext.restore();
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (isInvariantMap) {
|
|
279
|
+
const value = currentPosStats[currentMonomer].count;
|
|
280
|
+
const positionCol = model.df.getCol(currentPosition);
|
|
281
|
+
const positionColData = positionCol.getRawData();
|
|
282
|
+
const positionColCategories = positionCol.categories;
|
|
283
|
+
|
|
284
|
+
const colorColData = colorCol!.getRawData();
|
|
285
|
+
const colorDataList: number[] = [];
|
|
286
|
+
for (let i = 0; i < positionColData.length; ++i) {
|
|
287
|
+
if (positionColCategories[positionColData[i]] === currentMonomer)
|
|
288
|
+
colorDataList.push(colorColData[i]);
|
|
289
|
+
}
|
|
290
|
+
const cellColorDataCol = DG.Column.fromList('double', '', colorDataList);
|
|
291
|
+
const colorColStats = colorCol!.stats;
|
|
292
|
+
|
|
293
|
+
const color = DG.Color.scaleColor(cellColorDataCol.aggregate(colorAgg!), colorColStats.min, colorColStats.max);
|
|
294
|
+
CR.renderInvaraintMapCell(
|
|
295
|
+
canvasContext, currentMonomer, currentPosition, model.invariantMapSelection, value, bound, color);
|
|
296
|
+
} else {
|
|
297
|
+
CR.renderMutationCliffCell(canvasContext, currentMonomer, currentPosition, model.monomerPositionStats, bound,
|
|
298
|
+
model.mutationCliffsSelection, model.substitutionsInfo, model.settings.isBidirectional);
|
|
286
299
|
}
|
|
300
|
+
args.preventDefault();
|
|
287
301
|
canvasContext.restore();
|
|
288
302
|
}
|
|
289
303
|
|
|
@@ -299,9 +313,9 @@ function showTooltip(cell: DG.GridCell, x: number, y: number, model: PeptidesMod
|
|
|
299
313
|
|
|
300
314
|
if (tableCol.semType == C.SEM_TYPES.MONOMER)
|
|
301
315
|
model.showMonomerTooltip(currentAAR, x, y);
|
|
302
|
-
else if (
|
|
316
|
+
else if (renderColNames.includes(tableColName!)) {
|
|
303
317
|
const currentPosition = tableColName !== C.COLUMNS_NAMES.MEAN_DIFFERENCE ? tableColName :
|
|
304
|
-
table.get(C.COLUMNS_NAMES.POSITION, tableRowIndex);
|
|
318
|
+
table.get(C.COLUMNS_NAMES.POSITION, tableRowIndex).toFixed();
|
|
305
319
|
|
|
306
320
|
model.showTooltipAt(currentAAR, currentPosition, x, y);
|
|
307
321
|
}
|