Skip to content

Commit 468f70c

Browse files
author
Vinzenz May
committed
added crs stats debug script
1 parent 830f940 commit 468f70c

2 files changed

Lines changed: 157 additions & 1 deletion

File tree

pixi.lock

Lines changed: 46 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#!/usr/bin/env python3
2+
"""Load candidate regions and print summary statistics to the terminal."""
3+
4+
import argparse
5+
import logging
6+
from collections import Counter
7+
from pathlib import Path
8+
9+
from ..candidateregions import signalstrength_to_crs
10+
11+
log = logging.getLogger(__name__)
12+
13+
14+
def compute_percentiles(values: list[int], steps: int = 10) -> dict[int, int]:
15+
if not values:
16+
return {}
17+
18+
sorted_values = sorted(values)
19+
n = len(sorted_values)
20+
percentiles = {}
21+
for p in range(0, 101, steps):
22+
index = min(max(int(round(p / 100 * (n - 1))), 0), n - 1)
23+
percentiles[p] = sorted_values[index]
24+
return percentiles
25+
26+
27+
def crs_stats(crs: list[signalstrength_to_crs.datatypes.CandidateRegion]) -> None:
28+
region_sizes = [cr.referenceEnd - cr.referenceStart for cr in crs]
29+
chromosomes = [cr.chr for cr in crs]
30+
read_counts = [len(cr.get_read_names()) for cr in crs]
31+
32+
log.info("Candidate regions loaded: %d", len(crs))
33+
34+
print("=== CRS Summary ===")
35+
print(f"Total candidate regions: {len(crs)}")
36+
print("")
37+
38+
print("Region size distribution (percentiles):")
39+
for p, value in compute_percentiles(region_sizes, steps=10).items():
40+
print(f" {p:3d}th percentile: {value}")
41+
print("")
42+
43+
print("Signal read counts per region:")
44+
print(f" min: {min(read_counts) if read_counts else 0}")
45+
print(f" max: {max(read_counts) if read_counts else 0}")
46+
print(f" mean: {sum(read_counts) / len(read_counts) if read_counts else 0:.2f}")
47+
print(f" median: {compute_percentiles(read_counts, steps=50).get(50, 0)}")
48+
print("")
49+
50+
print("Regions per chromosome:")
51+
for chrom, count in Counter(chromosomes).most_common():
52+
print(f" {chrom}: {count}")
53+
print("")
54+
55+
print("20 largest candidate regions:")
56+
largest = sorted(crs, key=lambda x: x.referenceEnd - x.referenceStart, reverse=True)[:20]
57+
for cr in largest:
58+
size = cr.referenceEnd - cr.referenceStart
59+
print(
60+
f" {cr.chr}\t{cr.referenceStart}\t{cr.referenceEnd}\t{size}\t{cr.crID}"
61+
)
62+
63+
64+
def get_parser() -> argparse.ArgumentParser:
65+
parser = argparse.ArgumentParser(
66+
description="Print statistics for a candidate regions database.",
67+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
68+
)
69+
parser.add_argument(
70+
"-i",
71+
"--input",
72+
type=Path,
73+
required=True,
74+
help="Path to the candidate regions database file (e.g. crs.db).",
75+
metavar="DB",
76+
)
77+
parser.add_argument(
78+
"-v",
79+
"--verbose",
80+
action="store_true",
81+
help="Enable verbose logging.",
82+
)
83+
return parser
84+
85+
86+
def main() -> int:
87+
parser = get_parser()
88+
args = parser.parse_args()
89+
90+
log_level = logging.DEBUG if args.verbose else logging.INFO
91+
logging.basicConfig(
92+
level=log_level,
93+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
94+
)
95+
96+
if not args.input.exists():
97+
log.error("Input database file not found: %s", args.input)
98+
return 1
99+
100+
try:
101+
crs = signalstrength_to_crs.load_crs_from_db(path_db=args.input)
102+
except Exception as exc:
103+
log.error("Failed to load candidate regions: %s", exc)
104+
return 1
105+
106+
crs_stats(crs)
107+
return 0
108+
109+
110+
if __name__ == "__main__":
111+
raise SystemExit(main())

0 commit comments

Comments
 (0)