@datagrok/peptides 1.16.0 → 1.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +17 -6
- package/CHANGELOG.md +33 -8
- package/README.md +12 -7
- package/dist/196.js +2 -3
- package/dist/23.js +2 -0
- package/dist/282.js +2 -0
- package/dist/361.js +2 -2
- package/dist/40.js +2 -0
- package/dist/436.js +2 -2
- package/dist/65.js +2 -0
- package/dist/704.js +2 -0
- package/dist/package-test.js +2 -3
- package/dist/package.js +2 -3
- package/package.json +13 -13
- package/setup-unlink-clean.cmd +6 -0
- package/setup.cmd +2 -2
- package/src/demo/fasta.ts +8 -2
- package/src/model.ts +857 -560
- package/src/package-test.ts +1 -3
- package/src/package.ts +28 -50
- package/src/tests/benchmarks.ts +31 -11
- package/src/tests/core.ts +11 -6
- package/src/tests/misc.ts +6 -6
- package/src/tests/model.ts +80 -45
- package/src/tests/table-view.ts +49 -39
- package/src/tests/utils.ts +0 -76
- package/src/tests/viewers.ts +30 -12
- package/src/tests/widgets.ts +30 -11
- package/src/utils/algorithms.ts +115 -38
- package/src/utils/cell-renderer.ts +217 -96
- package/src/utils/constants.ts +37 -7
- package/src/utils/misc.ts +285 -30
- package/src/utils/parallel-mutation-cliffs.ts +18 -15
- package/src/utils/statistics.ts +70 -14
- package/src/utils/tooltips.ts +46 -25
- package/src/utils/types.ts +29 -26
- package/src/utils/worker-creator.ts +5 -0
- package/src/viewers/logo-summary.ts +597 -135
- package/src/viewers/sar-viewer.ts +946 -249
- package/src/widgets/distribution.ts +291 -196
- package/src/widgets/manual-alignment.ts +18 -11
- package/src/widgets/mutation-cliffs.ts +45 -21
- package/src/widgets/peptides.ts +86 -91
- package/src/widgets/selection.ts +56 -22
- package/src/widgets/settings.ts +94 -44
- package/src/workers/dimensionality-reducer.ts +5 -6
- package/src/workers/mutation-cliffs-worker.ts +3 -16
- package/dist/196.js.LICENSE.txt +0 -51
- package/dist/209.js +0 -2
- package/dist/381.js +0 -2
- package/dist/694.js +0 -2
- package/dist/831.js +0 -2
- package/dist/868.js +0 -2
- package/dist/package-test.js.LICENSE.txt +0 -51
- package/dist/package.js.LICENSE.txt +0 -51
- package/src/tests/peptide-space-test.ts +0 -48
- package/src/tests/test-data.ts +0 -649
- package/src/utils/molecular-measure.ts +0 -174
- package/src/utils/peptide-similarity-space.ts +0 -216
- package/src/viewers/peptide-space-viewer.ts +0 -150
|
@@ -1,234 +1,121 @@
|
|
|
1
1
|
import * as ui from 'datagrok-api/ui';
|
|
2
2
|
import * as DG from 'datagrok-api/dg';
|
|
3
|
-
|
|
4
3
|
import {StringDictionary} from '@datagrok-libraries/utils/src/type-declarations';
|
|
4
|
+
|
|
5
5
|
import $ from 'cash-dom';
|
|
6
6
|
|
|
7
7
|
import * as C from '../utils/constants';
|
|
8
|
-
import {getAggregatedColumnValues, getStats,
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
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';
|
|
11
12
|
import BitArray from '@datagrok-libraries/utils/src/bit-array';
|
|
13
|
+
import {Selection} from '../utils/types';
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (!table.selection.anyTrue)
|
|
18
|
-
return new DG.Widget(ui.divText('No distribution'));
|
|
19
|
-
|
|
20
|
-
const activityCol = table.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
21
|
-
const activityColData = activityCol.getRawData();
|
|
22
|
-
const rowCount = activityCol.length;
|
|
23
|
-
const selectionObject = model.invariantMapSelection;
|
|
24
|
-
const clustersColName = model.settings.clustersColumnName;
|
|
25
|
-
let clustersProcessedObject: string[] = [];
|
|
26
|
-
if (clustersColName)
|
|
27
|
-
clustersProcessedObject = Object.values(model.clusterSelection).flat();
|
|
28
|
-
|
|
29
|
-
const positions = Object.keys(selectionObject);
|
|
30
|
-
let monomerStr = allConst;
|
|
31
|
-
let otherStr = '';
|
|
32
|
-
|
|
33
|
-
const updateDistributionHost = (): void => {
|
|
34
|
-
model.splitByPos = splitByPosition.value!;
|
|
35
|
-
model.splitByMonomer = splitByMonomer.value!;
|
|
36
|
-
const res: HTMLDivElement[] = [];
|
|
37
|
-
if (splitByPosition.value && splitByMonomer.value) {
|
|
38
|
-
otherStr = otherConst;
|
|
39
|
-
for (const position of positions) {
|
|
40
|
-
const monomerList = selectionObject[position];
|
|
41
|
-
if (monomerList.length === 0)
|
|
42
|
-
continue;
|
|
43
|
-
|
|
44
|
-
const posCol = table.getCol(position);
|
|
45
|
-
const posColCategories = posCol.categories;
|
|
46
|
-
const posColData = posCol.getRawData();
|
|
47
|
-
|
|
48
|
-
for (const monomer of monomerList) {
|
|
49
|
-
const labels = getDistributionLegend(`${position} : ${monomer}`, otherStr);
|
|
50
|
-
|
|
51
|
-
const monomerCategoryIndex = posColCategories.indexOf(monomer);
|
|
52
|
-
const mask = DG.BitSet.create(rowCount, (i) => posColData[i] === monomerCategoryIndex);
|
|
53
|
-
const distributionTable = DG.DataFrame.fromColumns(
|
|
54
|
-
[activityCol, DG.Column.fromBitSet(C.COLUMNS_NAMES.SPLIT_COL, mask)]);
|
|
55
|
-
const hist = getActivityDistribution(prepareTableForHistogram(distributionTable));
|
|
56
|
-
|
|
57
|
-
const stats = model.monomerPositionStats[position]![monomer]!;
|
|
58
|
-
const tableMap = getStatsTableMap(stats);
|
|
59
|
-
|
|
60
|
-
const aggregatedColMap = getAggregatedColumnValues(model.df, model.settings.columns!, {filterDf: true, mask});
|
|
61
|
-
|
|
62
|
-
const resultMap = {...tableMap, ...aggregatedColMap};
|
|
63
|
-
const distributionRoot = getStatsSummary(labels, hist, resultMap);
|
|
64
|
-
$(distributionRoot).addClass('d4-flex-col');
|
|
65
|
-
|
|
66
|
-
res.push(distributionRoot);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
} else if (splitByPosition.value) {
|
|
70
|
-
otherStr = otherConst;
|
|
71
|
-
for (const position of positions) {
|
|
72
|
-
const monomerList = selectionObject[position];
|
|
73
|
-
if (monomerList.length === 0)
|
|
74
|
-
continue;
|
|
75
|
-
|
|
76
|
-
monomerStr = `${position}: {${monomerList.join(', ')}}`;
|
|
77
|
-
const labels = getDistributionLegend(monomerStr, otherStr);
|
|
78
|
-
|
|
79
|
-
const posCol = table.getCol(position);
|
|
80
|
-
const posColCategories = posCol.categories;
|
|
81
|
-
const posColData = posCol.getRawData();
|
|
82
|
-
const monomerIndexesList = monomerList.map((monomer) => posColCategories.indexOf(monomer));
|
|
83
|
-
const mask = DG.BitSet.create(rowCount, (i) => monomerIndexesList.includes(posColData[i]));
|
|
84
|
-
const splitCol = DG.Column.fromBitSet(C.COLUMNS_NAMES.SPLIT_COL, mask);
|
|
85
|
-
|
|
86
|
-
const aggregatedColMap = getAggregatedColumnValues(model.df, model.settings.columns!, {filterDf: true, mask});
|
|
87
|
-
|
|
88
|
-
const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
|
|
89
|
-
const hist = getActivityDistribution(prepareTableForHistogram(distributionTable));
|
|
15
|
+
export type DistributionItemOptions = {
|
|
16
|
+
peptideSelection: DG.BitSet, columns: AggregationColumns, clusterColName?: string,
|
|
17
|
+
activityCol: DG.Column<number>, monomerPositionSelection: Selection, clusterSelection: Selection,
|
|
18
|
+
};
|
|
90
19
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
20
|
+
export enum DISTRIBUTION_CATEGORIES_KEYS {
|
|
21
|
+
SEPARATE_MONOMERS = 'separateMonomers',
|
|
22
|
+
SEPARATE_POSITIONS = 'separatePositions',
|
|
23
|
+
SEPARATE_CLUSTERS = 'separateClusters',
|
|
24
|
+
}
|
|
94
25
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
26
|
+
const general = 'general';
|
|
27
|
+
const key2category = (key: DISTRIBUTION_CATEGORIES_KEYS | typeof general): string => {
|
|
28
|
+
if (key === general)
|
|
29
|
+
return 'General';
|
|
98
30
|
|
|
99
|
-
res.push(distributionRoot);
|
|
100
|
-
}
|
|
101
|
-
} else if (splitByMonomer.value) {
|
|
102
|
-
const reversedSelectionObject: {[monomer: string]: string[]} = {};
|
|
103
|
-
const monomers = [];
|
|
104
|
-
for (const position of positions) {
|
|
105
|
-
for (const monomer of selectionObject[position]) {
|
|
106
|
-
if (!reversedSelectionObject.hasOwnProperty(monomer)) {
|
|
107
|
-
reversedSelectionObject[monomer] = [position];
|
|
108
|
-
monomers.push(monomer);
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
if (!reversedSelectionObject[monomer].includes(position))
|
|
112
|
-
reversedSelectionObject[monomer].push(position);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
31
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const posColList = posList.map((pos) => table.getCol(pos));
|
|
120
|
-
const posColCategoriesList = posColList.map((posCol) => posCol.categories);
|
|
121
|
-
const posColDataList = posColList.map((posCol) => posCol.getRawData());
|
|
122
|
-
const monomerCategoryIndexList = posColCategoriesList.map((posColCategories) => posColCategories.indexOf(monomer));
|
|
32
|
+
return key.substring(8);
|
|
33
|
+
};
|
|
34
|
+
export type PeptideViewer = SARViewer | LogoSummaryTable;
|
|
123
35
|
|
|
124
|
-
monomerStr = `${monomer}: {${posList.join(', ')}}`;
|
|
125
|
-
const labels = getDistributionLegend(monomerStr, otherStr);
|
|
126
36
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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');
|
|
130
47
|
|
|
131
|
-
const splitCol = DG.Column.fromBitSet(C.COLUMNS_NAMES.SPLIT_COL, mask);
|
|
132
|
-
const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
|
|
133
|
-
const hist = getActivityDistribution(prepareTableForHistogram(distributionTable));
|
|
134
48
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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;
|
|
138
55
|
|
|
139
|
-
const resultMap: {[key: string]: any} = {...tableMap, ...aggregatedColMap};
|
|
140
|
-
const distributionRoot = getStatsSummary(labels, hist, resultMap);
|
|
141
|
-
$(distributionRoot).addClass('d4-flex-col');
|
|
142
56
|
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
} else {
|
|
146
|
-
if (!table.selection.anyTrue)
|
|
147
|
-
res.push(ui.divText('No distribution'));
|
|
148
|
-
else {
|
|
149
|
-
otherStr = '';
|
|
150
|
-
if (Object.values(selectionObject).some((selectedAar) => selectedAar.length !== 0) ||
|
|
151
|
-
clustersProcessedObject.length !== 0) {
|
|
152
|
-
monomerStr = '';
|
|
153
|
-
for (const position of positions) {
|
|
154
|
-
const monomerList = selectionObject[position];
|
|
155
|
-
if (monomerList.length !== 0)
|
|
156
|
-
monomerStr += `${position}: {${monomerList.join(', ')}}; `;
|
|
157
|
-
}
|
|
158
|
-
if (clustersProcessedObject.length !== 0)
|
|
159
|
-
monomerStr += `Clusters: ${clustersProcessedObject.join(', ')}`;
|
|
160
|
-
otherStr = otherConst;
|
|
161
|
-
}
|
|
162
|
-
const labels = getDistributionLegend(monomerStr, otherStr);
|
|
163
|
-
|
|
164
|
-
const distributionTable = DG.DataFrame.fromColumns([activityCol, DG.Column.fromBitSet(C.COLUMNS_NAMES.SPLIT_COL, table.selection)]);
|
|
165
|
-
const hist = getActivityDistribution(prepareTableForHistogram(distributionTable));
|
|
166
|
-
const bitArray = BitArray.fromString(table.selection.toBinaryString());
|
|
167
|
-
const mask = DG.BitSet.create(rowCount,
|
|
168
|
-
bitArray.allFalse || bitArray.allTrue ? (_): boolean => true : (i): boolean => bitArray.getBit(i));
|
|
169
|
-
const aggregatedColMap = getAggregatedColumnValues(model.df, model.settings.columns!, {filterDf: true, mask});
|
|
170
|
-
const stats = bitArray.allFalse || bitArray.allTrue ?
|
|
171
|
-
{count: rowCount, pValue: null, meanDifference: 0, ratio: 1, mask: bitArray,
|
|
172
|
-
mean: activityCol.stats.avg} :
|
|
173
|
-
getStats(activityColData, bitArray);
|
|
174
|
-
const tableMap = getStatsTableMap(stats);
|
|
175
|
-
const resultMap: {[key: string]: any} = {...tableMap, ...aggregatedColMap};
|
|
176
|
-
const distributionRoot = getStatsSummary(labels, hist, resultMap);
|
|
177
|
-
$(distributionRoot).addClass('d4-flex-col');
|
|
178
|
-
|
|
179
|
-
res.push(distributionRoot);
|
|
180
|
-
}
|
|
57
|
+
distributionCategories.push(getDistributionCategory(tag, table, options));
|
|
181
58
|
}
|
|
182
|
-
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
const setDefaultProperties = (input: DG.InputBase): void => {
|
|
186
|
-
input.enabled = !model.isMutationCliffsSelectionEmpty;
|
|
187
|
-
$(input.root).find('.ui-input-editor').css('margin', '0px');
|
|
188
|
-
$(input.root).find('.ui-input-description').css('padding', '0px').css('padding-left', '5px');
|
|
189
|
-
$(input.captionLabel).addClass('ui-label-right');
|
|
59
|
+
return (distributionCategories.length === 1) ? distributionCategories[0] : ui.div(distributionCategories);
|
|
190
60
|
};
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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;
|
|
197
81
|
}
|
|
198
82
|
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
$(splitByPosition.root).css('margin-right', '10px');
|
|
203
|
-
const splitByMonomer = ui.boolInput('Split by monomer', defaultValueMonomer, updateDistributionHost);
|
|
204
|
-
splitByMonomer.setTooltip('Constructs distribution for each monomer separately');
|
|
205
|
-
setDefaultProperties(splitByMonomer);
|
|
206
|
-
|
|
207
|
-
const controlsHost = ui.divH([splitByPosition.root, splitByMonomer.root]);
|
|
208
|
-
const distributionHost = ui.div([], 'd4-flex-wrap');
|
|
209
|
-
splitByMonomer.fireChanged();
|
|
210
|
-
|
|
211
|
-
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]);
|
|
212
86
|
}
|
|
213
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Builds activity distribution histogram
|
|
90
|
+
* @param table - Dataframe with peptides
|
|
91
|
+
* @param isTooltip - Is histogram for tooltip
|
|
92
|
+
* @return - Histogram viewer
|
|
93
|
+
*/
|
|
214
94
|
export function getActivityDistribution(table: DG.DataFrame, isTooltip: boolean = false,
|
|
215
95
|
): DG.Viewer<DG.IHistogramLookSettings> {
|
|
216
96
|
const hist = table.plot.histogram({
|
|
217
97
|
filteringEnabled: false,
|
|
218
|
-
valueColumnName: C.COLUMNS_NAMES.
|
|
98
|
+
valueColumnName: C.COLUMNS_NAMES.ACTIVITY,
|
|
219
99
|
splitColumnName: C.COLUMNS_NAMES.SPLIT_COL,
|
|
220
100
|
legendVisibility: 'Never',
|
|
221
101
|
showXAxis: true,
|
|
222
102
|
showColumnSelector: false,
|
|
223
103
|
showRangeSlider: false,
|
|
224
|
-
showBinSelector:
|
|
104
|
+
showBinSelector: false,
|
|
225
105
|
backColor: isTooltip ? '#fdffe5' : '#fffff',
|
|
226
106
|
}) as DG.Viewer<DG.IHistogramLookSettings>;
|
|
227
107
|
hist.root.style.width = 'auto';
|
|
228
108
|
return hist;
|
|
229
109
|
}
|
|
230
110
|
|
|
231
|
-
|
|
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 {
|
|
232
119
|
options.fractionDigits ??= 3;
|
|
233
120
|
const tableMap: StringDictionary = {
|
|
234
121
|
'Count': `${stats.count} (${stats.ratio.toFixed(options.fractionDigits)}%)`,
|
|
@@ -237,11 +124,219 @@ export function getStatsTableMap(stats: Stats, options: {fractionDigits?: number
|
|
|
237
124
|
};
|
|
238
125
|
if (stats.pValue !== null)
|
|
239
126
|
tableMap['p-value'] = stats.pValue < 0.01 ? '<0.01' : stats.pValue.toFixed(options.fractionDigits);
|
|
127
|
+
|
|
128
|
+
|
|
240
129
|
return tableMap;
|
|
241
130
|
}
|
|
242
131
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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;
|
|
247
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'),
|