trackplot 0.5.2__tar.gz → 0.5.4__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 (94) hide show
  1. {trackplot-0.5.2 → trackplot-0.5.4}/PKG-INFO +24 -29
  2. trackplot-0.5.4/pyproject.toml +48 -0
  3. trackplot-0.5.4/setup.cfg +4 -0
  4. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/Junction.py +13 -0
  5. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/Annotation.py +23 -7
  6. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/Bam.py +22 -4
  7. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/ReadSegments.py +3 -1
  8. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/plot.py +2 -2
  9. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/plot_func.py +90 -57
  10. trackplot-0.5.4/trackplot.egg-info/PKG-INFO +296 -0
  11. trackplot-0.5.4/trackplot.egg-info/SOURCES.txt +91 -0
  12. trackplot-0.5.4/trackplot.egg-info/dependency_links.txt +1 -0
  13. trackplot-0.5.4/trackplot.egg-info/requires.txt +17 -0
  14. trackplot-0.5.4/trackplot.egg-info/top_level.txt +2 -0
  15. trackplot-0.5.4/ui/assets/Home-7GzAh8lS.js +1 -0
  16. trackplot-0.5.4/ui/assets/Home-BV58jH3t.js +1 -0
  17. trackplot-0.5.4/ui/assets/Home-CDW3Zwoa.js +1 -0
  18. trackplot-0.5.4/ui/assets/Home-zRV7yePL.css +1 -0
  19. trackplot-0.5.4/ui/assets/Plot-BALbchCV.css +1 -0
  20. trackplot-0.5.4/ui/assets/Plot-BrjU8Kwg.js +17 -0
  21. trackplot-0.5.4/ui/assets/Plot-COvGnprQ.css +1 -0
  22. trackplot-0.5.4/ui/assets/Plot-Cnt8iJB8.js +21 -0
  23. trackplot-0.5.4/ui/assets/Plot-Cyj_LlDt.js +17 -0
  24. trackplot-0.5.4/ui/assets/Plot-DpL7z7tp.css +1 -0
  25. trackplot-0.5.4/ui/assets/el-divider-BVZhQIwQ.js +1 -0
  26. trackplot-0.5.4/ui/assets/el-divider-Brt4-Qvr.js +1 -0
  27. trackplot-0.5.4/ui/assets/el-divider-Cwxg0Ado.css +1 -0
  28. trackplot-0.5.4/ui/assets/el-divider-DcvrsrBa.css +1 -0
  29. trackplot-0.5.4/ui/assets/el-divider-IbBQ8ZK2.js +4 -0
  30. trackplot-0.5.4/ui/assets/el-divider-i9JMIXVR.css +1 -0
  31. trackplot-0.5.4/ui/assets/index-4hxJ_zbq.js +26 -0
  32. trackplot-0.5.4/ui/assets/index-C4Mi9Kmf.js +30 -0
  33. trackplot-0.5.4/ui/assets/index-CWfdj0DH.js +26 -0
  34. trackplot-0.5.4/ui/assets/index-DgEIiwRJ.css +1 -0
  35. trackplot-0.5.4/ui/assets/index-O8P0XkxB.css +1 -0
  36. trackplot-0.5.4/ui/assets/index-ns9n7-F7.css +1 -0
  37. {trackplot-0.5.2 → trackplot-0.5.4}/ui/index.html +2 -2
  38. trackplot-0.5.2/pyproject.toml +0 -49
  39. {trackplot-0.5.2 → trackplot-0.5.4}/LICENSE +0 -0
  40. {trackplot-0.5.2 → trackplot-0.5.4}/README.md +0 -0
  41. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/__init__.py +0 -0
  42. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/anno/AxLabel.py +0 -0
  43. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/anno/__init__.py +0 -0
  44. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/anno/theme.py +0 -0
  45. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/CoordinateMap.py +0 -0
  46. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/GenomicLoci.py +0 -0
  47. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/Protein.py +0 -0
  48. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/ReadDepth.py +0 -0
  49. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/Readder.py +0 -0
  50. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/Stroke.py +0 -0
  51. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/Transcript.py +0 -0
  52. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/__init__.py +0 -0
  53. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/pyUniprot.py +0 -0
  54. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/cli.py +0 -0
  55. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/conf/DomainSetting.py +0 -0
  56. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/conf/__init__.py +0 -0
  57. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/conf/config.py +0 -0
  58. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/conf/drawing.py +0 -0
  59. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/conf/ui.py +0 -0
  60. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/ATAC.py +0 -0
  61. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/BedGraph.py +0 -0
  62. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/Bigwig.py +0 -0
  63. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/Depth.py +0 -0
  64. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/Fasta.py +0 -0
  65. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/File.py +0 -0
  66. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/HiCMatrixTrack.py +0 -0
  67. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/Junction.py +0 -0
  68. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/Motif.py +0 -0
  69. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/__init__.py +0 -0
  70. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/plot_tests.py +0 -0
  71. {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/server.py +0 -0
  72. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Home-DOO13BH7.js +0 -0
  73. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Home-QmeAKOl4.js +0 -0
  74. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Home-RdVPWns6.js +0 -0
  75. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Home-jSR0MsHI.css +0 -0
  76. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Plot-BmqHZ4QE.css +0 -0
  77. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Plot-Bvyo6ju9.css +0 -0
  78. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Plot-CTM-EDrj.js +0 -0
  79. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Plot-DiuFnwNK.js +0 -0
  80. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Plot-hvkDteAn.js +0 -0
  81. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Plot-rbQz1TOM.css +0 -0
  82. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/el-divider-BHm65SRq.css +0 -0
  83. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/el-divider-BuEUMHwE.css +0 -0
  84. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/el-divider-SYT5K-ds.css +0 -0
  85. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/el-divider-VYjL3C7L.js +0 -0
  86. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/el-divider-eEJXnQD5.js +0 -0
  87. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/el-divider-u9f0bZWY.js +0 -0
  88. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/index-CETGMNio.css +0 -0
  89. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/index-Cexhr_fn.css +0 -0
  90. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/index-CrzyEb9s.js +0 -0
  91. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/index-D_Cw0sbX.js +0 -0
  92. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/index-Dd6Bavnk.js +0 -0
  93. {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/index-Sq2gI4sE.css +0 -0
  94. {trackplot-0.5.2 → trackplot-0.5.4}/ui/vite.svg +0 -0
@@ -1,34 +1,30 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: trackplot
3
- Version: 0.5.2
3
+ Version: 0.5.4
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
- License: BSD-3
6
- Author: ygidtu
7
- Author-email: ygidtu@gmail.com
8
- Requires-Python: >=3.10,<3.13
9
- Classifier: License :: Other/Proprietary License
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.10
12
- Classifier: Programming Language :: Python :: 3.11
13
- Classifier: Programming Language :: Python :: 3.12
14
- Requires-Dist: adjusttext (>=0.7.3,<0.8.0)
15
- Requires-Dist: cairocffi (>=1.4.0,<2.0.0)
16
- Requires-Dist: click (>=8.1.3,<9.0.0)
17
- Requires-Dist: click-option-group (>=0.5.5,<0.6.0)
18
- Requires-Dist: filetype (>=1.2.0,<2.0.0)
19
- Requires-Dist: flask (>=2.3.2,<3.0.0)
20
- Requires-Dist: hicmatrix (>=17,<18)
21
- Requires-Dist: loguru (>=0.6.0,<0.7.0)
22
- Requires-Dist: matplotlib (>=3.6.3,<4.0.0)
23
- Requires-Dist: numpy (>=1.24.1,<2.0.0)
24
- Requires-Dist: pandas (>=1.5.3,<2.0.0)
25
- Requires-Dist: pybigwig (>=0.3.18,<0.4.0)
26
- Requires-Dist: pysam (>=0.21.0,<0.22.0)
27
- Requires-Dist: requests (>=2.28.2,<3.0.0)
28
- Requires-Dist: scipy (>=1.10.0,<2.0.0)
29
- Requires-Dist: seaborn (>=0.12.2,<0.13.0)
30
- Requires-Dist: xmltodict (>=0.13.0,<0.14.0)
5
+ Author-email: ygidtu <ygidtu@gmail.com>
6
+ License-Expression: BSD-3-Clause
7
+ Requires-Python: >=3.11
31
8
  Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: adjusttext>=1.3.0
11
+ Requires-Dist: cairocffi>=1.7.1
12
+ Requires-Dist: click>=8.2.1
13
+ Requires-Dist: click-option-group>=0.5.7
14
+ Requires-Dist: filetype>=1.2.0
15
+ Requires-Dist: flask>=3.1.1
16
+ Requires-Dist: hicmatrix>=17.2
17
+ Requires-Dist: loguru>=0.7.3
18
+ Requires-Dist: matplotlib>=3.10.3
19
+ Requires-Dist: numpy>=2.3.0
20
+ Requires-Dist: pandas>=2.3.0
21
+ Requires-Dist: pybigwig>=0.3.24
22
+ Requires-Dist: pysam>=0.23.3
23
+ Requires-Dist: requests>=2.32.4
24
+ Requires-Dist: scipy>=1.15.3
25
+ Requires-Dist: seaborn>=0.13.2
26
+ Requires-Dist: xmltodict>=0.14.2
27
+ Dynamic: license-file
32
28
 
33
29
  # trackplot
34
30
 
@@ -298,4 +294,3 @@ contact [Yiming Zhang](https://github.com/ygidtu) or
298
294
  If you use the tool in your publication, please cite by
299
295
 
300
296
  [Zhang Y, Zhou R, Liu L, et al. Trackplot: A flexible toolkit for combinatorial analysis of genomic data[J]. PLoS computational biology, 2023, 19(9): e1011477.](https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1011477)
301
-
@@ -0,0 +1,48 @@
1
+ [project]
2
+ name = "trackplot"
3
+ version = "0.5.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
+ authors = [
6
+ { name = "ygidtu", email = "ygidtu@gmail.com" }
7
+ ]
8
+ license = "BSD-3-Clause"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ dependencies = [
12
+ "adjusttext>=1.3.0",
13
+ "cairocffi>=1.7.1",
14
+ "click>=8.2.1",
15
+ "click-option-group>=0.5.7",
16
+ "filetype>=1.2.0",
17
+ "flask>=3.1.1",
18
+ "hicmatrix>=17.2",
19
+ "loguru>=0.7.3",
20
+ "matplotlib>=3.10.3",
21
+ "numpy>=2.3.0",
22
+ "pandas>=2.3.0",
23
+ "pybigwig>=0.3.24",
24
+ "pysam>=0.23.3",
25
+ "requests>=2.32.4",
26
+ "scipy>=1.15.3",
27
+ "seaborn>=0.13.2",
28
+ "xmltodict>=0.14.2",
29
+ ]
30
+
31
+
32
+ [build-system]
33
+ requires = ["setuptools>=65.0", "wheel"]
34
+ build-backend = "setuptools.build_meta"
35
+
36
+
37
+ [tool.setuptools.packages.find]
38
+ # 搜索目前目录下的trackplot和ui及其子文件夹
39
+ where = ["."]
40
+ include = ["trackplot*", "ui*"] # 通配符匹配所有子包
41
+
42
+
43
+ [tool.setuptools.package-data]
44
+ # 不限定父目录(等号前为父目录),包含其下匹配特定正则的文件
45
+ "*" = [
46
+ "pyproject.toml", "*"
47
+ ]
48
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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
@@ -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):
@@ -275,14 +275,32 @@ class Bam(SingleCell):
275
275
  site_minus[start - region.start] += 1
276
276
 
277
277
  for k, v in spanned_junctions.items():
278
- 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:
279
282
  continue
280
-
281
- 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:
282
299
  if k.strand == "+":
283
300
  spanned_junctions_plus[k] = 1 + spanned_junctions_plus.get(k, v)
284
301
  elif k.strand == "-":
285
302
  spanned_junctions_minus[k] = -1 + spanned_junctions_minus.get(k, v)
303
+
286
304
  except IOError as err:
287
305
  logger.error('There is no .bam file at {0}'.format(self.path))
288
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.2"
33
+ __version__ = "0.5.4"
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,59 @@ 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)))
165
193
  else:
166
194
  # if there is not any exons, just init graph_coords by region
167
195
  for i, j in enumerate(range(region.start, region.end + 1)):
@@ -175,7 +203,6 @@ def init_graph_coords(region: GenomicLoci, exons: Optional[List[List[int]]] = No
175
203
  graph_coords[i] = max(graph_coords) + 1
176
204
  return graph_coords
177
205
 
178
-
179
206
  def set_x_ticks(
180
207
  ax: mpl.axes.Axes,
181
208
  region: GenomicLoci,
@@ -284,6 +311,8 @@ def set_y_ticks(
284
311
  universal_y_ticks = sorted(universal_y_ticks)
285
312
 
286
313
  for lab in universal_y_ticks:
314
+ curr_y_tick_labels.append(f"{int(lab)}")
315
+ """
287
316
  if y_axis_skip_zero and lab == 0:
288
317
  # Exclude label for 0
289
318
  curr_y_tick_labels.append("")
@@ -291,6 +320,7 @@ def set_y_ticks(
291
320
  curr_y_tick_labels.append(f"{Decimal(lab):.2E}")
292
321
  else:
293
322
  curr_y_tick_labels.append(f"{lab:.1f}" if lab % 1 != 0 else f"{int(lab)}")
323
+ """
294
324
  u"""
295
325
  @2019.01.04
296
326
  If there is no bam file, draw a blank y-axis
@@ -306,6 +336,7 @@ def set_y_ticks(
306
336
  """
307
337
 
308
338
  if show_y_label:
339
+
309
340
  def __dynamic_distance__(distance_between_label_axis: float, label: str, scale: int = 100) -> float:
310
341
  if distance_between_label_axis != 0:
311
342
  return -distance_between_label_axis
@@ -314,9 +345,7 @@ def set_y_ticks(
314
345
 
315
346
  curr_y_tick_labels = sorted(curr_y_tick_labels, key=lambda x: len(x), reverse=True)
316
347
  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),
348
+ x=__dynamic_distance__(distance_between_label_axis, curr_y_tick_labels[0] if curr_y_tick_labels else "") * max(graph_coords),
320
349
  y=(max_used_y_val + min_used_y_val) / 2,
321
350
  s=label, fontsize=font_size, ha="right"
322
351
  )
@@ -629,7 +658,7 @@ def plot_density(
629
658
  color="blue",
630
659
  font_size: int = 8,
631
660
  show_junction_number: bool = True,
632
- junction_number_font_size: int = 5,
661
+ junction_number_font_size: int = 12,
633
662
  n_y_ticks: int = 4,
634
663
  distance_between_label_axis: float = .1,
635
664
  show_y_label: bool = True,
@@ -664,6 +693,8 @@ def plot_density(
664
693
  :param kwargs:
665
694
  :return:
666
695
  """
696
+ # max_used_y_val is None
697
+ # distance_between_label_axis: 0
667
698
  if obj:
668
699
  assert obj.region is not None, "please load data first"
669
700
  region = obj.region
@@ -684,7 +715,17 @@ def plot_density(
684
715
  except AttributeError:
685
716
  # depth do not have junctions
686
717
  jxns = {}
718
+
719
+ # AD - convert junction counts to log scale early if requested
720
+ if obj.log_trans and obj.log_trans.isdigit() and int(obj.log_trans) > 0: # in ["2", "10"]:
721
+
722
+ y_label += f" (log{obj.log_trans})"
723
+ denominator = np.log(int(obj.log_trans))
724
+ for k, v in jxns.items():
725
+ jxns[k] = np.log1p(v) / denominator
687
726
 
727
+ min_used_y_val = 0 # AD
728
+ fixed_min_used_y = True # AD
688
729
  fixed_max_used_y, fixed_min_used_y = max_used_y_val is not None, min_used_y_val is not None
689
730
  if max_used_y_val is None:
690
731
  if isinstance(data, dict):
@@ -720,7 +761,7 @@ def plot_density(
720
761
  if data.strand_aware:
721
762
  max_used_y_val = max(abs(min_used_y_val), max_used_y_val)
722
763
  min_used_y_val = -max(abs(min_used_y_val), max_used_y_val) if data.minus is not None else 0
723
-
764
+
724
765
  if jxns:
725
766
  # sort the junctions by intron length for better plotting look
726
767
  jxns_sorted_list = sorted(jxns.keys(), key=lambda x: (x.end - x.start, x.start, x.end), reverse=True)
@@ -732,10 +773,8 @@ def plot_density(
732
773
  min_junction_count = min(jxns.values())
733
774
  junction_count_gap = max_junction_count - min_junction_count
734
775
 
735
- recorded_pts = set()
736
776
  jxn_numbers = []
737
777
  for jxn_idx, jxn in enumerate(jxns_sorted_list):
738
- logger.debug(f"junctions of {y_label}: {jxn} - {round(jxns[jxn], 2)}")
739
778
  leftss, rightss = jxn.start, jxn.end
740
779
 
741
780
  # junction must at least have one anchor located in plotted region
@@ -753,15 +792,18 @@ def plot_density(
753
792
  add two new variables to make it clear which one is index, which one is genomic site
754
793
  """
755
794
  ss1, ss2 = graph_coords[ss1_idx], graph_coords[ss2_idx]
795
+ # AD = keep junction arcs on top
796
+ jxn_on_top = True
756
797
 
757
- # draw junction on bottom
798
+ # draw junction on bottom
758
799
  if kwargs.get("density_by_strand"):
759
800
  jxn_on_top = jxn.strand == "+"
760
801
  else:
761
802
  jxn_on_top = jxn_idx % 2 == 0
803
+ #jxn_on_top = True # AD - keep all junctions on same strand
762
804
  if abs(min_used_y_val) < max_used_y_val:
763
805
  min_used_y_val = -max_used_y_val
764
-
806
+
765
807
  if fill_step == "post":
766
808
  ss1_idx = max(0, ss1_idx - 1)
767
809
  elif fill_step == "pre":
@@ -785,27 +827,22 @@ def plot_density(
785
827
  -right_dens - current_height,
786
828
  -right_dens if not ss2_modified else -right_dens - current_height
787
829
  ]
788
-
789
- if sum(pts) > 0:
790
- pts_ = "_".join([str(x) for x in pts])
791
- while pts_ in recorded_pts:
792
- pts[1], pts[2] = pts[1] * 1.1, pts[2] * 1.1
793
- pts_ = "_".join([str(x) for x in pts])
794
- recorded_pts.add(pts_)
795
-
830
+
796
831
  """
797
832
  @2018.12.26
798
833
  scale the junctions line width
799
834
  """
800
835
  if junction_count_gap > 0:
801
- line_width = (jxns[jxn] - min_junction_count) / junction_count_gap
836
+ #line_width = (jxns[jxn] - min_junction_count) / junction_count_gap
837
+ line_width = .5 + 1.5 * (jxns[jxn] - min_junction_count) / junction_count_gap
802
838
  else:
803
839
  line_width = 0
840
+ #line_width = max(.5,np.log())
804
841
 
805
842
  pts = [(ss1, pts[0]), (ss1, pts[1]), (ss2, pts[2]), (ss2, pts[3])]
806
843
  ax.add_patch(PathPatch(Path(pts, [Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.CURVE4]),
807
844
  ec=color, lw=line_width + 0.2, fc='none'))
808
-
845
+
809
846
  if show_junction_number:
810
847
  midpt = cubic_bezier(pts, .5)
811
848
 
@@ -821,10 +858,6 @@ def plot_density(
821
858
  t.set_bbox(dict(alpha=0))
822
859
  jxn_numbers.append(t)
823
860
 
824
- try:
825
- adjust_text(jxn_numbers, force_text=(0.2, 0.2), arrowprops=dict(arrowstyle="-", color='black', lw=1))
826
- except IndexError as err:
827
- logger.debug(err)
828
861
 
829
862
  if obj and obj.title:
830
863
  ax.text(max(graph_coords) - len(obj.title), max_used_y_val, obj.title, color=color, fontsize=font_size)
@@ -849,13 +882,13 @@ def plot_density(
849
882
  set_y_ticks(
850
883
  ax, label=y_label, theme=theme,
851
884
  graph_coords=graph_coords,
852
- max_used_y_val=max_used_y_val * 1.1, # keep a buffer at top and bottom of junctions
853
- min_used_y_val=min_used_y_val * 1.1,
885
+ max_used_y_val=max_used_y_val, # * 1.1, # keep a buffer at top and bottom of junctions
886
+ min_used_y_val=min_used_y_val, # * 1.1, # AD
854
887
  n_y_ticks=n_y_ticks,
855
888
  distance_between_label_axis=distance_between_label_axis,
856
889
  font_size=font_size,
857
890
  show_y_label=show_y_label,
858
- y_axis_skip_zero=False if not isinstance(data, dict) and data.strand_aware else True
891
+ y_axis_skip_zero=False #if not isinstance(data, dict) and data.strand_aware else True
859
892
  )
860
893
 
861
894