@datagrok/peptides 1.9.2 → 1.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +81 -0
- package/dist/209.js +2 -0
- package/dist/521.js +2 -0
- package/dist/535.js +2 -0
- package/dist/729.js +2 -0
- package/dist/959.js +2 -0
- package/dist/package-test.js +2 -2
- package/dist/package.js +2 -2
- package/package.json +13 -11
- package/setup-unlink-clean.sh +0 -0
- package/src/model.ts +302 -232
- package/src/package-test.ts +2 -1
- package/src/package.ts +14 -16
- package/src/tests/algorithms.ts +4 -1
- package/src/tests/core.ts +100 -14
- package/src/tests/model.ts +16 -14
- package/src/tests/peptide-space-test.ts +7 -7
- package/src/tests/table-view.ts +63 -54
- package/src/tests/utils.ts +1 -1
- package/src/tests/viewers.ts +40 -6
- package/src/tests/widgets.ts +100 -18
- package/src/utils/cell-renderer.ts +38 -20
- package/src/utils/constants.ts +3 -0
- package/src/utils/misc.ts +44 -12
- package/src/utils/peptide-similarity-space.ts +3 -4
- package/src/utils/statistics.ts +2 -2
- package/src/utils/types.ts +3 -1
- package/src/viewers/logo-summary.ts +132 -140
- package/src/viewers/peptide-space-viewer.ts +2 -1
- package/src/viewers/sar-viewer.ts +46 -39
- package/src/widgets/distribution.ts +17 -17
- package/src/widgets/mutation-cliffs.ts +91 -19
- package/src/widgets/peptides.ts +12 -13
- package/src/widgets/settings.ts +47 -25
- package/src/widgets/similarity.ts +39 -0
- package/dist/168.js +0 -2
- package/dist/478.js +0 -2
- package/dist/802.js +0 -2
- package/dist/951.js +0 -2
|
@@ -7,7 +7,7 @@ import $ from 'cash-dom';
|
|
|
7
7
|
import * as C from '../utils/constants';
|
|
8
8
|
import {getStats, Stats} from '../utils/statistics';
|
|
9
9
|
import {PeptidesModel} from '../model';
|
|
10
|
-
import {getStatsSummary} from '../utils/misc';
|
|
10
|
+
import {getStatsSummary, prepareTableForHistogram} from '../utils/misc';
|
|
11
11
|
import BitArray from '@datagrok-libraries/utils/src/bit-array';
|
|
12
12
|
|
|
13
13
|
const allConst = 'All';
|
|
@@ -17,7 +17,7 @@ export function getDistributionWidget(table: DG.DataFrame, model: PeptidesModel)
|
|
|
17
17
|
const activityCol = table.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
18
18
|
const activityColData = activityCol.getRawData();
|
|
19
19
|
const rowCount = activityCol.length;
|
|
20
|
-
const selectionObject = model.
|
|
20
|
+
const selectionObject = model.mutationCliffsSelection;
|
|
21
21
|
const clustersColName = model.settings.clustersColumnName;
|
|
22
22
|
let clustersProcessedObject: string[] = [];
|
|
23
23
|
if (clustersColName)
|
|
@@ -49,12 +49,12 @@ export function getDistributionWidget(table: DG.DataFrame, model: PeptidesModel)
|
|
|
49
49
|
const mask = DG.BitSet.create(rowCount, (i) => posColData[i] === aarCategoryIndex);
|
|
50
50
|
const distributionTable = DG.DataFrame.fromColumns(
|
|
51
51
|
[activityCol, DG.Column.fromBitSet(C.COLUMNS_NAMES.SPLIT_COL, mask)]);
|
|
52
|
-
const hist = getActivityDistribution(distributionTable);
|
|
52
|
+
const hist = getActivityDistribution(prepareTableForHistogram(distributionTable));
|
|
53
53
|
|
|
54
54
|
const stats = model.monomerPositionStats[position][aar];
|
|
55
|
-
const tableMap = getStatsTableMap(stats
|
|
55
|
+
const tableMap = getStatsTableMap(stats);
|
|
56
56
|
|
|
57
|
-
const aggregatedColMap = model.getAggregatedColumnValues({filterDf: true, mask
|
|
57
|
+
const aggregatedColMap = model.getAggregatedColumnValues({filterDf: true, mask});
|
|
58
58
|
|
|
59
59
|
const resultMap = {...tableMap, ...aggregatedColMap};
|
|
60
60
|
const distributionRoot = getStatsSummary(labels, hist, resultMap);
|
|
@@ -80,14 +80,14 @@ export function getDistributionWidget(table: DG.DataFrame, model: PeptidesModel)
|
|
|
80
80
|
const mask = DG.BitSet.create(rowCount, (i) => aarIndexesList.includes(posColData[i]));
|
|
81
81
|
const splitCol = DG.Column.fromBitSet(C.COLUMNS_NAMES.SPLIT_COL, mask);
|
|
82
82
|
|
|
83
|
-
const aggregatedColMap = model.getAggregatedColumnValues({filterDf: true, mask
|
|
83
|
+
const aggregatedColMap = model.getAggregatedColumnValues({filterDf: true, mask});
|
|
84
84
|
|
|
85
85
|
const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
|
|
86
|
-
const hist = getActivityDistribution(distributionTable);
|
|
86
|
+
const hist = getActivityDistribution(prepareTableForHistogram(distributionTable));
|
|
87
87
|
|
|
88
88
|
const bitArray = BitArray.fromUint32Array(rowCount, splitCol.getRawData() as Uint32Array);
|
|
89
89
|
const stats = getStats(activityColData, bitArray);
|
|
90
|
-
const tableMap = getStatsTableMap(stats
|
|
90
|
+
const tableMap = getStatsTableMap(stats);
|
|
91
91
|
|
|
92
92
|
const resultMap = {...tableMap, ...aggregatedColMap};
|
|
93
93
|
const distributionRoot = getStatsSummary(labels, hist, resultMap);
|
|
@@ -123,15 +123,15 @@ export function getDistributionWidget(table: DG.DataFrame, model: PeptidesModel)
|
|
|
123
123
|
|
|
124
124
|
const mask = DG.BitSet.create(rowCount,
|
|
125
125
|
(i) => posColDataList.some((posColData, j) => posColData[i] === aarCategoryIndexList[j]));
|
|
126
|
-
const aggregatedColMap = model.getAggregatedColumnValues({filterDf: true, mask
|
|
126
|
+
const aggregatedColMap = model.getAggregatedColumnValues({filterDf: true, mask});
|
|
127
127
|
|
|
128
128
|
const splitCol = DG.Column.fromBitSet(C.COLUMNS_NAMES.SPLIT_COL, mask);
|
|
129
129
|
const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
|
|
130
|
-
const hist = getActivityDistribution(distributionTable);
|
|
130
|
+
const hist = getActivityDistribution(prepareTableForHistogram(distributionTable));
|
|
131
131
|
|
|
132
132
|
const bitArray = BitArray.fromUint32Array(rowCount, splitCol.getRawData() as Uint32Array);
|
|
133
133
|
const stats = getStats(activityColData, bitArray);
|
|
134
|
-
const tableMap = getStatsTableMap(stats
|
|
134
|
+
const tableMap = getStatsTableMap(stats);
|
|
135
135
|
|
|
136
136
|
const resultMap: {[key: string]: any} = {...tableMap, ...aggregatedColMap};
|
|
137
137
|
const distributionRoot = getStatsSummary(labels, hist, resultMap);
|
|
@@ -160,14 +160,15 @@ export function getDistributionWidget(table: DG.DataFrame, model: PeptidesModel)
|
|
|
160
160
|
const labels = getDistributionLegend(aarStr, otherStr);
|
|
161
161
|
|
|
162
162
|
const distributionTable = DG.DataFrame.fromColumns([activityCol, splitCol]);
|
|
163
|
-
|
|
163
|
+
|
|
164
|
+
const hist = getActivityDistribution(prepareTableForHistogram(distributionTable));
|
|
164
165
|
|
|
165
166
|
const bitArray = BitArray.fromUint32Array(rowCount, splitCol.getRawData() as Uint32Array);
|
|
166
167
|
const mask = DG.BitSet.create(rowCount, (i) => bitArray.getBit(i));
|
|
167
|
-
const aggregatedColMap = model.getAggregatedColumnValues({filterDf: true, mask
|
|
168
|
+
const aggregatedColMap = model.getAggregatedColumnValues({filterDf: true, mask});
|
|
168
169
|
|
|
169
170
|
const stats = getStats(activityColData, bitArray);
|
|
170
|
-
const tableMap = getStatsTableMap(stats
|
|
171
|
+
const tableMap = getStatsTableMap(stats);
|
|
171
172
|
|
|
172
173
|
const resultMap: {[key: string]: any} = {...tableMap, ...aggregatedColMap};
|
|
173
174
|
const distributionRoot = getStatsSummary(labels, hist, resultMap);
|
|
@@ -225,10 +226,9 @@ export function getActivityDistribution(table: DG.DataFrame, isTooltip: boolean
|
|
|
225
226
|
}
|
|
226
227
|
|
|
227
228
|
export function getStatsTableMap(stats: Stats, options: {fractionDigits?: number} = {}): StringDictionary {
|
|
229
|
+
options.fractionDigits ??= 3;
|
|
228
230
|
const tableMap = {
|
|
229
|
-
'
|
|
230
|
-
'Count': stats.count.toString(),
|
|
231
|
-
'Ratio': stats.ratio.toFixed(options.fractionDigits),
|
|
231
|
+
'Count': `${stats.count} (${stats.ratio.toFixed(options.fractionDigits)}%)`,
|
|
232
232
|
'p-value': stats.pValue < 0.01 ? '<0.01' : stats.pValue.toFixed(options.fractionDigits),
|
|
233
233
|
'Mean difference': stats.meanDifference.toFixed(options.fractionDigits),
|
|
234
234
|
};
|
|
@@ -4,11 +4,12 @@ import * as C from '../utils/constants';
|
|
|
4
4
|
import * as type from '../utils/types';
|
|
5
5
|
import {PeptidesModel} from '../model';
|
|
6
6
|
import {getSeparator} from '../utils/misc';
|
|
7
|
+
import {renderCellSelection} from '../utils/cell-renderer';
|
|
7
8
|
|
|
8
9
|
export function mutationCliffsWidget(table: DG.DataFrame, model: PeptidesModel): DG.Widget {
|
|
9
10
|
const filteredIndexes = table.filter.getSelectedIndexes();
|
|
10
11
|
const substInfo = model.mutationCliffs;
|
|
11
|
-
const currentCell = model.
|
|
12
|
+
const currentCell = model.mutationCliffsSelection;
|
|
12
13
|
const positions = Object.keys(currentCell);
|
|
13
14
|
|
|
14
15
|
if (!positions.length || substInfo === null)
|
|
@@ -17,12 +18,15 @@ export function mutationCliffsWidget(table: DG.DataFrame, model: PeptidesModel):
|
|
|
17
18
|
const substitutionsArray: string[] = [];
|
|
18
19
|
const deltaArray: number[] = [];
|
|
19
20
|
const substitutedToArray: string[] = [];
|
|
21
|
+
const fromIdxArray: number[] = [];
|
|
22
|
+
const toIdxArray: number[] = [];
|
|
20
23
|
const alignedSeqCol = table.getCol(model.settings.sequenceColumnName!);
|
|
21
24
|
const alignedSeqColCategories = alignedSeqCol.categories;
|
|
22
25
|
const alignedSeqColData = alignedSeqCol.getRawData();
|
|
23
26
|
const activityScaledCol = table.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED);
|
|
24
27
|
const activityScaledColData = activityScaledCol.getRawData();
|
|
25
28
|
const seenIndexes = new Map<number, number[]>();
|
|
29
|
+
const uniqueSequencesBitSet = DG.BitSet.create(table.rowCount);
|
|
26
30
|
|
|
27
31
|
for (const pos of positions) {
|
|
28
32
|
const posCol = table.getCol(pos);
|
|
@@ -39,9 +43,7 @@ export function mutationCliffsWidget(table: DG.DataFrame, model: PeptidesModel):
|
|
|
39
43
|
continue;
|
|
40
44
|
|
|
41
45
|
const forbiddentIndexes = seenIndexes.get(referenceIdx) ?? [];
|
|
42
|
-
// const baseSequence = alignedSeqCol.get(referenceIdx);
|
|
43
46
|
const baseSequence = alignedSeqColCategories[alignedSeqColData[referenceIdx]];
|
|
44
|
-
// const baseActivity = activityScaledCol.get(referenceIdx);
|
|
45
47
|
const baseActivity = activityScaledColData[referenceIdx];
|
|
46
48
|
|
|
47
49
|
for (const subIdx of indexArray) {
|
|
@@ -56,6 +58,10 @@ export function mutationCliffsWidget(table: DG.DataFrame, model: PeptidesModel):
|
|
|
56
58
|
substitutionsArray.push(`${baseSequence}#${subSeq}`);
|
|
57
59
|
deltaArray.push(baseActivity - activityScaledColData[subIdx]);
|
|
58
60
|
substitutedToArray.push(posColCategories[posColData[subIdx]]);
|
|
61
|
+
fromIdxArray.push(referenceIdx);
|
|
62
|
+
toIdxArray.push(subIdx);
|
|
63
|
+
uniqueSequencesBitSet.set(referenceIdx, true);
|
|
64
|
+
uniqueSequencesBitSet.set(subIdx, true);
|
|
59
65
|
}
|
|
60
66
|
}
|
|
61
67
|
}
|
|
@@ -65,27 +71,93 @@ export function mutationCliffsWidget(table: DG.DataFrame, model: PeptidesModel):
|
|
|
65
71
|
return new DG.Widget(ui.label('No mutations table generated'));
|
|
66
72
|
|
|
67
73
|
const substCol = DG.Column.fromStrings('Mutation', substitutionsArray);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
const hiddenSubstToAarCol = DG.Column.fromStrings(toColName, substitutedToArray);
|
|
74
|
-
const substTable =
|
|
75
|
-
DG.DataFrame.fromColumns([substCol, DG.Column.fromList('double', 'Delta', deltaArray), hiddenSubstToAarCol]);
|
|
74
|
+
const activityDeltaCol = DG.Column.fromList('double', 'Delta', deltaArray);
|
|
75
|
+
const hiddenSubstToAarCol = DG.Column.fromStrings('~to', substitutedToArray);
|
|
76
|
+
const toIdxCol = DG.Column.fromList(DG.COLUMN_TYPE.INT, '~toIdx', toIdxArray);
|
|
77
|
+
const fromIdxCol = DG.Column.fromList(DG.COLUMN_TYPE.INT, '~fromIdx', fromIdxArray);
|
|
78
|
+
const pairsTable = DG.DataFrame.fromColumns([substCol, activityDeltaCol, hiddenSubstToAarCol, toIdxCol, fromIdxCol]);
|
|
76
79
|
|
|
77
80
|
const aminoToInput = ui.stringInput('Mutated to:', '', () => {
|
|
78
81
|
const substitutedToAar = aminoToInput.stringValue;
|
|
79
82
|
if (substitutedToAar !== '')
|
|
80
|
-
|
|
83
|
+
pairsTable.filter.init((idx) => hiddenSubstToAarCol.get(idx) === substitutedToAar);
|
|
81
84
|
else
|
|
82
|
-
|
|
85
|
+
pairsTable.filter.setAll(true);
|
|
86
|
+
});
|
|
87
|
+
aminoToInput.setTooltip('Monomer to which the mutation was made');
|
|
88
|
+
|
|
89
|
+
const pairsGrid = pairsTable.plot.grid();
|
|
90
|
+
pairsGrid.props.allowEdit = false;
|
|
91
|
+
pairsGrid.props.allowRowSelection = false;
|
|
92
|
+
pairsGrid.props.allowBlockSelection = false;
|
|
93
|
+
pairsGrid.props.allowColSelection = false;
|
|
94
|
+
pairsGrid.root.style.width = '100%';
|
|
95
|
+
pairsGrid.root.style.height = '150px';
|
|
96
|
+
substCol.semType = C.SEM_TYPES.MACROMOLECULE_DIFFERENCE;
|
|
97
|
+
substCol.tags[C.TAGS.SEPARATOR] = getSeparator(alignedSeqCol);
|
|
98
|
+
substCol.tags[DG.TAGS.UNITS] = alignedSeqCol.tags[DG.TAGS.UNITS];
|
|
99
|
+
substCol.tags[DG.TAGS.CELL_RENDERER] = 'MacromoleculeDifference';
|
|
100
|
+
|
|
101
|
+
const pairsSelectedIndexes: number[] = [];
|
|
102
|
+
pairsGrid.root.addEventListener('click', (event) => {
|
|
103
|
+
const gridCell = pairsGrid.hitTest(event.offsetX, event.offsetY);
|
|
104
|
+
if (gridCell === null || gridCell.tableRowIndex === null)
|
|
105
|
+
return;
|
|
106
|
+
|
|
107
|
+
const rowIdx = gridCell.tableRowIndex;
|
|
108
|
+
if (!event.shiftKey) {
|
|
109
|
+
pairsSelectedIndexes.length = 0;
|
|
110
|
+
pairsSelectedIndexes.push(rowIdx);
|
|
111
|
+
} else {
|
|
112
|
+
const rowIdxIdx = pairsSelectedIndexes.indexOf(rowIdx);
|
|
113
|
+
if (rowIdxIdx === -1)
|
|
114
|
+
pairsSelectedIndexes.push(rowIdx);
|
|
115
|
+
else
|
|
116
|
+
pairsSelectedIndexes.splice(rowIdxIdx, 1);
|
|
117
|
+
}
|
|
118
|
+
uniqueSequencesTable.filter.fireChanged();
|
|
119
|
+
gridCell.cell.dataFrame.currentRowIdx = -1;
|
|
120
|
+
pairsGrid.invalidate();
|
|
121
|
+
});
|
|
122
|
+
pairsGrid.onCellRender.subscribe((gcArgs) => {
|
|
123
|
+
if (gcArgs.cell.tableColumn?.name !== substCol.name || !pairsSelectedIndexes.includes(gcArgs.cell.tableRowIndex!))
|
|
124
|
+
return;
|
|
125
|
+
|
|
126
|
+
renderCellSelection(gcArgs.g, gcArgs.bounds);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const gridCols = model.analysisView.grid.columns;
|
|
130
|
+
const originalGridColCount = gridCols.length;
|
|
131
|
+
const positionColumns = model.splitSeqDf.columns;
|
|
132
|
+
const columnNames: string[] = [];
|
|
133
|
+
for (let colIdx = 1; colIdx < originalGridColCount; colIdx++) {
|
|
134
|
+
const gridCol = gridCols.byIndex(colIdx);
|
|
135
|
+
if (gridCol?.name === model.settings.sequenceColumnName || (gridCol?.visible === true && !positionColumns.contains(gridCol.name)))
|
|
136
|
+
columnNames.push(gridCol!.name);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const uniqueSequencesTable = table.clone(uniqueSequencesBitSet, columnNames);
|
|
140
|
+
const seqIdxCol = uniqueSequencesTable.columns.addNewInt('~seqIdx');
|
|
141
|
+
const seqIdxColData = seqIdxCol.getRawData();
|
|
142
|
+
const selectedIndexes = uniqueSequencesBitSet.getSelectedIndexes();
|
|
143
|
+
seqIdxCol.init((idx) => selectedIndexes[idx]);
|
|
144
|
+
const uniqueSequencesGrid = uniqueSequencesTable.plot.grid();
|
|
145
|
+
uniqueSequencesGrid.props.allowEdit = false;
|
|
146
|
+
uniqueSequencesGrid.props.allowRowSelection = false;
|
|
147
|
+
uniqueSequencesGrid.props.allowBlockSelection = false;
|
|
148
|
+
uniqueSequencesGrid.props.allowColSelection = false;
|
|
149
|
+
uniqueSequencesGrid.props.rowHeight = 20;
|
|
150
|
+
uniqueSequencesGrid.root.style.width = '100%';
|
|
151
|
+
uniqueSequencesGrid.root.style.height = '250px';
|
|
152
|
+
uniqueSequencesTable.filter.onChanged.subscribe(() => {
|
|
153
|
+
const uniqueSelectedIndexes: number[] = [];
|
|
154
|
+
for (const idx of pairsSelectedIndexes) {
|
|
155
|
+
uniqueSelectedIndexes.push(fromIdxCol.get(idx)!);
|
|
156
|
+
uniqueSelectedIndexes.push(toIdxCol.get(idx)!);
|
|
157
|
+
}
|
|
158
|
+
uniqueSequencesTable.filter.init(
|
|
159
|
+
(idx) => pairsSelectedIndexes.length === 0 || uniqueSelectedIndexes.includes(seqIdxColData[idx]), false);
|
|
83
160
|
});
|
|
84
161
|
|
|
85
|
-
|
|
86
|
-
grid.props.allowEdit = false;
|
|
87
|
-
const gridRoot = grid.root;
|
|
88
|
-
gridRoot.style.width = 'auto';
|
|
89
|
-
gridRoot.style.height = '150px';
|
|
90
|
-
return new DG.Widget(ui.divV([aminoToInput.root, gridRoot]));
|
|
162
|
+
return new DG.Widget(ui.divV([aminoToInput.root, pairsGrid.root, uniqueSequencesGrid.root], {style: {width: '100%'}}));
|
|
91
163
|
}
|
package/src/widgets/peptides.ts
CHANGED
|
@@ -13,7 +13,6 @@ import {scaleActivity} from '../utils/misc';
|
|
|
13
13
|
import {ALIGNMENT, NOTATION, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule';
|
|
14
14
|
|
|
15
15
|
/** Peptide analysis widget.
|
|
16
|
-
*
|
|
17
16
|
* @param {DG.DataFrame} df Working table
|
|
18
17
|
* @param {DG.Column} col Aligned sequence column
|
|
19
18
|
* @return {Promise<DG.Widget>} Widget containing peptide analysis */
|
|
@@ -39,7 +38,8 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>):
|
|
|
39
38
|
viewer.root.style.setProperty('height', '130px');
|
|
40
39
|
return viewer.root;
|
|
41
40
|
}));
|
|
42
|
-
|
|
41
|
+
//TODO: add when new version of datagrok-api is available
|
|
42
|
+
}, {filter: (col: DG.Column) => col.semType === DG.SEMTYPE.MACROMOLECULE});
|
|
43
43
|
} else if (!(col.getTag(bioTAGS.aligned) === ALIGNMENT.SEQ_MSA) &&
|
|
44
44
|
col.getTag(DG.TAGS.UNITS) !== NOTATION.HELM) {
|
|
45
45
|
return {
|
|
@@ -95,18 +95,16 @@ export function analyzePeptidesUI(df: DG.DataFrame, col?: DG.Column<string>):
|
|
|
95
95
|
DG.Stats.fromColumn(activityColumnChoice.value!).min > 0;
|
|
96
96
|
activityScalingMethod.fireChanged();
|
|
97
97
|
};
|
|
98
|
-
|
|
98
|
+
//TODO: add when new version of datagrok-api is available
|
|
99
|
+
const activityColumnChoice = ui.columnInput('Activity', df, defaultActivityColumn, activityScalingMethodState,
|
|
100
|
+
{filter: (col: DG.Column) => col.type === DG.TYPE.INT || col.type === DG.TYPE.FLOAT});
|
|
99
101
|
const clustersColumnChoice = ui.columnInput('Clusters', df, null);
|
|
100
102
|
clustersColumnChoice.nullable = true;
|
|
101
103
|
activityColumnChoice.fireChanged();
|
|
102
104
|
activityScalingMethod.fireChanged();
|
|
103
105
|
|
|
104
|
-
const targetColumnChoice = ui.columnInput('Target', df, null,
|
|
105
|
-
|
|
106
|
-
grok.shell.warning('Target column should be of string type');
|
|
107
|
-
targetColumnChoice.value = null;
|
|
108
|
-
}
|
|
109
|
-
});
|
|
106
|
+
const targetColumnChoice = ui.columnInput('Target', df, null, null,
|
|
107
|
+
{filter: (col: DG.Column) => col.type === DG.TYPE.STRING});
|
|
110
108
|
targetColumnChoice.nullable = true;
|
|
111
109
|
|
|
112
110
|
const inputsList = [activityColumnChoice, activityScalingMethod, clustersColumnChoice, targetColumnChoice];
|
|
@@ -158,8 +156,7 @@ export async function startAnalysis(activityColumn: DG.Column<number>, peptidesC
|
|
|
158
156
|
targetColumn: DG.Column<string> | null = null): Promise<PeptidesModel | null> {
|
|
159
157
|
const progress = DG.TaskBarProgressIndicator.create('Loading SAR...');
|
|
160
158
|
let model = null;
|
|
161
|
-
if (activityColumn.type === DG.COLUMN_TYPE.FLOAT || activityColumn.type === DG.COLUMN_TYPE.INT
|
|
162
|
-
activityColumn.type === DG.COLUMN_TYPE.BIG_INT || activityColumn.type === DG.COLUMN_TYPE.QNUM) {
|
|
159
|
+
if (activityColumn.type === DG.COLUMN_TYPE.FLOAT || activityColumn.type === DG.COLUMN_TYPE.INT) {
|
|
163
160
|
//prepare new DF
|
|
164
161
|
const newDf = DG.DataFrame.create(currentDf.rowCount);
|
|
165
162
|
const newDfCols = newDf.columns;
|
|
@@ -200,8 +197,10 @@ export async function startAnalysis(activityColumn: DG.Column<number>, peptidesC
|
|
|
200
197
|
const dfUuid = uuid.v4();
|
|
201
198
|
newDf.setTag(C.TAGS.UUID, dfUuid);
|
|
202
199
|
newDf.setTag('monomerType', monomerType);
|
|
203
|
-
|
|
204
|
-
//
|
|
200
|
+
|
|
201
|
+
// Cloning dataframe with applied filter. If filter is not applied, cloning is
|
|
202
|
+
// needed anyway to allow filtering on the original dataframe
|
|
203
|
+
model = PeptidesModel.getInstance(newDf.clone(currentDf.filter));
|
|
205
204
|
if (clustersColumn) await model.addLogoSummaryTable();
|
|
206
205
|
await model.addMonomerPosition();
|
|
207
206
|
await model.addMostPotentResidues();
|
package/src/widgets/settings.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {PeptidesModel, VIEWER_TYPE} from '../model';
|
|
|
7
7
|
|
|
8
8
|
import $ from 'cash-dom';
|
|
9
9
|
import wu from 'wu';
|
|
10
|
+
import {getTreeHelperInstance} from '../package';
|
|
10
11
|
|
|
11
12
|
type PaneInputs = {[paneName: string]: DG.InputBase[]};
|
|
12
13
|
type SettingsElements = {dialog: DG.Dialog, accordion: DG.Accordion, inputs: PaneInputs};
|
|
@@ -19,6 +20,7 @@ export enum SETTINGS_PANES {
|
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
export enum GENERAL_INPUTS {
|
|
23
|
+
ACTIVITY = 'Activity',
|
|
22
24
|
ACTIVITY_SCALING = 'Activity scaling',
|
|
23
25
|
BIDIRECTIONAL_ANALYSIS = 'Bidirectional analysis',
|
|
24
26
|
}
|
|
@@ -48,17 +50,27 @@ export const PANES_INPUTS = {
|
|
|
48
50
|
export function getSettingsDialog(model: PeptidesModel): SettingsElements {
|
|
49
51
|
const accordion = ui.accordion();
|
|
50
52
|
const settings = model.settings;
|
|
51
|
-
const
|
|
53
|
+
const currentScaling = settings.scaling ?? C.SCALING_METHODS.NONE;
|
|
54
|
+
const currentBidirectional = settings.isBidirectional ?? false;
|
|
55
|
+
const currentMaxMutations = settings.maxMutations ?? 1;
|
|
56
|
+
const currentMinActivityDelta = settings.minActivityDelta ?? 0;
|
|
57
|
+
const currentColumns = settings.columns ?? {};
|
|
58
|
+
|
|
59
|
+
const result: type.PeptidesSettings = {};
|
|
52
60
|
const inputs: PaneInputs = {};
|
|
53
61
|
|
|
54
62
|
// General pane options
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
const activityCol = ui.columnInput(GENERAL_INPUTS.ACTIVITY, model.df,
|
|
64
|
+
model.df.getCol(model.settings.activityColumnName!), () => result.activityColumnName = activityCol.value!.name,
|
|
65
|
+
{filter: (col: DG.Column) => (col.type === DG.TYPE.FLOAT || col.type === DG.TYPE.INT) && col.name !== C.COLUMNS_NAMES.ACTIVITY_SCALED});
|
|
66
|
+
const activityScaling =
|
|
67
|
+
ui.choiceInput(GENERAL_INPUTS.ACTIVITY_SCALING, currentScaling, Object.values(C.SCALING_METHODS),
|
|
68
|
+
() => result.scaling = activityScaling.value as C.SCALING_METHODS) as DG.InputBase<C.SCALING_METHODS>;
|
|
69
|
+
const bidirectionalAnalysis = ui.boolInput(GENERAL_INPUTS.BIDIRECTIONAL_ANALYSIS, currentBidirectional,
|
|
70
|
+
() => result.isBidirectional = bidirectionalAnalysis.value) as DG.InputBase<boolean>;
|
|
71
|
+
|
|
72
|
+
accordion.addPane(SETTINGS_PANES.GENERAL, () => ui.inputs([activityCol, activityScaling, bidirectionalAnalysis]), true);
|
|
73
|
+
inputs[SETTINGS_PANES.GENERAL] = [activityCol, activityScaling, bidirectionalAnalysis];
|
|
62
74
|
|
|
63
75
|
// Viewers pane options
|
|
64
76
|
/* FIXME: combinations of adding and deleting viewers are not working properly
|
|
@@ -75,26 +87,27 @@ export function getSettingsDialog(model: PeptidesModel): SettingsElements {
|
|
|
75
87
|
*/
|
|
76
88
|
const isDendrogramEnabled = wu(model.analysisView.viewers).some((v) => v.type === VIEWER_TYPE.DENDROGRAM);
|
|
77
89
|
const dendrogram = ui.boolInput(VIEWER_TYPE.DENDROGRAM, isDendrogramEnabled ?? false,
|
|
78
|
-
() => result.showDendrogram = dendrogram.value
|
|
90
|
+
() => result.showDendrogram = dendrogram.value) as DG.InputBase<boolean>;
|
|
91
|
+
dendrogram.enabled = getTreeHelperInstance() !== null;
|
|
79
92
|
|
|
80
93
|
accordion.addPane(SETTINGS_PANES.VIEWERS, () => ui.inputs([dendrogram]), true);
|
|
81
94
|
inputs[SETTINGS_PANES.VIEWERS] = [dendrogram];
|
|
82
95
|
|
|
83
96
|
// Mutation Cliffs pane options
|
|
84
|
-
const maxMutations = ui.sliderInput(MUTATION_CLIFFS_INPUTS.MAX_MUTATIONS,
|
|
85
|
-
const val = Math.round(maxMutations.value
|
|
97
|
+
const maxMutations = ui.sliderInput(MUTATION_CLIFFS_INPUTS.MAX_MUTATIONS, currentMaxMutations, 1, 50, () => {
|
|
98
|
+
const val = Math.round(maxMutations.value);
|
|
86
99
|
$(maxMutations.root).find('label.ui-input-description').remove();
|
|
87
100
|
result.maxMutations = val;
|
|
88
101
|
maxMutations.addPostfix(val.toString());
|
|
89
|
-
})
|
|
102
|
+
}) as DG.InputBase<number>;
|
|
90
103
|
maxMutations.addPostfix((settings.maxMutations ?? 1).toString());
|
|
91
|
-
const minActivityDelta = ui.sliderInput(MUTATION_CLIFFS_INPUTS.MIN_ACTIVITY_DELTA,
|
|
104
|
+
const minActivityDelta = ui.sliderInput(MUTATION_CLIFFS_INPUTS.MIN_ACTIVITY_DELTA, currentMinActivityDelta, 0,
|
|
92
105
|
100, () => {
|
|
93
|
-
const val = minActivityDelta.value
|
|
106
|
+
const val = minActivityDelta.value.toFixed(3);
|
|
94
107
|
result.minActivityDelta = parseFloat(val);
|
|
95
108
|
$(minActivityDelta.root).find('label.ui-input-description').remove();
|
|
96
109
|
minActivityDelta.addPostfix(val);
|
|
97
|
-
})
|
|
110
|
+
}) as DG.InputBase<number>;
|
|
98
111
|
minActivityDelta.addPostfix((settings.minActivityDelta ?? 0).toString());
|
|
99
112
|
accordion.addPane(SETTINGS_PANES.MUTATION_CLIFFS, () => ui.inputs([maxMutations, minActivityDelta]), true);
|
|
100
113
|
inputs[SETTINGS_PANES.MUTATION_CLIFFS] = [maxMutations, minActivityDelta];
|
|
@@ -107,19 +120,28 @@ export function getSettingsDialog(model: PeptidesModel): SettingsElements {
|
|
|
107
120
|
if (colName === settings.activityColumnName || colName === C.COLUMNS_NAMES.ACTIVITY_SCALED)
|
|
108
121
|
continue;
|
|
109
122
|
|
|
110
|
-
const isIncludedInput = ui.boolInput(COLUMNS_INPUTS.IS_INCLUDED,
|
|
111
|
-
|
|
123
|
+
const isIncludedInput = ui.boolInput(COLUMNS_INPUTS.IS_INCLUDED, typeof (currentColumns)[colName] !== 'undefined',
|
|
124
|
+
() => {
|
|
125
|
+
result.columns ??= {};
|
|
112
126
|
if (isIncludedInput.value)
|
|
113
|
-
result.columns
|
|
114
|
-
else
|
|
115
|
-
delete result.columns
|
|
127
|
+
result.columns[colName] = aggregationInput.value;
|
|
128
|
+
else {
|
|
129
|
+
delete result.columns[colName];
|
|
130
|
+
if (Object.keys(result.columns).length === Object.keys(currentColumns).length)
|
|
131
|
+
delete result.columns;
|
|
132
|
+
}
|
|
116
133
|
}) as DG.InputBase<boolean>;
|
|
117
|
-
|
|
118
|
-
|
|
134
|
+
|
|
135
|
+
const aggregationInput = ui.choiceInput(COLUMNS_INPUTS.AGGREGATION, (currentColumns)[colName] ?? DG.AGG.AVG,
|
|
136
|
+
Object.values(DG.STATS), () => {
|
|
137
|
+
result.columns ??= {};
|
|
119
138
|
if (isIncludedInput.value)
|
|
120
|
-
result.columns
|
|
121
|
-
else
|
|
122
|
-
delete result.columns
|
|
139
|
+
result.columns[colName] = aggregationInput.value;
|
|
140
|
+
else {
|
|
141
|
+
delete result.columns[col.name];
|
|
142
|
+
if (Object.keys(result.columns).length === Object.keys(currentColumns).length)
|
|
143
|
+
delete result.columns;
|
|
144
|
+
}
|
|
123
145
|
}) as DG.InputBase<DG.AggregationType>;
|
|
124
146
|
$(aggregationInput.root).find('label').css('width', 'auto');
|
|
125
147
|
const inputsRow = ui.inputsRow(col.name, [isIncludedInput, aggregationInput]);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
|
|
2
|
+
import {sequenceChemSimilarity} from '@datagrok-libraries/bio/src/monomer-works/monomer-utils';
|
|
3
|
+
import {ISeqSplitted} from '@datagrok-libraries/bio/src/utils/macromolecule/types';
|
|
4
|
+
import * as DG from 'datagrok-api/dg';
|
|
5
|
+
|
|
6
|
+
export function calculateIdentity(template: ISeqSplitted, splitSeqDf: DG.DataFrame): DG.Column<number> {
|
|
7
|
+
const numPositions = splitSeqDf.columns.length;
|
|
8
|
+
const positionCols: Uint32Array[] = new Array(numPositions);
|
|
9
|
+
const positionEmptyCategories: number[] = new Array(numPositions);
|
|
10
|
+
const categoryIndexesTemplate: number[] = new Array(numPositions);
|
|
11
|
+
|
|
12
|
+
for (let posIdx = 0; posIdx < numPositions; ++posIdx) {
|
|
13
|
+
const posCol = splitSeqDf.columns.byIndex(posIdx);
|
|
14
|
+
positionCols[posIdx] = posCol.getRawData() as Uint32Array;
|
|
15
|
+
positionEmptyCategories[posIdx] = posCol.categories.indexOf('');
|
|
16
|
+
categoryIndexesTemplate[posIdx] = posCol.categories.indexOf(template[posIdx] ?? '');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const identityScoresCol = DG.Column.float('Identity', splitSeqDf.rowCount);
|
|
20
|
+
const identityScoresData = identityScoresCol.getRawData();
|
|
21
|
+
for (let rowIndex = 0; rowIndex < splitSeqDf.rowCount; ++rowIndex) {
|
|
22
|
+
identityScoresData[rowIndex] = 0;
|
|
23
|
+
for (let posIdx = 0; posIdx < template.length; ++posIdx) {
|
|
24
|
+
const categoryIndex = positionCols[posIdx][rowIndex];
|
|
25
|
+
if (categoryIndex === categoryIndexesTemplate[posIdx])
|
|
26
|
+
++identityScoresData[rowIndex];
|
|
27
|
+
}
|
|
28
|
+
identityScoresData[rowIndex] /= template.length;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return identityScoresCol;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
export async function calculateSimilarity(template: ISeqSplitted, splitSeqDf: DG.DataFrame): Promise<DG.Column<number>> {
|
|
36
|
+
const columns = splitSeqDf.columns.toList() as DG.Column<string>[];
|
|
37
|
+
const scoresCol = await sequenceChemSimilarity(columns, template);
|
|
38
|
+
return scoresCol;
|
|
39
|
+
}
|