@datagrok/bio 2.11.21 → 2.11.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/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "Leonid Stolbov",
6
6
  "email": "lstolbov@datagrok.ai"
7
7
  },
8
- "version": "2.11.21",
8
+ "version": "2.11.23",
9
9
  "description": "Bioinformatics support (import/export of sequences, conversion, visualization, analysis). [See more](https://github.com/datagrok-ai/public/blob/master/packages/Bio/README.md) for details.",
10
10
  "repository": {
11
11
  "type": "git",
@@ -39,6 +39,7 @@
39
39
  "@datagrok-libraries/ml": "^6.3.68",
40
40
  "@datagrok-libraries/tutorials": "^1.3.11",
41
41
  "@datagrok-libraries/utils": "^4.1.36",
42
+ "@datagrok-libraries/math": "^1.0.7",
42
43
  "cash-dom": "^8.0.0",
43
44
  "css-loader": "^6.7.3",
44
45
  "datagrok-api": "^1.16.0",
@@ -3,13 +3,11 @@ import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
 
5
5
  import {_package} from '../package';
6
-
7
- import * as lev from 'fastest-levenshtein';
8
- import {DistanceMatrix} from '@datagrok-libraries/ml/src/distance-matrix';
9
6
  import {getTreeHelper, ITreeHelper} from '@datagrok-libraries/bio/src/trees/tree-helper';
10
7
  import {getDendrogramService, IDendrogramService} from '@datagrok-libraries/bio/src/trees/dendrogram';
11
8
  import {demoSequenceSpace, handleError} from './utils';
12
9
  import {DemoScript} from '@datagrok-libraries/tutorials/src/demo-script';
10
+ import {getClusterMatrixWorker} from '@datagrok-libraries/math';
13
11
 
14
12
  const dataFn = 'data/sample_FASTA_PT_activity.csv';
15
13
  const seqColName = 'sequence';
@@ -55,13 +53,11 @@ export async function demoBio01aUI() {
55
53
  delay: 2000,
56
54
  })
57
55
  .step('Cluster sequences', async () => {
58
- const seqCol: DG.Column<string> = df.getCol(seqColName);
59
- const seqList = seqCol.toList();
60
- const distance: DistanceMatrix = DistanceMatrix.calc(seqList, (aSeq: string, bSeq: string) => {
61
- const levDistance = lev.distance(aSeq, bSeq);
62
- return levDistance / ((aSeq.length + bSeq.length) / 2);
63
- });
64
- const treeRoot = await treeHelper.hierarchicalClusteringByDistance(distance, 'ward');
56
+ const distance = await treeHelper.calcDistanceMatrix(df, [seqColName]);
57
+ const clusterMatrix = await getClusterMatrixWorker(
58
+ distance!.data, df.rowCount, 1,
59
+ );
60
+ const treeRoot = treeHelper.parseClusterMatrix(clusterMatrix);
65
61
  dendrogramSvc.injectTreeForGrid(view.grid, treeRoot, undefined, 150, undefined);
66
62
  }, {
67
63
  description: `Perform hierarchical clustering to reveal relationships between sequences.`,
@@ -6,14 +6,13 @@ import {_package, activityCliffs} from '../package';
6
6
  import $ from 'cash-dom';
7
7
 
8
8
  import {TEMPS as acTEMPS} from '@datagrok-libraries/ml/src/viewers/activity-cliffs';
9
- import * as lev from 'fastest-levenshtein';
10
- import {DistanceMatrix} from '@datagrok-libraries/ml/src/distance-matrix';
11
9
  import {getTreeHelper, ITreeHelper} from '@datagrok-libraries/bio/src/trees/tree-helper';
12
10
  import {getDendrogramService, IDendrogramService} from '@datagrok-libraries/bio/src/trees/dendrogram';
13
11
  import {handleError} from './utils';
14
12
  import {DemoScript} from '@datagrok-libraries/tutorials/src/demo-script';
15
13
  import {DimReductionMethods} from '@datagrok-libraries/ml/src/reduce-dimensionality';
16
14
  import {MmDistanceFunctionsNames} from '@datagrok-libraries/ml/src/macromolecule-distance-functions';
15
+ import {getClusterMatrixWorker} from '@datagrok-libraries/math';
17
16
 
18
17
  const dataFn: string = 'data/sample_FASTA_PT_activity.csv';
19
18
 
@@ -67,13 +66,12 @@ export async function demoBio01bUI() {
67
66
  })
68
67
  .step('Cluster sequences', async () => {
69
68
  const progressBar = DG.TaskBarProgressIndicator.create(`Running sequence clustering...`);
70
- const seqCol: DG.Column<string> = df.getCol('sequence');
71
- const seqList = seqCol.toList();
72
- const distance: DistanceMatrix = DistanceMatrix.calc(seqList, (aSeq: string, bSeq: string) => {
73
- const levDistance = lev.distance(aSeq, bSeq);
74
- return levDistance / ((aSeq.length + bSeq.length) / 2);
75
- });
76
- const treeRoot = await treeHelper.hierarchicalClusteringByDistance(distance, 'ward');
69
+
70
+ const distance = await treeHelper.calcDistanceMatrix(df, ['sequence']);
71
+ const clusterMatrix = await getClusterMatrixWorker(
72
+ distance!.data, df.rowCount, 1,
73
+ );
74
+ const treeRoot = treeHelper.parseClusterMatrix(clusterMatrix);
77
75
  progressBar.close();
78
76
  dendrogramSvc.injectTreeForGrid(view.grid, treeRoot, undefined, 150, undefined);
79
77
 
@@ -12,7 +12,7 @@ import {UnitsHandler} from '@datagrok-libraries/bio/src/utils/units-handler';
12
12
  export class MacromoleculeColumnWidget extends DG.Widget {
13
13
  private viewed: boolean = false;
14
14
 
15
- private seqCol: DG.Column<string>;
15
+ private readonly seqCol: DG.Column<string>;
16
16
 
17
17
  private wlViewer: WebLogoViewer;
18
18
 
@@ -44,4 +44,9 @@ export class MacromoleculeColumnWidget extends DG.Widget {
44
44
  this.root.style.width = '100%';
45
45
  }
46
46
  }
47
+
48
+ override detach() {
49
+ this.wlViewer.detach();
50
+ super.detach();
51
+ }
47
52
  }
@@ -2,6 +2,7 @@ import * as grok from 'datagrok-api/grok';
2
2
  import * as ui from 'datagrok-api/ui';
3
3
  import * as DG from 'datagrok-api/dg';
4
4
 
5
+ import $ from 'cash-dom';
5
6
  import wu from 'wu';
6
7
  import {fromEvent, Observable, Subject, Unsubscribable} from 'rxjs';
7
8
 
@@ -293,6 +294,10 @@ enum WlRenderLevel {
293
294
  Freqs = 2,
294
295
  }
295
296
 
297
+ export const Debounces = new class {
298
+ render: number = 20;
299
+ }();
300
+
296
301
  const POSITION_LABELS_HEIGHT: number = 12;
297
302
 
298
303
  export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
@@ -506,7 +511,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
506
511
  const dataFrameTxt: string = this.dataFrame ? 'data' : 'null';
507
512
  _package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.buildView( dataFrame = ${dataFrameTxt} ) start`);
508
513
  const dpr = window.devicePixelRatio;
509
- this.viewSubs.push(DG.debounce(this.renderRequest)
514
+ this.viewSubs.push(DG.debounce(this.renderRequest, Debounces.render)
510
515
  .subscribe(this.renderRequestOnDebounce.bind(this)));
511
516
 
512
517
  this.helpUrl = '/help/visualize/viewers/web-logo.md';
@@ -558,7 +563,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
558
563
  this.viewSubs.push(
559
564
  fromEvent<WheelEvent>(this.canvas, 'wheel').subscribe(this.canvasOnWheel.bind(this)));
560
565
 
561
- await this.render(WlRenderLevel.Freqs, 'buildView');
566
+ this.render(WlRenderLevel.Freqs, 'buildView');
562
567
  _package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.buildView() end`);
563
568
  }
564
569
 
@@ -589,7 +594,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
589
594
  this.unitsHandler = UnitsHandler.getOrCreate(this.seqCol);
590
595
 
591
596
  this.palette = pickUpPalette(this.seqCol);
592
- this.updatePositions();
597
+ this.render(WlRenderLevel.Freqs, 'updateSeqCol()');
593
598
  this.error = null;
594
599
  } catch (err: any) {
595
600
  this.seqCol = null;
@@ -608,36 +613,6 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
608
613
  }
609
614
  }
610
615
 
611
- /** Updates {@link positionNames} and calculates {@link startPosition} and {@link endPosition}.
612
- * Calls {@link render}() with {@link WlRenderLevel.Freqs}
613
- */
614
- private updatePositions(): void {
615
- if (!this.seqCol) return;
616
-
617
- const dfFilter = this.getFilter();
618
- const maxLength: number = dfFilter.trueCount === 0 ? this.unitsHandler!.maxLength :
619
- wu.enumerate(this.unitsHandler!.splitted).map(([mList, rowI]) => {
620
- return dfFilter.get(rowI) && !!mList ? mList.length : 0;
621
- }).reduce((max, l) => Math.max(max, l), 0);
622
-
623
- /** positionNames and positionLabel can be set up through the column's tags only */
624
- const positionNamesTxt = this.seqCol.getTag(bioTAGS.positionNames);
625
- const positionLabelsTxt = this.seqCol.getTag(bioTAGS.positionLabels);
626
- this.positionNames = !!positionNamesTxt ? positionNamesTxt.split(positionSeparator).map((v) => v.trim()) :
627
- [...Array(maxLength).keys()].map((jPos) => `${jPos + 1}`)/* fallback if tag is not provided */;
628
- this.positionLabels = !!positionLabelsTxt ? positionLabelsTxt.split(positionSeparator).map((v) => v.trim()) :
629
- undefined;
630
-
631
- this.startPosition = (this.startPositionName && this.positionNames &&
632
- this.positionNames.includes(this.startPositionName)) ?
633
- this.positionNames.indexOf(this.startPositionName) : 0;
634
- this.endPosition = (this.endPositionName && this.positionNames &&
635
- this.positionNames.includes(this.endPositionName)) ?
636
- this.positionNames.indexOf(this.endPositionName) : (maxLength - 1);
637
-
638
- this.render(WlRenderLevel.Freqs, 'updatePositions');
639
- }
640
-
641
616
  private getFilter(): DG.BitSet {
642
617
  let dfFilterRes: DG.BitSet;
643
618
  switch (this.filterSource) {
@@ -851,13 +826,13 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
851
826
  case PROPS.shrinkEmptyTail:
852
827
  case PROPS.skipEmptyPositions:
853
828
  case PROPS.positionHeight: {
854
- this.updatePositions();
829
+ this.render(WlRenderLevel.Freqs, `onPropertyChanged( ${property.name} )`);
855
830
  break;
856
831
  }
857
832
 
858
833
  case PROPS.valueColumnName:
859
834
  case PROPS.valueAggrType: {
860
- this.render(WlRenderLevel.Freqs, `onPropertyChanged(${property.name})`);
835
+ this.render(WlRenderLevel.Freqs, `onPropertyChanged( ${property.name} )`);
861
836
  break;
862
837
  }
863
838
  // this.positionWidth obtains a new value
@@ -892,7 +867,7 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
892
867
  }
893
868
 
894
869
  /** Remove all handlers when table is a detach */
895
- public override async detach() {
870
+ public override detach() {
896
871
  const logPrefix = `${this.viewerToLog()}.detach()`;
897
872
  _package.logger.debug(`${logPrefix}, in`);
898
873
 
@@ -984,6 +959,31 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
984
959
  _package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.render.calculateFreqsInt(), start `);
985
960
  if (!this.host || !this.seqCol || !this.dataFrame) return;
986
961
 
962
+ // region updatePositions
963
+
964
+ const dfFilter = this.getFilter();
965
+ const maxLength: number = dfFilter.trueCount === 0 ? this.unitsHandler!.maxLength :
966
+ wu.enumerate(this.unitsHandler!.splitted).map(([mList, rowI]) => {
967
+ return dfFilter.get(rowI) && !!mList ? mList.length : 0;
968
+ }).reduce((max, l) => Math.max(max, l), 0);
969
+
970
+ /** positionNames and positionLabel can be set up through the column's tags only */
971
+ const positionNamesTxt = this.seqCol.getTag(bioTAGS.positionNames);
972
+ const positionLabelsTxt = this.seqCol.getTag(bioTAGS.positionLabels);
973
+ this.positionNames = !!positionNamesTxt ? positionNamesTxt.split(positionSeparator).map((v) => v.trim()) :
974
+ [...Array(maxLength).keys()].map((jPos) => `${jPos + 1}`)/* fallback if tag is not provided */;
975
+ this.positionLabels = !!positionLabelsTxt ? positionLabelsTxt.split(positionSeparator).map((v) => v.trim()) :
976
+ undefined;
977
+
978
+ this.startPosition = (this.startPositionName && this.positionNames &&
979
+ this.positionNames.includes(this.startPositionName)) ?
980
+ this.positionNames.indexOf(this.startPositionName) : 0;
981
+ this.endPosition = (this.endPositionName && this.positionNames &&
982
+ this.positionNames.includes(this.endPositionName)) ?
983
+ this.positionNames.indexOf(this.endPositionName) : (maxLength - 1);
984
+
985
+ // endregion updatePositions
986
+
987
987
  const length: number = this.startPosition <= this.endPosition ? this.endPosition - this.startPosition + 1 : 0;
988
988
  this.unitsHandler = UnitsHandler.getOrCreate(this.seqCol);
989
989
  const posCount: number = this.startPosition <= this.endPosition ? this.endPosition - this.startPosition + 1 : 0;
@@ -996,7 +996,6 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
996
996
  }
997
997
 
998
998
  // 2022-05-05 askalkin instructed to show WebLogo based on filter (not selection)
999
- const dfFilter = this.getFilter();
1000
999
  const dfRowCount = this.dataFrame.rowCount;
1001
1000
  const splitted = this.unitsHandler.splitted;
1002
1001
 
@@ -1136,6 +1135,11 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
1136
1135
  }
1137
1136
 
1138
1137
  private renderRequestOnDebounce(renderLevel: WlRenderLevel): void {
1138
+ const logPrefix = `${this.viewerToLog()}.renderRequestOnDebounce()`;
1139
+ if ($(this.root).offsetParent().get()[0]?.tagName === 'HTML') {
1140
+ _package.logger.warning(`${logPrefix}, $(this.root).offsetParent() is the 'HTML' tag.`);
1141
+ return;
1142
+ }
1139
1143
  this.requestedRenderLevel = WlRenderLevel.None;
1140
1144
  this.renderInt(renderLevel)
1141
1145
  .catch((err: any) => {
@@ -1176,7 +1180,6 @@ export class WebLogoViewer extends DG.JsViewer implements IWebLogoViewer {
1176
1180
  private dataFrameFilterOnChanged(_value: any): void {
1177
1181
  _package.logger.debug(`Bio: WebLogoViewer<${this.viewerId}>.dataFrameFilterChanged()`);
1178
1182
  try {
1179
- this.updatePositions();
1180
1183
  if (this.filterSource === FilterSources.Filtered)
1181
1184
  this.render(WlRenderLevel.Freqs, 'dataFrameFilterOnChanged');
1182
1185
  } catch (err: any) {