@datagrok/peptides 1.15.0 → 1.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,7 @@ import {findMutations} from '../utils/algorithms';
7
7
  import * as type from '../utils/types';
8
8
  import {extractColInfo} from '../utils/misc';
9
9
 
10
+
10
11
  category('Algorithms', () => {
11
12
  let activityCol: type.RawData;
12
13
  let monomerColumns: type.RawColumn[];
@@ -30,7 +31,7 @@ category('Algorithms', () => {
30
31
 
31
32
  test('MutationCliffs', async () => {
32
33
  // Check every pair
33
- let mutationCliffsInfo: type.MutationCliffs = findMutations(activityCol, monomerColumns, settings);
34
+ let mutationCliffsInfo: type.MutationCliffs = await findMutations(activityCol, monomerColumns, settings);
34
35
  expect(mutationCliffsInfo.has('C'), true, `MutationCliffsInfo should have key 'C'`);
35
36
  expect(mutationCliffsInfo.has('D'), true, `MutationCliffsInfo should have key 'D'`);
36
37
  expect(mutationCliffsInfo.has('A'), false, `MutationCliffsInfo should not have key 'A'`);
@@ -51,21 +52,7 @@ category('Algorithms', () => {
51
52
  expect(d32[0], 1, `MutationCliffsInfo['D']['3'][2] should have value 1`);
52
53
 
53
54
  // Check with target
54
- mutationCliffsInfo = findMutations(activityCol, monomerColumns, settings, {targetCol, currentTarget: '1'});
55
+ mutationCliffsInfo = await findMutations(activityCol, monomerColumns, settings, {targetCol, currentTarget: '1'});
55
56
  expect(mutationCliffsInfo.size, 0, `MutationCliffsInfo should be empty for target '1'`);
56
57
  });
57
-
58
- test('MutationCliffs - Benchmark 5k', async () => {
59
- if (!DG.Test.isInBenchmark)
60
- return;
61
-
62
- const df = (await _package.files.readBinaryDataFrames('tests/aligned_5k.d42'))[0];
63
- const activityCol: type.RawData = df.getCol('Activity').getRawData();
64
- const monomerCols: type.RawColumn[] = [];
65
- for (let i = 1; i < 16; ++i) {
66
- const col = df.getCol(i.toString());
67
- monomerCols.push({name: col.name, rawData: col.getRawData(), cat: col.categories});
68
- }
69
- DG.time('MutationCliffs', () => findMutations(activityCol, monomerCols));
70
- }, {timeout: 5000});
71
58
  });
@@ -74,19 +74,6 @@ category('Model: Settings', () => {
74
74
  expectFloat(scaledActivityData[i], origActivityData[i], tolerance, getError(i, SCALING_METHODS.NONE));
75
75
  });
76
76
 
77
- test('Bidirectional analysis', async () => {
78
- // Check that bidirectional analysis is disabled by default
79
- expect(model.settings.isBidirectional, false, 'Bidirectional analysis is enabled by default');
80
-
81
- // Check that bidirectional analysis can be enabled
82
- model.settings = {isBidirectional: true};
83
- expect(model.settings.isBidirectional, true, 'Bidirectional analysis is disabled after enabling');
84
-
85
- // Check that bidirectional analysis can be disabled
86
- model.settings = {isBidirectional: false};
87
- expect(model.settings.isBidirectional, false, 'Bidirectional analysis is enabled after disabling');
88
- });
89
-
90
77
  test('Mutation Cliffs', async () => {
91
78
  // Check default mutation cliffs parameters
92
79
  expect(model.settings.maxMutations, mutationCliffsDefaultParams.maxMutations, `Max mutations mismatch: expected ` +
@@ -48,7 +48,7 @@ category('Table view', () => {
48
48
 
49
49
  test('Visible columns', async () => {
50
50
  const gridCols = model.analysisView.grid.columns;
51
- const posCols = model.splitSeqDf.columns.names();
51
+ const posCols = model.positionColumns.toArray().map((col) => col.name);
52
52
  for (let colIdx = 1; colIdx < gridCols.length; colIdx++) {
53
53
  const col = gridCols.byIndex(colIdx)!;
54
54
  const tableColName = col.column!.name;
@@ -1,22 +1,32 @@
1
+ import * as DG from 'datagrok-api/dg';
1
2
  import * as C from './constants';
2
3
  import * as type from './types';
3
- import {getTypedArrayConstructor} from './misc';
4
+ import {ParallelMutationCliffs} from './parallel-mutation-cliffs';
5
+ import {CLUSTER_TYPE, ClusterStats, ClusterTypeStats,
6
+ MonomerPositionStats, PositionStats, SummaryStats} from '../model';
7
+ import BitArray from '@datagrok-libraries/utils/src/bit-array';
8
+ import {Stats, getStats} from './statistics';
4
9
 
5
- type MutationCliffInfo = {pos: string, seq1monomer: string, seq2monomer: string, seq1Idx: number, seq2Idx: number};
6
10
 
7
- export function findMutations(activityArray: type.RawData, monomerInfoArray: type.RawColumn[],
11
+ export async function findMutations(activityArray: type.RawData, monomerInfoArray: type.RawColumn[],
8
12
  settings: type.PeptidesSettings = {},
9
- targetOptions: {targetCol?: type.RawColumn | null, currentTarget?: string | null} = {}): type.MutationCliffs {
13
+ targetOptions: {targetCol?: type.RawColumn | null, currentTarget?: string | null} = {},
14
+ ): Promise<type.MutationCliffs> {
10
15
  const nCols = monomerInfoArray.length;
11
16
  if (nCols === 0)
12
17
  throw new Error(`PepAlgorithmError: Couldn't find any column of semType '${C.SEM_TYPES.MONOMER}'`);
13
18
 
14
19
  settings.minActivityDelta ??= 0;
15
20
  settings.maxMutations ??= 1;
16
- const currentTargetIdx = targetOptions.targetCol?.cat!.indexOf(targetOptions.currentTarget!) ?? -1;
21
+ //const currentTargetIdx = targetOptions.targetCol?.cat!.indexOf(targetOptions.currentTarget!) ?? -1;
17
22
 
18
- const substitutionsInfo: type.MutationCliffs = new Map();
19
- const nRows = activityArray.length;
23
+ //const substitutionsInfo: type.MutationCliffs = new Map();
24
+ //const nRows = activityArray.length;
25
+
26
+ const substitutionsInfo =
27
+ await new ParallelMutationCliffs().calc(activityArray, monomerInfoArray, settings, targetOptions);
28
+ return substitutionsInfo;
29
+ /*
20
30
  for (let seq1Idx = 0; seq1Idx < nRows - 1; seq1Idx++) {
21
31
  if (currentTargetIdx !== -1 && targetOptions.targetCol?.rawData[seq1Idx] !== currentTargetIdx)
22
32
  continue;
@@ -33,11 +43,12 @@ export function findMutations(activityArray: type.RawData, monomerInfoArray: typ
33
43
  continue;
34
44
 
35
45
  let substCounterFlag = false;
36
- const tempData: MutationCliffInfo[] = [];
46
+ const tempData: MutationCliffInfo[] = new Array(monomerInfoArray.length);
47
+ let tempDataIdx = 0;
37
48
  for (const monomerInfo of monomerInfoArray) {
38
- const seq1category = monomerInfo.rawData[seq1Idx];
39
- const seq2category = monomerInfo.rawData[seq2Idx];
40
- if (seq1category === seq2category)
49
+ const seq1categoryIdx = monomerInfo.rawData[seq1Idx];
50
+ const seq2categoryIdx = monomerInfo.rawData[seq2Idx];
51
+ if (seq1categoryIdx === seq2categoryIdx)
41
52
  continue;
42
53
 
43
54
  substCounter++;
@@ -45,19 +56,22 @@ export function findMutations(activityArray: type.RawData, monomerInfoArray: typ
45
56
  if (substCounterFlag)
46
57
  break;
47
58
 
48
- tempData.push({
59
+ tempData[tempDataIdx++] ={
49
60
  pos: monomerInfo.name,
50
- seq1monomer: monomerInfo.cat![seq1category],
51
- seq2monomer: monomerInfo.cat![seq2category],
61
+ seq1monomer: monomerInfo.cat![seq1categoryIdx],
62
+ seq2monomer: monomerInfo.cat![seq2categoryIdx],
52
63
  seq1Idx: seq1Idx,
53
64
  seq2Idx: seq2Idx,
54
- });
65
+ };
55
66
  }
56
67
 
57
68
  if (substCounterFlag || substCounter === 0)
58
69
  continue;
59
70
 
60
- for (const tempDataElement of tempData) {
71
+ // Separate processing loop in case substCOunter is 0 or out of restricted range to
72
+ // prevent unnecessary computations
73
+ for (let i = 0; i < tempDataIdx; i++) {
74
+ const tempDataElement = tempData[i];
61
75
  //Working with seq1monomer
62
76
  const seq1monomer = tempDataElement.seq1monomer;
63
77
  if (!substitutionsInfo.has(seq1monomer))
@@ -102,4 +116,140 @@ export function findMutations(activityArray: type.RawData, monomerInfoArray: typ
102
116
  }
103
117
 
104
118
  return substitutionsInfo;
119
+ */
120
+ }
121
+
122
+ export function calculateMonomerPositionStatistics(df: DG.DataFrame, positionColumns: DG.Column<string>[],
123
+ options: {isFiltered?: boolean, columns?: string[]} = {}): MonomerPositionStats {
124
+ options.isFiltered ??= false;
125
+ const monomerPositionObject = {general: {}} as MonomerPositionStats & {general: SummaryStats};
126
+ let activityColData: Float64Array = df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).getRawData() as Float64Array;
127
+ let sourceDfLen = df.rowCount;
128
+
129
+ if (options.isFiltered) {
130
+ sourceDfLen = df.filter.trueCount;
131
+ const tempActivityData = new Float64Array(sourceDfLen);
132
+ const selectedIndexes = df.filter.getSelectedIndexes();
133
+ for (let i = 0; i < sourceDfLen; ++i)
134
+ tempActivityData[i] = activityColData[selectedIndexes[i]];
135
+ activityColData = tempActivityData;
136
+ positionColumns = DG.DataFrame.fromColumns(positionColumns).clone(df.filter).columns.toList();
137
+ }
138
+ options.columns ??= positionColumns.map((col) => col.name);
139
+
140
+ for (const posCol of positionColumns) {
141
+ if (!options.columns.includes(posCol.name))
142
+ continue;
143
+ const posColData = posCol.getRawData();
144
+ const posColCateogries = posCol.categories;
145
+ const currentPositionObject = {general: {}} as PositionStats & {general: SummaryStats};
146
+
147
+ for (let categoryIndex = 0; categoryIndex < posColCateogries.length; ++categoryIndex) {
148
+ const monomer = posColCateogries[categoryIndex];
149
+ if (monomer === '')
150
+ continue;
151
+
152
+ const boolArray: boolean[] = new Array(sourceDfLen).fill(false);
153
+ for (let i = 0; i < sourceDfLen; ++i) {
154
+ if (posColData[i] === categoryIndex)
155
+ boolArray[i] = true;
156
+ }
157
+ const bitArray = BitArray.fromValues(boolArray);
158
+ const stats = bitArray.allFalse || bitArray.allTrue ?
159
+ {count: sourceDfLen, meanDifference: 0, ratio: 1.0, pValue: null, mask: bitArray} :
160
+ getStats(activityColData, bitArray);
161
+ currentPositionObject[monomer] = stats;
162
+ getSummaryStats(currentPositionObject.general, stats);
163
+ }
164
+ monomerPositionObject[posCol.name] = currentPositionObject;
165
+ getSummaryStats(monomerPositionObject.general, null, currentPositionObject.general);
166
+ }
167
+ return monomerPositionObject;
168
+ }
169
+
170
+ export function getSummaryStats(
171
+ genObj: SummaryStats, stats: Stats | null = null, summaryStats: SummaryStats | null = null,
172
+ ): void {
173
+ if (stats === null && summaryStats === null)
174
+ throw new Error(`MonomerPositionStatsError: either stats or summaryStats must be present`);
175
+
176
+ const possibleMaxCount = stats?.count ?? summaryStats!.maxCount;
177
+ genObj.maxCount ??= possibleMaxCount;
178
+ if (genObj.maxCount < possibleMaxCount)
179
+ genObj.maxCount = possibleMaxCount;
180
+
181
+ const possibleMinCount = stats?.count ?? summaryStats!.minCount;
182
+ genObj.minCount ??= possibleMinCount;
183
+ if (genObj.minCount > possibleMinCount)
184
+ genObj.minCount = possibleMinCount;
185
+
186
+ const possibleMaxMeanDifference = stats?.meanDifference ?? summaryStats!.maxMeanDifference;
187
+ genObj.maxMeanDifference ??= possibleMaxMeanDifference;
188
+ if (genObj.maxMeanDifference < possibleMaxMeanDifference)
189
+ genObj.maxMeanDifference = possibleMaxMeanDifference;
190
+
191
+ const possibleMinMeanDifference = stats?.meanDifference ?? summaryStats!.minMeanDifference;
192
+ genObj.minMeanDifference ??= possibleMinMeanDifference;
193
+ if (genObj.minMeanDifference > possibleMinMeanDifference)
194
+ genObj.minMeanDifference = possibleMinMeanDifference;
195
+
196
+ if (!isNaN(stats?.pValue ?? NaN)) {
197
+ const possibleMaxPValue = stats?.pValue ?? summaryStats!.maxPValue;
198
+ genObj.maxPValue ??= possibleMaxPValue;
199
+ if (genObj.maxPValue < possibleMaxPValue)
200
+ genObj.maxPValue = possibleMaxPValue;
201
+
202
+ const possibleMinPValue = stats?.pValue ?? summaryStats!.minPValue;
203
+ genObj.minPValue ??= possibleMinPValue;
204
+ if (genObj.minPValue > possibleMinPValue)
205
+ genObj.minPValue = possibleMinPValue;
206
+ }
207
+
208
+ const possibleMaxRatio = stats?.ratio ?? summaryStats!.maxRatio;
209
+ genObj.maxRatio ??= possibleMaxRatio;
210
+ if (genObj.maxRatio < possibleMaxRatio)
211
+ genObj.maxRatio = possibleMaxRatio;
212
+
213
+ const possibleMinRatio = stats?.ratio ?? summaryStats!.minRatio;
214
+ genObj.minRatio ??= possibleMinRatio;
215
+ if (genObj.minRatio > possibleMinRatio)
216
+ genObj.minRatio = possibleMinRatio;
217
+ }
218
+
219
+ export function calculateClusterStatistics(df: DG.DataFrame, clustersColumnName: string,
220
+ customClusters: DG.Column<boolean>[]): ClusterTypeStats {
221
+ const rowCount = df.rowCount;
222
+ const origClustCol = df.getCol(clustersColumnName);
223
+ const origClustColData = origClustCol.getRawData();
224
+ const origClustColCat = origClustCol.categories;
225
+ const origClustMasks: BitArray[] = Array.from({length: origClustColCat.length},
226
+ () => new BitArray(rowCount, false));
227
+ for (let rowIdx = 0; rowIdx < rowCount; ++rowIdx)
228
+ origClustMasks[origClustColData[rowIdx]].setTrue(rowIdx);
229
+
230
+ const customClustMasks = customClusters.map(
231
+ (v) => BitArray.fromUint32Array(rowCount, v.getRawData() as Uint32Array));
232
+ const customClustColNamesList = customClusters.map((v) => v.name);
233
+
234
+ const activityColData: type.RawData = df.getCol(C.COLUMNS_NAMES.ACTIVITY_SCALED).getRawData();
235
+
236
+ const origClustStats: ClusterStats = {};
237
+ const customClustStats: ClusterStats = {};
238
+
239
+ for (const clustType of Object.values(CLUSTER_TYPE)) {
240
+ const masks = clustType === CLUSTER_TYPE.ORIGINAL ? origClustMasks : customClustMasks;
241
+ const clustNames = clustType === CLUSTER_TYPE.ORIGINAL ? origClustColCat : customClustColNamesList;
242
+ const resultStats = clustType === CLUSTER_TYPE.ORIGINAL ? origClustStats : customClustStats;
243
+ for (let maskIdx = 0; maskIdx < masks.length; ++maskIdx) {
244
+ const mask = masks[maskIdx];
245
+ const stats = mask.allTrue || mask.allFalse ?
246
+ {count: mask.length, meanDifference: 0, ratio: 1.0, pValue: null, mask: mask} : getStats(activityColData, mask);
247
+ resultStats[clustNames[maskIdx]] = stats;
248
+ }
249
+ }
250
+
251
+ const resultStats = {} as ClusterTypeStats;
252
+ resultStats[CLUSTER_TYPE.ORIGINAL] = origClustStats;
253
+ resultStats[CLUSTER_TYPE.CUSTOM] = customClustStats;
254
+ return resultStats;
105
255
  }
@@ -22,7 +22,7 @@ export function setMonomerRenderer(col: DG.Column, alphabet: string): void {
22
22
  export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D, currentMonomer: string,
23
23
  currentPosition: string, monomerPositionStats: MonomerPositionStats, bound: DG.Rect,
24
24
  mutationCliffsSelection: types.Selection, substitutionsInfo: types.MutationCliffs | null = null,
25
- _twoColorMode: boolean = false, renderNums: boolean = true): void {
25
+ renderNums: boolean = true): void {
26
26
  const positionStats = monomerPositionStats[currentPosition];
27
27
  const pVal = positionStats![currentMonomer]!.pValue;
28
28
  const currentMeanDifference = positionStats![currentMonomer]!.meanDifference;
@@ -44,6 +44,7 @@ export enum TAGS {
44
44
  IDENTITY_TEMPLATE = 'Identity template',
45
45
  SIMILARITY_TEMPLATE = 'Similarity template',
46
46
  ANALYSIS_COL = 'isAnalysisCol',
47
+ POSITION_COL = 'isPositionCol',
47
48
  }
48
49
 
49
50
  export enum SEM_TYPES {
@@ -0,0 +1,106 @@
1
+
2
+ import * as type from './types';
3
+
4
+ export type ParallelMutationReturnType = {
5
+ // monomers1: string[],
6
+ // monomers2: string[],
7
+ pos: string[],
8
+ seq1Idxs: Uint32Array,
9
+ seq2Idxs: Uint32Array,
10
+ }
11
+ export class ParallelMutationCliffs {
12
+ private _workers: Worker[];
13
+ private _workerCount: number;
14
+
15
+ constructor() {
16
+ const threadCount = navigator.hardwareConcurrency;
17
+ this._workerCount = Math.max(threadCount - 2, 1);
18
+ this._workers = new Array(this._workerCount).fill(null)
19
+ .map(() => new Worker(new URL('../workers/mutation-cliffs-worker', import.meta.url)));
20
+ }
21
+
22
+ public async calc(activityArray: type.RawData, monomerInfoArray: type.RawColumn[],
23
+ settings: type.PeptidesSettings = {},
24
+ targetOptions: {targetCol?: type.RawColumn | null, currentTarget?: string | null} = {},
25
+ ): Promise<type.MutationCliffs> {
26
+ const substitutionsInfo: type.MutationCliffs = new Map();
27
+ try {
28
+ const currentTargetIdx = targetOptions.targetCol?.cat!.indexOf(targetOptions.currentTarget!) ?? -1;
29
+
30
+ const len = activityArray.length;
31
+ const promises = new Array<Promise<ParallelMutationReturnType>>(this._workerCount);
32
+ const matSize = len * (len - 1) / 2; // size of reduced upper triangular matrix
33
+ this._workerCount = Math.min(this._workerCount, matSize);
34
+ const chunkSize = matSize / this._workerCount;
35
+ // monomerInfoArray[m].cat and targetCol can contain some function from datagrok-api,
36
+ //which the worker can't serialize and fails. so we need to remove it
37
+ monomerInfoArray.forEach((monomerInfo) => {
38
+ monomerInfo.cat = monomerInfo.cat?.slice();
39
+ });
40
+ targetOptions.targetCol?.cat && (targetOptions.targetCol.cat = targetOptions.targetCol.cat.slice());
41
+ for (let idx = 0; idx < this._workerCount; idx++) {
42
+ promises[idx] = new Promise((resolveWorker, rejectWorker) => {
43
+ const startIdx = Math.floor(idx * chunkSize);
44
+ const endIdx = idx === this._workerCount - 1 ? matSize : Math.floor((idx + 1) * chunkSize);
45
+ this._workers[idx].postMessage(
46
+ {startIdx, endIdx, activityArray, monomerInfoArray, settings, currentTargetIdx, targetOptions});
47
+ this._workers[idx].onmessage = ({data: {pos, seq1Idxs, seq2Idxs, error}}): void => {
48
+ if (error) {
49
+ this._workers[idx]?.terminate();
50
+ rejectWorker(error);
51
+ } else {
52
+ this._workers[idx].terminate();
53
+ resolveWorker({pos, seq1Idxs, seq2Idxs});
54
+ }
55
+ };
56
+ });
57
+ }
58
+
59
+ const results = await Promise.all(promises);
60
+ const monomerPositionsMap = new Map<string, number>();
61
+ monomerInfoArray.forEach((monomerInfo, i) => {
62
+ monomerPositionsMap.set(monomerInfo.name, i);
63
+ });
64
+ results.filter(Boolean).forEach((result) => {
65
+ for (let i = 0; i< result.pos.length; i++) {
66
+ //getting monomers from monomerInfoArray by position
67
+ const monomerPos = monomerPositionsMap.get(result.pos[i])!;
68
+ const monomer1Cat = monomerInfoArray[monomerPos].rawData[result.seq1Idxs[i]];
69
+ const monomer1 = monomerInfoArray[monomerPos].cat![monomer1Cat];
70
+ const monomer2Cat = monomerInfoArray[monomerPos].rawData[result.seq2Idxs[i]];
71
+ const monomer2 = monomerInfoArray[monomerPos].cat![monomer2Cat];
72
+
73
+ // filling map
74
+ if (!substitutionsInfo.has(monomer1))
75
+ substitutionsInfo.set(monomer1, new Map());
76
+ if (!substitutionsInfo.has(monomer2))
77
+ substitutionsInfo.set(monomer2, new Map());
78
+ const position1Map = substitutionsInfo.get(monomer1)!;
79
+ const position2Map = substitutionsInfo.get(monomer2)!;
80
+ if (!position1Map.has(result.pos[i]))
81
+ position1Map.set(result.pos[i], new Map());
82
+ if (!position2Map.has(result.pos[i]))
83
+ position2Map.set(result.pos[i], new Map());
84
+ const indexes1Map = position1Map.get(result.pos[i])!;
85
+ const indexes2Map = position2Map.get(result.pos[i])!;
86
+ if (!indexes1Map.has(result.seq1Idxs[i]))
87
+ indexes1Map.set(result.seq1Idxs[i], []);
88
+ if (!indexes2Map.has(result.seq2Idxs[i]))
89
+ indexes2Map.set(result.seq2Idxs[i], []);
90
+ const indexes1 = indexes1Map.get(result.seq1Idxs[i])!;
91
+ const indexes2 = indexes2Map.get(result.seq2Idxs[i])!;
92
+ (indexes1 as number[]).push(result.seq2Idxs[i]);
93
+ (indexes2 as number[]).push(result.seq1Idxs[i]);
94
+ }
95
+ });
96
+ } catch (error) {
97
+ this.terminate();
98
+ console.error(error);
99
+ }
100
+ return substitutionsInfo;
101
+ }
102
+
103
+ public terminate(): void {
104
+ this._workers?.forEach((worker) => worker?.terminate());
105
+ }
106
+ }
@@ -18,7 +18,6 @@ export type PeptidesSettings = {
18
18
  clustersColumnName?: string,
19
19
  targetColumnName?: string,
20
20
  scaling?: SCALING_METHODS,
21
- isBidirectional?: boolean,
22
21
  showMonomerPosition?: boolean,
23
22
  showMostPotentResidues?: boolean,
24
23
  showLogoSummaryTable?: boolean,
@@ -214,7 +214,6 @@ export class LogoSummaryTable extends DG.JsViewer {
214
214
  this.viewerGrid.sort([C.LST_COLUMN_NAMES.MEMBERS], [false]);
215
215
  this.updateFilter();
216
216
  const gridClustersCol = this.viewerGrid.col(C.LST_COLUMN_NAMES.CLUSTER)!;
217
- // gridClustersCol.column!.name = C.LST_COLUMN_NAMES.CLUSTER;
218
217
  gridClustersCol.visible = true;
219
218
  this.viewerGrid.columns.setOrder([C.LST_COLUMN_NAMES.CLUSTER, C.LST_COLUMN_NAMES.MEMBERS,
220
219
  C.LST_COLUMN_NAMES.WEB_LOGO, C.LST_COLUMN_NAMES.DISTRIBUTION, C.LST_COLUMN_NAMES.MEAN_DIFFERENCE,
@@ -224,7 +223,7 @@ export class LogoSummaryTable extends DG.JsViewer {
224
223
 
225
224
  const webLogoCache = new DG.LruCache<number, DG.Viewer & IWebLogoViewer>();
226
225
  const distCache = new DG.LruCache<number, DG.Viewer<DG.IHistogramLookSettings>>();
227
- const maxSequenceLen = this.model.splitSeqDf.columns.length;
226
+ const maxSequenceLen = this.model.positionColumns.toArray().length;
228
227
  const webLogoGridCol = this.viewerGrid.columns.byName(C.LST_COLUMN_NAMES.WEB_LOGO)!;
229
228
  webLogoGridCol.cellType = 'html';
230
229
  webLogoGridCol.width = 350;
@@ -270,7 +269,7 @@ export class LogoSummaryTable extends DG.JsViewer {
270
269
  const webLogoTable = this.createWebLogoDf(pepCol, clusterBitSet);
271
270
  viewer = await webLogoTable.plot
272
271
  .fromType('WebLogo', {positionHeight: this.webLogoMode, horizontalAlignment: HorizontalAlignments.LEFT,
273
- maxHeight: 1000, minHeight: height, positionWidth: positionWidth});
272
+ maxHeight: 1000, minHeight: height, positionWidth: positionWidth, showPositionLabels: false});
274
273
  webLogoCache.set(currentRowIdx, viewer);
275
274
  }
276
275
  gridCell.element = viewer.root;
@@ -27,7 +27,7 @@ export class MonomerPosition extends DG.JsViewer {
27
27
  _titleHost = ui.divText(SELECTION_MODE.MUTATION_CLIFFS, {id: 'pep-viewer-title'});
28
28
  _viewerGrid!: DG.Grid;
29
29
  _model!: PeptidesModel;
30
- colorCol: string;
30
+ color: string;
31
31
  aggregation: string;
32
32
  target: string;
33
33
  keyPressed: boolean = false;
@@ -37,7 +37,7 @@ export class MonomerPosition extends DG.JsViewer {
37
37
  super();
38
38
  this.target = this.string(MONOMER_POSITION_PROPERTIES.TARGET, null,
39
39
  {category: SELECTION_MODE.MUTATION_CLIFFS, choices: []});
40
- this.colorCol = this.string(MONOMER_POSITION_PROPERTIES.COLOR_COLUMN_NAME, C.COLUMNS_NAMES.ACTIVITY_SCALED,
40
+ this.color = this.string(MONOMER_POSITION_PROPERTIES.COLOR_COLUMN_NAME, C.COLUMNS_NAMES.ACTIVITY_SCALED,
41
41
  {category: SELECTION_MODE.INVARIANT_MAP,
42
42
  choices: wu(grok.shell.t.columns.numerical).toArray().map((col) => col.name)});
43
43
  this.aggregation = this.string(MONOMER_POSITION_PROPERTIES.AGGREGATION, DG.AGG.AVG,
@@ -82,14 +82,14 @@ export class MonomerPosition extends DG.JsViewer {
82
82
  onPropertyChanged(property: DG.Property): void {
83
83
  super.onPropertyChanged(property);
84
84
  if (property.name === MONOMER_POSITION_PROPERTIES.TARGET)
85
- this.model.updateMutationCliffs();
85
+ this.model.updateMutationCliffs().then(() => this.render(true));
86
86
 
87
- this.render();
87
+ this.render(true);
88
88
  }
89
89
 
90
90
  createMonomerPositionDf(): DG.DataFrame {
91
91
  const uniqueMonomers = new Set<string>();
92
- const splitSeqCols = this.model.splitSeqDf.columns;
92
+ const splitSeqCols = this.model.positionColumns.toArray();
93
93
  for (const col of splitSeqCols) {
94
94
  const colCat = col.categories;
95
95
  for (const cat of colCat) {
@@ -112,11 +112,11 @@ export class MonomerPosition extends DG.JsViewer {
112
112
  const monomerPositionDf = this.createMonomerPositionDf();
113
113
  this.viewerGrid = monomerPositionDf.plot.grid();
114
114
  this.viewerGrid.sort([C.COLUMNS_NAMES.MONOMER]);
115
- this.viewerGrid.columns.setOrder([C.COLUMNS_NAMES.MONOMER, ...this.model.splitSeqDf.columns.names()]);
115
+ this.viewerGrid.columns.setOrder([C.COLUMNS_NAMES.MONOMER, ...this.model.positionColumns.toArray().map((col) => col.name)]);
116
116
  const monomerCol = monomerPositionDf.getCol(C.COLUMNS_NAMES.MONOMER);
117
117
  CR.setMonomerRenderer(monomerCol, this.model.alphabet);
118
118
  this.viewerGrid.onCellRender.subscribe((args: DG.GridCellRenderArgs) => renderCell(args, this.model,
119
- this.mode === SELECTION_MODE.INVARIANT_MAP, this.dataFrame.getCol(this.colorCol), this.aggregation as DG.AGG));
119
+ this.mode === SELECTION_MODE.INVARIANT_MAP, this.dataFrame.getCol(this.color), this.aggregation as DG.AGG));
120
120
 
121
121
  this.viewerGrid.onCellTooltip((gridCell: DG.GridCell, x: number, y: number) => {
122
122
  if (!gridCell.isTableCell) {
@@ -367,7 +367,7 @@ export class MostPotentResidues extends DG.JsViewer {
367
367
  return;
368
368
  const monomerPosition = this.getMonomerPosition(gridCell);
369
369
  if (this.currentGridRowIdx !== null) {
370
- const previousMonomerPosition = this.getMonomerPosition(this.viewerGrid.cell("Diff", this.currentGridRowIdx));
370
+ const previousMonomerPosition = this.getMonomerPosition(this.viewerGrid.cell('Diff', this.currentGridRowIdx));
371
371
  this.model.modifyMutationCliffsSelection(previousMonomerPosition, {shiftPressed: true, ctrlPressed: true}, false);
372
372
  }
373
373
  if (this.model.mutationCliffs?.get(monomerPosition.monomerOrCluster)?.get(monomerPosition.positionOrClusterType)?.size)
@@ -420,7 +420,7 @@ export class MostPotentResidues extends DG.JsViewer {
420
420
 
421
421
  function renderCell(args: DG.GridCellRenderArgs, model: PeptidesModel, isInvariantMap?: boolean,
422
422
  colorCol?: DG.Column<number>, colorAgg?: DG.AGG, renderNums?: boolean): void {
423
- const renderColNames = [...model.splitSeqDf.columns.names(), C.COLUMNS_NAMES.MEAN_DIFFERENCE];
423
+ const renderColNames = [...model.positionColumns.toArray().map((col) => col.name), C.COLUMNS_NAMES.MEAN_DIFFERENCE];
424
424
  const canvasContext = args.g;
425
425
  const bound = args.bounds;
426
426
 
@@ -478,7 +478,7 @@ function renderCell(args: DG.GridCellRenderArgs, model: PeptidesModel, isInvaria
478
478
  canvasContext, currentMonomer, currentPosition, model.invariantMapSelection, value, bound, color);
479
479
  } else {
480
480
  CR.renderMutationCliffCell(canvasContext, currentMonomer, currentPosition, model.monomerPositionStats, bound,
481
- model.mutationCliffsSelection, model.mutationCliffs, model.settings.isBidirectional, renderNums);
481
+ model.mutationCliffsSelection, model.mutationCliffs, renderNums);
482
482
  }
483
483
  args.preventDefault();
484
484
  canvasContext.restore();
@@ -147,11 +147,11 @@ export function mutationCliffsWidget(table: DG.DataFrame, model: PeptidesModel):
147
147
 
148
148
  const gridCols = model.analysisView.grid.columns;
149
149
  const originalGridColCount = gridCols.length;
150
- const positionColumns = model.splitSeqDf.columns;
150
+ const positionColumns = model.positionColumns.toArray().map((col) => col.name);
151
151
  const columnNames: string[] = [];
152
152
  for (let colIdx = 1; colIdx < originalGridColCount; colIdx++) {
153
153
  const gridCol = gridCols.byIndex(colIdx);
154
- if (gridCol?.name === model.settings.sequenceColumnName || (gridCol?.visible === true && !positionColumns.contains(gridCol.name)))
154
+ if (gridCol?.name === model.settings.sequenceColumnName || (gridCol?.visible === true && !positionColumns.includes(gridCol.name)))
155
155
  columnNames.push(gridCol!.name);
156
156
  }
157
157
 
@@ -180,7 +180,6 @@ export async function startAnalysis(activityColumn: DG.Column<number>, peptidesC
180
180
  sequenceColumnName: peptidesCol.name,
181
181
  activityColumnName: activityColumn.name,
182
182
  scaling: scaling,
183
- isBidirectional: false,
184
183
  columns: {},
185
184
  maxMutations: 1,
186
185
  minActivityDelta: 0,
@@ -22,7 +22,6 @@ export enum SETTINGS_PANES {
22
22
  export enum GENERAL_INPUTS {
23
23
  ACTIVITY = 'Activity',
24
24
  ACTIVITY_SCALING = 'Activity scaling',
25
- BIDIRECTIONAL_ANALYSIS = 'Bidirectional analysis',
26
25
  }
27
26
 
28
27
  export enum VIEWERS_INPUTS {
@@ -51,7 +50,7 @@ export function getSettingsDialog(model: PeptidesModel): SettingsElements {
51
50
  const accordion = ui.accordion();
52
51
  const settings = model.settings;
53
52
  const currentScaling = settings.scaling ?? C.SCALING_METHODS.NONE;
54
- const currentBidirectional = settings.isBidirectional ?? false;
53
+ // const currentBidirectional = settings.isBidirectional ?? false;
55
54
  const currentMaxMutations = settings.maxMutations ?? 1;
56
55
  const currentMinActivityDelta = settings.minActivityDelta ?? 0;
57
56
  const currentColumns = settings.columns ?? {};
@@ -69,13 +68,9 @@ export function getSettingsDialog(model: PeptidesModel): SettingsElements {
69
68
  ui.choiceInput(GENERAL_INPUTS.ACTIVITY_SCALING, currentScaling, Object.values(C.SCALING_METHODS),
70
69
  () => result.scaling = activityScaling.value as C.SCALING_METHODS) as DG.InputBase<C.SCALING_METHODS>;
71
70
  activityScaling.setTooltip('Activity column transformation method');
72
- const bidirectionalAnalysis = ui.boolInput(GENERAL_INPUTS.BIDIRECTIONAL_ANALYSIS, currentBidirectional,
73
- () => result.isBidirectional = bidirectionalAnalysis.value) as DG.InputBase<boolean>;
74
- bidirectionalAnalysis.setTooltip('Distinguish between positive and negative mean activity difference in ' +
75
- 'Monomer-Position and Most Potent Residues viewers');
76
71
 
77
- accordion.addPane(SETTINGS_PANES.GENERAL, () => ui.inputs([activityCol, activityScaling, bidirectionalAnalysis]), true);
78
- inputs[SETTINGS_PANES.GENERAL] = [activityCol, activityScaling, bidirectionalAnalysis];
72
+ accordion.addPane(SETTINGS_PANES.GENERAL, () => ui.inputs([activityCol, activityScaling]), true);
73
+ inputs[SETTINGS_PANES.GENERAL] = [activityCol, activityScaling];
79
74
 
80
75
  // Viewers pane options
81
76
  /* FIXME: combinations of adding and deleting viewers are not working properly