trackplot 0.5.1__tar.gz → 0.5.3__tar.gz

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.
Files changed (80) hide show
  1. {trackplot-0.5.1 → trackplot-0.5.3}/PKG-INFO +4 -5
  2. {trackplot-0.5.1 → trackplot-0.5.3}/pyproject.toml +4 -4
  3. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/base/Junction.py +13 -0
  4. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/cli.py +1 -1
  5. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/file/Annotation.py +23 -7
  6. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/file/Bam.py +23 -6
  7. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/file/ReadSegments.py +3 -1
  8. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/plot.py +2 -2
  9. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/plot_func.py +136 -67
  10. trackplot-0.5.3/ui/assets/Home-7GzAh8lS.js +1 -0
  11. trackplot-0.5.3/ui/assets/Home-CDW3Zwoa.js +1 -0
  12. trackplot-0.5.3/ui/assets/Home-RdVPWns6.js +1 -0
  13. trackplot-0.5.3/ui/assets/Home-zRV7yePL.css +1 -0
  14. trackplot-0.5.3/ui/assets/Plot-BrjU8Kwg.js +17 -0
  15. trackplot-0.5.3/ui/assets/Plot-COvGnprQ.css +1 -0
  16. trackplot-0.5.3/ui/assets/Plot-Cyj_LlDt.js +17 -0
  17. trackplot-0.5.3/ui/assets/Plot-DiuFnwNK.js +25 -0
  18. trackplot-0.5.3/ui/assets/Plot-DpL7z7tp.css +1 -0
  19. trackplot-0.5.3/ui/assets/Plot-rbQz1TOM.css +1 -0
  20. trackplot-0.5.3/ui/assets/el-divider-BHm65SRq.css +1 -0
  21. trackplot-0.5.3/ui/assets/el-divider-BVZhQIwQ.js +1 -0
  22. trackplot-0.5.3/ui/assets/el-divider-Brt4-Qvr.js +1 -0
  23. trackplot-0.5.3/ui/assets/el-divider-Cwxg0Ado.css +1 -0
  24. trackplot-0.5.3/ui/assets/el-divider-eEJXnQD5.js +1 -0
  25. trackplot-0.5.3/ui/assets/el-divider-i9JMIXVR.css +1 -0
  26. trackplot-0.5.3/ui/assets/index-4hxJ_zbq.js +26 -0
  27. trackplot-0.5.3/ui/assets/index-CETGMNio.css +1 -0
  28. trackplot-0.5.3/ui/assets/index-CWfdj0DH.js +26 -0
  29. trackplot-0.5.3/ui/assets/index-Dd6Bavnk.js +26 -0
  30. trackplot-0.5.3/ui/assets/index-O8P0XkxB.css +1 -0
  31. trackplot-0.5.3/ui/assets/index-ns9n7-F7.css +1 -0
  32. {trackplot-0.5.1 → trackplot-0.5.3}/ui/index.html +2 -2
  33. {trackplot-0.5.1 → trackplot-0.5.3}/LICENSE +0 -0
  34. {trackplot-0.5.1 → trackplot-0.5.3}/README.md +0 -0
  35. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/__init__.py +0 -0
  36. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/anno/AxLabel.py +0 -0
  37. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/anno/__init__.py +0 -0
  38. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/anno/theme.py +0 -0
  39. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/base/CoordinateMap.py +0 -0
  40. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/base/GenomicLoci.py +0 -0
  41. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/base/Protein.py +0 -0
  42. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/base/ReadDepth.py +0 -0
  43. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/base/Readder.py +0 -0
  44. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/base/Stroke.py +0 -0
  45. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/base/Transcript.py +0 -0
  46. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/base/__init__.py +0 -0
  47. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/base/pyUniprot.py +0 -0
  48. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/conf/DomainSetting.py +0 -0
  49. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/conf/__init__.py +0 -0
  50. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/conf/config.py +0 -0
  51. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/conf/drawing.py +0 -0
  52. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/conf/ui.py +0 -0
  53. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/file/ATAC.py +0 -0
  54. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/file/BedGraph.py +0 -0
  55. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/file/Bigwig.py +0 -0
  56. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/file/Depth.py +0 -0
  57. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/file/Fasta.py +0 -0
  58. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/file/File.py +0 -0
  59. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/file/HiCMatrixTrack.py +0 -0
  60. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/file/Junction.py +0 -0
  61. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/file/Motif.py +0 -0
  62. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/file/__init__.py +0 -0
  63. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/plot_tests.py +0 -0
  64. {trackplot-0.5.1 → trackplot-0.5.3}/trackplot/server.py +0 -0
  65. {trackplot-0.5.1 → trackplot-0.5.3}/ui/assets/Home-DOO13BH7.js +0 -0
  66. {trackplot-0.5.1 → trackplot-0.5.3}/ui/assets/Home-QmeAKOl4.js +0 -0
  67. {trackplot-0.5.1 → trackplot-0.5.3}/ui/assets/Home-jSR0MsHI.css +0 -0
  68. {trackplot-0.5.1 → trackplot-0.5.3}/ui/assets/Plot-BmqHZ4QE.css +0 -0
  69. {trackplot-0.5.1 → trackplot-0.5.3}/ui/assets/Plot-Bvyo6ju9.css +0 -0
  70. {trackplot-0.5.1 → trackplot-0.5.3}/ui/assets/Plot-CTM-EDrj.js +0 -0
  71. {trackplot-0.5.1 → trackplot-0.5.3}/ui/assets/Plot-hvkDteAn.js +0 -0
  72. {trackplot-0.5.1 → trackplot-0.5.3}/ui/assets/el-divider-BuEUMHwE.css +0 -0
  73. {trackplot-0.5.1 → trackplot-0.5.3}/ui/assets/el-divider-SYT5K-ds.css +0 -0
  74. {trackplot-0.5.1 → trackplot-0.5.3}/ui/assets/el-divider-VYjL3C7L.js +0 -0
  75. {trackplot-0.5.1 → trackplot-0.5.3}/ui/assets/el-divider-u9f0bZWY.js +0 -0
  76. {trackplot-0.5.1 → trackplot-0.5.3}/ui/assets/index-Cexhr_fn.css +0 -0
  77. {trackplot-0.5.1 → trackplot-0.5.3}/ui/assets/index-CrzyEb9s.js +0 -0
  78. {trackplot-0.5.1 → trackplot-0.5.3}/ui/assets/index-D_Cw0sbX.js +0 -0
  79. {trackplot-0.5.1 → trackplot-0.5.3}/ui/assets/index-Sq2gI4sE.css +0 -0
  80. {trackplot-0.5.1 → trackplot-0.5.3}/ui/vite.svg +0 -0
@@ -1,24 +1,23 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: trackplot
3
- Version: 0.5.1
3
+ Version: 0.5.3
4
4
  Summary: The trackplot is a tool for visualizing various next-generation sequencing (NGS) data, including DNA-seq, RNA-seq, single-cell RNA-seq and full-length sequencing datasets. https://sashimi.readthedocs.io/
5
5
  License: BSD-3
6
6
  Author: ygidtu
7
7
  Author-email: ygidtu@gmail.com
8
- Requires-Python: >=3.8,<3.12
8
+ Requires-Python: >=3.10,<3.13
9
9
  Classifier: License :: Other/Proprietary License
10
10
  Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.8
12
- Classifier: Programming Language :: Python :: 3.9
13
11
  Classifier: Programming Language :: Python :: 3.10
14
12
  Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
15
14
  Requires-Dist: adjusttext (>=0.7.3,<0.8.0)
16
15
  Requires-Dist: cairocffi (>=1.4.0,<2.0.0)
17
16
  Requires-Dist: click (>=8.1.3,<9.0.0)
18
17
  Requires-Dist: click-option-group (>=0.5.5,<0.6.0)
19
18
  Requires-Dist: filetype (>=1.2.0,<2.0.0)
20
19
  Requires-Dist: flask (>=2.3.2,<3.0.0)
21
- Requires-Dist: hicmatrix (>=15,<16)
20
+ Requires-Dist: hicmatrix (>=17,<18)
22
21
  Requires-Dist: loguru (>=0.6.0,<0.7.0)
23
22
  Requires-Dist: matplotlib (>=3.6.3,<4.0.0)
24
23
  Requires-Dist: numpy (>=1.24.1,<2.0.0)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "trackplot"
3
- version = "0.5.1"
3
+ version = "0.5.3"
4
4
  description = "The trackplot is a tool for visualizing various next-generation sequencing (NGS) data, including DNA-seq, RNA-seq, single-cell RNA-seq and full-length sequencing datasets. https://sashimi.readthedocs.io/"
5
5
  authors = ["ygidtu <ygidtu@gmail.com>"]
6
6
  license = "BSD-3"
@@ -13,13 +13,13 @@ include = [
13
13
  ]
14
14
 
15
15
  [tool.poetry.dependencies]
16
- python = ">=3.8,<3.12"
16
+ python = ">=3.10,<3.13"
17
17
  adjusttext = "^0.7.3"
18
18
  cairocffi = "^1.4.0"
19
19
  click = "^8.1.3"
20
20
  click-option-group = "^0.5.5"
21
21
  filetype = "^1.2.0"
22
- hicmatrix = "^15"
22
+ hicmatrix = "^17"
23
23
  loguru = "^0.6.0"
24
24
  matplotlib = "^3.6.3"
25
25
  numpy = "^1.24.1"
@@ -34,7 +34,7 @@ flask = "^2.3.2"
34
34
 
35
35
  [[tool.poetry.source]]
36
36
  name = "mirrors"
37
- url = "https://pypi.tuna.tsinghua.edu.cn/simple/"
37
+ url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/"
38
38
  priority = "primary"
39
39
 
40
40
  [[tool.poetry.source]]
@@ -134,6 +134,19 @@ class Junction(object):
134
134
 
135
135
  return self.start > other.end
136
136
 
137
+ def str(self, with_strand: bool = True) -> str:
138
+ u"""convert junctions to string, with or without strand
139
+
140
+ Keyword arguments:
141
+ :param with_strand: as name says
142
+ Return: chr:strat-end:strand or chr:start-end
143
+ """
144
+ if with_strand:
145
+ return str(self)
146
+ return f"{self.chromosome}:{self.start}-{self.end}"
147
+
148
+
149
+
137
150
 
138
151
  if __name__ == '__main__':
139
152
  pass
@@ -8,7 +8,7 @@ This script contains all the command line parameters
8
8
  import os
9
9
  import sys
10
10
  from multiprocessing import cpu_count
11
- from typing import Optional, Dict, Set, Tuple
11
+ from typing import Optional
12
12
 
13
13
  import click
14
14
  from click_option_group import optgroup
@@ -2,8 +2,10 @@
2
2
  # -*- coding:utf-8 -*-
3
3
  u"""
4
4
  Created by ygidtu@gmail.com at 2020.05.07
5
-
6
5
  This scripts contains the class handle the reference file
6
+
7
+ Modified by AD 2025/01/12 to include only specified transcripts, otherwise their exon coordinates still occupy the alignments.
8
+
7
9
  """
8
10
  import glob
9
11
  import gzip
@@ -349,16 +351,15 @@ class Annotation(File):
349
351
 
350
352
  return output_gtf
351
353
 
352
- def __load_gtf__(self):
353
-
354
+ def __load_gtf__(self, transcripts_to_show: list[str]|None = None):
354
355
  u"""
355
356
  Load transcripts inside of region from gtf file
356
357
  :param region: target region
357
358
  :return: list of Transcript
358
- """
359
+ """
360
+ # AD - passing transcripts_to_show
359
361
  transcripts = {}
360
362
  exons = {}
361
-
362
363
  for rec in Reader.read_gtf(self.path, self.region):
363
364
  start = max(rec.start, self.region.start)
364
365
  end = min(rec.end, self.region.end)
@@ -369,6 +370,13 @@ class Annotation(File):
369
370
  break
370
371
 
371
372
  if re.search(r"(rna|transcript|cds)", rec.feature, re.I):
373
+
374
+ if transcripts_to_show:
375
+ _name = rec.transcript_name if "transcript_name" in rec.attributes else rec.transcript_id
376
+ if _name not in transcripts_to_show:
377
+ logger.info(f"Skipping transcript {_name}")
378
+ continue
379
+
372
380
  if rec.transcript_id not in transcripts.keys():
373
381
  transcripts[rec.transcript_id] = Transcript(
374
382
  chromosome=rec.contig,
@@ -380,7 +388,8 @@ class Annotation(File):
380
388
  gene=rec.gene_name if "gene_name" in rec.attributes else "",
381
389
  transcript=rec.transcript_name if "transcript_name" in rec.attributes else "",
382
390
  exons=[]
383
- )
391
+ )
392
+
384
393
  elif re.search(r"(exon)", rec.feature, re.I):
385
394
  if rec.transcript_id not in exons.keys():
386
395
  exons[rec.transcript_id] = []
@@ -581,9 +590,16 @@ class Annotation(File):
581
590
  assert isinstance(region, GenomicLoci), "region should be a GenomicLoci object"
582
591
  if transcripts is None:
583
592
  transcripts = []
593
+ elif isinstance(transcripts, str):
594
+ # AD when no transcripts are specified, transcripts is an empty string, not None
595
+ if transcripts == "":
596
+ transcripts = []
597
+ else:
598
+ transcripts = transcripts.split(",")
584
599
  self.region = region
600
+ # AD - pass specified transcripts early
585
601
  if self.category == "gtf":
586
- self.__load_gtf__()
602
+ self.__load_gtf__(transcripts)
587
603
  if self.add_local_domain:
588
604
  self.__load_local_domain__(region)
589
605
 
@@ -20,7 +20,7 @@ from trackplot.base.Junction import Junction
20
20
  from trackplot.base.ReadDepth import ReadDepth
21
21
  from trackplot.base.Readder import Reader
22
22
  from trackplot.conf.config import NORMALIZATION
23
- from trackplot.file.File import SingleCell
23
+ from trackplot.file.File import SingleCell
24
24
 
25
25
 
26
26
  class Bam(SingleCell):
@@ -161,8 +161,7 @@ class Bam(SingleCell):
161
161
  spanned_junctions = kwargs.get("junctions", {})
162
162
  included_junctions = kwargs.get("included_junctions", {})
163
163
  remove_duplicate_umi = kwargs.get("remove_duplicate_umi", False)
164
- spanned_junctions_plus = dict()
165
- spanned_junctions_minus = dict()
164
+ spanned_junctions_plus, spanned_junctions_minus = {}, {}
166
165
  plus, minus = np.zeros(len(region), dtype=np.int32), np.zeros(len(region), dtype=np.int32)
167
166
  site_plus, site_minus = np.zeros(len(region), dtype=np.int32), np.zeros(len(region), dtype=np.int32)
168
167
 
@@ -276,14 +275,32 @@ class Bam(SingleCell):
276
275
  site_minus[start - region.start] += 1
277
276
 
278
277
  for k, v in spanned_junctions.items():
279
- if included_junctions and str(k) not in included_junctions:
278
+ kept = v >= threshold
279
+
280
+ # if the number of junctiosn is lower than threshold, then skip
281
+ if not kept:
280
282
  continue
281
-
282
- if v >= threshold:
283
+
284
+ # if included_junctions is provided, then skip all junctions by default
285
+ if included_junctions:
286
+ kept = False
287
+
288
+ # check whether junctions should be kept
289
+ if k.str(with_strand = True) in included_junctions:
290
+ logger.debug(f"{str(k)} is included")
291
+ kept = True
292
+ elif k.str(with_strand = False) in included_junctions:
293
+ logger.debug(f"{str(k)} is included, but strand is ignored")
294
+ kept = True
295
+
296
+ if not kept:
297
+ logger.debug(f"{str(k)} is not included")
298
+ else:
283
299
  if k.strand == "+":
284
300
  spanned_junctions_plus[k] = 1 + spanned_junctions_plus.get(k, v)
285
301
  elif k.strand == "-":
286
302
  spanned_junctions_minus[k] = -1 + spanned_junctions_minus.get(k, v)
303
+
287
304
  except IOError as err:
288
305
  logger.error('There is no .bam file at {0}'.format(self.path))
289
306
  logger.error(err)
@@ -382,7 +382,9 @@ class ReadSegment(File):
382
382
  current_ignore_num = min(
383
383
  [self.deletion_ignore, read.query_alignment_length * self.del_ratio_ignore])
384
384
  else:
385
- current_ignore_num = np.Inf
385
+ current_ignore_num = np.inf
386
+ # AD - AttributeError: `np.Inf` was removed in the NumPy 2.0 release. Use `np.inf` instead.
387
+
386
388
 
387
389
  exon_bound = []
388
390
  intron_bound = []
@@ -30,7 +30,7 @@ logging.getLogger('matplotlib.font_manager').setLevel(logging.ERROR)
30
30
  faulthandler.enable()
31
31
 
32
32
 
33
- __version__ = "0.5.1"
33
+ __version__ = "0.5.3"
34
34
  __author__ = "ygidtu & Ran Zhou"
35
35
  __email__ = "ygidtu@gmail.com"
36
36
 
@@ -1437,7 +1437,7 @@ class Plot(object):
1437
1437
 
1438
1438
  if output:
1439
1439
  logger.info(f"saving fig into {output}")
1440
- fig.savefig(output, transparent=True, bbox_inches='tight')
1440
+ fig.savefig(output, transparent=False, bbox_inches='tight') # AD
1441
1441
  elif return_image:
1442
1442
  output = io.BytesIO()
1443
1443
  if return_image == "png":
@@ -2,11 +2,13 @@
2
2
  # -*- coding:utf-8 -*-
3
3
  u"""
4
4
  This script contains the functions to draw different images
5
+ Modified by AD 2025/01/13
5
6
  """
6
- import gzip
7
+ import itertools # AD - for cumulative sum of graph_coords
8
+ #import gzip
7
9
  import math
8
10
  from copy import deepcopy
9
- from decimal import Decimal
11
+ #from decimal import Decimal
10
12
  from typing import Dict, List, Optional, Union, Tuple, Set
11
13
 
12
14
  import matplotlib as mpl
@@ -25,6 +27,7 @@ from matplotlib.textpath import TextPath
25
27
  from matplotlib.transforms import Affine2D
26
28
  from scipy.cluster.hierarchy import dendrogram, linkage
27
29
  from scipy.stats import gaussian_kde, zscore
30
+ from scipy.stats import beta # AD
28
31
 
29
32
  from trackplot.anno.theme import Theme
30
33
  from trackplot.base.GenomicLoci import GenomicLoci
@@ -100,7 +103,6 @@ def cubic_bezier(pts, t):
100
103
  p3 = np.array(p3)
101
104
  return p0 * (1 - t) ** 3 + 3 * t * p1 * (1 - t) ** 2 + 3 * t ** 2 * (1 - t) * p2 + t ** 3 * p3
102
105
 
103
-
104
106
  def __merge_exons__(exons: List[List[int]]):
105
107
  u"""
106
108
  merge the overlap exons into one
@@ -124,7 +126,7 @@ def __merge_exons__(exons: List[List[int]]):
124
126
 
125
127
 
126
128
  def init_graph_coords(region: GenomicLoci, exons: Optional[List[List[int]]] = None, exon_scale=1,
127
- intron_scale=.5) -> np.array:
129
+ intron_scale=0.5) -> np.array:
128
130
  u"""
129
131
  init the default
130
132
  :param region: the plot region
@@ -135,33 +137,62 @@ def init_graph_coords(region: GenomicLoci, exons: Optional[List[List[int]]] = No
135
137
  graph_coords = np.zeros(len(region), dtype=int)
136
138
 
137
139
  if exons:
138
- # if there have exons, init graph_coords by exon and intron scales
139
- for i in range(0, exons[0][0] - region.start):
140
- graph_coords[i] = (i - 0) * intron_scale
141
-
142
- exons = __merge_exons__(exons)
143
- for i in range(0, len(exons)):
144
- exon = exons[i]
145
-
146
- if i > 0:
147
- intron = [exons[i - 1][1], exons[i][0]]
148
-
149
- for j in range(intron[0], intron[1]):
140
+ # AD - if user specifies an intron scale in line with Trackplot, addition is transparent
141
+ if intron_scale <= 1:
142
+ # if there have exons, init graph_coords by exon and intron scales
143
+ for i in range(0, exons[0][0] - region.start):
144
+ graph_coords[i] = (i - 0) * intron_scale
145
+ exons = __merge_exons__(exons)
146
+ for i in range(0, len(exons)):
147
+ exon = exons[i]
148
+ if i > 0:
149
+ intron = [exons[i - 1][1], exons[i][0]]
150
+
151
+ for j in range(intron[0], intron[1]):
152
+ if j >= region.start:
153
+ graph_coords[j - region.start] = graph_coords[intron[0] - region.start - 1] + (
154
+ j - intron[0] + 1) * intron_scale
155
+ for j in range(exon[0], exon[1] + 1):
150
156
  if j >= region.start:
151
- graph_coords[j - region.start] = graph_coords[intron[0] - region.start - 1] + (
152
- j - intron[0] + 1) * intron_scale
153
-
154
- for j in range(exon[0], exon[1] + 1):
155
- if j >= region.start:
156
- graph_coords[j - region.start] = graph_coords[exon[0] - region.start - 1] + (
157
- j - exon[0] + 1) * exon_scale
158
-
159
- intron = [exons[-1][-1], region.end]
160
- for i in range(intron[0], intron[1]):
161
- if i >= region.start:
162
- graph_coords[i - region.start] = graph_coords[intron[0] - region.start - 1] + (
163
- i - intron[0] + 1) * intron_scale
164
-
157
+ graph_coords[j - region.start] = graph_coords[exon[0] - region.start - 1] + (
158
+ j - exon[0] + 1) * exon_scale
159
+ intron = [exons[-1][-1], region.end]
160
+ for i in range(intron[0], intron[1]):
161
+ if i >= region.start:
162
+ graph_coords[i - region.start] = graph_coords[intron[0] - region.start - 1] + (
163
+ i - intron[0] + 1) * intron_scale
164
+ else:
165
+ exons = __merge_exons__(exons)
166
+ while exons[0][1] < region.start:
167
+ exons = exons[1:]
168
+ while exons[-1][0] > region.end:
169
+ exons = exons[:-1]
170
+ exons[0][0] = max(exons[0][0], region.start)
171
+ exons[-1][1] = min(exons[-1][1], region.end)
172
+ last_interval = region.end - exons[-1][1]
173
+ for i, e in enumerate(exons):
174
+ exons[i][0] -= region.start
175
+ exons[i][1] -= region.start
176
+
177
+ steps = [float(exon_scale)] * len(region)
178
+ if exons[0][0] > 0:
179
+ step = intron_scale / exons[0][0]
180
+ for i in range(exons[0][0]):
181
+ steps[i] = step
182
+ for e in range(1, len(exons)):
183
+ interval = exons[e][0] - exons[e-1][1] - 2
184
+ step = intron_scale / interval
185
+ for i in range(exons[e-1][1], exons[e][0]):
186
+ steps[i] = step
187
+ interval = exons[e][0] - exons[e-1][1] - 2
188
+ if last_interval := len(region) - exons[-1][1] - 1:
189
+ step = intron_scale / last_interval
190
+ for i in range(exons[1][1] +1, len(region)):
191
+ steps[i] = step
192
+ graph_coords = list(map(int, itertools.accumulate(steps)))
193
+ #increments match exactly with original code
194
+ #for i, e in enumerate(graph_objects):
195
+ # print(f"{i}\t{e}")
165
196
  else:
166
197
  # if there is not any exons, just init graph_coords by region
167
198
  for i, j in enumerate(range(region.start, region.end + 1)):
@@ -175,7 +206,6 @@ def init_graph_coords(region: GenomicLoci, exons: Optional[List[List[int]]] = No
175
206
  graph_coords[i] = max(graph_coords) + 1
176
207
  return graph_coords
177
208
 
178
-
179
209
  def set_x_ticks(
180
210
  ax: mpl.axes.Axes,
181
211
  region: GenomicLoci,
@@ -284,6 +314,8 @@ def set_y_ticks(
284
314
  universal_y_ticks = sorted(universal_y_ticks)
285
315
 
286
316
  for lab in universal_y_ticks:
317
+ curr_y_tick_labels.append(f"{int(lab)}")
318
+ """
287
319
  if y_axis_skip_zero and lab == 0:
288
320
  # Exclude label for 0
289
321
  curr_y_tick_labels.append("")
@@ -291,6 +323,7 @@ def set_y_ticks(
291
323
  curr_y_tick_labels.append(f"{Decimal(lab):.2E}")
292
324
  else:
293
325
  curr_y_tick_labels.append(f"{lab:.1f}" if lab % 1 != 0 else f"{int(lab)}")
326
+ """
294
327
  u"""
295
328
  @2019.01.04
296
329
  If there is no bam file, draw a blank y-axis
@@ -306,6 +339,7 @@ def set_y_ticks(
306
339
  """
307
340
 
308
341
  if show_y_label:
342
+
309
343
  def __dynamic_distance__(distance_between_label_axis: float, label: str, scale: int = 100) -> float:
310
344
  if distance_between_label_axis != 0:
311
345
  return -distance_between_label_axis
@@ -314,9 +348,7 @@ def set_y_ticks(
314
348
 
315
349
  curr_y_tick_labels = sorted(curr_y_tick_labels, key=lambda x: len(x), reverse=True)
316
350
  ax.text(
317
- x=__dynamic_distance__(
318
- distance_between_label_axis, curr_y_tick_labels[0] if curr_y_tick_labels else ""
319
- ) * max(graph_coords),
351
+ x=__dynamic_distance__(distance_between_label_axis, curr_y_tick_labels[0] if curr_y_tick_labels else "") * max(graph_coords),
320
352
  y=(max_used_y_val + min_used_y_val) / 2,
321
353
  s=label, fontsize=font_size, ha="right"
322
354
  )
@@ -436,7 +468,13 @@ def plot_annotation(
436
468
  for transcript in data:
437
469
  strand = transcript.strand
438
470
  # @2018.12.20 add transcript id, based on fixed coordinates
439
- if transcript.transcript:
471
+ # @2025.02.04 add warnings for transcript name and id check
472
+ if transcript.transcript or transcript.transcript_id:
473
+ show_text = transcript.transcript
474
+ if not transcript.transcript:
475
+ logger.warning(f"there is not transcript_name, using transcript_id instead")
476
+ show_text = transcript.transcript_id
477
+
440
478
  if show_gene and transcript.gene and transcript.gene_id != transcript.transcript_id:
441
479
  if show_id:
442
480
  ax.text(x=-.01 * max(graph_coords), y=y_loc + 0.25,
@@ -445,9 +483,11 @@ def plot_annotation(
445
483
  s=transcript.transcript_id, fontsize=font_size, ha="right")
446
484
  else:
447
485
  ax.text(x=-.01 * max(graph_coords), y=y_loc,
448
- s=transcript.gene + " | " + transcript.transcript, fontsize=font_size, ha="right")
486
+ s=transcript.gene + " | " + show_text, fontsize=font_size, ha="right")
449
487
  else:
450
- ax.text(x=-1, y=y_loc - 0.1, s=transcript.transcript, fontsize=font_size, ha="right")
488
+ ax.text(x=-1, y=y_loc - 0.1, s=show_text, fontsize=font_size, ha="right")
489
+ else:
490
+ logger.warning(f"there is no transcript_name and transcript_id")
451
491
 
452
492
  # @2018.12.19
453
493
  # s and e is the start and end site of single exon
@@ -621,7 +661,7 @@ def plot_density(
621
661
  color="blue",
622
662
  font_size: int = 8,
623
663
  show_junction_number: bool = True,
624
- junction_number_font_size: int = 5,
664
+ junction_number_font_size: int = 12,
625
665
  n_y_ticks: int = 4,
626
666
  distance_between_label_axis: float = .1,
627
667
  show_y_label: bool = True,
@@ -656,6 +696,8 @@ def plot_density(
656
696
  :param kwargs:
657
697
  :return:
658
698
  """
699
+ # max_used_y_val is None
700
+ # distance_between_label_axis: 0
659
701
  if obj:
660
702
  assert obj.region is not None, "please load data first"
661
703
  region = obj.region
@@ -676,7 +718,17 @@ def plot_density(
676
718
  except AttributeError:
677
719
  # depth do not have junctions
678
720
  jxns = {}
721
+
722
+ # AD - convert junction counts to log scale early if requested
723
+ if obj.log_trans and obj.log_trans.isdigit() and int(obj.log_trans) > 0: # in ["2", "10"]:
679
724
 
725
+ y_label += f" (log{obj.log_trans})"
726
+ denominator = np.log(int(obj.log_trans))
727
+ for k, v in jxns.items():
728
+ jxns[k] = np.log1p(v) / denominator
729
+
730
+ min_used_y_val = 0 # AD
731
+ fixed_min_used_y = True # AD
680
732
  fixed_max_used_y, fixed_min_used_y = max_used_y_val is not None, min_used_y_val is not None
681
733
  if max_used_y_val is None:
682
734
  if isinstance(data, dict):
@@ -712,7 +764,6 @@ def plot_density(
712
764
  if data.strand_aware:
713
765
  max_used_y_val = max(abs(min_used_y_val), max_used_y_val)
714
766
  min_used_y_val = -max(abs(min_used_y_val), max_used_y_val) if data.minus is not None else 0
715
-
716
767
  if jxns:
717
768
  # sort the junctions by intron length for better plotting look
718
769
  jxns_sorted_list = sorted(jxns.keys(), key=lambda x: (x.end - x.start, x.start, x.end), reverse=True)
@@ -724,10 +775,11 @@ def plot_density(
724
775
  min_junction_count = min(jxns.values())
725
776
  junction_count_gap = max_junction_count - min_junction_count
726
777
 
727
- recorded_pts = set()
778
+ #recorded_pts = set()
728
779
  jxn_numbers = []
780
+
729
781
  for jxn_idx, jxn in enumerate(jxns_sorted_list):
730
- logger.debug(f"junctions of {y_label}: {jxn} - {round(jxns[jxn], 2)}")
782
+ #logger.info(f"junctions of {y_label}: {jxn} - {round(jxns[jxn], 2)}")
731
783
  leftss, rightss = jxn.start, jxn.end
732
784
 
733
785
  # junction must at least have one anchor located in plotted region
@@ -740,20 +792,26 @@ def plot_density(
740
792
  # the junction out of boundaries, set the boundaries as coordinate
741
793
  ss1_idx, ss1_modified = get_limited_index(leftss - region.start, len(graph_coords))
742
794
  ss2_idx, ss2_modified = get_limited_index(rightss - region.start, len(graph_coords))
795
+ #logger.info(f"{y_label} ss1_idx {ss1_idx} ss1_modified {ss1_modified} ss2_idx {ss2_idx} ss2_modified {ss2_modified}")
743
796
  u"""
744
797
  @2019.01.14
745
798
  add two new variables to make it clear which one is index, which one is genomic site
746
799
  """
747
800
  ss1, ss2 = graph_coords[ss1_idx], graph_coords[ss2_idx]
748
-
801
+ # AD = keep junction arcs on top
802
+ #min_used_y_val = 1
803
+ jxn_on_top = True
804
+ min_used_y_val = 0
749
805
  # draw junction on bottom
806
+ """
750
807
  if kwargs.get("density_by_strand"):
751
808
  jxn_on_top = jxn.strand == "+"
752
809
  else:
753
810
  jxn_on_top = jxn_idx % 2 == 0
811
+ #jxn_on_top = True # AD - keep all junctions on same strand
754
812
  if abs(min_used_y_val) < max_used_y_val:
755
813
  min_used_y_val = -max_used_y_val
756
-
814
+ """
757
815
  if fill_step == "post":
758
816
  ss1_idx = max(0, ss1_idx - 1)
759
817
  elif fill_step == "pre":
@@ -777,46 +835,57 @@ def plot_density(
777
835
  -right_dens - current_height,
778
836
  -right_dens if not ss2_modified else -right_dens - current_height
779
837
  ]
780
-
838
+ """
781
839
  if sum(pts) > 0:
782
840
  pts_ = "_".join([str(x) for x in pts])
783
841
  while pts_ in recorded_pts:
784
842
  pts[1], pts[2] = pts[1] * 1.1, pts[2] * 1.1
785
843
  pts_ = "_".join([str(x) for x in pts])
786
844
  recorded_pts.add(pts_)
787
-
845
+ """
788
846
  """
789
847
  @2018.12.26
790
848
  scale the junctions line width
791
849
  """
792
850
  if junction_count_gap > 0:
793
- line_width = (jxns[jxn] - min_junction_count) / junction_count_gap
851
+ #line_width = (jxns[jxn] - min_junction_count) / junction_count_gap
852
+ line_width = .5 + 1.5 * (jxns[jxn] - min_junction_count) / junction_count_gap
794
853
  else:
795
854
  line_width = 0
796
-
797
- pts = [(ss1, pts[0]), (ss1, pts[1]), (ss2, pts[2]), (ss2, pts[3])]
855
+ #line_width = max(.5,np.log())
856
+ #pts = [(ss1, pts[0]), (ss1, pts[1]), (ss2, pts[2]), (ss2, pts[3])]
857
+
858
+
859
+ temp = np.linspace(0, 1.0, 100)
860
+ bdist = beta.pdf(temp, 3, 3) # or 2, 2
861
+ bdist /= np.max(bdist) # max = 1
862
+ bdist *= jxns[jxn]
863
+ pts_x = np.linspace(ss1, ss2, 100)
864
+
865
+ #pts = [(ss1, 0), (ss1+5, jxns[jxn]), (ss2-5, jxns[jxn]), (ss2, 0)]
866
+ path = Path(np.array([pts_x, bdist]).T)
867
+ patch = PathPatch(path, facecolor='none', edgecolor="#BA55D3", linewidth=line_width)
868
+ ax.add_patch(patch)
869
+ """
870
+ # pts = [(ss1, 0), (midpt-5, jxns[jxn]), (midpt+5, jxns[jxn]), (ss2, 0)]
798
871
  ax.add_patch(PathPatch(Path(pts, [Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.CURVE4]),
799
- ec=color, lw=line_width + 0.2, fc='none'))
800
-
872
+ ec="#BA55D3", lw=line_width, fc='none'))
873
+ #ec=color, lw=line_width + 0.2, fc='none'))
874
+ """
801
875
  if show_junction_number:
802
- midpt = cubic_bezier(pts, .5)
803
-
804
- t = ax.text(
805
- midpt[0], midpt[1],
806
- '{0}'.format(round(jxns[jxn], 2)),
807
- fontsize=junction_number_font_size,
808
- ha='center', va='center',
809
- backgroundcolor='w'
810
- )
811
-
876
+ t = ax.text( (ss1+ss2)/2, jxns[jxn] * 1.1,
877
+ '{0}'.format(round(jxns[jxn], 2)),
878
+ fontsize=junction_number_font_size, ha='center', va='center')
879
+ #, backgroundcolor='w' ) AD - white here overrides transparency below
812
880
  # @2018.12.19 transparent background
813
881
  t.set_bbox(dict(alpha=0))
814
882
  jxn_numbers.append(t)
815
883
 
816
- try:
817
- adjust_text(jxn_numbers, force_text=(0.2, 0.2), arrowprops=dict(arrowstyle="-", color='black', lw=1))
818
- except IndexError as err:
819
- logger.debug(err)
884
+ # AD - this adds a dot next to the counts
885
+ #try:
886
+ # adjust_text(jxn_numbers, force_text=(0.2, 0.2), arrowprops=dict(arrowstyle="-", color='black', lw=1))
887
+ #except IndexError as err:
888
+ # logger.debug(err)
820
889
 
821
890
  if obj and obj.title:
822
891
  ax.text(max(graph_coords) - len(obj.title), max_used_y_val, obj.title, color=color, fontsize=font_size)
@@ -841,13 +910,13 @@ def plot_density(
841
910
  set_y_ticks(
842
911
  ax, label=y_label, theme=theme,
843
912
  graph_coords=graph_coords,
844
- max_used_y_val=max_used_y_val * 1.1, # keep a buffer at top and bottom of junctions
845
- min_used_y_val=min_used_y_val * 1.1,
913
+ max_used_y_val=max_used_y_val, # * 1.1, # keep a buffer at top and bottom of junctions
914
+ min_used_y_val=min_used_y_val, # * 1.1, # AD
846
915
  n_y_ticks=n_y_ticks,
847
916
  distance_between_label_axis=distance_between_label_axis,
848
917
  font_size=font_size,
849
918
  show_y_label=show_y_label,
850
- y_axis_skip_zero=False if not isinstance(data, dict) and data.strand_aware else True
919
+ y_axis_skip_zero=False #if not isinstance(data, dict) and data.strand_aware else True
851
920
  )
852
921
 
853
922
 
@@ -0,0 +1 @@
1
+ import{d as _,o as m,c as u,a as c,t as f,b as e,w as t,E as g,e as n,u as i,f as h,_ as y}from"./index-CWfdj0DH.js";import{E as k,a as w,b as E,m as v,v as b}from"./el-divider-BVZhQIwQ.js";const x={name:"Home",data(){return{msg:"Welcome to trackplot App",example:"https://trackplot.readthedocs.io/en/latest/imgs/diagram.png"}}},C=_({...x,setup(B){return(r,a)=>{const s=k,l=w,o=h,p=g,d=E;return m(),u("div",null,[c("h1",null,f(r.msg),1),e(s),e(p,null,{default:t(()=>[e(o,{span:12,offset:3},{default:t(()=>[e(l,{type:"primary",href:"/#/plot"},{default:t(()=>a[0]||(a[0]=[n("Create your own plot")])),_:1})]),_:1}),e(o,{span:6},{default:t(()=>[e(l,{type:"primary",href:"https://github.com/ygidtu/trackplot/issues",icon:i(v)},{default:t(()=>a[1]||(a[1]=[n("Report bug at Github")])),_:1},8,["icon"]),e(s,{direction:"vertical"}),e(l,{type:"primary",icon:i(b),href:"https://trackplot.readthedocs.io/en/latest/web/"},{default:t(()=>a[2]||(a[2]=[n("Read the tutorial")])),_:1},8,["icon"])]),_:1}),e(o,{span:6})]),_:1}),e(s),e(p,null,{default:t(()=>[e(o,{span:20,offset:2},{default:t(()=>[e(d,{src:r.example,width:"100%"},null,8,["src"])]),_:1})]),_:1})])}}}),V=y(C,[["__scopeId","data-v-3d7de397"]]);export{V as default};
@@ -0,0 +1 @@
1
+ import{d as _,c as m,a as u,b as e,t as c,w as t,E as f,o as g,e as h,f as n,u as i,_ as y}from"./index-4hxJ_zbq.js";import{E as k,a as w,m as E,v,b}from"./el-divider-Brt4-Qvr.js";const x={name:"Home",data(){return{msg:"Welcome to trackplot App",example:"https://trackplot.readthedocs.io/en/latest/imgs/diagram.png"}}},C=_({...x,setup(B){return(r,a)=>{const s=k,l=w,o=h,p=f,d=b;return g(),m("div",null,[u("h1",null,c(r.msg),1),e(s),e(p,null,{default:t(()=>[e(o,{span:12,offset:3},{default:t(()=>[e(l,{type:"primary",href:"/#/plot"},{default:t(()=>a[0]||(a[0]=[n("Create your own plot")])),_:1})]),_:1}),e(o,{span:6},{default:t(()=>[e(l,{type:"primary",href:"https://github.com/ygidtu/trackplot/issues",icon:i(E)},{default:t(()=>a[1]||(a[1]=[n("Report bug at Github")])),_:1},8,["icon"]),e(s,{direction:"vertical"}),e(l,{type:"primary",icon:i(v),href:"https://trackplot.readthedocs.io/en/latest/web/"},{default:t(()=>a[2]||(a[2]=[n("Read the tutorial")])),_:1},8,["icon"])]),_:1}),e(o,{span:6})]),_:1}),e(s),e(p,null,{default:t(()=>[e(o,{span:20,offset:2},{default:t(()=>[e(d,{src:r.example,width:"100%"},null,8,["src"])]),_:1})]),_:1})])}}}),V=y(C,[["__scopeId","data-v-3d7de397"]]);export{V as default};
@@ -0,0 +1 @@
1
+ import{d as _,o as m,c as u,a as c,t as f,b as e,w as t,E as g,e as n,u as i,f as h,_ as y}from"./index-Dd6Bavnk.js";import{E as k,a as w,b as E,m as v,v as b}from"./el-divider-eEJXnQD5.js";const x={name:"Home",data(){return{msg:"Welcome to trackplot App",example:"https://trackplot.readthedocs.io/en/latest/imgs/diagram.png"}}},C=_({...x,setup(B){return(r,a)=>{const s=k,l=w,o=h,p=g,d=E;return m(),u("div",null,[c("h1",null,f(r.msg),1),e(s),e(p,null,{default:t(()=>[e(o,{span:12,offset:3},{default:t(()=>[e(l,{type:"primary",href:"/#/plot"},{default:t(()=>a[0]||(a[0]=[n("Create your own plot")])),_:1})]),_:1}),e(o,{span:6},{default:t(()=>[e(l,{type:"primary",href:"https://github.com/ygidtu/trackplot/issues",icon:i(v)},{default:t(()=>a[1]||(a[1]=[n("Report bug at Github")])),_:1},8,["icon"]),e(s,{direction:"vertical"}),e(l,{type:"primary",icon:i(b),href:"https://trackplot.readthedocs.io/en/latest/web/"},{default:t(()=>a[2]||(a[2]=[n("Read the tutorial")])),_:1},8,["icon"])]),_:1}),e(o,{span:6})]),_:1}),e(s),e(p,null,{default:t(()=>[e(o,{span:20,offset:2},{default:t(()=>[e(d,{src:r.example,width:"100%"},null,8,["src"])]),_:1})]),_:1})])}}}),V=y(C,[["__scopeId","data-v-3d7de397"]]);export{V as default};
@@ -0,0 +1 @@
1
+ h3[data-v-3d7de397]{margin:40px 0 0}ul[data-v-3d7de397]{list-style-type:none;padding:0}li[data-v-3d7de397]{display:inline-block;margin:0 10px}a[data-v-3d7de397]{color:#42b983}