@datagrok/peptides 1.17.21 → 1.17.23
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 +13 -0
- package/dist/111.js +1 -1
- package/dist/111.js.map +1 -0
- package/dist/216.js +1 -1
- package/dist/216.js.map +1 -0
- package/dist/501.js +1 -1
- package/dist/501.js.map +1 -0
- package/dist/603.js +1 -1
- package/dist/603.js.map +1 -0
- package/dist/682.js +1 -1
- package/dist/682.js.map +1 -0
- package/dist/705.js +1 -1
- package/dist/705.js.map +1 -0
- package/dist/778.js +1 -1
- package/dist/778.js.map +1 -0
- package/dist/795.js +1 -1
- package/dist/795.js.map +1 -0
- package/dist/950.js +1 -1
- package/dist/950.js.map +1 -0
- package/dist/package-test.js +2 -2
- package/dist/package-test.js.map +1 -0
- package/dist/package.js +2 -2
- package/dist/package.js.map +1 -0
- package/package.json +2 -2
- package/src/package.ts +0 -1
- package/src/tests/benchmarks.ts +4 -4
- package/src/utils/algorithms.ts +23 -7
- package/src/utils/constants.ts +2 -0
- package/src/utils/statistics.ts +18 -10
- package/src/viewers/cluster-max-activity-viewer.ts +3 -1
- package/src/viewers/logo-summary.ts +5 -1
- package/src/viewers/sar-viewer.ts +151 -32
- package/src/widgets/distribution.ts +5 -4
- package/src/widgets/mutation-cliffs.ts +3 -0
- package/src/widgets/settings.ts +5 -4
- package/tsconfig.json +3 -3
- package/webpack.config.js +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datagrok/peptides",
|
|
3
3
|
"friendlyName": "Peptides",
|
|
4
|
-
"version": "1.17.
|
|
4
|
+
"version": "1.17.23",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Davit Rizhinashvili",
|
|
7
7
|
"email": "drizhinashvili@datagrok.ai"
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@datagrok-libraries/ml": "^6.6.13",
|
|
19
19
|
"@datagrok-libraries/statistics": "^1.2.12",
|
|
20
20
|
"@datagrok-libraries/tutorials": "^1.3.12",
|
|
21
|
-
"@datagrok-libraries/utils": "^4.2.
|
|
21
|
+
"@datagrok-libraries/utils": "^4.2.13",
|
|
22
22
|
"datagrok-api": "^1.19.0",
|
|
23
23
|
"@webgpu/types": "^0.1.40",
|
|
24
24
|
"cash-dom": "latest",
|
package/src/package.ts
CHANGED
package/src/tests/benchmarks.ts
CHANGED
|
@@ -17,7 +17,7 @@ const benchmarkDatasetSizes = [5, 50, 100, 200];
|
|
|
17
17
|
category('Benchmarks: Mutation Cliffs', () => {
|
|
18
18
|
for (const size of benchmarkDatasetSizes)
|
|
19
19
|
test(`${size}k sequences`, async () => await mutationCliffsBenchmark(size), {timeout: 300000});
|
|
20
|
-
});
|
|
20
|
+
}, {benchmarks: true});
|
|
21
21
|
|
|
22
22
|
category('Benchmarks: Cluster stats', () => {
|
|
23
23
|
for (const size of benchmarkDatasetSizes) {
|
|
@@ -38,7 +38,7 @@ category('Benchmarks: Cluster stats', () => {
|
|
|
38
38
|
() => calculateClusterStatistics(df, clustersColumnName, [], scaledActivity));
|
|
39
39
|
}, {timeout: 100000});
|
|
40
40
|
}
|
|
41
|
-
});
|
|
41
|
+
}, {benchmarks: true});
|
|
42
42
|
|
|
43
43
|
category('Benchmarks: Monomer-Position stats', () => {
|
|
44
44
|
for (const size of benchmarkDatasetSizes) {
|
|
@@ -64,7 +64,7 @@ category('Benchmarks: Monomer-Position stats', () => {
|
|
|
64
64
|
() => calculateMonomerPositionStatistics(scaledActivity, DG.BitSet.create(0), positionCols));
|
|
65
65
|
}, {timeout: 100000});
|
|
66
66
|
}
|
|
67
|
-
});
|
|
67
|
+
}, {benchmarks: true});
|
|
68
68
|
|
|
69
69
|
category('Benchmarks: Analysis start', () => {
|
|
70
70
|
for (const size of benchmarkDatasetSizes) {
|
|
@@ -89,7 +89,7 @@ category('Benchmarks: Analysis start', () => {
|
|
|
89
89
|
});
|
|
90
90
|
}, {timeout: 100000});
|
|
91
91
|
}
|
|
92
|
-
});
|
|
92
|
+
}, {benchmarks: true});
|
|
93
93
|
|
|
94
94
|
async function mutationCliffsBenchmark(size: number): Promise<void> {
|
|
95
95
|
if (!DG.Test.isInBenchmark)
|
package/src/utils/algorithms.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
1
2
|
import * as DG from 'datagrok-api/dg';
|
|
2
3
|
import * as C from './constants';
|
|
3
4
|
import * as type from './types';
|
|
@@ -89,18 +90,27 @@ export function calculateCliffsStatistics(
|
|
|
89
90
|
* @param [options] - Options for the algorithm.
|
|
90
91
|
* @param [options.isFiltered] - Whether the dataframe is filtered.
|
|
91
92
|
* @param [options.columns] - Columns to consider when calculating statistics.
|
|
93
|
+
* @param [options.target] - Target column and category to consider.
|
|
94
|
+
* @param [options.aggValue] - Column and aggregation type to consider instead of count.
|
|
92
95
|
* @return - Statistics for each monomer position.
|
|
93
96
|
*/
|
|
94
97
|
export function calculateMonomerPositionStatistics(activityCol: DG.Column<number>, filter: DG.BitSet,
|
|
95
98
|
positionColumns: DG.Column<string>[], options: {
|
|
96
99
|
isFiltered?: boolean,
|
|
97
|
-
columns?: string[]
|
|
100
|
+
columns?: string[],
|
|
101
|
+
target?: {
|
|
102
|
+
col: DG.Column<string>,
|
|
103
|
+
cat: string,
|
|
104
|
+
},
|
|
105
|
+
aggValue?: {
|
|
106
|
+
col: DG.Column,
|
|
107
|
+
type: DG.AGG
|
|
108
|
+
}
|
|
98
109
|
} = {}): MonomerPositionStats {
|
|
99
110
|
options.isFiltered ??= false;
|
|
100
111
|
const monomerPositionObject = {general: {}} as MonomerPositionStats & { general: SummaryStats };
|
|
101
112
|
let activityColData: Float64Array = activityCol.getRawData() as Float64Array;
|
|
102
113
|
let sourceDfLen = activityCol.length;
|
|
103
|
-
|
|
104
114
|
if (options.isFiltered) {
|
|
105
115
|
sourceDfLen = filter.trueCount;
|
|
106
116
|
const tempActivityData = new Float64Array(sourceDfLen);
|
|
@@ -111,9 +121,15 @@ export function calculateMonomerPositionStatistics(activityCol: DG.Column<number
|
|
|
111
121
|
|
|
112
122
|
activityColData = tempActivityData;
|
|
113
123
|
positionColumns = DG.DataFrame.fromColumns(positionColumns).clone(filter).columns.toList();
|
|
124
|
+
if (options.target)
|
|
125
|
+
options.target.col = options.target.col.clone(filter);
|
|
126
|
+
if (options.aggValue)
|
|
127
|
+
options.aggValue.col = options.aggValue.col.clone(filter);
|
|
114
128
|
}
|
|
115
129
|
options.columns ??= positionColumns.map((col) => col.name);
|
|
116
|
-
|
|
130
|
+
const targetColIndexes = options.target?.col?.getRawData();
|
|
131
|
+
const targetColCat = options.target?.col.categories;
|
|
132
|
+
const targetIndex = options.target?.cat ? targetColCat?.indexOf(options.target.cat) : -1;
|
|
117
133
|
for (const posCol of positionColumns) {
|
|
118
134
|
if (!options.columns.includes(posCol.name))
|
|
119
135
|
continue;
|
|
@@ -131,13 +147,13 @@ export function calculateMonomerPositionStatistics(activityCol: DG.Column<number
|
|
|
131
147
|
|
|
132
148
|
const boolArray: boolean[] = new Array(sourceDfLen).fill(false);
|
|
133
149
|
for (let i = 0; i < sourceDfLen; ++i) {
|
|
134
|
-
if (posColData[i] === categoryIndex)
|
|
150
|
+
if (posColData[i] === categoryIndex && (!targetColIndexes || targetIndex === -1 || targetColIndexes[i] === targetIndex))
|
|
135
151
|
boolArray[i] = true;
|
|
136
152
|
}
|
|
137
153
|
const bitArray = BitArray.fromValues(boolArray);
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
154
|
+
if (bitArray.allFalse)
|
|
155
|
+
continue;
|
|
156
|
+
const stats = getStats(activityColData, bitArray, options.aggValue);
|
|
141
157
|
currentPositionObject[monomer] = stats;
|
|
142
158
|
getSummaryStats(currentPositionObject.general, stats);
|
|
143
159
|
}
|
package/src/utils/constants.ts
CHANGED
|
@@ -47,6 +47,8 @@ export enum TAGS {
|
|
|
47
47
|
POSITION_COL = 'isPositionCol',
|
|
48
48
|
M_P_STATS_CACHE = '.MPStatsCache',
|
|
49
49
|
INVARIANT_MAP_COLOR_CACHE = '.InvariantMapColorCache',
|
|
50
|
+
INVARIANT_MAP_COLOR_MAX_CACHE = '.InvariantMapColorMaxCache',
|
|
51
|
+
INVARIANT_MAP_COLOR_MIN_CACHE = '.InvariantMapColorMinCache',
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
export enum SEM_TYPES {
|
package/src/utils/statistics.ts
CHANGED
|
@@ -13,6 +13,7 @@ export type StatsItem = {
|
|
|
13
13
|
ratio: number,
|
|
14
14
|
mask: BitArray,
|
|
15
15
|
mean: number,
|
|
16
|
+
aggValue?: number,
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
export type PositionStats = { [monomer: string]: StatsItem } & { general: SummaryStats };
|
|
@@ -42,18 +43,23 @@ export type AggregationColumns = { [col: string]: DG.AggregationType };
|
|
|
42
43
|
* @param bitArray - Bit array to use for the calculation.
|
|
43
44
|
* @return - Statistics for the given data and bit array.
|
|
44
45
|
*/
|
|
45
|
-
export function getStats(data: RawData | number[], bitArray: BitArray
|
|
46
|
+
export function getStats(data: RawData | number[], bitArray: BitArray,
|
|
47
|
+
aggData?: {col: DG.Column, type: DG.AGG}): StatsItem {
|
|
46
48
|
if (data.length !== bitArray.length && data.some((v, i) => i >= bitArray.length ? v !== 0 : false))
|
|
47
49
|
throw new Error('PeptidesError: Data and bit array have different lengths');
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
if (bitArray.trueCount() === 0)
|
|
51
|
-
throw new Error('PeptidesError: One of the samples is empty');
|
|
52
|
-
|
|
53
|
-
|
|
54
51
|
const selected = new Float32Array(bitArray.trueCount());
|
|
55
52
|
const rest = new Float32Array(bitArray.falseCount());
|
|
56
|
-
|
|
53
|
+
let aggValue: number | undefined;
|
|
54
|
+
if (aggData) {
|
|
55
|
+
try {
|
|
56
|
+
aggValue = DG.DataFrame.fromColumns([aggData.col])
|
|
57
|
+
.clone(DG.BitSet.fromBytes(bitArray.buffer.buffer, bitArray.length)).col(aggData.col.name)
|
|
58
|
+
?.aggregate(aggData.type);
|
|
59
|
+
} catch (e) {
|
|
60
|
+
console.error(e);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
57
63
|
let selectedIndex = 0;
|
|
58
64
|
let restIndex = 0;
|
|
59
65
|
for (let i = 0; i < bitArray.length; ++i) {
|
|
@@ -63,9 +69,9 @@ export function getStats(data: RawData | number[], bitArray: BitArray): StatsIte
|
|
|
63
69
|
rest[restIndex++] = data[i];
|
|
64
70
|
}
|
|
65
71
|
|
|
66
|
-
const selectedMean = selected.reduce((a, b) => a + b, 0) / selected.length;
|
|
67
|
-
if (selected.length
|
|
68
|
-
const restMean = rest.reduce((a, b) => a + b, 0) / rest.length;
|
|
72
|
+
const selectedMean = selected.reduce((a, b) => a + b, 0) / Math.max(selected.length, 1);
|
|
73
|
+
if (selected.length < 2 || rest.length < 2) {
|
|
74
|
+
const restMean = rest.reduce((a, b) => a + b, 0) / Math.max(rest.length, 1);
|
|
69
75
|
return {
|
|
70
76
|
count: selected.length,
|
|
71
77
|
pValue: null,
|
|
@@ -73,6 +79,7 @@ export function getStats(data: RawData | number[], bitArray: BitArray): StatsIte
|
|
|
73
79
|
meanDifference: selectedMean - restMean,
|
|
74
80
|
ratio: selected.length / (bitArray.length),
|
|
75
81
|
mask: bitArray,
|
|
82
|
+
aggValue,
|
|
76
83
|
};
|
|
77
84
|
}
|
|
78
85
|
|
|
@@ -85,6 +92,7 @@ export function getStats(data: RawData | number[], bitArray: BitArray): StatsIte
|
|
|
85
92
|
meanDifference: currentMeanDiff,
|
|
86
93
|
ratio: selected.length / (bitArray.length),
|
|
87
94
|
mask: bitArray,
|
|
95
|
+
aggValue,
|
|
88
96
|
};
|
|
89
97
|
}
|
|
90
98
|
|
|
@@ -290,7 +290,7 @@ export class ClusterMaxActivityViewer extends DG.JsViewer implements IClusterMax
|
|
|
290
290
|
|
|
291
291
|
this.render();
|
|
292
292
|
|
|
293
|
-
this.dataFrame
|
|
293
|
+
this.dataFrame?.onDataChanged.subscribe(() => {
|
|
294
294
|
this.render();
|
|
295
295
|
});
|
|
296
296
|
}
|
|
@@ -299,6 +299,8 @@ export class ClusterMaxActivityViewer extends DG.JsViewer implements IClusterMax
|
|
|
299
299
|
if (this.renderTimeout)
|
|
300
300
|
clearTimeout(this.renderTimeout);
|
|
301
301
|
this.renderTimeout = setTimeout(() => {
|
|
302
|
+
if (!this.dataFrame)
|
|
303
|
+
return;
|
|
302
304
|
$(this.root).empty();
|
|
303
305
|
const scViewer = this.scViewer;
|
|
304
306
|
if (scViewer == null) {
|
|
@@ -97,8 +97,11 @@ export class LogoSummaryTable extends DG.JsViewer implements ILogoSummaryTable {
|
|
|
97
97
|
{
|
|
98
98
|
category: LST_CATEGORIES.GENERAL,
|
|
99
99
|
nullable: false,
|
|
100
|
+
columnTypeFilter: DG.TYPE.CATEGORICAL,
|
|
100
101
|
});
|
|
101
|
-
this.activityColumnName = this.column(LST_PROPERTIES.ACTIVITY, {
|
|
102
|
+
this.activityColumnName = this.column(LST_PROPERTIES.ACTIVITY, {
|
|
103
|
+
category: LST_CATEGORIES.GENERAL, nullable: false, columnTypeFilter: DG.TYPE.NUMERICAL,
|
|
104
|
+
});
|
|
102
105
|
this.activityScaling = this.string(LST_PROPERTIES.ACTIVITY_SCALING, C.SCALING_METHODS.NONE,
|
|
103
106
|
{
|
|
104
107
|
category: LST_CATEGORIES.GENERAL,
|
|
@@ -672,6 +675,7 @@ export class LogoSummaryTable extends DG.JsViewer implements ILogoSummaryTable {
|
|
|
672
675
|
showBinSelector: false,
|
|
673
676
|
backColor: DG.Color.toHtml(DG.Color.white),
|
|
674
677
|
xAxisHeight: 1,
|
|
678
|
+
showSplitSelector: false,
|
|
675
679
|
});
|
|
676
680
|
viewer.root.style.width = 'auto';
|
|
677
681
|
distCache.set(currentRowIdx, viewer);
|
|
@@ -55,11 +55,20 @@ export enum SAR_PROPERTIES {
|
|
|
55
55
|
COLUMNS = 'columns',
|
|
56
56
|
AGGREGATION = 'aggregation',
|
|
57
57
|
ACTIVITY_TARGET = 'activityTarget',
|
|
58
|
+
VALUE_INVARIANT_MAP = 'value',
|
|
59
|
+
AGGREGATION_INVARIANT_MAP_VALUE = 'valueAggregation',
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
export enum MONOMER_POSITION_PROPERTIES {
|
|
61
63
|
COLOR = 'color',
|
|
62
64
|
COLOR_AGGREGATION = 'colorAggregation',
|
|
65
|
+
CUSTOM_COLOR_RANGE = 'customColorRange',
|
|
66
|
+
MIN_COLOR_VALUE = 'minColorValue',
|
|
67
|
+
MAX_COLOR_VALUE = 'maxColorValue',
|
|
68
|
+
LOWER_BOUND_COLOR = 'lowerBoundColor',
|
|
69
|
+
MIDDLE_COLOR = 'middleColor',
|
|
70
|
+
UPPER_BOUND_COLOR = 'upperBoundColor',
|
|
71
|
+
LOG_SCALE_COLOR = 'logScaleColor',
|
|
63
72
|
}
|
|
64
73
|
|
|
65
74
|
export enum PROPERTY_CATEGORIES {
|
|
@@ -90,12 +99,16 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
90
99
|
columns: string[];
|
|
91
100
|
aggregation: string;
|
|
92
101
|
targetColumnName: string;
|
|
93
|
-
targetCategory: string;
|
|
94
102
|
minActivityDelta: number;
|
|
95
103
|
maxMutations: number;
|
|
96
104
|
_scaledActivityColumn: DG.Column | null = null;
|
|
97
105
|
doRender: boolean = true;
|
|
98
106
|
activityTarget: C.ACTIVITY_TARGET;
|
|
107
|
+
targetColumnInput?: DG.InputBase<DG.Column | null>;
|
|
108
|
+
targetCategoryInput: DG.ChoiceInput<string | null | undefined>;
|
|
109
|
+
valueColumnName: string;
|
|
110
|
+
valueAggregation: DG.AGG;
|
|
111
|
+
|
|
99
112
|
mutationCliffsDebouncer: (
|
|
100
113
|
activityArray: type.RawData, monomerInfoArray: type.RawColumn[], options?: MutationCliffsOptions
|
|
101
114
|
) => Promise<type.MutationCliffs>;
|
|
@@ -114,10 +127,10 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
114
127
|
this.activityTarget = this.string(SAR_PROPERTIES.ACTIVITY_TARGET, C.ACTIVITY_TARGET.HIGH,
|
|
115
128
|
{category: PROPERTY_CATEGORIES.GENERAL, choices: Object.values(C.ACTIVITY_TARGET), nullable: false},
|
|
116
129
|
) as C.ACTIVITY_TARGET;
|
|
117
|
-
// Mutation Cliffs properties
|
|
118
|
-
|
|
119
|
-
this.
|
|
120
|
-
|
|
130
|
+
// Mutation Cliffs/invariant map properties
|
|
131
|
+
// hide it and make it editable through the code
|
|
132
|
+
this.targetColumnName = this.column(SAR_PROPERTIES.TARGET, {
|
|
133
|
+
category: PROPERTY_CATEGORIES.GENERAL, nullable: true, columnTypeFilter: 'categorical', userEditable: true});
|
|
121
134
|
this.minActivityDelta = this.float(SAR_PROPERTIES.MIN_ACTIVITY_DELTA, 0,
|
|
122
135
|
{category: PROPERTY_CATEGORIES.MUTATION_CLIFFS, min: 0, max: 100});
|
|
123
136
|
this.maxMutations = this.int(SAR_PROPERTIES.MAX_MUTATIONS, 1,
|
|
@@ -125,11 +138,35 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
125
138
|
this.columns = this.columnList(SAR_PROPERTIES.COLUMNS, [], {category: PROPERTY_CATEGORIES.AGGREGATION});
|
|
126
139
|
this.aggregation = this.string(SAR_PROPERTIES.AGGREGATION, DG.AGG.AVG,
|
|
127
140
|
{category: PROPERTY_CATEGORIES.AGGREGATION, choices: C.AGGREGATION_TYPES});
|
|
141
|
+
this.valueColumnName = this.column(SAR_PROPERTIES.VALUE_INVARIANT_MAP, {category: PROPERTY_CATEGORIES.INVARIANT_MAP, userEditable: true,
|
|
142
|
+
nullable: false, columnTypeFilter: 'numerical'});
|
|
143
|
+
this.valueAggregation = this.string(SAR_PROPERTIES.AGGREGATION_INVARIANT_MAP_VALUE, DG.AGG.TOTAL_COUNT, {
|
|
144
|
+
category: PROPERTY_CATEGORIES.INVARIANT_MAP, choices: C.AGGREGATION_TYPES, userEditable: true, nullable: false}) as DG.AGG;
|
|
128
145
|
|
|
129
146
|
this.mutationCliffsDebouncer = debounce(
|
|
130
147
|
async (activityArray: type.RawData, monomerInfoArray: type.RawColumn[], options?: MutationCliffsOptions) => {
|
|
131
148
|
return await findMutations(activityArray, monomerInfoArray, options);
|
|
132
149
|
});
|
|
150
|
+
|
|
151
|
+
this.targetCategoryInput = ui.input.choice('Category', {value: null, items: [], nullable: true,
|
|
152
|
+
onValueChanged: () => {
|
|
153
|
+
this._mutationCliffs = null;
|
|
154
|
+
this._mutationCliffStats = null;
|
|
155
|
+
this._mutationCliffsSelection = null;
|
|
156
|
+
this._invariantMapSelection = null;
|
|
157
|
+
this.doRender = false;
|
|
158
|
+
this._monomerPositionStats = null;
|
|
159
|
+
this.positionColumns?.forEach((col) => {
|
|
160
|
+
col.temp[C.TAGS.INVARIANT_MAP_COLOR_CACHE] = null;
|
|
161
|
+
});
|
|
162
|
+
if (this.sequenceColumnName && this.activityColumnName)
|
|
163
|
+
this.calculateMutationCliffs().then((mc) => {this.mutationCliffs = mc.cliffs; this.cliffStats = mc.cliffStats;});
|
|
164
|
+
this.viewerGrid.invalidate();
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
this.targetCategoryInput.root.style.display = 'none';
|
|
168
|
+
this.targetCategoryInput.root.style.width = '50%';
|
|
169
|
+
this.targetCategoryInput.root.style.marginLeft = '8px';
|
|
133
170
|
}
|
|
134
171
|
|
|
135
172
|
_viewerGrid: DG.Grid | null = null;
|
|
@@ -209,14 +246,21 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
209
246
|
const isMonomerPositionStatsEqual = (other: SARViewer | PeptidesSettings | null): boolean =>
|
|
210
247
|
this.sequenceColumnName === other?.sequenceColumnName &&
|
|
211
248
|
this.activityColumnName === other?.activityColumnName &&
|
|
212
|
-
this.activityScaling === other?.activityScaling
|
|
249
|
+
this.activityScaling === other?.activityScaling &&
|
|
250
|
+
((other instanceof SARViewer && this.targetColumnName == other?.targetColumnName &&
|
|
251
|
+
this.targetCategoryInput?.value === other?.targetCategoryInput?.value) ||
|
|
252
|
+
(!(other instanceof SARViewer) && (this.targetColumnName == null || this.targetCategoryInput?.value == null))
|
|
253
|
+
) &&
|
|
254
|
+
((other instanceof SARViewer && this.valueColumnName == other?.valueColumnName && this.valueAggregation == other?.valueAggregation) ||
|
|
255
|
+
(!(other instanceof SARViewer) &&
|
|
256
|
+
(!this.valueColumnName || !this.valueAggregation || this.valueAggregation == DG.AGG.VALUE_COUNT || this.valueAggregation == DG.AGG.TOTAL_COUNT))
|
|
257
|
+
);
|
|
213
258
|
|
|
214
259
|
const getSharedStats = (viewerType: VIEWER_TYPE): MonomerPositionStats | null => {
|
|
215
260
|
const viewer = this.model.findViewer(viewerType) as SARViewer | null;
|
|
216
261
|
if (isMonomerPositionStatsEqual(viewer))
|
|
217
262
|
return viewer!._monomerPositionStats;
|
|
218
263
|
|
|
219
|
-
|
|
220
264
|
return null;
|
|
221
265
|
};
|
|
222
266
|
|
|
@@ -228,14 +272,22 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
228
272
|
this._monomerPositionStats = getSharedStats(VIEWER_TYPE.MONOMER_POSITION);
|
|
229
273
|
|
|
230
274
|
|
|
275
|
+
const targetCol = this.targetColumnName ? this.dataFrame.col(this.targetColumnName) : null;
|
|
276
|
+
const targetCategory = this.targetCategoryInput.value;
|
|
277
|
+
const invariantMapValueCol = this.dataFrame.col(this.valueColumnName);
|
|
278
|
+
const invariantMapValueAgg = this.valueAggregation;
|
|
279
|
+
|
|
231
280
|
this._monomerPositionStats ??= calculateMonomerPositionStatistics(this.getScaledActivityColumn(),
|
|
232
|
-
this.dataFrame.filter, this.positionColumns
|
|
281
|
+
this.dataFrame.filter, this.positionColumns,
|
|
282
|
+
{target: (targetCol && targetCategory) ? {col: targetCol, cat: targetCategory} : undefined,
|
|
283
|
+
aggValue: (invariantMapValueAgg && invariantMapValueCol) ? {col: invariantMapValueCol, type: invariantMapValueAgg} : undefined,
|
|
284
|
+
});
|
|
233
285
|
return this._monomerPositionStats;
|
|
234
286
|
}
|
|
235
287
|
|
|
236
288
|
_mutationCliffs: type.MutationCliffs | null = null;
|
|
237
289
|
_mutationCliffStats: type.MutationCliffStats | null = null;
|
|
238
|
-
|
|
290
|
+
_invariantMapSelection: type.Selection | null = null;
|
|
239
291
|
/**
|
|
240
292
|
* Gets mutation cliffs. If mutation cliffs are not attached to the viewer, it tries to get them from other viewers,
|
|
241
293
|
* or calculates its own.
|
|
@@ -251,7 +303,7 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
251
303
|
v1.activityColumnName === v2.activityColumnName &&
|
|
252
304
|
v1.activityScaling === v2.activityScaling &&
|
|
253
305
|
v1.targetColumnName === v2?.targetColumnName &&
|
|
254
|
-
v1.
|
|
306
|
+
v1.targetCategoryInput?.value === v2?.targetCategoryInput?.value &&
|
|
255
307
|
v1.minActivityDelta === v2?.minActivityDelta &&
|
|
256
308
|
v1.maxMutations === v2?.maxMutations;
|
|
257
309
|
|
|
@@ -368,12 +420,25 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
368
420
|
this._mutationCliffsSelection = modifySelection(this.mutationCliffsSelection, monomerPosition, options);
|
|
369
421
|
}
|
|
370
422
|
|
|
423
|
+
private resetTargetCategoryValue(): void {
|
|
424
|
+
const colName = this.targetColumnName;
|
|
425
|
+
const col = this.dataFrame.col(colName);
|
|
426
|
+
this.targetCategoryInput.items = col?.categories ?? [];
|
|
427
|
+
this.targetCategoryInput.value = null;
|
|
428
|
+
if (!colName)
|
|
429
|
+
this.targetCategoryInput.root.style.display = 'none';
|
|
430
|
+
else
|
|
431
|
+
this.targetCategoryInput.root.style.display = 'flex';
|
|
432
|
+
}
|
|
433
|
+
|
|
371
434
|
/**
|
|
372
435
|
* Processes property changes.
|
|
373
436
|
* @param property - changed property.
|
|
374
437
|
*/
|
|
375
438
|
onPropertyChanged(property: DG.Property): void {
|
|
376
439
|
super.onPropertyChanged(property);
|
|
440
|
+
|
|
441
|
+
|
|
377
442
|
this.doRender = true;
|
|
378
443
|
switch (property.name) {
|
|
379
444
|
case `${SAR_PROPERTIES.SEQUENCE}${COLUMN_NAME}`:
|
|
@@ -393,8 +458,12 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
393
458
|
this._viewerGrid = null;
|
|
394
459
|
this._scaledActivityColumn = null;
|
|
395
460
|
break;
|
|
396
|
-
case `${SAR_PROPERTIES.
|
|
397
|
-
case SAR_PROPERTIES.
|
|
461
|
+
case `${SAR_PROPERTIES.VALUE_INVARIANT_MAP}${COLUMN_NAME}`:
|
|
462
|
+
case SAR_PROPERTIES.AGGREGATION_INVARIANT_MAP_VALUE:
|
|
463
|
+
this._monomerPositionStats = null;
|
|
464
|
+
this._viewerGrid = null;
|
|
465
|
+
this._invariantMapSelection = null;
|
|
466
|
+
break;
|
|
398
467
|
case SAR_PROPERTIES.MIN_ACTIVITY_DELTA:
|
|
399
468
|
case SAR_PROPERTIES.MAX_MUTATIONS:
|
|
400
469
|
this._mutationCliffs = null;
|
|
@@ -414,6 +483,12 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
414
483
|
}
|
|
415
484
|
if (this._mutationCliffs === null && this.sequenceColumnName && this.activityColumnName)
|
|
416
485
|
this.calculateMutationCliffs().then((mc) => {this.mutationCliffs = mc.cliffs; this.cliffStats = mc.cliffStats;});
|
|
486
|
+
|
|
487
|
+
// do this last to avoid recalculating mutation cliffs
|
|
488
|
+
if (property.name === `${SAR_PROPERTIES.TARGET}${COLUMN_NAME}` && this.targetColumnInput) {
|
|
489
|
+
this.targetColumnInput.value = this.targetColumnName ? this.dataFrame.col(this.targetColumnName) : null;
|
|
490
|
+
this.resetTargetCategoryValue();
|
|
491
|
+
}
|
|
417
492
|
}
|
|
418
493
|
|
|
419
494
|
/**
|
|
@@ -454,6 +529,8 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
454
529
|
?.set(this, this.dataFrame.columns.bySemType(DG.SEMTYPE.MACROMOLECULE)!.name);
|
|
455
530
|
this.getProperty(`${SAR_PROPERTIES.ACTIVITY}${COLUMN_NAME}`)
|
|
456
531
|
?.set(this, wu(this.dataFrame.columns.numerical).next().value.name);
|
|
532
|
+
this.getProperty(`${SAR_PROPERTIES.VALUE_INVARIANT_MAP}${COLUMN_NAME}`)
|
|
533
|
+
?.set(this, wu(this.dataFrame.columns.numerical).next().value.name);
|
|
457
534
|
if (this.mutationCliffs === null && this.sequenceColumnName && this.activityColumnName)
|
|
458
535
|
this.calculateMutationCliffs().then((mc) => {this.mutationCliffs = mc.cliffs; this.cliffStats = mc.cliffStats;});
|
|
459
536
|
} else {
|
|
@@ -475,7 +552,7 @@ export abstract class SARViewer extends DG.JsViewer implements ISARViewer {
|
|
|
475
552
|
|
|
476
553
|
const options: MutationCliffsOptions = {
|
|
477
554
|
maxMutations: this.maxMutations, minActivityDelta: this.minActivityDelta,
|
|
478
|
-
targetCol, currentTarget: this.
|
|
555
|
+
targetCol, currentTarget: this.targetCategoryInput.value,
|
|
479
556
|
};
|
|
480
557
|
const activityRawData = scaledActivityCol.getRawData();
|
|
481
558
|
|
|
@@ -490,16 +567,29 @@ export class MonomerPosition extends SARViewer {
|
|
|
490
567
|
colorColumnName: string;
|
|
491
568
|
colorAggregation: string;
|
|
492
569
|
currentGridCell: DG.GridCell | null = null;
|
|
493
|
-
|
|
570
|
+
customColorRange: boolean = false;
|
|
571
|
+
minColorValue: number = 0;
|
|
572
|
+
maxColorValue: number = 0;
|
|
573
|
+
lowerBoundColor: number;
|
|
574
|
+
middleColor: number;
|
|
575
|
+
upperBoundColor: number;
|
|
576
|
+
logScaleColor: boolean = false;
|
|
494
577
|
/** Sets MonomerPosition properties. */
|
|
495
578
|
constructor() {
|
|
496
579
|
super();
|
|
497
580
|
|
|
498
|
-
const colorChoices = wu(grok.shell.t.columns.numerical).toArray().map((col) => col.name);
|
|
499
581
|
this.colorColumnName = this.column(MONOMER_POSITION_PROPERTIES.COLOR,
|
|
500
|
-
{category: PROPERTY_CATEGORIES.INVARIANT_MAP,
|
|
582
|
+
{category: PROPERTY_CATEGORIES.INVARIANT_MAP, nullable: false, columnTypeFilter: 'numerical'});
|
|
501
583
|
this.colorAggregation = this.string(MONOMER_POSITION_PROPERTIES.COLOR_AGGREGATION, DG.AGG.AVG,
|
|
502
584
|
{category: PROPERTY_CATEGORIES.INVARIANT_MAP, choices: C.AGGREGATION_TYPES});
|
|
585
|
+
this.lowerBoundColor = this.int(MONOMER_POSITION_PROPERTIES.LOWER_BOUND_COLOR, 0xFF0000FF, {category: PROPERTY_CATEGORIES.INVARIANT_MAP, editor: 'color'});
|
|
586
|
+
this.middleColor = this.int(MONOMER_POSITION_PROPERTIES.MIDDLE_COLOR, 0xFFFFFFFF, {category: PROPERTY_CATEGORIES.INVARIANT_MAP, editor: 'color'});
|
|
587
|
+
this.upperBoundColor = this.int(MONOMER_POSITION_PROPERTIES.UPPER_BOUND_COLOR, 0xFFFF0000, {category: PROPERTY_CATEGORIES.INVARIANT_MAP, editor: 'color'});
|
|
588
|
+
|
|
589
|
+
this.logScaleColor = this.bool(MONOMER_POSITION_PROPERTIES.LOG_SCALE_COLOR, false, {category: PROPERTY_CATEGORIES.INVARIANT_MAP});
|
|
590
|
+
this.customColorRange = this.bool(MONOMER_POSITION_PROPERTIES.CUSTOM_COLOR_RANGE, false, {category: PROPERTY_CATEGORIES.INVARIANT_MAP});
|
|
591
|
+
this.minColorValue = this.float(MONOMER_POSITION_PROPERTIES.MIN_COLOR_VALUE, 0, {category: PROPERTY_CATEGORIES.INVARIANT_MAP});
|
|
592
|
+
this.maxColorValue = this.float(MONOMER_POSITION_PROPERTIES.MAX_COLOR_VALUE, 0, {category: PROPERTY_CATEGORIES.INVARIANT_MAP});
|
|
503
593
|
}
|
|
504
594
|
|
|
505
595
|
/**
|
|
@@ -529,8 +619,6 @@ export class MonomerPosition extends SARViewer {
|
|
|
529
619
|
// setTimeout(() => this.viewerGrid.invalidate(), 300);
|
|
530
620
|
}
|
|
531
621
|
|
|
532
|
-
_invariantMapSelection: type.Selection | null = null;
|
|
533
|
-
|
|
534
622
|
/**
|
|
535
623
|
* Gets invariant map selection. Initializes it if it is null.
|
|
536
624
|
* @return - invariant map selection.
|
|
@@ -559,6 +647,13 @@ export class MonomerPosition extends SARViewer {
|
|
|
559
647
|
if (isApplicableDataframe(this.dataFrame)) {
|
|
560
648
|
this.getProperty(`${MONOMER_POSITION_PROPERTIES.COLOR}${COLUMN_NAME}`)
|
|
561
649
|
?.set(this, this.activityColumnName);
|
|
650
|
+
this.targetColumnInput = ui.input.column('Target', {value: undefined, nullable: true, table: this.dataFrame,
|
|
651
|
+
onValueChanged: () => {
|
|
652
|
+
const prop = this.getProperty(`${SAR_PROPERTIES.TARGET}${COLUMN_NAME}`);
|
|
653
|
+
if (prop && prop.get(this) !== this.targetColumnInput!.value?.name)
|
|
654
|
+
prop?.set(this, this.targetColumnInput!.value?.name ?? null);
|
|
655
|
+
},
|
|
656
|
+
});
|
|
562
657
|
} else {
|
|
563
658
|
const msg = 'PeptidesError: dataframe is missing Macromolecule or numeric columns';
|
|
564
659
|
grok.log.error(msg);
|
|
@@ -593,21 +688,19 @@ export class MonomerPosition extends SARViewer {
|
|
|
593
688
|
onPropertyChanged(property: DG.Property): void {
|
|
594
689
|
super.onPropertyChanged(property);
|
|
595
690
|
switch (property.name) {
|
|
596
|
-
case MONOMER_POSITION_PROPERTIES.COLOR:
|
|
597
|
-
case MONOMER_POSITION_PROPERTIES.COLOR_AGGREGATION:
|
|
598
|
-
this.viewerGrid.invalidate();
|
|
599
|
-
break;
|
|
600
691
|
case SAR_PROPERTIES.SEQUENCE:
|
|
601
692
|
this._invariantMapSelection = null;
|
|
602
693
|
break;
|
|
603
694
|
}
|
|
604
695
|
|
|
605
696
|
// this will cause colors to recalculate
|
|
606
|
-
this.
|
|
697
|
+
this.positionColumns?.forEach((col) => {
|
|
607
698
|
col.temp[C.TAGS.INVARIANT_MAP_COLOR_CACHE] = null;
|
|
608
699
|
});
|
|
609
700
|
if (this.doRender)
|
|
610
701
|
this.render();
|
|
702
|
+
else
|
|
703
|
+
this.viewerGrid.invalidate();
|
|
611
704
|
}
|
|
612
705
|
|
|
613
706
|
/**
|
|
@@ -642,6 +735,10 @@ export class MonomerPosition extends SARViewer {
|
|
|
642
735
|
const colorColData = colorCol!.getRawData();
|
|
643
736
|
let minColorVal = 9999999;
|
|
644
737
|
let maxColorVal = -9999999;
|
|
738
|
+
const targetCol = this.targetColumnName ? this.dataFrame.col(this.targetColumnName) : null;
|
|
739
|
+
const targetColRawData = targetCol?.getRawData();
|
|
740
|
+
const targetCategory = this.targetCategoryInput.value;
|
|
741
|
+
const targetCategoryIndex = targetCategory == null ? null : targetCol?.categories.indexOf(targetCategory);
|
|
645
742
|
for (const pCol of this.positionColumns) {
|
|
646
743
|
pCol.temp[C.TAGS.INVARIANT_MAP_COLOR_CACHE] = {};
|
|
647
744
|
const colorCache = pCol.temp[C.TAGS.INVARIANT_MAP_COLOR_CACHE];
|
|
@@ -657,7 +754,9 @@ export class MonomerPosition extends SARViewer {
|
|
|
657
754
|
//const pStatItem = pStats[pMonomer]!;
|
|
658
755
|
const colorValuesIndexes: number[] = [];
|
|
659
756
|
for (let i = 0; i < pCol.length; ++i) {
|
|
660
|
-
|
|
757
|
+
const isCurrentMonomer = positionColCategories[positionColData[i]] === pMonomer;
|
|
758
|
+
const isTarget = !targetColRawData || targetCategoryIndex == null || targetCategoryIndex == -1 || targetColRawData[i] === targetCategoryIndex;
|
|
759
|
+
if (isCurrentMonomer && isTarget)
|
|
661
760
|
colorValuesIndexes.push(i);
|
|
662
761
|
}
|
|
663
762
|
const cellColorDataCol = DG.Column.float('color', colorValuesIndexes.length)
|
|
@@ -670,6 +769,14 @@ export class MonomerPosition extends SARViewer {
|
|
|
670
769
|
pCol.temp[C.TAGS.INVARIANT_MAP_COLOR_CACHE] = colorCache;
|
|
671
770
|
}
|
|
672
771
|
|
|
772
|
+
const isCustomRangeSet = this.customColorRange && this.minColorValue != null && this.maxColorValue != null;
|
|
773
|
+
let usedMinValue = isCustomRangeSet ? this.minColorValue : minColorVal;
|
|
774
|
+
let usedMaxValue = isCustomRangeSet ? this.maxColorValue : maxColorVal;
|
|
775
|
+
const logScaleUsed = this.logScaleColor && usedMinValue > 1e-30 && usedMaxValue > 1e-30 && minColorVal > 1e-30 && maxColorVal > 1e-30;
|
|
776
|
+
if (logScaleUsed) {
|
|
777
|
+
usedMinValue = Math.log(usedMinValue);
|
|
778
|
+
usedMaxValue = Math.log(usedMaxValue);
|
|
779
|
+
}
|
|
673
780
|
// do another swing to normalize colors
|
|
674
781
|
for (const pCol of this.positionColumns) {
|
|
675
782
|
const colorCache = pCol.temp[C.TAGS.INVARIANT_MAP_COLOR_CACHE];
|
|
@@ -677,10 +784,15 @@ export class MonomerPosition extends SARViewer {
|
|
|
677
784
|
continue;
|
|
678
785
|
for (const pMonomer of Object.keys(colorCache)) {
|
|
679
786
|
if (this.activityTarget === C.ACTIVITY_TARGET.LOW)
|
|
680
|
-
colorCache[pMonomer] =
|
|
681
|
-
colorCache[pMonomer] = DG.Color.scaleColor(
|
|
787
|
+
colorCache[pMonomer] = usedMaxValue - colorCache[pMonomer] + usedMinValue;
|
|
788
|
+
colorCache[pMonomer] = DG.Color.scaleColor(
|
|
789
|
+
logScaleUsed ? Math.log(colorCache[pMonomer]) : colorCache[pMonomer], usedMinValue, usedMaxValue, undefined,
|
|
790
|
+
[this.lowerBoundColor, this.middleColor, this.upperBoundColor],
|
|
791
|
+
);
|
|
682
792
|
}
|
|
683
793
|
pCol.temp[C.TAGS.INVARIANT_MAP_COLOR_CACHE] = colorCache;
|
|
794
|
+
pCol.temp[C.TAGS.INVARIANT_MAP_COLOR_MIN_CACHE] = minColorVal;
|
|
795
|
+
pCol.temp[C.TAGS.INVARIANT_MAP_COLOR_MAX_CACHE] = maxColorVal;
|
|
684
796
|
}
|
|
685
797
|
}
|
|
686
798
|
}
|
|
@@ -713,7 +825,12 @@ export class MonomerPosition extends SARViewer {
|
|
|
713
825
|
highlightMonomerPosition(monomerPosition, this.dataFrame, this.monomerPositionStats);
|
|
714
826
|
this.model.isHighlighting = true;
|
|
715
827
|
const columnEntries = this.getTotalViewerAggColumns();
|
|
716
|
-
|
|
828
|
+
if (this.mode === SELECTION_MODE.INVARIANT_MAP) {
|
|
829
|
+
if (this.colorColumnName && this.colorAggregation)
|
|
830
|
+
columnEntries.unshift([this.colorColumnName, this.colorAggregation as DG.AGG]);
|
|
831
|
+
if (this.valueColumnName && this.valueAggregation && this.valueAggregation !== DG.AGG.VALUE_COUNT && this.valueAggregation !== DG.AGG.TOTAL_COUNT)
|
|
832
|
+
columnEntries.unshift([this.valueColumnName, this.valueAggregation as DG.AGG]);
|
|
833
|
+
}
|
|
717
834
|
return showTooltip(this.model.df, this.getScaledActivityColumn(), columnEntries, {
|
|
718
835
|
fromViewer: true,
|
|
719
836
|
isMutationCliffs: this.mode === SELECTION_MODE.MUTATION_CLIFFS, monomerPosition, x, y,
|
|
@@ -938,8 +1055,10 @@ export class MonomerPosition extends SARViewer {
|
|
|
938
1055
|
this.viewerGrid.invalidate();
|
|
939
1056
|
}, 'Show Monomer Position Table in full screen');
|
|
940
1057
|
$(expand).addClass('pep-help-icon');
|
|
941
|
-
|
|
942
|
-
const
|
|
1058
|
+
this.targetColumnInput && (this.targetColumnInput.root.style.width = '50%');
|
|
1059
|
+
const targetInputsHost = ui.divH([this.targetColumnInput?.root ?? ui.div(), this.targetCategoryInput.root],
|
|
1060
|
+
{style: {alignSelf: 'center', justifyContent: 'center'}});
|
|
1061
|
+
const header = ui.divH([expand, switchHost, targetInputsHost], {style: {alignSelf: 'center', lineHeight: 'normal', flexDirection: 'column'}});
|
|
943
1062
|
this.root.appendChild(ui.divV([header, viewerRoot]));
|
|
944
1063
|
this.viewerGrid?.invalidate();
|
|
945
1064
|
}
|
|
@@ -1013,7 +1132,7 @@ export class MostPotentResidues extends SARViewer {
|
|
|
1013
1132
|
continue;
|
|
1014
1133
|
|
|
1015
1134
|
|
|
1016
|
-
if ((monomerStats as StatsItem).count > 1 && (monomerStats as StatsItem).pValue
|
|
1135
|
+
if ((monomerStats as StatsItem).count > 1 && ((monomerStats as StatsItem).pValue == null || ((monomerStats as StatsItem).pValue ?? 1) <= 0.05))
|
|
1017
1136
|
filteredMonomerStats.push([monomer, monomerStats as StatsItem]);
|
|
1018
1137
|
|
|
1019
1138
|
|
|
@@ -1281,12 +1400,12 @@ function renderCell(args: DG.GridCellRenderArgs, viewer: SARViewer, isInvariantM
|
|
|
1281
1400
|
}
|
|
1282
1401
|
|
|
1283
1402
|
if (isInvariantMap) {
|
|
1284
|
-
const value = currentPosStats![currentMonomer]!.count;
|
|
1403
|
+
const value = currentPosStats![currentMonomer]!.aggValue ?? currentPosStats![currentMonomer]!.count;
|
|
1285
1404
|
const positionCol = viewer.positionColumns.find((col) => col.name === currentPosition)!;
|
|
1286
1405
|
const colorCache: { [_: string]: number } = positionCol.temp[C.TAGS.INVARIANT_MAP_COLOR_CACHE] ?? {};
|
|
1287
1406
|
let color: number = DG.Color.white;
|
|
1288
1407
|
// const colorColStats = colorCol!.stats;
|
|
1289
|
-
if (colorCache[currentMonomer])
|
|
1408
|
+
if (colorCache[currentMonomer] != null)
|
|
1290
1409
|
color = colorCache[currentMonomer];
|
|
1291
1410
|
else if (viewer instanceof MonomerPosition) {
|
|
1292
1411
|
viewer.cacheInvariantMapColors();
|