ModDotPlot 1.0.0__cp38-abi3-win_amd64.whl
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.
- moddotplot/__init__.py +29 -0
- moddotplot/__main__.py +26 -0
- moddotplot/_nthash.pyd +0 -0
- moddotplot/annotations.py +113 -0
- moddotplot/color_palettes.py +1744 -0
- moddotplot/const.py +294 -0
- moddotplot/estimate_identity.py +1258 -0
- moddotplot/interactive.py +2003 -0
- moddotplot/moddotplot.py +3749 -0
- moddotplot/native_render.py +504 -0
- moddotplot/optional_dependencies.py +19 -0
- moddotplot/parse_fasta.py +789 -0
- moddotplot/plot_summary.py +113 -0
- moddotplot/static_plots.py +1785 -0
- moddotplot-1.0.0.dist-info/DELVEWHEEL +2 -0
- moddotplot-1.0.0.dist-info/METADATA +530 -0
- moddotplot-1.0.0.dist-info/RECORD +26 -0
- moddotplot-1.0.0.dist-info/WHEEL +5 -0
- moddotplot-1.0.0.dist-info/entry_points.txt +2 -0
- moddotplot-1.0.0.dist-info/licenses/LICENSE +21 -0
- moddotplot-1.0.0.dist-info/licenses/LICENSE.ntHash +21 -0
- moddotplot-1.0.0.dist-info/licenses/THIRD_PARTY_LICENSES.md +33 -0
- moddotplot-1.0.0.dist-info/top_level.txt +1 -0
- moddotplot.libs/.load-order-moddotplot-1.0.0 +2 -0
- moddotplot.libs/msvcp140-44758176d33d274ead9ea914025ed277.dll +0 -0
- moddotplot.libs/vcruntime140_1-6a99bc0128e0c7d6cbbf615fcc269095.dll +0 -0
moddotplot/__init__.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""""" # start delvewheel patch
|
|
2
|
+
def _delvewheel_patch_1_13_0():
|
|
3
|
+
import ctypes
|
|
4
|
+
import os
|
|
5
|
+
import platform
|
|
6
|
+
import sys
|
|
7
|
+
libs_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, 'moddotplot.libs'))
|
|
8
|
+
is_conda_cpython = platform.python_implementation() == 'CPython' and (hasattr(ctypes.pythonapi, 'Anaconda_GetVersion') or 'packaged by conda-forge' in sys.version)
|
|
9
|
+
if sys.version_info[:2] >= (3, 8) and not is_conda_cpython or sys.version_info[:2] >= (3, 10):
|
|
10
|
+
if os.path.isdir(libs_dir):
|
|
11
|
+
os.add_dll_directory(libs_dir)
|
|
12
|
+
else:
|
|
13
|
+
load_order_filepath = os.path.join(libs_dir, '.load-order-moddotplot-1.0.0')
|
|
14
|
+
if os.path.isfile(load_order_filepath):
|
|
15
|
+
import ctypes.wintypes
|
|
16
|
+
with open(os.path.join(libs_dir, '.load-order-moddotplot-1.0.0')) as file:
|
|
17
|
+
load_order = file.read().split()
|
|
18
|
+
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
|
|
19
|
+
kernel32.LoadLibraryExW.restype = ctypes.wintypes.HMODULE
|
|
20
|
+
kernel32.LoadLibraryExW.argtypes = ctypes.wintypes.LPCWSTR, ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD
|
|
21
|
+
for lib in load_order:
|
|
22
|
+
lib_path = os.path.join(os.path.join(libs_dir, lib))
|
|
23
|
+
if os.path.isfile(lib_path) and not kernel32.LoadLibraryExW(lib_path, None, 8):
|
|
24
|
+
raise OSError('Error loading {}; {}'.format(lib, ctypes.FormatError(ctypes.get_last_error())))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
_delvewheel_patch_1_13_0()
|
|
28
|
+
del _delvewheel_patch_1_13_0
|
|
29
|
+
# end delvewheel patch
|
moddotplot/__main__.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from contextlib import redirect_stderr, redirect_stdout
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _run(arguments):
|
|
8
|
+
from moddotplot.moddotplot import main as run_moddotplot
|
|
9
|
+
|
|
10
|
+
return run_moddotplot(arguments)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main(arguments=None):
|
|
14
|
+
"""Load the CLI under quiet redirection to cover import-time output."""
|
|
15
|
+
|
|
16
|
+
raw_arguments = list(sys.argv[1:] if arguments is None else arguments)
|
|
17
|
+
if "--quiet" not in raw_arguments:
|
|
18
|
+
return _run(raw_arguments)
|
|
19
|
+
|
|
20
|
+
with open(os.devnull, "w", encoding="utf-8") as sink:
|
|
21
|
+
with redirect_stdout(sink), redirect_stderr(sink):
|
|
22
|
+
return _run(raw_arguments)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if __name__ == "__main__":
|
|
26
|
+
sys.exit(main())
|
moddotplot/_nthash.pyd
ADDED
|
Binary file
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Shared BED annotation parsing and interval-selection helpers."""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
DEFAULT_ANNOTATION_COLOR = "#4C72B0"
|
|
7
|
+
BED_COLUMNS = [
|
|
8
|
+
"chrom",
|
|
9
|
+
"start",
|
|
10
|
+
"end",
|
|
11
|
+
"name",
|
|
12
|
+
"score",
|
|
13
|
+
"strand",
|
|
14
|
+
"thickStart",
|
|
15
|
+
"thickEnd",
|
|
16
|
+
"itemRgb",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def read_annotation_bed(filepath):
|
|
21
|
+
"""Read the BED3-BED9 subset used by ModDotPlot annotations."""
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
dataframe = pd.read_csv(filepath, sep="\t", comment="#", header=None, dtype=str)
|
|
25
|
+
except pd.errors.EmptyDataError:
|
|
26
|
+
return pd.DataFrame(columns=BED_COLUMNS[:3])
|
|
27
|
+
|
|
28
|
+
if not 3 <= dataframe.shape[1] <= len(BED_COLUMNS):
|
|
29
|
+
raise ValueError(
|
|
30
|
+
"Invalid BED file: expected between 3 and 9 tab-separated columns."
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
dataframe.columns = BED_COLUMNS[: dataframe.shape[1]]
|
|
34
|
+
dataframe["chrom"] = dataframe["chrom"].astype(str)
|
|
35
|
+
|
|
36
|
+
for column in ("start", "end"):
|
|
37
|
+
try:
|
|
38
|
+
values = pd.to_numeric(dataframe[column], errors="raise")
|
|
39
|
+
except (TypeError, ValueError) as error:
|
|
40
|
+
raise ValueError(
|
|
41
|
+
f"Invalid BED file: '{column}' must contain only integers."
|
|
42
|
+
) from error
|
|
43
|
+
if values.isna().any() or not np.all(np.isfinite(values)):
|
|
44
|
+
raise ValueError(
|
|
45
|
+
f"Invalid BED file: '{column}' must contain only finite integers."
|
|
46
|
+
)
|
|
47
|
+
if np.any(values % 1 != 0):
|
|
48
|
+
raise ValueError(
|
|
49
|
+
f"Invalid BED file: '{column}' must contain only integers."
|
|
50
|
+
)
|
|
51
|
+
dataframe[column] = values.astype(np.int64)
|
|
52
|
+
|
|
53
|
+
if (dataframe["start"] < 0).any():
|
|
54
|
+
raise ValueError("Invalid BED file: 'start' must be non-negative.")
|
|
55
|
+
if (dataframe["end"] <= dataframe["start"]).any():
|
|
56
|
+
raise ValueError(
|
|
57
|
+
"Invalid BED file: 'end' must be greater than 'start' for every interval."
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return dataframe
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def read_annotation_beds(filepaths):
|
|
64
|
+
"""Read and combine one or more annotation BED files."""
|
|
65
|
+
|
|
66
|
+
frames = [read_annotation_bed(filepath) for filepath in filepaths]
|
|
67
|
+
if not frames:
|
|
68
|
+
return pd.DataFrame(columns=BED_COLUMNS[:3])
|
|
69
|
+
return pd.concat(frames, ignore_index=True, sort=False)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def annotation_color(value, fallback=DEFAULT_ANNOTATION_COLOR):
|
|
73
|
+
"""Return an RGB tuple for a BED ``itemRgb`` value."""
|
|
74
|
+
|
|
75
|
+
if value is None or pd.isna(value):
|
|
76
|
+
return fallback
|
|
77
|
+
|
|
78
|
+
fields = [field.strip() for field in str(value).split(",")]
|
|
79
|
+
if len(fields) != 3:
|
|
80
|
+
return fallback
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
channels = tuple(int(field) for field in fields)
|
|
84
|
+
except ValueError:
|
|
85
|
+
return fallback
|
|
86
|
+
if any(channel < 0 or channel > 255 for channel in channels):
|
|
87
|
+
return fallback
|
|
88
|
+
return tuple(channel / 255 for channel in channels)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def visible_annotation_intervals(
|
|
92
|
+
bed_df, chrom, region_start, region_end, fallback=DEFAULT_ANNOTATION_COLOR
|
|
93
|
+
):
|
|
94
|
+
"""Select and clip BED intervals to a plotted genomic region."""
|
|
95
|
+
|
|
96
|
+
if region_end <= region_start:
|
|
97
|
+
raise ValueError("Annotation region end must be greater than its start.")
|
|
98
|
+
if bed_df.empty:
|
|
99
|
+
return []
|
|
100
|
+
|
|
101
|
+
intervals = []
|
|
102
|
+
matching = bed_df[bed_df["chrom"] == str(chrom)]
|
|
103
|
+
has_item_rgb = "itemRgb" in matching.columns
|
|
104
|
+
for row in matching.itertuples(index=False):
|
|
105
|
+
interval_start = int(row.start)
|
|
106
|
+
interval_end = int(row.end)
|
|
107
|
+
clipped_start = max(interval_start, region_start)
|
|
108
|
+
clipped_end = min(interval_end, region_end)
|
|
109
|
+
if clipped_end <= clipped_start:
|
|
110
|
+
continue
|
|
111
|
+
rgb = getattr(row, "itemRgb", None) if has_item_rgb else None
|
|
112
|
+
intervals.append((clipped_start, clipped_end, annotation_color(rgb, fallback)))
|
|
113
|
+
return intervals
|