@datagrok/peptides 1.17.0 → 1.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +17 -6
- package/dist/196.js +2 -0
- package/dist/23.js +2 -0
- package/dist/282.js +2 -0
- package/dist/361.js +2 -2
- package/dist/40.js +2 -0
- package/dist/436.js +2 -2
- package/dist/65.js +2 -0
- package/dist/704.js +2 -0
- package/dist/package-test.js +2 -3
- package/dist/package.js +2 -3
- package/package.json +14 -14
- package/src/demo/fasta.ts +8 -2
- package/src/model.ts +780 -531
- package/src/package-test.ts +1 -3
- package/src/package.ts +15 -28
- package/src/tests/benchmarks.ts +31 -11
- package/src/tests/core.ts +11 -6
- package/src/tests/misc.ts +6 -6
- package/src/tests/model.ts +79 -44
- package/src/tests/table-view.ts +48 -38
- package/src/tests/utils.ts +0 -76
- package/src/tests/viewers.ts +30 -12
- package/src/tests/widgets.ts +30 -11
- package/src/utils/algorithms.ts +115 -38
- package/src/utils/cell-renderer.ts +181 -72
- package/src/utils/constants.ts +33 -7
- package/src/utils/misc.ts +244 -10
- package/src/utils/parallel-mutation-cliffs.ts +18 -15
- package/src/utils/statistics.ts +70 -15
- package/src/utils/tooltips.ts +42 -17
- package/src/utils/types.ts +29 -26
- package/src/utils/worker-creator.ts +5 -0
- package/src/viewers/logo-summary.ts +591 -130
- package/src/viewers/sar-viewer.ts +893 -239
- package/src/widgets/distribution.ts +305 -64
- package/src/widgets/manual-alignment.ts +18 -11
- package/src/widgets/mutation-cliffs.ts +44 -18
- package/src/widgets/peptides.ts +86 -91
- package/src/widgets/selection.ts +56 -22
- package/src/widgets/settings.ts +94 -44
- package/src/workers/dimensionality-reducer.ts +5 -6
- package/src/workers/mutation-cliffs-worker.ts +3 -16
- package/dist/209.js +0 -2
- package/dist/381.js +0 -2
- package/dist/770.js +0 -2
- package/dist/831.js +0 -2
- package/dist/868.js +0 -2
- package/dist/931.js +0 -3
- package/dist/931.js.LICENSE.txt +0 -51
- package/dist/932.js +0 -2
- package/dist/package-test.js.LICENSE.txt +0 -51
- package/dist/package.js.LICENSE.txt +0 -51
- package/src/tests/peptide-space-test.ts +0 -48
- package/src/tests/test-data.ts +0 -649
- package/src/utils/molecular-measure.ts +0 -174
- package/src/utils/peptide-similarity-space.ts +0 -216
- package/src/viewers/peptide-space-viewer.ts +0 -150
|
@@ -1,78 +1,98 @@
|
|
|
1
1
|
import * as ui from 'datagrok-api/ui';
|
|
2
2
|
import * as DG from 'datagrok-api/dg';
|
|
3
|
-
|
|
4
|
-
import BitArray from '@datagrok-libraries/utils/src/bit-array';
|
|
5
3
|
import {StringDictionary} from '@datagrok-libraries/utils/src/type-declarations';
|
|
6
4
|
|
|
7
5
|
import $ from 'cash-dom';
|
|
8
6
|
|
|
9
7
|
import * as C from '../utils/constants';
|
|
10
|
-
import {getAggregatedColumnValues, getStats,
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const tableMap = getStatsTableMap(stats);
|
|
51
|
-
const resultMap: { [key: string]: any } = {...tableMap, ...aggregatedColMap};
|
|
52
|
-
const distributionRoot = getDistributionPanel(hist, resultMap);
|
|
53
|
-
$(distributionRoot).addClass('d4-flex-col');
|
|
54
|
-
|
|
55
|
-
res.push(distributionRoot);
|
|
56
|
-
}
|
|
8
|
+
import {AggregationColumns, getAggregatedColumnValues, getStats, StatsItem} from '../utils/statistics';
|
|
9
|
+
import {DistributionLabelMap, getDistributionPanel, getDistributionTable, SPLIT_CATEGORY} from '../utils/misc';
|
|
10
|
+
import {SARViewer} from '../viewers/sar-viewer';
|
|
11
|
+
import {CLUSTER_TYPE, LogoSummaryTable} from '../viewers/logo-summary';
|
|
12
|
+
import BitArray from '@datagrok-libraries/utils/src/bit-array';
|
|
13
|
+
import {Selection} from '../utils/types';
|
|
14
|
+
|
|
15
|
+
export type DistributionItemOptions = {
|
|
16
|
+
peptideSelection: DG.BitSet, columns: AggregationColumns, clusterColName?: string,
|
|
17
|
+
activityCol: DG.Column<number>, monomerPositionSelection: Selection, clusterSelection: Selection,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export enum DISTRIBUTION_CATEGORIES_KEYS {
|
|
21
|
+
SEPARATE_MONOMERS = 'separateMonomers',
|
|
22
|
+
SEPARATE_POSITIONS = 'separatePositions',
|
|
23
|
+
SEPARATE_CLUSTERS = 'separateClusters',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const general = 'general';
|
|
27
|
+
const key2category = (key: DISTRIBUTION_CATEGORIES_KEYS | typeof general): string => {
|
|
28
|
+
if (key === general)
|
|
29
|
+
return 'General';
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
return key.substring(8);
|
|
33
|
+
};
|
|
34
|
+
export type PeptideViewer = SARViewer | LogoSummaryTable;
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Builds Distribution panel
|
|
39
|
+
* @param table - Dataframe with peptides
|
|
40
|
+
* @param options - Distribution options
|
|
41
|
+
* @return - Distribution panel
|
|
42
|
+
*/
|
|
43
|
+
export function getDistributionWidget(table: DG.DataFrame, options: DistributionItemOptions): HTMLDivElement {
|
|
44
|
+
const mask = table.selection;
|
|
45
|
+
if (!mask.anyTrue)
|
|
46
|
+
return ui.divText('No distribution');
|
|
47
|
+
|
|
57
48
|
|
|
58
|
-
|
|
49
|
+
const getDistributionCategoreisHost = (): HTMLDivElement => {
|
|
50
|
+
const distributionCategories: HTMLDivElement[] = [getDistributionCategory(general, table, options)];
|
|
51
|
+
for (const tag of Object.values(DISTRIBUTION_CATEGORIES_KEYS)) {
|
|
52
|
+
if (table.getTag(tag) !== `${true}` ||
|
|
53
|
+
(tag === DISTRIBUTION_CATEGORIES_KEYS.SEPARATE_CLUSTERS && !options.clusterColName))
|
|
54
|
+
continue;
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
distributionCategories.push(getDistributionCategory(tag, table, options));
|
|
58
|
+
}
|
|
59
|
+
return (distributionCategories.length === 1) ? distributionCategories[0] : ui.div(distributionCategories);
|
|
59
60
|
};
|
|
61
|
+
const distributionCategoriesHost = ui.div(getDistributionCategoreisHost());
|
|
62
|
+
const inputsNames = Object.values(DISTRIBUTION_CATEGORIES_KEYS);
|
|
63
|
+
const inputsArray: DG.InputBase[] = new Array(inputsNames.length);
|
|
64
|
+
for (let inputIdx = 0; inputIdx < inputsNames.length; inputIdx++) {
|
|
65
|
+
const inputName = inputsNames[inputIdx].substring(8);
|
|
66
|
+
inputsArray[inputIdx] = ui.boolInput(inputName,
|
|
67
|
+
table.getTag(inputsNames[inputIdx]) === `${true}`, () => {
|
|
68
|
+
table.setTag(inputsNames[inputIdx], `${inputsArray[inputIdx].value}`);
|
|
69
|
+
$(distributionCategoriesHost).empty();
|
|
70
|
+
distributionCategoriesHost.append(getDistributionCategoreisHost());
|
|
71
|
+
}) as DG.InputBase<boolean>;
|
|
72
|
+
$(inputsArray[inputIdx].captionLabel).addClass('ui-label-right').css('text-align', 'left');
|
|
73
|
+
$(inputsArray[inputIdx].root).find('.ui-input-editor').css('margin', '0px');
|
|
74
|
+
$(inputsArray[inputIdx].root).find('.ui-input-description').css('margin', '0px');
|
|
75
|
+
inputsArray[inputIdx].setTooltip(`Show distribution for each ${inputName}`);
|
|
76
|
+
if (inputName === DISTRIBUTION_CATEGORIES_KEYS.SEPARATE_CLUSTERS)
|
|
77
|
+
inputsArray[inputIdx].enabled = !!(options.clusterColName && options.clusterSelection[CLUSTER_TYPE.ORIGINAL]);
|
|
78
|
+
else if (inputName === DISTRIBUTION_CATEGORIES_KEYS.SEPARATE_MONOMERS ||
|
|
79
|
+
inputName === DISTRIBUTION_CATEGORIES_KEYS.SEPARATE_POSITIONS)
|
|
80
|
+
inputsArray[inputIdx].enabled = Object.entries(options.monomerPositionSelection).length !== 0;
|
|
81
|
+
}
|
|
60
82
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
// $(splitByPosition.root).css('margin-right', '10px');
|
|
65
|
-
// const splitByMonomer = ui.boolInput('Split by monomer', defaultValueMonomer, updateDistributionHost);
|
|
66
|
-
// splitByMonomer.setTooltip('Constructs distribution for each monomer separately');
|
|
67
|
-
// setDefaultProperties(splitByMonomer);
|
|
68
|
-
|
|
69
|
-
// const controlsHost = ui.divH([splitByPosition.root, splitByMonomer.root]);
|
|
70
|
-
// splitByMonomer.fireChanged();
|
|
71
|
-
updateDistributionHost();
|
|
72
|
-
return new DG.Widget(ui.divV([/*controlsHost,*/ distributionHost]));
|
|
83
|
+
const inputsHost = ui.form(inputsArray);
|
|
84
|
+
$(inputsHost).css('display', 'inline-flex');
|
|
85
|
+
return ui.divV([inputsHost, distributionCategoriesHost]);
|
|
73
86
|
}
|
|
74
87
|
|
|
75
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Builds activity distribution histogram
|
|
90
|
+
* @param table - Dataframe with peptides
|
|
91
|
+
* @param isTooltip - Is histogram for tooltip
|
|
92
|
+
* @return - Histogram viewer
|
|
93
|
+
*/
|
|
94
|
+
export function getActivityDistribution(table: DG.DataFrame, isTooltip: boolean = false,
|
|
95
|
+
): DG.Viewer<DG.IHistogramLookSettings> {
|
|
76
96
|
const hist = table.plot.histogram({
|
|
77
97
|
filteringEnabled: false,
|
|
78
98
|
valueColumnName: C.COLUMNS_NAMES.ACTIVITY,
|
|
@@ -88,7 +108,14 @@ export function getActivityDistribution(table: DG.DataFrame, isTooltip: boolean
|
|
|
88
108
|
return hist;
|
|
89
109
|
}
|
|
90
110
|
|
|
91
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Builds stats table map
|
|
113
|
+
* @param stats - Stats item
|
|
114
|
+
* @param options - Stats table map options
|
|
115
|
+
* @param options.fractionDigits - Number of fraction digits for stats values
|
|
116
|
+
* @return - Stats table map
|
|
117
|
+
*/
|
|
118
|
+
export function getStatsTableMap(stats: StatsItem, options: { fractionDigits?: number } = {}): StringDictionary {
|
|
92
119
|
options.fractionDigits ??= 3;
|
|
93
120
|
const tableMap: StringDictionary = {
|
|
94
121
|
'Count': `${stats.count} (${stats.ratio.toFixed(options.fractionDigits)}%)`,
|
|
@@ -97,5 +124,219 @@ export function getStatsTableMap(stats: Stats, options: {fractionDigits?: number
|
|
|
97
124
|
};
|
|
98
125
|
if (stats.pValue !== null)
|
|
99
126
|
tableMap['p-value'] = stats.pValue < 0.01 ? '<0.01' : stats.pValue.toFixed(options.fractionDigits);
|
|
127
|
+
|
|
128
|
+
|
|
100
129
|
return tableMap;
|
|
101
130
|
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Builds distribution for signle item
|
|
134
|
+
* @param table - Dataframe with peptides
|
|
135
|
+
* @param stats - Stats item
|
|
136
|
+
* @param options - Distribution options
|
|
137
|
+
* @param [labelMap] - Map for histogram legend labels
|
|
138
|
+
* @return - Distribution panel
|
|
139
|
+
*/
|
|
140
|
+
function getSingleDistribution(table: DG.DataFrame, stats: StatsItem, options: DistributionItemOptions,
|
|
141
|
+
labelMap: DistributionLabelMap = {}): HTMLDivElement {
|
|
142
|
+
const hist = getActivityDistribution(getDistributionTable(options.activityCol, table.selection,
|
|
143
|
+
options.peptideSelection));
|
|
144
|
+
const aggregatedColMap = getAggregatedColumnValues(table, Object.entries(options.columns),
|
|
145
|
+
{filterDf: true, mask: DG.BitSet.fromBytes(stats.mask.buffer.buffer, stats.mask.length)});
|
|
146
|
+
const tableMap = getStatsTableMap(stats);
|
|
147
|
+
const resultMap: { [key: string]: any } = {...tableMap, ...aggregatedColMap};
|
|
148
|
+
const distributionRoot = getDistributionPanel(hist, resultMap, labelMap);
|
|
149
|
+
$(distributionRoot).addClass('d4-flex-col');
|
|
150
|
+
|
|
151
|
+
return distributionRoot;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Builds distribution item for specified category
|
|
156
|
+
* @param category - Distribution category
|
|
157
|
+
* @param table - Dataframe with peptides
|
|
158
|
+
* @param options - Distribution options
|
|
159
|
+
* @return - Distribution item
|
|
160
|
+
*/
|
|
161
|
+
function getDistributionCategory(category: DISTRIBUTION_CATEGORIES_KEYS | typeof general, table: DG.DataFrame,
|
|
162
|
+
options: DistributionItemOptions): HTMLDivElement {
|
|
163
|
+
let body: HTMLDivElement = ui.divText('No distribution');
|
|
164
|
+
switch (category) {
|
|
165
|
+
case general:
|
|
166
|
+
const bitArray = BitArray.fromSeq(table.selection.length, (i: number) => table.selection.get(i));
|
|
167
|
+
const stats = !table.selection.anyTrue || !table.selection.anyFalse ?
|
|
168
|
+
{
|
|
169
|
+
count: options.activityCol.length, pValue: null, meanDifference: 0, ratio: 1, mask: bitArray,
|
|
170
|
+
mean: options.activityCol.stats.avg,
|
|
171
|
+
} :
|
|
172
|
+
getStats(options.activityCol.getRawData(), bitArray);
|
|
173
|
+
|
|
174
|
+
body = getSingleDistribution(table, stats, options);
|
|
175
|
+
break;
|
|
176
|
+
case DISTRIBUTION_CATEGORIES_KEYS.SEPARATE_CLUSTERS:
|
|
177
|
+
body = getDistributionForClusters(table, options as Required<DistributionItemOptions>, options.clusterSelection);
|
|
178
|
+
break;
|
|
179
|
+
case DISTRIBUTION_CATEGORIES_KEYS.SEPARATE_MONOMERS:
|
|
180
|
+
const reversedSelectionObject = getReversedObject(options.monomerPositionSelection);
|
|
181
|
+
body = getDistributionForMonomers(table, options, reversedSelectionObject);
|
|
182
|
+
break;
|
|
183
|
+
case DISTRIBUTION_CATEGORIES_KEYS.SEPARATE_POSITIONS:
|
|
184
|
+
body = getDistributionForPositions(table, options, options.monomerPositionSelection);
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return ui.divV([ui.h1(key2category(category)), body]);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Builds distribution group for clusters
|
|
193
|
+
* @param table - Dataframe with peptides
|
|
194
|
+
* @param options - Distribution options
|
|
195
|
+
* @param selectionObject - Selection object
|
|
196
|
+
* @return - Distribution group host
|
|
197
|
+
*/
|
|
198
|
+
function getDistributionForClusters(table: DG.DataFrame, options: Required<DistributionItemOptions>,
|
|
199
|
+
selectionObject: Selection): HTMLDivElement {
|
|
200
|
+
const rowCount = table.rowCount;
|
|
201
|
+
const distributions: HTMLDivElement[] = [];
|
|
202
|
+
const activityColData = options.activityCol.getRawData();
|
|
203
|
+
const clusterCol = table.getCol(options.clusterColName);
|
|
204
|
+
const clusterColCategories = clusterCol.categories;
|
|
205
|
+
const clusterColData = clusterCol.getRawData() as Int32Array;
|
|
206
|
+
|
|
207
|
+
// Build distributions for original clusters
|
|
208
|
+
const selectedClustersCategoryIndexes = selectionObject[CLUSTER_TYPE.ORIGINAL]
|
|
209
|
+
.map((cluster: string) => clusterColCategories.indexOf(cluster));
|
|
210
|
+
const clusterMasks: BitArray[] = new Array(selectedClustersCategoryIndexes.length).fill(new BitArray(rowCount));
|
|
211
|
+
for (let i = 0; i < rowCount; i++) {
|
|
212
|
+
const cluster = clusterColData[i];
|
|
213
|
+
const selectedIndex = selectedClustersCategoryIndexes.indexOf(cluster);
|
|
214
|
+
if (selectedIndex !== -1)
|
|
215
|
+
clusterMasks[selectedIndex].setTrue(i);
|
|
216
|
+
}
|
|
217
|
+
for (let selectedClusterIdx = 0; selectedClusterIdx < selectedClustersCategoryIndexes.length; selectedClusterIdx++) {
|
|
218
|
+
const selectedClusterCategoryIndex = selectedClustersCategoryIndexes[selectedClusterIdx];
|
|
219
|
+
const stats = getStats(activityColData, clusterMasks[selectedClusterIdx]);
|
|
220
|
+
distributions.push(getSingleDistribution(table, stats, options,
|
|
221
|
+
{[SPLIT_CATEGORY.SELECTION]: clusterColCategories[selectedClusterCategoryIndex]}));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Build distributions for custom clusters
|
|
225
|
+
const customClusterSelection = selectionObject[CLUSTER_TYPE.CUSTOM];
|
|
226
|
+
for (const clusterColumnName of customClusterSelection) {
|
|
227
|
+
const customClustCol = table.getCol(clusterColumnName);
|
|
228
|
+
const bitArray = BitArray.fromUint32Array(rowCount, customClustCol.getRawData() as Uint32Array);
|
|
229
|
+
const stats = getStats(activityColData, bitArray);
|
|
230
|
+
distributions.push(getSingleDistribution(table, stats, options,
|
|
231
|
+
{[SPLIT_CATEGORY.SELECTION]: clusterColumnName}));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return ui.div(distributions, 'd4-flex-wrap');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Builds distribution group for positions category
|
|
239
|
+
* @param table - Dataframe with peptides
|
|
240
|
+
* @param options - Distribution options
|
|
241
|
+
* @param selectionObject - Selection object
|
|
242
|
+
* @return - Distribution group host
|
|
243
|
+
*/
|
|
244
|
+
function getDistributionForPositions(table: DG.DataFrame, options: DistributionItemOptions,
|
|
245
|
+
selectionObject: Selection): HTMLDivElement {
|
|
246
|
+
const positions = Object.keys(selectionObject);
|
|
247
|
+
const rowCount = table.rowCount;
|
|
248
|
+
const distributions: HTMLDivElement[] = [];
|
|
249
|
+
const activityColData = options.activityCol.getRawData();
|
|
250
|
+
const positionColumns: (DG.Column<string> | undefined)[] = [];
|
|
251
|
+
const positionColumnsCategories: (string[] | undefined)[] = [];
|
|
252
|
+
const positionColumnsData: (Int32Array | undefined)[] = [];
|
|
253
|
+
|
|
254
|
+
for (let posIdx = 0; posIdx < positions.length; posIdx++) {
|
|
255
|
+
const position = positions[posIdx];
|
|
256
|
+
const monomerList = selectionObject[position];
|
|
257
|
+
if (monomerList.length === 0)
|
|
258
|
+
continue;
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
positionColumns[posIdx] ??= table.getCol(position);
|
|
262
|
+
positionColumnsCategories[posIdx] ??= positionColumns[posIdx]!.categories;
|
|
263
|
+
positionColumnsData[posIdx] ??= positionColumns[posIdx]!.getRawData() as Int32Array;
|
|
264
|
+
|
|
265
|
+
const mask = new BitArray(table.rowCount);
|
|
266
|
+
for (let monomerIdx = 0; monomerIdx < monomerList.length; monomerIdx++) {
|
|
267
|
+
const monomer = monomerList[monomerIdx];
|
|
268
|
+
const monomerCategoryIndex = positionColumnsCategories[posIdx]!.indexOf(monomer);
|
|
269
|
+
|
|
270
|
+
for (let i = 0; i < rowCount; i++) {
|
|
271
|
+
if (positionColumnsData[posIdx]![i] === monomerCategoryIndex)
|
|
272
|
+
mask.setTrue(i);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const stats = getStats(activityColData, mask);
|
|
276
|
+
distributions.push(getSingleDistribution(table, stats, options, {[SPLIT_CATEGORY.SELECTION]: position}));
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return ui.div(distributions, 'd4-flex-wrap');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Builds distribution group for monomers category
|
|
284
|
+
* @param table - Dataframe with peptides
|
|
285
|
+
* @param options - Distribution options
|
|
286
|
+
* @param reversedSelectionObject - Selection object with monomers as keys and list of positions as values
|
|
287
|
+
* @return - Distribution group host
|
|
288
|
+
*/
|
|
289
|
+
function getDistributionForMonomers(table: DG.DataFrame, options: DistributionItemOptions,
|
|
290
|
+
reversedSelectionObject: Selection): HTMLDivElement {
|
|
291
|
+
const monomers = Object.keys(reversedSelectionObject);
|
|
292
|
+
const rowCount = table.rowCount;
|
|
293
|
+
const distributions: HTMLDivElement[] = [];
|
|
294
|
+
const positionColumns: (DG.Column<string> | undefined)[] = [];
|
|
295
|
+
const positionColumnsCategories: (string[] | undefined)[] = [];
|
|
296
|
+
const positionColumnsData: (Int32Array | undefined)[] = [];
|
|
297
|
+
const activityColData = options.activityCol.getRawData();
|
|
298
|
+
|
|
299
|
+
for (const monomer of monomers) {
|
|
300
|
+
const posList = reversedSelectionObject[monomer];
|
|
301
|
+
const mask = new BitArray(rowCount);
|
|
302
|
+
|
|
303
|
+
for (let posIdx = 0; posIdx < posList.length; posIdx++) {
|
|
304
|
+
const position = posList[posIdx];
|
|
305
|
+
positionColumns[posIdx] ??= table.getCol(position);
|
|
306
|
+
positionColumnsCategories[posIdx] ??= positionColumns[posIdx]!.categories;
|
|
307
|
+
positionColumnsData[posIdx] ??= positionColumns[posIdx]!.getRawData() as Int32Array;
|
|
308
|
+
|
|
309
|
+
const monomerCategoryIndex = positionColumnsCategories[posIdx]!.indexOf(monomer);
|
|
310
|
+
for (let i = 0; i < rowCount; i++) {
|
|
311
|
+
if (positionColumnsData[posIdx]![i] === monomerCategoryIndex)
|
|
312
|
+
mask.setTrue(i);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
const stats = getStats(activityColData, mask);
|
|
316
|
+
|
|
317
|
+
distributions.push(getSingleDistribution(table, stats, options, {[SPLIT_CATEGORY.SELECTION]: monomer}));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return ui.div(distributions, 'd4-flex-wrap');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Converts monomer-position selection object to have monomers as keys and list of positions as values
|
|
325
|
+
* @param selectionObject - Selection object
|
|
326
|
+
* @return - Reversed selection object
|
|
327
|
+
*/
|
|
328
|
+
function getReversedObject(selectionObject: Selection): Selection {
|
|
329
|
+
const reversedSelectionObject: Selection = {};
|
|
330
|
+
const positions = Object.keys(selectionObject);
|
|
331
|
+
for (const position of positions) {
|
|
332
|
+
for (const monomer of selectionObject[position]) {
|
|
333
|
+
if (!reversedSelectionObject.hasOwnProperty(monomer)) {
|
|
334
|
+
reversedSelectionObject[monomer] = [position];
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
if (!reversedSelectionObject[monomer].includes(position))
|
|
338
|
+
reversedSelectionObject[monomer].push(position);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return reversedSelectionObject;
|
|
342
|
+
}
|
|
@@ -5,32 +5,39 @@ import * as DG from 'datagrok-api/dg';
|
|
|
5
5
|
import $ from 'cash-dom';
|
|
6
6
|
import '../styles.css';
|
|
7
7
|
import {PeptidesModel} from '../model';
|
|
8
|
-
import {
|
|
8
|
+
import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
*
|
|
12
|
-
* @param
|
|
13
|
-
* @
|
|
10
|
+
/**
|
|
11
|
+
* Allows to edit sequence and apply changes to the table and analysis.
|
|
12
|
+
* @param alignedSequenceCol Aligned sequence column
|
|
13
|
+
* @param currentDf Working table
|
|
14
|
+
* @return Widget for manual sequence alignment
|
|
15
|
+
*/
|
|
14
16
|
export function manualAlignmentWidget(alignedSequenceCol: DG.Column<string>, currentDf: DG.DataFrame): DG.Widget {
|
|
15
17
|
const sequenceInput = ui.textInput('', alignedSequenceCol.get(currentDf.currentRowIdx)!);
|
|
16
18
|
$(sequenceInput.root).addClass('pep-textinput');
|
|
17
19
|
|
|
18
20
|
const applyChangesBtn = ui.button('Apply', async () => {
|
|
21
|
+
const uh = UnitsHandler.getOrCreate(alignedSequenceCol);
|
|
19
22
|
const newSequence = sequenceInput.value;
|
|
23
|
+
const splitter = uh.getSplitter();
|
|
24
|
+
const splitSequence = splitter(newSequence);
|
|
20
25
|
const affectedRowIndex = currentDf.currentRowIdx;
|
|
21
|
-
const splitSequence = splitAlignedSequences(DG.Column.fromStrings('splitSequence', [newSequence]));
|
|
22
|
-
|
|
23
26
|
alignedSequenceCol.set(affectedRowIndex, newSequence);
|
|
24
|
-
for (
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
for (let i = 0; i < splitSequence.length; i++) {
|
|
28
|
+
const part = splitSequence[i];
|
|
29
|
+
if (currentDf.col(i.toString()) !== null)
|
|
30
|
+
currentDf.set(i.toString(), affectedRowIndex, part);
|
|
27
31
|
}
|
|
28
32
|
const temp = grok.shell.o;
|
|
29
33
|
grok.shell.o = null;
|
|
30
|
-
grok.shell.o = temp;
|
|
31
34
|
|
|
32
35
|
const peptidesController = PeptidesModel.getInstance(currentDf);
|
|
33
36
|
peptidesController.updateGrid();
|
|
37
|
+
|
|
38
|
+
setTimeout(() => {
|
|
39
|
+
grok.shell.o = temp;
|
|
40
|
+
}, 100);
|
|
34
41
|
}, 'Apply changes');
|
|
35
42
|
|
|
36
43
|
const resetBtn = ui.button(ui.iconFA('redo'),
|
|
@@ -2,46 +2,61 @@ import * as ui from 'datagrok-api/ui';
|
|
|
2
2
|
import * as DG from 'datagrok-api/dg';
|
|
3
3
|
import * as C from '../utils/constants';
|
|
4
4
|
import * as type from '../utils/types';
|
|
5
|
-
import {PeptidesModel} from '../model';
|
|
6
5
|
import {addExpandIcon, getSeparator, setGridProps} from '../utils/misc';
|
|
7
6
|
import {renderCellSelection} from '../utils/cell-renderer';
|
|
8
7
|
|
|
9
|
-
export
|
|
8
|
+
export type MutationCliffsOptions = {
|
|
9
|
+
mutationCliffs: type.MutationCliffs, mutationCliffsSelection: type.Selection, sequenceColumnName: string,
|
|
10
|
+
positionColumns: DG.Column<string>[], gridColumns: DG.GridColumnList, activityCol: DG.Column<number>,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Creates mutation cliffs widget that shows grid of sequences that form mutation cliffs pairs as well as
|
|
15
|
+
* grid of mutation cliffs pairs themselves.
|
|
16
|
+
* @param table - table with sequences.
|
|
17
|
+
* @param options - options for mutation cliffs widget.
|
|
18
|
+
* @return - mutation cliffs widget.
|
|
19
|
+
*/
|
|
20
|
+
export function mutationCliffsWidget(table: DG.DataFrame, options: MutationCliffsOptions): DG.Widget {
|
|
10
21
|
const filteredIndexes = table.filter.getSelectedIndexes();
|
|
11
|
-
const
|
|
12
|
-
const currentCell = model.mutationCliffsSelection;
|
|
13
|
-
const positions = Object.keys(currentCell);
|
|
22
|
+
const positions = Object.keys(options.mutationCliffsSelection);
|
|
14
23
|
|
|
15
|
-
if (!positions.length ||
|
|
24
|
+
if (!positions.length || options.mutationCliffs === null)
|
|
16
25
|
return new DG.Widget(ui.label('No mutations table generated'));
|
|
17
26
|
|
|
27
|
+
|
|
18
28
|
const substitutionsArray: string[] = [];
|
|
19
29
|
const deltaArray: number[] = [];
|
|
20
30
|
const substitutedToArray: string[] = [];
|
|
21
31
|
const fromIdxArray: number[] = [];
|
|
22
32
|
const toIdxArray: number[] = [];
|
|
23
|
-
const alignedSeqCol = table.getCol(
|
|
33
|
+
const alignedSeqCol = table.getCol(options.sequenceColumnName!);
|
|
24
34
|
const alignedSeqColCategories = alignedSeqCol.categories;
|
|
25
35
|
const alignedSeqColData = alignedSeqCol.getRawData();
|
|
26
|
-
const activityScaledCol = table.getCol(C.COLUMNS_NAMES.ACTIVITY);
|
|
27
|
-
const activityScaledColData =
|
|
36
|
+
// const activityScaledCol = table.getCol(C.COLUMNS_NAMES.ACTIVITY);
|
|
37
|
+
const activityScaledColData = options.activityCol.getRawData();
|
|
28
38
|
const seenIndexes = new Map<number, number[]>();
|
|
29
39
|
const uniqueSequencesBitSet = DG.BitSet.create(table.rowCount);
|
|
30
40
|
|
|
41
|
+
const positionColumns: { [colName: string]: DG.Column<string> } =
|
|
42
|
+
Object.fromEntries(options.positionColumns.map((col) => [col.name, col]));
|
|
31
43
|
for (const pos of positions) {
|
|
32
|
-
const posCol =
|
|
44
|
+
const posCol = positionColumns[pos];
|
|
33
45
|
const posColCategories = posCol.categories;
|
|
34
46
|
const posColData = posCol.getRawData();
|
|
35
47
|
|
|
36
|
-
for (const monomer of
|
|
37
|
-
const substitutionsMap =
|
|
48
|
+
for (const monomer of options.mutationCliffsSelection[pos]) {
|
|
49
|
+
const substitutionsMap = options.mutationCliffs.get(monomer)
|
|
50
|
+
?.get(pos) as Map<number, type.UTypedArray> | undefined;
|
|
38
51
|
if (typeof substitutionsMap === 'undefined')
|
|
39
52
|
continue;
|
|
40
53
|
|
|
54
|
+
|
|
41
55
|
for (const [referenceIdx, indexArray] of substitutionsMap.entries()) {
|
|
42
56
|
if (!filteredIndexes.includes(referenceIdx))
|
|
43
57
|
continue;
|
|
44
58
|
|
|
59
|
+
|
|
45
60
|
const forbiddentIndexes = seenIndexes.get(referenceIdx) ?? [];
|
|
46
61
|
const baseSequence = alignedSeqColCategories[alignedSeqColData[referenceIdx]];
|
|
47
62
|
const baseActivity = activityScaledColData[referenceIdx];
|
|
@@ -50,8 +65,11 @@ export function mutationCliffsWidget(table: DG.DataFrame, model: PeptidesModel):
|
|
|
50
65
|
if (forbiddentIndexes.includes(subIdx) || !filteredIndexes.includes(subIdx))
|
|
51
66
|
continue;
|
|
52
67
|
|
|
68
|
+
|
|
53
69
|
if (!seenIndexes.has(subIdx))
|
|
54
70
|
seenIndexes.set(subIdx, []);
|
|
71
|
+
|
|
72
|
+
|
|
55
73
|
const subSeq = alignedSeqColCategories[alignedSeqColData[subIdx]];
|
|
56
74
|
|
|
57
75
|
seenIndexes.get(subIdx)!.push(referenceIdx);
|
|
@@ -70,6 +88,7 @@ export function mutationCliffsWidget(table: DG.DataFrame, model: PeptidesModel):
|
|
|
70
88
|
if (substitutionsArray.length === 0)
|
|
71
89
|
return new DG.Widget(ui.label('No mutations table generated'));
|
|
72
90
|
|
|
91
|
+
|
|
73
92
|
const substCol = DG.Column.fromStrings('Mutation', substitutionsArray);
|
|
74
93
|
const activityDeltaCol = DG.Column.fromList('double', 'Delta', deltaArray);
|
|
75
94
|
const hiddenSubstToAarCol = DG.Column.fromStrings('~to', substitutedToArray);
|
|
@@ -102,11 +121,16 @@ export function mutationCliffsWidget(table: DG.DataFrame, model: PeptidesModel):
|
|
|
102
121
|
const rowIdx = gridCell.tableRowIndex;
|
|
103
122
|
if (!keyPress)
|
|
104
123
|
return;
|
|
124
|
+
|
|
125
|
+
|
|
105
126
|
if (rowIdx === null)
|
|
106
127
|
return;
|
|
128
|
+
|
|
129
|
+
|
|
107
130
|
if (lastSelectedIndex !== null)
|
|
108
131
|
pairsSelectedIndexes.splice(pairsSelectedIndexes.indexOf(lastSelectedIndex), 1);
|
|
109
132
|
|
|
133
|
+
|
|
110
134
|
if (!pairsSelectedIndexes.includes(rowIdx)) {
|
|
111
135
|
pairsSelectedIndexes.push(rowIdx);
|
|
112
136
|
pairsGrid.invalidate();
|
|
@@ -123,6 +147,7 @@ export function mutationCliffsWidget(table: DG.DataFrame, model: PeptidesModel):
|
|
|
123
147
|
if (!gridCell || gridCell.tableRowIndex === null)
|
|
124
148
|
return;
|
|
125
149
|
|
|
150
|
+
|
|
126
151
|
const rowIdx = gridCell.tableRowIndex;
|
|
127
152
|
if (!event.shiftKey) {
|
|
128
153
|
pairsSelectedIndexes.length = 0;
|
|
@@ -141,16 +166,16 @@ export function mutationCliffsWidget(table: DG.DataFrame, model: PeptidesModel):
|
|
|
141
166
|
if (gcArgs.cell.tableColumn?.name !== substCol.name || !pairsSelectedIndexes.includes(gcArgs.cell.tableRowIndex!))
|
|
142
167
|
return;
|
|
143
168
|
|
|
169
|
+
|
|
144
170
|
renderCellSelection(gcArgs.g, gcArgs.bounds);
|
|
145
171
|
});
|
|
146
172
|
|
|
147
|
-
const
|
|
148
|
-
const originalGridColCount = gridCols.length;
|
|
149
|
-
const positionColumns = model.positionColumns.toArray().map((col) => col.name);
|
|
173
|
+
const originalGridColCount = options.gridColumns.length;
|
|
150
174
|
const columnNames: string[] = [];
|
|
151
175
|
for (let colIdx = 1; colIdx < originalGridColCount; colIdx++) {
|
|
152
|
-
const gridCol =
|
|
153
|
-
if (gridCol?.name ===
|
|
176
|
+
const gridCol = options.gridColumns.byIndex(colIdx);
|
|
177
|
+
if (gridCol?.name === options.sequenceColumnName ||
|
|
178
|
+
(gridCol?.visible === true && !Object.hasOwn(positionColumns, gridCol.name)))
|
|
154
179
|
columnNames.push(gridCol!.name);
|
|
155
180
|
}
|
|
156
181
|
|
|
@@ -176,5 +201,6 @@ export function mutationCliffsWidget(table: DG.DataFrame, model: PeptidesModel):
|
|
|
176
201
|
addExpandIcon(pairsGrid);
|
|
177
202
|
addExpandIcon(uniqueSequencesGrid);
|
|
178
203
|
|
|
179
|
-
return new DG.Widget(ui.divV([aminoToInput.root, pairsGrid.root, uniqueSequencesGrid.root],
|
|
204
|
+
return new DG.Widget(ui.divV([aminoToInput.root, pairsGrid.root, uniqueSequencesGrid.root],
|
|
205
|
+
{style: {width: '100%'}}));
|
|
180
206
|
}
|