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.
- {trackplot-0.5.2 → trackplot-0.5.4}/PKG-INFO +24 -29
- trackplot-0.5.4/pyproject.toml +48 -0
- trackplot-0.5.4/setup.cfg +4 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/Junction.py +13 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/Annotation.py +23 -7
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/Bam.py +22 -4
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/ReadSegments.py +3 -1
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/plot.py +2 -2
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/plot_func.py +90 -57
- trackplot-0.5.4/trackplot.egg-info/PKG-INFO +296 -0
- trackplot-0.5.4/trackplot.egg-info/SOURCES.txt +91 -0
- trackplot-0.5.4/trackplot.egg-info/dependency_links.txt +1 -0
- trackplot-0.5.4/trackplot.egg-info/requires.txt +17 -0
- trackplot-0.5.4/trackplot.egg-info/top_level.txt +2 -0
- trackplot-0.5.4/ui/assets/Home-7GzAh8lS.js +1 -0
- trackplot-0.5.4/ui/assets/Home-BV58jH3t.js +1 -0
- trackplot-0.5.4/ui/assets/Home-CDW3Zwoa.js +1 -0
- trackplot-0.5.4/ui/assets/Home-zRV7yePL.css +1 -0
- trackplot-0.5.4/ui/assets/Plot-BALbchCV.css +1 -0
- trackplot-0.5.4/ui/assets/Plot-BrjU8Kwg.js +17 -0
- trackplot-0.5.4/ui/assets/Plot-COvGnprQ.css +1 -0
- trackplot-0.5.4/ui/assets/Plot-Cnt8iJB8.js +21 -0
- trackplot-0.5.4/ui/assets/Plot-Cyj_LlDt.js +17 -0
- trackplot-0.5.4/ui/assets/Plot-DpL7z7tp.css +1 -0
- trackplot-0.5.4/ui/assets/el-divider-BVZhQIwQ.js +1 -0
- trackplot-0.5.4/ui/assets/el-divider-Brt4-Qvr.js +1 -0
- trackplot-0.5.4/ui/assets/el-divider-Cwxg0Ado.css +1 -0
- trackplot-0.5.4/ui/assets/el-divider-DcvrsrBa.css +1 -0
- trackplot-0.5.4/ui/assets/el-divider-IbBQ8ZK2.js +4 -0
- trackplot-0.5.4/ui/assets/el-divider-i9JMIXVR.css +1 -0
- trackplot-0.5.4/ui/assets/index-4hxJ_zbq.js +26 -0
- trackplot-0.5.4/ui/assets/index-C4Mi9Kmf.js +30 -0
- trackplot-0.5.4/ui/assets/index-CWfdj0DH.js +26 -0
- trackplot-0.5.4/ui/assets/index-DgEIiwRJ.css +1 -0
- trackplot-0.5.4/ui/assets/index-O8P0XkxB.css +1 -0
- trackplot-0.5.4/ui/assets/index-ns9n7-F7.css +1 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/index.html +2 -2
- trackplot-0.5.2/pyproject.toml +0 -49
- {trackplot-0.5.2 → trackplot-0.5.4}/LICENSE +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/README.md +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/__init__.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/anno/AxLabel.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/anno/__init__.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/anno/theme.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/CoordinateMap.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/GenomicLoci.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/Protein.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/ReadDepth.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/Readder.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/Stroke.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/Transcript.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/__init__.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/base/pyUniprot.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/cli.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/conf/DomainSetting.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/conf/__init__.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/conf/config.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/conf/drawing.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/conf/ui.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/ATAC.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/BedGraph.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/Bigwig.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/Depth.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/Fasta.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/File.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/HiCMatrixTrack.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/Junction.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/Motif.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/file/__init__.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/plot_tests.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/trackplot/server.py +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Home-DOO13BH7.js +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Home-QmeAKOl4.js +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Home-RdVPWns6.js +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Home-jSR0MsHI.css +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Plot-BmqHZ4QE.css +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Plot-Bvyo6ju9.css +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Plot-CTM-EDrj.js +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Plot-DiuFnwNK.js +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Plot-hvkDteAn.js +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/Plot-rbQz1TOM.css +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/el-divider-BHm65SRq.css +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/el-divider-BuEUMHwE.css +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/el-divider-SYT5K-ds.css +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/el-divider-VYjL3C7L.js +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/el-divider-eEJXnQD5.js +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/el-divider-u9f0bZWY.js +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/index-CETGMNio.css +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/index-Cexhr_fn.css +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/index-CrzyEb9s.js +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/index-D_Cw0sbX.js +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/index-Dd6Bavnk.js +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/assets/index-Sq2gI4sE.css +0 -0
- {trackplot-0.5.2 → trackplot-0.5.4}/ui/vite.svg +0 -0
|
@@ -1,34 +1,30 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: trackplot
|
|
3
|
-
Version: 0.5.
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
+
|
|
@@ -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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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=
|
|
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
|
|
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
|
|
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
|
|
139
|
-
|
|
140
|
-
graph_coords
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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[
|
|
152
|
-
j -
|
|
153
|
-
|
|
154
|
-
for
|
|
155
|
-
if
|
|
156
|
-
graph_coords[
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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 =
|
|
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
|
|