@datagrok/peptides 1.25.1 → 1.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@datagrok/peptides",
3
3
  "friendlyName": "Peptides",
4
- "version": "1.25.1",
4
+ "version": "1.26.1",
5
5
  "author": {
6
6
  "name": "Davit Rizhinashvili",
7
7
  "email": "drizhinashvili@datagrok.ai"
package/src/model.ts CHANGED
@@ -511,53 +511,54 @@ export class PeptidesModel {
511
511
  descritionsHost,
512
512
  ], 'css-gap-small'));
513
513
 
514
- if (filterAndSelectionBs.anyTrue) {
515
- acc.addPane('Actions', () => {
516
- try {
517
- const newView = ui.label('New view');
518
- $(newView).addClass('d4-link-action');
519
- newView.onclick = (): string => trueModel.createNewView();
520
- newView.onmouseover = (ev): void =>
521
- ui.tooltip.show('Creates a new view from current selection', ev.clientX + 5, ev.clientY + 5);
522
- if (trueLSTViewer === null)
523
- return ui.divV([newView]);
524
-
525
-
526
- const newCluster = ui.label('New cluster');
527
- $(newCluster).addClass('d4-link-action');
528
- newCluster.onclick = (): void => {
529
- if (trueLSTViewer === null)
530
- throw new Error('Logo summary table viewer is not found');
531
-
532
-
533
- trueLSTViewer.clusterFromSelection();
534
- };
535
- newCluster.onmouseover = (ev): void =>
536
- ui.tooltip.show('Creates a new cluster from selection', ev.clientX + 5, ev.clientY + 5);
537
-
538
- const removeCluster = ui.label('Remove cluster');
539
- $(removeCluster).addClass('d4-link-action');
540
- removeCluster.onclick = (): void => {
541
- const lstViewer = trueModel.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable | null;
542
- if (lstViewer === null)
543
- throw new Error('Logo summary table viewer is not found');
544
-
545
-
546
- lstViewer.removeCluster();
547
- };
548
- removeCluster.onmouseover = (ev): void =>
549
- ui.tooltip.show('Removes currently selected custom cluster', ev.clientX + 5, ev.clientY + 5);
550
- removeCluster.style.visibility = trueLSTViewer.clusterSelection[CLUSTER_TYPE.CUSTOM].length === 0 ? 'hidden' :
551
- 'visible';
552
-
553
- return ui.divV([newView, newCluster, removeCluster]);
554
- } catch (e) {
555
- const errorDiv = ui.divText('Error in Actions');
556
- ui.tooltip.bind(errorDiv, String(e));
557
- return errorDiv;
558
- }
559
- }, true);
560
- }
514
+ // ACTIONS PANE temporarily disabled
515
+ // if (filterAndSelectionBs.anyTrue) {
516
+ // acc.addPane('Actions', () => {
517
+ // try {
518
+ // const newView = ui.label('New view');
519
+ // $(newView).addClass('d4-link-action');
520
+ // newView.onclick = (): string => trueModel.createNewView();
521
+ // newView.onmouseover = (ev): void =>
522
+ // ui.tooltip.show('Creates a new view from current selection', ev.clientX + 5, ev.clientY + 5);
523
+ // if (trueLSTViewer === null)
524
+ // return ui.divV([newView]);
525
+
526
+
527
+ // const newCluster = ui.label('New cluster');
528
+ // $(newCluster).addClass('d4-link-action');
529
+ // newCluster.onclick = (): void => {
530
+ // if (trueLSTViewer === null)
531
+ // throw new Error('Logo summary table viewer is not found');
532
+
533
+
534
+ // trueLSTViewer.clusterFromSelection();
535
+ // };
536
+ // newCluster.onmouseover = (ev): void =>
537
+ // ui.tooltip.show('Creates a new cluster from selection', ev.clientX + 5, ev.clientY + 5);
538
+
539
+ // const removeCluster = ui.label('Remove cluster');
540
+ // $(removeCluster).addClass('d4-link-action');
541
+ // removeCluster.onclick = (): void => {
542
+ // const lstViewer = trueModel.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable | null;
543
+ // if (lstViewer === null)
544
+ // throw new Error('Logo summary table viewer is not found');
545
+
546
+
547
+ // lstViewer.removeCluster();
548
+ // };
549
+ // removeCluster.onmouseover = (ev): void =>
550
+ // ui.tooltip.show('Removes currently selected custom cluster', ev.clientX + 5, ev.clientY + 5);
551
+ // removeCluster.style.visibility = trueLSTViewer.clusterSelection[CLUSTER_TYPE.CUSTOM].length === 0 ? 'hidden' :
552
+ // 'visible';
553
+
554
+ // return ui.divV([newView, newCluster, removeCluster]);
555
+ // } catch (e) {
556
+ // const errorDiv = ui.divText('Error in Actions');
557
+ // ui.tooltip.bind(errorDiv, String(e));
558
+ // return errorDiv;
559
+ // }
560
+ // }, true);
561
+ // }
561
562
 
562
563
  // Get the source of the bitset change and find viewers that share the same parameters as source
563
564
  let requestSource: SARViewer | LogoSummaryTable | PeptidesSettings | null = trueModel.settings;
@@ -587,27 +588,27 @@ export class PeptidesModel {
587
588
  v !== null && areParametersEqual(requestSource!, v) && (v !== trueModel.settings || trueModel.isInitialized);
588
589
  const panelDataSources = viewers.filter(notEmpty);
589
590
  panelDataSources.push(requestSource);
590
- const combinedBitset: DG.BitSet | null = DG.BitSet.create(trueModel.df.rowCount);
591
- for (const panelDataSource of panelDataSources) {
592
- const bitset =
593
- (panelDataSource === this.settings) ? getSelectionBitset(this.webLogoSelection, this.monomerPositionStats!) :
594
- (panelDataSource instanceof LogoSummaryTable) ?
595
- getSelectionBitset(panelDataSource.clusterSelection, panelDataSource.clusterStats) :
596
- (panelDataSource instanceof SARViewer) ?
597
- getSelectionBitset(panelDataSource.mutationCliffsSelection,
598
- mutationCliffsToMaskInfo(panelDataSource.mutationCliffs ?? new Map(), trueModel.df.rowCount)) :
599
- null;
600
- if (bitset !== null)
601
- combinedBitset.or(bitset);
602
-
603
-
604
- if (panelDataSource instanceof MonomerPosition) {
605
- const invariantMapSelectionBitset = getSelectionBitset(panelDataSource.invariantMapSelection,
606
- panelDataSource.monomerPositionStats);
607
- if (invariantMapSelectionBitset !== null)
608
- combinedBitset.or(invariantMapSelectionBitset);
609
- }
610
- }
591
+ //const combinedBitset: DG.BitSet | null = DG.BitSet.create(trueModel.df.rowCount);
592
+ // for (const panelDataSource of panelDataSources) {
593
+ // const bitset =
594
+ // (panelDataSource === this.settings) ? getSelectionBitset(this.webLogoSelection, this.monomerPositionStats!) :
595
+ // (panelDataSource instanceof LogoSummaryTable) ?
596
+ // getSelectionBitset(panelDataSource.clusterSelection, panelDataSource.clusterStats) :
597
+ // (panelDataSource instanceof SARViewer) ?
598
+ // getSelectionBitset(panelDataSource.mutationCliffsSelection,
599
+ // mutationCliffsToMaskInfo(panelDataSource.mutationCliffs ?? new Map(), trueModel.df.rowCount)) :
600
+ // null;
601
+ // if (bitset !== null)
602
+ // combinedBitset.or(bitset);
603
+
604
+
605
+ // if (panelDataSource instanceof MonomerPosition) {
606
+ // const invariantMapSelectionBitset = getSelectionBitset(panelDataSource.invariantMapSelection,
607
+ // panelDataSource.monomerPositionStats);
608
+ // if (invariantMapSelectionBitset !== null)
609
+ // combinedBitset.or(invariantMapSelectionBitset);
610
+ // }
611
+ // }
611
612
 
612
613
  const sarViewer = requestSource as any as SARViewer | LogoSummaryTable;
613
614
  if (requestSource !== trueModel.settings && !(sarViewer instanceof LogoSummaryTable) &&
@@ -630,7 +631,6 @@ export class PeptidesModel {
630
631
  acc.addPane('Distribution', () => {
631
632
  try {
632
633
  return getDistributionWidget(trueModel.df, {
633
- peptideSelection: combinedBitset,
634
634
  columns: isModelSource ? trueModel.settings!.columns ?? {} :
635
635
  (requestSource as PeptideViewer).getAggregationColumns(),
636
636
  activityCol: isModelSource ? trueModel.getScaledActivityColumn()! :
@@ -869,7 +869,8 @@ export class PeptidesModel {
869
869
  }
870
870
  }
871
871
 
872
- return DG.BitSet.fromBytes(combinedSelection.buffer.buffer as ArrayBuffer, combinedSelection.length);
872
+ const res = DG.BitSet.fromBytes(combinedSelection.buffer.buffer as ArrayBuffer, combinedSelection.length);
873
+ return this.df.filter.anyFalse ? res.and(this.df.filter) : res;
873
874
  }
874
875
 
875
876
 
@@ -1402,6 +1403,13 @@ export class PeptidesModel {
1402
1403
  await DG.delay(500);
1403
1404
  this._mclViewer = this.findViewer(VIEWER_TYPE.MCL) as MCLViewer;
1404
1405
  await this._mclViewer.initPromise;
1406
+ // after waiting for init promise, it can be resolved by just removing the viewer, so check again
1407
+ this._mclViewer = this.findViewer(VIEWER_TYPE.MCL) as MCLViewer | null;
1408
+ if (!this._mclViewer || !this._mclViewer.isDetached) {
1409
+ mclAdditionSub?.unsubscribe();
1410
+ columnAddedSub?.unsubscribe();
1411
+ return;
1412
+ }
1405
1413
  const lstViewer = this.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable | null;
1406
1414
  if (lstViewer) { // beware, this is accessing private things
1407
1415
  lstViewer._clusterStats = null;
@@ -48,6 +48,13 @@ export namespace funcs {
48
48
  return await grok.functions.call('Peptides:MostPotentResidues', {});
49
49
  }
50
50
 
51
+ /**
52
+ Mutation Cliffs Line Chart
53
+ */
54
+ export async function mutationCliffs(): Promise<any> {
55
+ return await grok.functions.call('Peptides:MutationCliffs', {});
56
+ }
57
+
51
58
  export async function logoSummaryTable(): Promise<any> {
52
59
  return await grok.functions.call('Peptides:LogoSummaryTable', {});
53
60
  }
package/src/package.g.ts CHANGED
@@ -48,6 +48,15 @@ export function mostPotentResidues() : any {
48
48
  return PackageFunctions.mostPotentResidues();
49
49
  }
50
50
 
51
+ //name: Sequence Mutation Cliffs
52
+ //description: Mutation Cliffs Line Chart
53
+ //tags: viewer
54
+ //output: viewer result
55
+ //meta.icon: files/icons/sequence-statistics-viewer.svg
56
+ export function mutationCliffs() : any {
57
+ return PackageFunctions.mutationCliffs();
58
+ }
59
+
51
60
  //name: Logo Summary Table
52
61
  //tags: viewer
53
62
  //output: viewer result
package/src/package.ts CHANGED
@@ -18,6 +18,7 @@ import {ClusterMaxActivityViewer} from './viewers/cluster-max-activity-viewer';
18
18
  import {LSTPieChartRenderer} from './utils/cell-renderer';
19
19
  import {PeptideUtils} from './peptideUtils';
20
20
  import {SequencePositionStatsViewer} from './viewers/position-statistics-viewer';
21
+ import {MutationCliffsViewer} from './viewers/mutation-cliffs-viewer';
21
22
 
22
23
  let monomerWorks: MonomerWorks | null = null;
23
24
  let treeHelper: ITreeHelper;
@@ -193,6 +194,17 @@ export class PackageFunctions {
193
194
  return new MostPotentResidues();
194
195
  }
195
196
 
197
+ @grok.decorators.func({
198
+ meta: {icon: 'files/icons/sequence-statistics-viewer.svg'},
199
+ tags: ['viewer'],
200
+ name: 'Sequence Mutation Cliffs',
201
+ description: 'Mutation Cliffs Line Chart',
202
+ outputs: [{type: 'viewer', name: 'result'}],
203
+ })
204
+ static mutationCliffs(): DG.Viewer {
205
+ return new MutationCliffsViewer();
206
+ }
207
+
196
208
 
197
209
  @grok.decorators.func({
198
210
  meta: {icon: 'files/icons/logo-summary-viewer.svg'},
@@ -14,24 +14,28 @@ import {TEST_COLUMN_NAMES} from './utils';
14
14
  import {showTooltip} from '../utils/tooltips';
15
15
  import {PeptideUtils} from '../peptideUtils';
16
16
 
17
- category('Viewers: Basic', () => {
18
- let df: DG.DataFrame;
19
-
20
- before(async () => {
21
- await PeptideUtils.loadComponents();
22
-
23
- df = DG.DataFrame.fromCsv(await _package.files.readAsText('tests/HELM_small.csv'));
24
- await delay(500);
25
- });
26
-
27
- const viewers = DG.Func.find({package: 'Peptides', tags: ['viewer']}).map((f) => f.friendlyName);
28
- for (const v of viewers) {
29
- test(v, async () => {
30
- await testViewer(v, df.clone(), {detectSemanticTypes: true, arbitraryDfTest: false, readOnly: true});
31
- await delay(1000);
32
- });
33
- }
34
- });
17
+ // category('Viewers: Basic', () => {
18
+ // let df: DG.DataFrame;
19
+
20
+ // before(async () => {
21
+ // await PeptideUtils.loadComponents();
22
+
23
+ // df = DG.DataFrame.fromCsv(await _package.files.readAsText('tests/HELM_small.csv'));
24
+ // const clusterCol = df.col('cluster')!.convertTo(DG.COLUMN_TYPE.STRING);
25
+ // clusterCol!.name = 'cluster';
26
+ // df.columns.remove('cluster');
27
+ // df.columns.add(clusterCol!);
28
+ // await delay(500);
29
+ // });
30
+
31
+ // const viewers = DG.Func.find({package: 'Peptides', tags: ['viewer']}).map((f) => f.friendlyName);
32
+ // for (const v of viewers) {
33
+ // test(v, async () => {
34
+ // await testViewer(v, df.clone(), {detectSemanticTypes: true, arbitraryDfTest: false, readOnly: true});
35
+ // await delay(1000);
36
+ // });
37
+ // }
38
+ // });
35
39
 
36
40
  category('Viewers: Monomer-Position', () => {
37
41
  let df: DG.DataFrame;
@@ -98,7 +98,7 @@ category('Widgets: Distribution panel', () => {
98
98
  await delay(1000);
99
99
  const lstViewer = model.findViewer(VIEWER_TYPE.LOGO_SUMMARY_TABLE) as LogoSummaryTable | null;
100
100
  getDistributionWidget(model.df, {
101
- peptideSelection: DG.BitSet.create(model.df.rowCount), columns: model.settings!.columns!,
101
+ columns: model.settings!.columns!,
102
102
  activityCol: scaledActivityCol, clusterSelection: lstViewer!.clusterSelection, clusterColName: clusterCol.name,
103
103
  monomerPositionSelection: model.webLogoSelection,
104
104
  });
@@ -6,6 +6,7 @@ import {ParallelMutationCliffs} from './parallel-mutation-cliffs';
6
6
  import {CLUSTER_TYPE} from '../viewers/logo-summary';
7
7
  import BitArray from '@datagrok-libraries/utils/src/bit-array';
8
8
  import {
9
+ bitSetToBitArray,
9
10
  ClusterStats,
10
11
  ClusterTypeStats,
11
12
  getStats,
@@ -18,7 +19,8 @@ import {
18
19
  export type MutationCliffsOptions = {
19
20
  maxMutations?: number,
20
21
  minActivityDelta?: number,
21
- filter?: Uint32Array
22
+ filter?: Uint32Array,
23
+ singlePosition?: {position: number}
22
24
  };
23
25
 
24
26
  /**
@@ -120,6 +122,7 @@ export function calculateMonomerPositionStatistics(activityCol: DG.Column<number
120
122
  // if (options.aggValue)
121
123
  // options.aggValue.col = options.aggValue.col.clone(filter);
122
124
  // }
125
+ const filterBitArray = options.isFiltered ? bitSetToBitArray(filter) : undefined;
123
126
 
124
127
  for (const posCol of positionColumns) {
125
128
  if (!options.columns.includes(posCol.name))
@@ -144,7 +147,7 @@ export function calculateMonomerPositionStatistics(activityCol: DG.Column<number
144
147
  const bitArray = BitArray.fromValues(boolArray);
145
148
  if (bitArray.allFalse)
146
149
  continue;
147
- const stats = getStats(activityColData, bitArray, options.aggValue);
150
+ const stats = getStats(activityColData, bitArray, filterBitArray, options.aggValue);
148
151
  currentPositionObject[monomer] = stats;
149
152
  getSummaryStats(currentPositionObject.general, stats);
150
153
  }
@@ -245,6 +248,7 @@ export function calculateClusterStatistics(df: DG.DataFrame, clustersColumnName:
245
248
  const origClustStats: ClusterStats = {};
246
249
  const customClustStats: ClusterStats = {};
247
250
 
251
+ const filterMask = df.filter.anyFalse ? bitSetToBitArray(df.filter) : undefined;
248
252
  for (const clustType of Object.values(CLUSTER_TYPE)) {
249
253
  const masks = clustType === CLUSTER_TYPE.ORIGINAL ? origClustMasks : customClustMasks;
250
254
  const clustNames = clustType === CLUSTER_TYPE.ORIGINAL ? origClustColCat : customClustColNamesList;
@@ -253,7 +257,7 @@ export function calculateClusterStatistics(df: DG.DataFrame, clustersColumnName:
253
257
  const mask = masks[maskIdx];
254
258
  resultStats[clustNames[maskIdx]] = mask.allTrue || mask.allFalse ?
255
259
  {count: mask.length, meanDifference: 0, ratio: 1.0, pValue: null, mask: mask, mean: activityCol.stats.avg} :
256
- getStats(activityColData, mask);
260
+ getStats(activityColData, mask, filterMask);
257
261
  }
258
262
  }
259
263
 
@@ -410,7 +410,10 @@ export function setWebLogoRenderer(grid: DG.Grid, monomerPositionStats: MonomerP
410
410
  return;
411
411
  }
412
412
  tooltipOptions.monomerPosition = monomerPosition;
413
- requestWebLogoAction(ev, monomerPosition, df, activityCol, options, tooltipOptions);
413
+ const isDfFiltered = df.filter.anyFalse;
414
+ const actionDf = isDfFiltered ? df.clone(df.filter) : df;
415
+ const actionActivityCol = actionDf.getCol(activityCol.name) as DG.Column<number>;
416
+ requestWebLogoAction(ev, monomerPosition, actionDf, actionActivityCol, options, tooltipOptions);
414
417
  if (!options.isSelectionTable && options.highlightCallback != null)
415
418
  options.highlightCallback(monomerPosition, df, monomerPositionStats);
416
419
  } else if (options.unhighlightCallback != null)
package/src/utils/misc.ts CHANGED
@@ -131,39 +131,35 @@ export function getDistributionPanel(hist: DG.Viewer<DG.IHistogramSettings>, sta
131
131
  * Creates a table to plot activity distribution.
132
132
  * @param activityCol - Activity column.
133
133
  * @param selection - Selection bitset.
134
- * @param [peptideSelection] - Peptide selection bitset.
135
134
  * @return - Dataframe with activity distribution.
136
135
  */
137
- export function getDistributionTable(activityCol: DG.Column<number>, selection: DG.BitSet, peptideSelection?: DG.BitSet,
138
- ): DG.DataFrame {
139
- const selectionMismatch = peptideSelection?.clone().xor(selection).anyTrue ?? false;
136
+ export function getDistributionTable(activityCol: DG.Column<number>, selection: DG.BitSet): DG.DataFrame {
137
+ if (!activityCol.dataFrame)
138
+ DG.DataFrame.fromColumns([activityCol]); // to make sure that activityCol has a parent dataframe
139
+ const filter = activityCol.dataFrame!.filter;
140
+ const selectionAndFilter = selection.clone().and(filter);
140
141
  const rowCount = activityCol.length;
141
142
  const activityColData = activityCol.getRawData();
142
- const activityData = new Float32Array(rowCount + selection.trueCount +
143
- (selectionMismatch ? (peptideSelection?.trueCount ?? 0) : 0));
143
+ const filteredRowCount = filter.trueCount;
144
+ const activityData = new Float32Array(filteredRowCount + selectionAndFilter.trueCount);
144
145
  const categories: string[] = new Array(activityData.length);
145
146
 
146
- for (let i = 0, j = 0, k = 0; i < rowCount; ++i) {
147
- const isSelected = selection.get(i);
148
- activityData[i] = activityColData[i];
149
- categories[i] = isSelected ? SPLIT_CATEGORY.SELECTION : SPLIT_CATEGORY.ALL;
147
+ for (let i = 0, /*J is counting for filtered data*/j = 0, /*selected data*/k = 0; i < rowCount; ++i) {
148
+ if (!filter.get(i))
149
+ continue;
150
+ const isSelected = selectionAndFilter.get(i);
151
+ activityData[j] = activityColData[i];
152
+ categories[j] = isSelected ? SPLIT_CATEGORY.SELECTION : SPLIT_CATEGORY.ALL;
153
+ j++;
150
154
  if (isSelected) {
151
- activityData[rowCount + j] = activityColData[i];
152
- categories[rowCount + j] = SPLIT_CATEGORY.ALL;
153
- ++j;
154
- }
155
- if (selectionMismatch && peptideSelection?.get(i)) {
156
- activityData[rowCount + selection.trueCount + k] = activityColData[i];
157
- categories[rowCount + selection.trueCount + k] = SPLIT_CATEGORY.PEPTIDES_SELECTION;
155
+ activityData[filteredRowCount + k] = activityColData[i];
156
+ categories[filteredRowCount + k] = SPLIT_CATEGORY.ALL;
158
157
  ++k;
159
158
  }
160
159
  }
161
160
 
162
161
  const splitCol = DG.Column.fromStrings(C.COLUMNS_NAMES.SPLIT_COL, categories);
163
162
  const categoryOrder = [SPLIT_CATEGORY.ALL, SPLIT_CATEGORY.SELECTION];
164
- if (selectionMismatch)
165
- categoryOrder.push(SPLIT_CATEGORY.PEPTIDES_SELECTION);
166
-
167
163
 
168
164
  splitCol.setCategoryOrder(categoryOrder);
169
165
  splitCol.meta.colors.setCategorical();
@@ -1,3 +1,4 @@
1
+ /* eslint-disable max-len */
1
2
  import * as DG from 'datagrok-api/dg';
2
3
  import {tTest} from '@datagrok-libraries/statistics/src/tests';
3
4
  import {RawData} from './types';
@@ -37,24 +38,42 @@ export type SummaryStats = {
37
38
  export const getAggregatedColName = (aggF: string, colName: string): string => `${aggF}(${colName})`;
38
39
  export type AggregationColumns = { [col: string]: DG.AggregationType };
39
40
 
41
+ export function bitSetToBitArray(bitSet: DG.BitSet): BitArray {
42
+ return BitArray.fromUint32Array(
43
+ bitSet.length,
44
+ new Uint32Array(bitSet.getBuffer().buffer as ArrayBuffer),
45
+ );
46
+ }
47
+
48
+ export function bitArrayToBitSet(bitArray: BitArray): DG.BitSet {
49
+ return DG.BitSet.fromBytes(
50
+ bitArray.buffer.buffer as ArrayBuffer,
51
+ bitArray.length,
52
+ );
53
+ }
54
+
40
55
  /**
41
56
  * Returns statistics for the given activity data and bit array.
42
57
  * @param data - Activity data to calculate statistics for.
43
58
  * @param bitArray - Bit array to use for the calculation.
44
59
  * @return - Statistics for the given data and bit array.
45
60
  */
46
- export function getStats(data: RawData | number[], bitArray: BitArray,
61
+ export function getStats(data: RawData | number[], bitArray: BitArray, filter?: BitArray,
47
62
  aggData?: {col: DG.Column, type: DG.AGG}): StatsItem {
48
- if (data.length !== bitArray.length && data.some((v, i) => i >= bitArray.length ? v !== 0 : false))
63
+ // if column length is less than 16, raw data will be still of length 16
64
+ if ((data.length !== bitArray.length && data.length !== 16) || (filter && data.length !== filter.length && data.length !== 16))
49
65
  throw new Error('PeptidesError: Data and bit array have different lengths');
66
+ if (filter && filter.allTrue)
67
+ filter = undefined; // no need to filter if all true
50
68
 
51
- const selected = new Float32Array(bitArray.trueCount());
52
- const rest = new Float32Array(bitArray.falseCount());
69
+ const filterAndMask = filter ? bitArray.clone().and(filter) : bitArray;
70
+ const selected = new Float32Array(filterAndMask.trueCount());
71
+ const rest = new Float32Array(filter ? (filter.trueCount() - filterAndMask.trueCount()) : filterAndMask.falseCount());
53
72
  let aggValue: number | undefined;
54
73
  if (aggData) {
55
74
  try {
56
75
  aggValue = DG.DataFrame.fromColumns([aggData.col])
57
- .clone(DG.BitSet.fromBytes(bitArray.buffer.buffer, bitArray.length)).col(aggData.col.name)
76
+ .clone(bitArrayToBitSet(filterAndMask)).col(aggData.col.name)
58
77
  ?.aggregate(aggData.type);
59
78
  } catch (e) {
60
79
  console.error(e);
@@ -63,35 +82,40 @@ export function getStats(data: RawData | number[], bitArray: BitArray,
63
82
  let selectedIndex = 0;
64
83
  let restIndex = 0;
65
84
  for (let i = 0; i < bitArray.length; ++i) {
85
+ if (filter && !filter.getBit(i))
86
+ continue;
66
87
  if (bitArray.getBit(i))
67
88
  selected[selectedIndex++] = data[i];
68
89
  else
69
90
  rest[restIndex++] = data[i];
70
91
  }
92
+ const totalAnalyzed = selected.length + rest.length;
71
93
 
72
94
  const selectedMean = selected.reduce((a, b) => a + b, 0) / Math.max(selected.length, 1);
73
95
  if (selected.length < 2 || rest.length < 2) {
74
96
  const restMean = rest.reduce((a, b) => a + b, 0) / Math.max(rest.length, 1);
97
+
75
98
  return {
76
99
  count: selected.length,
77
100
  pValue: null,
78
101
  mean: selectedMean,
79
102
  meanDifference: selectedMean - restMean,
80
- ratio: selected.length / (bitArray.length),
81
- mask: bitArray,
103
+ ratio: selected.length / totalAnalyzed,
104
+ mask: filterAndMask,
82
105
  aggValue,
83
106
  };
84
107
  }
85
108
 
86
109
  const testResult = tTest(selected, rest);
110
+
87
111
  const currentMeanDiff = testResult['Mean difference']!;
88
112
  return {
89
113
  count: selected.length,
90
114
  pValue: testResult[currentMeanDiff >= 0 ? 'p-value more' : 'p-value less'],
91
115
  mean: selectedMean,
92
116
  meanDifference: currentMeanDiff,
93
- ratio: selected.length / (bitArray.length),
94
- mask: bitArray,
117
+ ratio: selected.length / totalAnalyzed,
118
+ mask: filterAndMask,
95
119
  aggValue,
96
120
  };
97
121
  }
@@ -129,7 +153,7 @@ export function getAggregatedColumnValues(df: DG.DataFrame, columns: [string, DG
129
153
  options.filterDf ??= false;
130
154
  options.fractionDigits ??= 3;
131
155
 
132
- const filteredDf = options.filterDf && df.filter.anyFalse ? df.clone(df.filter) : df;
156
+ const filteredDf = options.filterDf && df.filter.anyFalse && (!options.mask || options.mask.length === df.filter.trueCount) ? df.clone(df.filter) : df;
133
157
 
134
158
  const colResults: StringDictionary = {};
135
159
  for (const [colName, aggFn] of columns) {
@@ -60,6 +60,7 @@ export function showTooltipAt(df: DG.DataFrame, activityCol: DG.Column<number>,
60
60
 
61
61
 
62
62
  const mask = DG.BitSet.fromBytes(stats.mask.buffer.buffer as ArrayBuffer, activityCol.length);
63
+ mask.and(df.filter);
63
64
  const hist = getActivityDistribution(getDistributionTable(activityCol, mask), true);
64
65
  const tableMap = getStatsTableMap(stats);
65
66
  if (options.fromViewer) {
@@ -85,6 +86,7 @@ export function showTooltipAt(df: DG.DataFrame, activityCol: DG.Column<number>,
85
86
  if (!stats)
86
87
  return null;
87
88
  const mask = DG.BitSet.fromBytes(stats.mask.buffer.buffer as ArrayBuffer, activityCol.length);
89
+ mask.and(df.filter);
88
90
  const hist = getActivityDistribution(getDistributionTable(activityCol, mask), true);
89
91
  const tableMap = getStatsTableMap(stats, {countName: 'Unique count'});
90
92
  if (options.fromViewer) {
@@ -11,6 +11,7 @@ import * as CR from '../utils/cell-renderer';
11
11
  import {HorizontalAlignments, IWebLogoViewer, PositionHeight} from '@datagrok-libraries/bio/src/viewers/web-logo';
12
12
  import {
13
13
  AggregationColumns,
14
+ bitSetToBitArray,
14
15
  ClusterTypeStats,
15
16
  getAggregatedColumnValues,
16
17
  getAggregatedValue,
@@ -315,15 +316,30 @@ export class LogoSummaryTable extends DG.JsViewer implements ILogoSummaryTable {
315
316
  onTableAttached(): void {
316
317
  super.onTableAttached();
317
318
  if (isApplicableDataframe(this.dataFrame)) {
318
- const activityColName = wu(this.dataFrame.columns.numerical).next().value.name;
319
- this.getProperty(`${LST_PROPERTIES.SEQUENCE}${COLUMN_NAME}`)
320
- ?.set(this, this.dataFrame.columns.bySemType(DG.SEMTYPE.MACROMOLECULE)!.name);
321
- this.getProperty(`${LST_PROPERTIES.ACTIVITY}${COLUMN_NAME}`)
322
- ?.set(this, activityColName);
323
- this.getProperty(`${LST_PROPERTIES.WEB_LOGO_AGGREGATION_COLUMN_NAME}`)?.set(this, activityColName);
324
-
325
- this.getProperty(`${LST_PROPERTIES.CLUSTERS}${COLUMN_NAME}`)
326
- ?.set(this, wu(this.dataFrame.columns.categorical).next().value.name);
319
+ const potentialActivityColName = wu(this.dataFrame.columns.numerical).filter((col) => col.name?.includes('activ')).take(1).toArray()[0]?.name;
320
+ const activityColName = potentialActivityColName ??
321
+ wu(this.dataFrame.columns.numerical).take(1).toArray()?.[0]?.name;
322
+ const seqCol = this.dataFrame.columns.bySemType(DG.SEMTYPE.MACROMOLECULE);
323
+ // const clusterCol =
324
+ if (seqCol) {
325
+ this.getProperty(`${LST_PROPERTIES.SEQUENCE}${COLUMN_NAME}`)
326
+ ?.set(this, seqCol.name);
327
+ }
328
+ if (activityColName) {
329
+ this.getProperty(`${LST_PROPERTIES.ACTIVITY}${COLUMN_NAME}`)
330
+ ?.set(this, activityColName);
331
+ this.getProperty(`${LST_PROPERTIES.WEB_LOGO_AGGREGATION_COLUMN_NAME}`)?.set(this, activityColName);
332
+ }
333
+
334
+ const potentialClusterCol = wu(this.dataFrame.columns)
335
+ .filter((col) => (col.isCategorical) && col.name?.toLowerCase().includes('cluster') && col.name != seqCol?.name && col.type !== DG.COLUMN_TYPE.BOOL)?.take(1)?.toArray()?.[0];
336
+ const clusterCol = potentialClusterCol ??
337
+ wu(this.dataFrame.columns.categorical)
338
+ .filter((col) => col.name != seqCol?.name && col.categories.length <= 50 && col.type !== DG.COLUMN_TYPE.BOOL)?.take(1)?.toArray()?.[0];
339
+ if (clusterCol) {
340
+ this.getProperty(`${LST_PROPERTIES.CLUSTERS}${COLUMN_NAME}`)
341
+ ?.set(this, clusterCol.name);
342
+ }
327
343
  } else {
328
344
  const msg = 'PeptidesError: dataframe is missing Macromolecule or numeric columns';
329
345
  grok.log.error(msg);
@@ -914,8 +930,8 @@ export class LogoSummaryTable extends DG.JsViewer implements ILogoSummaryTable {
914
930
  const viewerDfColsLength = viewerDfCols.length;
915
931
  const newClusterVals = new Array(viewerDfCols.length);
916
932
  const activityScaledCol = this.getScaledActivityColumn();
917
- const bitArray = BitArray.fromString(currentSelection.toBinaryString());
918
- const stats = getStats(activityScaledCol.getRawData(), bitArray);
933
+ const bitArray = bitSetToBitArray(currentSelection);
934
+ const stats = getStats(activityScaledCol.getRawData(), bitArray, bitSetToBitArray(this.dataFrame.filter));
919
935
 
920
936
  this.bitsets.push(currentSelection.clone());
921
937