@datagrok/peptides 1.17.10 → 1.17.11

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@datagrok/peptides",
3
3
  "friendlyName": "Peptides",
4
- "version": "1.17.10",
4
+ "version": "1.17.11",
5
5
  "author": {
6
6
  "name": "Davit Rizhinashvili",
7
7
  "email": "drizhinashvili@datagrok.ai"
package/src/model.ts CHANGED
@@ -1126,6 +1126,7 @@ export class PeptidesModel {
1126
1126
  activityColumnName: this.settings!.activityColumnName,
1127
1127
  sequenceColumnName: this.settings!.sequenceColumnName,
1128
1128
  minActivityDelta: 0,
1129
+ activityTarget: C.ACTIVITY_TARGET.HIGH,
1129
1130
  };
1130
1131
  const monomerPosition = await this.df.plot
1131
1132
  .fromType(VIEWER_TYPE.MONOMER_POSITION, viewerProperties) as MonomerPosition;
@@ -1147,6 +1148,7 @@ export class PeptidesModel {
1147
1148
  sequenceColumnName: this.settings!.sequenceColumnName,
1148
1149
  minActivityDelta: 0,
1149
1150
  maxMutations: 1,
1151
+ activityTarget: C.ACTIVITY_TARGET.HIGH,
1150
1152
  };
1151
1153
  const mostPotentResidues =
1152
1154
  await this.df.plot.fromType(VIEWER_TYPE.MOST_POTENT_RESIDUES, viewerProperties) as MostPotentResidues;
@@ -16,7 +16,7 @@ const benchmarkDatasetSizes = [5, 50, 100, 200];
16
16
 
17
17
  category('Benchmarks: Mutation Cliffs', () => {
18
18
  for (const size of benchmarkDatasetSizes)
19
- test(`${size}k sequences`, async () => await mutationCliffsBenchmark(size), {timeout: 180000});
19
+ test(`${size}k sequences`, async () => await mutationCliffsBenchmark(size), {timeout: 300000});
20
20
  });
21
21
 
22
22
  category('Benchmarks: Cluster stats', () => {
@@ -43,6 +43,44 @@ export async function findMutations(activityArray: type.RawData, monomerInfoArra
43
43
  return substitutionsInfo;
44
44
  }
45
45
 
46
+ /**
47
+ * Calculates statistics for mutation cliffs, used for mutation cliffst table (coloring, tooltips, distribution...)
48
+ * @param cliffs - mutation cliffs data
49
+ * @param activityArray - array of activities
50
+ *
51
+ */
52
+ export function calculateCliffsStatistics(
53
+ cliffs: type.MutationCliffs, activityArray: type.RawData,
54
+ ): type.MutationCliffStats {
55
+ const res: type.MutationCliffStats['stats'] = new Map();
56
+ let minDiff = 999999; let maxDiff = -999999; let minCount = 2; let maxCount = 2;
57
+ for (const monomer of cliffs.keys()) {
58
+ const monomerStatsMap: Map<string, StatsItem> = new Map();
59
+ res.set(monomer, monomerStatsMap);
60
+ // monomer substitutions map from mutations cliffs
61
+ const monomerSubMap = cliffs.get(monomer)!;
62
+ for (const position of monomerSubMap.keys()) {
63
+ const subMap = monomerSubMap.get(position)!;
64
+ const mask = new BitArray(activityArray.length, false);
65
+ if (subMap.size === 0)
66
+ continue;
67
+ for (const index of subMap.keys()) {
68
+ mask.setFast(index, true);
69
+ const toIndexes = subMap.get(index)!;
70
+ toIndexes.forEach((i) => mask.setFast(i, true));
71
+ }
72
+ const stats = getStats(activityArray, mask);
73
+ minDiff = Math.min(minDiff, stats.meanDifference);
74
+ maxDiff = Math.max(maxDiff, stats.meanDifference);
75
+ minCount = Math.min(minCount, stats.count);
76
+ maxCount = Math.max(maxCount, stats.count);
77
+ monomerStatsMap.set(position, stats);
78
+ }
79
+ }
80
+ return {stats: res, minDiff, maxDiff, minCount, maxCount};
81
+ }
82
+
83
+
46
84
  /**
47
85
  * Calculates statistics for each monomer position.
48
86
  * @param activityCol - Activity column.
@@ -10,7 +10,7 @@ import * as rxjs from 'rxjs';
10
10
  import {showTooltipAt, TooltipOptions} from './tooltips';
11
11
  import {MonomerPositionStats, MonomerPositionStatsCache, PositionStats} from './statistics';
12
12
  import {CLUSTER_TYPE} from '../viewers/logo-summary';
13
- import {SARViewer} from '../viewers/sar-viewer';
13
+ import {MonomerPosition, MostPotentResidues, SARViewer} from '../viewers/sar-viewer';
14
14
 
15
15
  /**
16
16
  * Renders cell selection border.
@@ -44,37 +44,61 @@ export function setMonomerRenderer(col: DG.Column, alphabet: string): void {
44
44
  */
45
45
  export function renderMutationCliffCell(canvasContext: CanvasRenderingContext2D, currentMonomer: string,
46
46
  currentPosition: string, viewer: SARViewer, bounds: DG.Rect): void {
47
- const positionStats = viewer.monomerPositionStats[currentPosition];
48
- const pVal = positionStats![currentMonomer]!.pValue;
49
- const currentMeanDifference = positionStats![currentMonomer]!.meanDifference;
50
-
51
- // Transform p-value to increase intensity for smaller values and decrease for larger values
52
- const maxPValComplement = 1 - positionStats!.general.maxPValue;
53
- const minPValComplement = 1 - positionStats!.general.minPValue;
54
- const pValCentering = Math.min(maxPValComplement, minPValComplement);
55
- const centeredMaxPValComplement = maxPValComplement - pValCentering;
56
- const centeredMinPValComplement = minPValComplement - pValCentering;
57
- const centeredPValLimit = Math.max(centeredMaxPValComplement, centeredMinPValComplement);
58
- const pValComplement = pVal === null ? 0 : 1 - pVal - pValCentering;
59
-
60
- const x = currentMeanDifference >= 0 ? pValComplement : -pValComplement;
61
- const coef = DG.Color.toHtml(pVal === null ? DG.Color.lightLightGray :
62
- DG.Color.scaleColor(x, -centeredPValLimit, centeredPValLimit, 255));
63
-
64
47
  const halfWidth = bounds.width / 2;
65
- const maxMeanDifference = Math.max(Math.abs(viewer.monomerPositionStats.general.minMeanDifference),
66
- viewer.monomerPositionStats.general.maxMeanDifference);
67
- const rCoef = Math.abs(currentMeanDifference) / maxMeanDifference;
68
- const maxRadius = 0.9 * halfWidth / 2; // Fill at most 90% of the half of the cell width
69
- const radius = Math.floor(maxRadius * rCoef);
70
-
71
48
  const midX = Math.ceil(bounds.x + 1 + halfWidth);
72
49
  const midY = Math.ceil(bounds.y + 1 + bounds.height / 2);
73
- canvasContext.beginPath();
74
- canvasContext.fillStyle = coef;
75
- canvasContext.arc(midX - halfWidth / 2, midY, radius < 3 || pVal === null ? 3 : radius, 0, Math.PI * 2, true);
76
- canvasContext.closePath();
77
- canvasContext.fill();
50
+ const maxRadius = 0.9 * halfWidth / 2; // Fill at most 90% of the half of the cell width
51
+ // render most potent residues cells according to the p-value (color) and mean difference (size)
52
+ if (viewer instanceof MostPotentResidues) {
53
+ const positionStats = viewer.monomerPositionStats[currentPosition];
54
+ const pVal = positionStats![currentMonomer]!.pValue;
55
+ const currentMeanDifference = positionStats![currentMonomer]!.meanDifference;
56
+
57
+ // Transform p-value to increase intensity for smaller values and decrease for larger values
58
+ const maxPValComplement = 1 - positionStats!.general.maxPValue;
59
+ const minPValComplement = 1 - positionStats!.general.minPValue;
60
+ const pValCentering = Math.min(maxPValComplement, minPValComplement);
61
+ const centeredMaxPValComplement = maxPValComplement - pValCentering;
62
+ const centeredMinPValComplement = minPValComplement - pValCentering;
63
+ const centeredPValLimit = Math.max(centeredMaxPValComplement, centeredMinPValComplement);
64
+ const pValComplement = pVal === null ? 0 : 1 - pVal - pValCentering;
65
+
66
+ const x = currentMeanDifference >= 0 ? pValComplement : -pValComplement;
67
+ const coef = DG.Color.toHtml(pVal === null ? DG.Color.lightLightGray :
68
+ DG.Color.scaleColor(x, -centeredPValLimit, centeredPValLimit, 255));
69
+
70
+
71
+ const maxMeanDifference = Math.max(Math.abs(viewer.monomerPositionStats.general.minMeanDifference),
72
+ viewer.monomerPositionStats.general.maxMeanDifference);
73
+ const rCoef = Math.abs(currentMeanDifference) / maxMeanDifference;
74
+
75
+ const radius = Math.floor(maxRadius * rCoef);
76
+
77
+
78
+ canvasContext.beginPath();
79
+ canvasContext.fillStyle = coef;
80
+ canvasContext.arc(midX - halfWidth / 2, midY, radius < 3 || pVal === null ? 3 : radius, 0, Math.PI * 2, true);
81
+ canvasContext.closePath();
82
+ canvasContext.fill();
83
+ } else if (viewer instanceof MonomerPosition && viewer.mutationCliffs && viewer.cliffStats) {
84
+ const maxCount = viewer.cliffStats.maxCount;
85
+ const minDiff = viewer.cliffStats.minDiff;
86
+ const maxDiff = viewer.cliffStats.maxDiff;
87
+ const stats = viewer.cliffStats?.stats?.get(currentMonomer)?.get(currentPosition);
88
+ if (stats) {
89
+ const count = stats.count;
90
+ const diff = stats.meanDifference;
91
+ const radius = Math.floor(Math.abs((count / Math.max(maxCount, 2))) * maxRadius);
92
+ const diffScalingFactor = Math.max(Math.abs(minDiff), Math.abs(maxDiff));
93
+ const colorCoef = DG.Color.toHtml(diff === null ? DG.Color.lightLightGray :
94
+ DG.Color.scaleColor(diff, -diffScalingFactor, diffScalingFactor, 255));
95
+ canvasContext.beginPath();
96
+ canvasContext.fillStyle = colorCoef;
97
+ canvasContext.arc(midX - halfWidth / 2, midY, radius < 3 || count == 0 ? 3 : radius, 0, Math.PI * 2, true);
98
+ canvasContext.closePath();
99
+ canvasContext.fill();
100
+ }
101
+ }
78
102
 
79
103
  canvasContext.textBaseline = 'middle';
80
104
  canvasContext.textAlign = 'end';
@@ -404,6 +428,8 @@ function requestWebLogoAction(ev: MouseEvent, monomerPosition: type.SelectionIte
404
428
  function findWebLogoMonomerPosition(cell: DG.GridCell, ev: MouseEvent, webLogoBounds: WebLogoBounds,
405
429
  ): type.SelectionItem | null {
406
430
  const barCoords = webLogoBounds[cell.tableColumn!.name];
431
+ if (!barCoords)
432
+ return null;
407
433
  for (const [monomer, coords] of Object.entries(barCoords)) {
408
434
  const isIntersectingX = ev.offsetX >= coords.x && ev.offsetX <= coords.x + coords.width;
409
435
  const isIntersectingY = ev.offsetY >= coords.y && ev.offsetY <= coords.y + coords.height;
@@ -62,6 +62,11 @@ export enum SCALING_METHODS {
62
62
  MINUS_LG = '-lg',
63
63
  }
64
64
 
65
+ export enum ACTIVITY_TARGET {
66
+ HIGH = 'High',
67
+ LOW = 'Low',
68
+ }
69
+
65
70
  export enum SUFFIXES {
66
71
  LST = 'lst-', // Logo Summary Table
67
72
  MP = 'mp-', // Monomer Position
package/src/utils/misc.ts CHANGED
@@ -195,6 +195,66 @@ export function addExpandIcon(grid: DG.Grid): void {
195
195
  });
196
196
  }
197
197
 
198
+ export function addExpandIconsToGridPair(grids: DG.Grid[], name: string, onCloseFunc?: Function): void {
199
+ //const host = ui.divV([], {style: {height: '100%'}});
200
+ grids.forEach((grid) => {
201
+ const fullscreenIcon = ui.iconFA('expand-alt', () => {
202
+ const fullscreenGrids = grids.map((g) => {
203
+ const out = g;
204
+ setGridProps(out, false);
205
+ return out;
206
+ });
207
+ //setGridProps(fullscreenGrid, false);
208
+ //fullscreenGrid.root.style.height = '100%';
209
+ const pairsFullscreenDialog = ui.dialog(name);
210
+ const host = ui.divV([], {style: {height: '100%'}});
211
+ pairsFullscreenDialog.add(host);
212
+ fullscreenGrids.forEach((g) => {
213
+ host.appendChild(ui.h1(g.dataFrame.name));
214
+ host.appendChild(g.root);
215
+ });
216
+ //pairsFullscreenDialog.add(fullscreenGrid.root);
217
+ pairsFullscreenDialog.showModal(true);
218
+ pairsFullscreenDialog.onClose.subscribe(() => {
219
+ onCloseFunc?.();
220
+ });
221
+ //fullscreenGrid.invalidate();
222
+ fullscreenGrids.forEach((g) => g.invalidate());
223
+ });
224
+ grid.root.appendChild(fullscreenIcon);
225
+ fullscreenIcon.style.position = 'absolute';
226
+ fullscreenIcon.style.right = '0px';
227
+ fullscreenIcon.style.top = '0px';
228
+ fullscreenIcon.style.visibility = 'hidden';
229
+ grid.root.addEventListener('mouseenter', (_) => {
230
+ fullscreenIcon.style.visibility = 'visible';
231
+ });
232
+ grid.root.addEventListener('mouseleave', (_) => {
233
+ fullscreenIcon.style.visibility = 'hidden';
234
+ });
235
+ });
236
+ }
237
+
238
+ export function addExpandIconGen(dialogName: string,
239
+ root: HTMLElement, mouseOverRoot: HTMLElement, onClickElementFunc: () => HTMLElement,
240
+ ): void {
241
+ const fullScreenIcon = ui.iconFA('expand-alt', () => {
242
+ const fullScreenElement = onClickElementFunc();
243
+ const fullScreenDialog = ui.dialog(dialogName);
244
+ fullScreenDialog.add(fullScreenElement);
245
+ fullScreenDialog.showModal(true);
246
+ }, 'Expand to full screen');
247
+ fullScreenIcon.style.marginLeft = 'auto';
248
+ fullScreenIcon.style.marginRight = '15px';
249
+ mouseOverRoot.addEventListener('mouseenter', () => {
250
+ fullScreenIcon.style.visibility = 'visible';
251
+ });
252
+ mouseOverRoot.addEventListener('mouseleave', () => {
253
+ fullScreenIcon.style.visibility = 'hidden';
254
+ });
255
+ root.appendChild(fullScreenIcon);
256
+ }
257
+
198
258
  /**
199
259
  * Sets common properties for grid in property panel.
200
260
  * @param grid - Grid to set properties to.
@@ -319,8 +379,9 @@ export function getSelectionBitset(selection: type.Selection, stats: MasksInfo):
319
379
  const statsType = stats[positionOrClusterType];
320
380
  for (const monomerOrCluster of selected) {
321
381
  const statsItem = statsType[monomerOrCluster];
382
+ if (!statsItem) continue;
322
383
  combinedBitset ??= new BitArray(statsItem.mask.length, false);
323
- combinedBitset!.or(statsType[monomerOrCluster].mask);
384
+ combinedBitset!.or(statsItem.mask);
324
385
  }
325
386
  }
326
387
 
@@ -14,6 +14,7 @@ import {StringDictionary} from '@datagrok-libraries/utils/src/type-declarations'
14
14
  export type TooltipOptions = {
15
15
  fromViewer?: boolean, isMutationCliffs?: boolean, x: number, y: number, monomerPosition: type.SelectionItem,
16
16
  mpStats: MonomerPositionStats, aggrColValues?: StringDictionary,
17
+ isMostPotentResidues?: boolean, cliffStats?: type.MutationCliffStats['stats']
17
18
  };
18
19
 
19
20
  /**
@@ -55,6 +56,7 @@ export function showTooltip(df: DG.DataFrame, activityCol: DG.Column<number>, co
55
56
  options: TooltipOptions): boolean {
56
57
  options.fromViewer ??= false;
57
58
  options.isMutationCliffs ??= false;
59
+ options.isMostPotentResidues ??= false;
58
60
  if (options.monomerPosition.positionOrClusterType === C.COLUMNS_NAMES.MONOMER)
59
61
  showMonomerTooltip(options.monomerPosition.monomerOrCluster, options.x, options.y);
60
62
  else
@@ -77,23 +79,45 @@ export function showTooltipAt(df: DG.DataFrame, activityCol: DG.Column<number>,
77
79
  options: TooltipOptions): HTMLDivElement | null {
78
80
  options.fromViewer ??= false;
79
81
  options.isMutationCliffs ??= false;
80
- const stats = options
81
- .mpStats[options.monomerPosition.positionOrClusterType]![options.monomerPosition.monomerOrCluster];
82
- if (!stats?.count)
83
- return null;
82
+ options.isMostPotentResidues ??= false;
83
+ if (!options.cliffStats || !options.isMutationCliffs) {
84
+ const stats = options
85
+ .mpStats[options.monomerPosition.positionOrClusterType]![options.monomerPosition.monomerOrCluster];
86
+ if (!stats?.count)
87
+ return null;
84
88
 
85
89
 
86
- const mask = DG.BitSet.fromBytes(stats.mask.buffer.buffer, activityCol.length);
87
- const hist = getActivityDistribution(getDistributionTable(activityCol, mask), true);
88
- const tableMap = getStatsTableMap(stats);
89
- if (options.fromViewer) {
90
- tableMap['Mean difference'] = `${tableMap['Mean difference']}${options.isMutationCliffs ? ' (size)' : ''}`;
91
- if (tableMap['p-value'])
92
- tableMap['p-value'] = `${tableMap['p-value']}${options.isMutationCliffs ? ' (color)' : ''}`;
90
+ const mask = DG.BitSet.fromBytes(stats.mask.buffer.buffer, activityCol.length);
91
+ const hist = getActivityDistribution(getDistributionTable(activityCol, mask), true);
92
+ const tableMap = getStatsTableMap(stats);
93
+ if (options.fromViewer) {
94
+ tableMap['Mean difference'] = `${tableMap['Mean difference']}${options.isMostPotentResidues ? ' (size)' : ''}`;
95
+ if (tableMap['p-value'])
96
+ tableMap['p-value'] = `${tableMap['p-value']}${options.isMostPotentResidues ? ' (color)' : ''}`;
97
+ }
98
+ const aggregatedColMap = options.aggrColValues ?? getAggregatedColumnValues(df, columns, {mask: mask});
99
+ const resultMap = {...tableMap, ...aggregatedColMap};
100
+ const distroStatsElem = getDistributionPanel(hist, resultMap);
101
+ ui.tooltip.show(distroStatsElem, options.x, options.y);
102
+ return distroStatsElem;
103
+ } else {
104
+ const stats = options.cliffStats?.get(options.monomerPosition.monomerOrCluster)
105
+ ?.get(options.monomerPosition.positionOrClusterType)
106
+ ;
107
+ if (!stats)
108
+ return null;
109
+ const mask = DG.BitSet.fromBytes(stats.mask.buffer.buffer, activityCol.length);
110
+ const hist = getActivityDistribution(getDistributionTable(activityCol, mask), true);
111
+ const tableMap = getStatsTableMap(stats);
112
+ if (options.fromViewer) {
113
+ tableMap['Mean difference'] = `${tableMap['Mean difference']}${' (Color)'}`;
114
+ if (tableMap['Count'])
115
+ tableMap['Count'] = `${tableMap['Count']}${' (Size)'}`;
116
+ }
117
+ const aggregatedColMap = options.aggrColValues ?? getAggregatedColumnValues(df, columns, {mask: mask});
118
+ const resultMap = {...tableMap, ...aggregatedColMap};
119
+ const distroStatsElem = getDistributionPanel(hist, resultMap);
120
+ ui.tooltip.show(distroStatsElem, options.x, options.y);
121
+ return distroStatsElem;
93
122
  }
94
- const aggregatedColMap = options.aggrColValues ?? getAggregatedColumnValues(df, columns, {mask: mask});
95
- const resultMap = {...tableMap, ...aggregatedColMap};
96
- const distroStatsElem = getDistributionPanel(hist, resultMap);
97
- ui.tooltip.show(distroStatsElem, options.x, options.y);
98
- return distroStatsElem;
99
123
  }
@@ -1,13 +1,22 @@
1
1
  import * as DG from 'datagrok-api/dg';
2
2
  import {ClusterType} from '../viewers/logo-summary';
3
3
  import {SCALING_METHODS} from './constants';
4
- import {AggregationColumns} from './statistics';
4
+ import {AggregationColumns, StatsItem} from './statistics';
5
5
  import {MmDistanceFunctionsNames} from '@datagrok-libraries/ml/src/macromolecule-distance-functions';
6
6
 
7
7
  export type RawData = Int32Array | Uint32Array | Float32Array | Float64Array;
8
+ type MONOMER = string;
9
+ type POSITION = string;
10
+ type INDEX = number;
11
+ type INDEXES = number[] | UTypedArray;
8
12
  export type UTypedArray = Uint8Array | Uint16Array | Uint32Array;
9
13
  //Monomer: (Position: (index: indexList))
10
- export type MutationCliffs = Map<string, Map<string, Map<number, number[] | UTypedArray>>>;
14
+ export type MutationCliffs = Map<MONOMER, Map<POSITION, Map<INDEX, INDEXES>>>;
15
+ //Monomer: (Position: (Stats))
16
+ export type MutationCliffStats = {
17
+ stats: Map<MONOMER, Map<POSITION, StatsItem>>;
18
+ minDiff: number; maxDiff: number; maxCount: number; minCount: number;
19
+ };
11
20
  export type Selection = { [positionOrClusterType: string | ClusterType]: string[] };
12
21
  export type SelectionItem = { positionOrClusterType: string | ClusterType, monomerOrCluster: string };
13
22
  export type SelectionStats = { [positionOrClusterType: string | ClusterType]: { [monomerOrCluster: string]: number } };