telometer 0.76__tar.gz → 0.78__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.
- {telometer-0.76 → telometer-0.78}/PKG-INFO +1 -1
- {telometer-0.76 → telometer-0.78}/setup.py +1 -1
- {telometer-0.76 → telometer-0.78}/telometer/telometer.py +63 -52
- {telometer-0.76 → telometer-0.78}/telometer.egg-info/PKG-INFO +1 -1
- {telometer-0.76 → telometer-0.78}/LICENSE.txt +0 -0
- {telometer-0.76 → telometer-0.78}/README.md +0 -0
- {telometer-0.76 → telometer-0.78}/setup.cfg +0 -0
- {telometer-0.76 → telometer-0.78}/telometer/__init__.py +0 -0
- {telometer-0.76 → telometer-0.78}/telometer.egg-info/SOURCES.txt +0 -0
- {telometer-0.76 → telometer-0.78}/telometer.egg-info/dependency_links.txt +0 -0
- {telometer-0.76 → telometer-0.78}/telometer.egg-info/entry_points.txt +0 -0
- {telometer-0.76 → telometer-0.78}/telometer.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: telometer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.78
|
|
4
4
|
Summary: a simple regular expression based method for measuring individual, chromosome-specific telomere lengths from long-read sequencing data
|
|
5
5
|
Author: Santiago E Sanchez
|
|
6
6
|
Author-email: ses94@stanford.edu
|
|
@@ -2,7 +2,7 @@ import setuptools
|
|
|
2
2
|
|
|
3
3
|
setuptools.setup(
|
|
4
4
|
name="telometer",
|
|
5
|
-
version="0.
|
|
5
|
+
version="0.78",
|
|
6
6
|
author="Santiago E Sanchez",
|
|
7
7
|
author_email="ses94@stanford.edu",
|
|
8
8
|
description="a simple regular expression based method for measuring individual, chromosome-specific telomere lengths from long-read sequencing data",
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
# Telometer v0.
|
|
3
|
-
# Created by: Santiago E
|
|
4
|
-
# Artandi Lab, Stanford University,
|
|
2
|
+
# Telometer v0.78
|
|
3
|
+
# Created by: Santiago E Sanche8
|
|
4
|
+
# Artandi Lab, Stanford University, 2024
|
|
5
5
|
# Measures telomeres from ONT or PacBio long reads aligned to a T2T genome assembly
|
|
6
|
-
# Simple Usage: telometer -b sorted_t2t.bam -o output.
|
|
7
|
-
|
|
6
|
+
# Simple Usage: telometer -b sorted_t2t.bam -o output.ts8
|
|
8
7
|
import pysam
|
|
9
8
|
import re
|
|
10
|
-
import regex
|
|
9
|
+
import regex as re
|
|
11
10
|
import csv
|
|
12
11
|
import argparse
|
|
13
12
|
from multiprocessing import Pool, cpu_count
|
|
@@ -27,11 +26,11 @@ def get_adapters(chemistry):
|
|
|
27
26
|
adapters_rc = [reverse_complement(adapter) for adapter in adapters]
|
|
28
27
|
return adapters + adapters_rc
|
|
29
28
|
|
|
30
|
-
def
|
|
31
|
-
"""Returns
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return
|
|
29
|
+
def get_flexible_telomere_patterns():
|
|
30
|
+
"""Returns flexible telomere repeat patterns for both G-rich and C-rich strands."""
|
|
31
|
+
g_rich_telomere_pattern = r'(T{1,2}T{1,2}A{1,2}G{1,2}G{1,2}G{1,2}){2,}'
|
|
32
|
+
c_rich_telomere_pattern = r'(C{1,2}C{1,2}C{1,2}T{1,2}A{1,2}A{1,2}){2,}'
|
|
33
|
+
return g_rich_telomere_pattern, c_rich_telomere_pattern
|
|
35
34
|
|
|
36
35
|
def find_initial_boundary_region(sequence, patterns, max_mismatches):
|
|
37
36
|
"""Finds the initial boundary region with allowed mismatches."""
|
|
@@ -39,65 +38,78 @@ def find_initial_boundary_region(sequence, patterns, max_mismatches):
|
|
|
39
38
|
combined_pattern = '|'.join(f'({pattern})' for pattern in patterns)
|
|
40
39
|
regex_pattern = f'({combined_pattern}){{2,}}'
|
|
41
40
|
|
|
42
|
-
for match in
|
|
41
|
+
for match in re.finditer(f'({regex_pattern}){{e<={max_mismatches}}}', sequence, re.BESTMATCH):
|
|
43
42
|
boundary_length = max(boundary_length, len(match.group(0)))
|
|
44
43
|
return boundary_length
|
|
45
44
|
|
|
45
|
+
def determine_arm(reference_name, alignment_start, reference_length):
|
|
46
|
+
"""Determine the chromosome arm based on the alignment start position and reference name."""
|
|
47
|
+
if alignment_start < 15000 and "q" not in reference_name:
|
|
48
|
+
return "p"
|
|
49
|
+
return "q"
|
|
50
|
+
|
|
46
51
|
def process_read(args):
|
|
47
|
-
read_data,
|
|
52
|
+
read_data, g_rich_telomere_pattern, c_rich_telomere_pattern, adapters, minreadlen = args
|
|
48
53
|
|
|
49
54
|
if read_data['is_unmapped'] or read_data['query_sequence'] is None or len(read_data['query_sequence']) < minreadlen:
|
|
55
|
+
print(f"Skipping read {read_data['read_id']}: unmapped or too short")
|
|
50
56
|
return None
|
|
51
57
|
|
|
52
58
|
alignment_start = read_data['reference_start']
|
|
53
59
|
alignment_end = read_data['reference_end']
|
|
54
|
-
|
|
60
|
+
seq_to_check = read_data['query_sequence']
|
|
55
61
|
|
|
56
62
|
if read_data['is_reverse']:
|
|
57
63
|
direction = "rev"
|
|
58
|
-
|
|
64
|
+
seq_to_check = reverse_complement(seq_to_check)
|
|
59
65
|
else:
|
|
60
66
|
direction = "fwd"
|
|
61
67
|
|
|
62
68
|
reference_genome_length = read_data['reference_length']
|
|
63
|
-
|
|
64
69
|
if alignment_start >= 15000 and alignment_start <= reference_genome_length - 30000:
|
|
70
|
+
print(f"Skipping read {read_data['read_id']}: alignment start {alignment_start} within filtered range")
|
|
65
71
|
return None
|
|
66
72
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
arm = determine_arm(read_data['reference_name'], alignment_start, reference_genome_length)
|
|
74
|
+
|
|
75
|
+
telomere_starts = [m.start() for m in re.finditer(g_rich_telomere_pattern, seq_to_check)]
|
|
76
|
+
if not telomere_starts:
|
|
77
|
+
telomere_starts = [m.start() for m in re.finditer(c_rich_telomere_pattern, seq_to_check)]
|
|
71
78
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
telomere_start = telomere_start[0]
|
|
75
|
-
if telomere_start > 100 and (len(seq) - telomere_start > 200):
|
|
76
|
-
return None
|
|
79
|
+
if telomere_starts:
|
|
80
|
+
telomere_start = telomere_starts[0]
|
|
77
81
|
|
|
78
|
-
|
|
79
|
-
if
|
|
80
|
-
|
|
82
|
+
# Find the end of the telomere region
|
|
83
|
+
telomere_end = min((pos for pos in (seq_to_check.find(adapter) for adapter in adapters) if pos != -1), default=len(seq_to_check))
|
|
84
|
+
|
|
85
|
+
telomere_region = seq_to_check[telomere_start:telomere_end]
|
|
86
|
+
telomere_repeat = [m.group() for m in re.finditer(g_rich_telomere_pattern, telomere_region)]
|
|
87
|
+
if not telomere_repeat:
|
|
88
|
+
telomere_repeat = [m.group() for m in re.finditer(c_rich_telomere_pattern, telomere_region)]
|
|
81
89
|
|
|
82
|
-
telomere_region = seq[telomere_start:telomere_end]
|
|
83
|
-
telomere_repeat = [m.group() for m in re.finditer('|'.join(telomere_repeats_re.split('|')), telomere_region)]
|
|
84
90
|
telomere_length = len(''.join(telomere_repeat))
|
|
85
91
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
92
|
+
# Find the region immediately adjacent to the telomere region
|
|
93
|
+
boundary_mm1_region = seq_to_check[telomere_end:]
|
|
94
|
+
boundary_mm1_length = find_initial_boundary_region(boundary_mm1_region, g_rich_telomere_pattern.split('|') + c_rich_telomere_pattern.split('|'), max_mismatches=2)
|
|
95
|
+
else:
|
|
96
|
+
telomere_start = None
|
|
97
|
+
telomere_end = None
|
|
98
|
+
telomere_length = 0
|
|
99
|
+
boundary_mm1_length = 0
|
|
100
|
+
|
|
101
|
+
result = {
|
|
102
|
+
'read_id': read_data['read_id'],
|
|
103
|
+
'telomere_start': telomere_start,
|
|
104
|
+
'telomere_end': telomere_end,
|
|
105
|
+
'telomere_length': telomere_length,
|
|
106
|
+
'boundary_mm1_length': boundary_mm1_length,
|
|
107
|
+
'chromosome': read_data['reference_name'],
|
|
108
|
+
'mapping_quality': read_data['mapping_quality'],
|
|
109
|
+
'direction': direction,
|
|
110
|
+
'arm': arm
|
|
111
|
+
}
|
|
112
|
+
return result
|
|
101
113
|
|
|
102
114
|
def calculate_telomere_length():
|
|
103
115
|
parser = argparse.ArgumentParser(description='Calculate telomere length from a BAM file.')
|
|
@@ -106,28 +118,27 @@ def calculate_telomere_length():
|
|
|
106
118
|
parser.add_argument('-c', '--chemistry', default="r10", help="Sequencing chemistry (r9 or r10, default=r10). Optional", required=False)
|
|
107
119
|
parser.add_argument('-m', '--minreadlen', default=1000, type=int, help='Minimum read length to consider (Default: 1000 for telomere capture, use 4000 for WGS). Optional', required=False)
|
|
108
120
|
args = parser.parse_args()
|
|
109
|
-
bam_file = pysam.AlignmentFile(args.bam, "rb")
|
|
110
121
|
|
|
111
122
|
adapters = get_adapters(args.chemistry)
|
|
112
|
-
|
|
113
|
-
telomere_repeats_re = "|".join(f'({repeat}){{2,}}' for repeat in telomere_repeats)
|
|
123
|
+
g_rich_telomere_pattern, c_rich_telomere_pattern = get_flexible_telomere_patterns()
|
|
114
124
|
|
|
125
|
+
bam_file = pysam.AlignmentFile(args.bam, "rb")
|
|
115
126
|
read_data_list = [{
|
|
116
|
-
'
|
|
127
|
+
'read_id': read.query_name,
|
|
117
128
|
'is_unmapped': read.is_unmapped,
|
|
118
|
-
'is_reverse': read.is_reverse,
|
|
119
129
|
'reference_start': read.reference_start,
|
|
120
130
|
'reference_end': read.reference_end,
|
|
121
131
|
'reference_name': read.reference_name,
|
|
122
132
|
'mapping_quality': read.mapping_quality,
|
|
123
133
|
'query_sequence': read.query_sequence,
|
|
124
|
-
'reference_length': bam_file.get_reference_length(read.reference_name) if read.reference_name is not None else None
|
|
134
|
+
'reference_length': bam_file.get_reference_length(read.reference_name) if read.reference_name is not None else None,
|
|
135
|
+
'is_reverse': read.is_reverse
|
|
125
136
|
} for read in bam_file if read.reference_name is not None and read.reference_name != 'chrM']
|
|
126
137
|
|
|
127
138
|
with Pool(processes=cpu_count()) as pool:
|
|
128
139
|
results = pool.map(
|
|
129
140
|
process_read,
|
|
130
|
-
[(read_data,
|
|
141
|
+
[(read_data, g_rich_telomere_pattern, c_rich_telomere_pattern, adapters, args.minreadlen) for read_data in read_data_list]
|
|
131
142
|
)
|
|
132
143
|
|
|
133
144
|
results = [result for result in results if result]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: telometer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.78
|
|
4
4
|
Summary: a simple regular expression based method for measuring individual, chromosome-specific telomere lengths from long-read sequencing data
|
|
5
5
|
Author: Santiago E Sanchez
|
|
6
6
|
Author-email: ses94@stanford.edu
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|