countmut 0.0.2__tar.gz → 0.0.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: countmut
3
- Version: 0.0.2
3
+ Version: 0.0.4
4
4
  Summary: Ultra-fast strand-aware mutation counter
5
5
  Author-email: Ye Chang <yech1990@gmail.com>
6
6
  License-Expression: MIT
@@ -41,13 +41,13 @@ Dynamic: license-file
41
41
  [![Downloads](https://img.shields.io/pepy/dt/countmut)](https://pepy.tech/project/countmut)
42
42
  [![Development Status](https://img.shields.io/badge/status-alpha-orange.svg)](https://github.com/y9c/countmut)
43
43
 
44
- **Ultra-fast strand-aware mutation counter**
44
+ > **Ultra-fast strand-aware mutation counter for bisulfite sequencing analysis**
45
45
 
46
- CountMut counts mutations from bisulfite sequencing / CAM-seq / GLORI-seq / eTAM-seq BAM files with parallel processing, quality-based mate overlap deduplication, and optimized file I/O.
46
+ CountMut is a high-performance tool for counting mutations from bisulfite sequencing BAM files (BS-seq, CAM-seq, GLORI-seq, eTAM-seq). It features parallel processing, quality-based mate overlap deduplication, and optimized file I/O for maximum speed.
47
47
 
48
48
  ## Features
49
49
 
50
- - 🚀 **Ultra-Fast**: Direct FASTA index reading, shared file handles, BGZF multi-threading
50
+ - 🚀 **Ultra-Fast**: Call mutation without pileup reads
51
51
  - 🧬 **Bisulfite Support**: NS, Zf, Yf tag filtering for conversion analysis
52
52
  - 🎯 **Accurate**: Quality-based mate overlap deduplication prevents double-counting
53
53
  - ⚡ **Parallel**: Multi-threaded genomic window processing
@@ -72,51 +72,68 @@ countmut -i input.bam -r reference.fa -o mutations.tsv --ref-base T --mut-base C
72
72
  countmut -i input.bam -r reference.fa -o mutations.tsv -t 8 --max-unc 5 --min-con 2
73
73
  ```
74
74
 
75
- ## Key Options
75
+ ## Options
76
76
 
77
+ **Input/Output**
77
78
  ```bash
78
- Required:
79
- -i, --input PATH Input BAM file
80
- -r, --reference PATH Reference FASTA file
81
-
82
- Output:
83
- -o, --output PATH Output TSV file (default: stdout)
84
-
85
- Mutation:
86
- --ref-base TEXT Reference base [default: A]
87
- --mut-base TEXT Mutation base [default: G]
88
- --strand [both|forward|reverse] Strand processing [default: both]
89
- --region TEXT Specific region (e.g., 'chr1:1000000-2000000')
90
-
91
- Performance:
92
- -t, --threads INTEGER Number of threads [default: auto]
93
- -b, --bin-size INTEGER Genomic bin size [default: 10000]
94
-
95
- Filtering (Bisulfite):
96
- --pad INTEGER Motif window padding [default: 15]
97
- --trim-start INTEGER Trim 5' bases [default: 2]
98
- --trim-end INTEGER Trim 3' bases [default: 2]
99
- --max-unc INTEGER Max unconverted (Zf) [default: 3]
100
- --min-con INTEGER Min converted (Yf) [default: 1]
101
- --max-sub INTEGER Max substitutions (NS) [default: 1]
102
- --min-baseq INTEGER Min base quality (Phred) [default: 20]
103
- --min-mapq INTEGER Min mapping quality (MAPQ) [default: 0]
79
+ -i, --input PATH Input BAM file (coordinate-sorted) [required]
80
+ -r, --reference PATH Reference FASTA file [required]
81
+ -o, --output PATH Output TSV file (default: stdout)
82
+ -f, --force Overwrite output without prompting
104
83
  ```
105
84
 
106
- **Note**: BAM must have NS, Zf, and Yf tags for bisulfite analysis.
85
+ **Mutation Analysis**
86
+ ```bash
87
+ --ref-base TEXT Reference base to count from [default: A]
88
+ --mut-base TEXT Mutation base to count [default: G]
89
+ --strand TEXT Strand: both/forward/reverse [default: both]
90
+ --region TEXT Genomic region (e.g., 'chr1:1000000-2000000')
91
+ ```
107
92
 
108
- ## Output Format
93
+ **Performance**
94
+ ```bash
95
+ -t, --threads INTEGER Number of parallel threads [default: auto]
96
+ -b, --bin-size INTEGER Genomic bin size in bp [default: 10000]
97
+ ```
109
98
 
110
- TSV file with columns:
111
- - `chrom`, `pos`, `strand`, `motif` - Position and sequence context
112
- - `u0`, `u1`, `u2` - Unconverted (reference base) counts [drop, clean, unconverted]
113
- - `m0`, `m1`, `m2` - Mutation (mutation base only) counts [drop, clean, unconverted]
114
- - `o0`, `o1`, `o2` - Other bases counts [drop, clean, unconverted] (only with `--save-rest`)
99
+ **Quality Filters**
100
+ ```bash
101
+ --min-baseq INTEGER Min base quality (Phred score) [default: 20]
102
+ --min-mapq INTEGER Min mapping quality (MAPQ) [default: 0]
103
+ --max-sub INTEGER Max substitutions (NS tag) [default: 1]
104
+ --trim-start INTEGER Trim N bases from read 5' end (fragment orientation) [default: 2]
105
+ --trim-end INTEGER Trim N bases from read 3' end (fragment orientation) [default: 2]
106
+ --max-unc INTEGER Max unconverted (Zf tag) [default: 3]
107
+ --min-con INTEGER Min converted (Yf tag) [default: 1]
108
+ ```
109
+
110
+ **Output Records**
111
+ ```bash
112
+ -p, --pad INTEGER Motif window half-size [default: 15]
113
+ -s, --save-rest Include other bases (o0, o1, o2 columns)
114
+ ```
115
+
116
+ > **Note**: BAM files must have **NS**, **Zf**, and **Yf** tags (essential for bisulfite analysis).
117
+ > Indices (.bai, .fai) are created automatically if missing.
118
+
119
+ ## Output Format
115
120
 
116
- Where:
117
- - **drop** (x0): Bases failing quality filters (internal position, mismatch, mapq, baseq)
118
- - **clean** (x1): High-quality bases passing all filters
119
- - **unconverted** (x2): Bases in unconverted reads (high Zf or low Yf)
121
+ TSV file with the following columns:
122
+
123
+ | Column | Description |
124
+ |--------|-------------|
125
+ | `chrom` | Chromosome name |
126
+ | `pos` | Genomic position (1-based) |
127
+ | `strand` | Strand (+ or -) |
128
+ | `motif` | Sequence context (2×pad+1 bp window) |
129
+ | `u0`, `u1`, `u2` | **Unconverted** (reference base) counts |
130
+ | `m0`, `m1`, `m2` | **Mutation** (mutation base only) counts |
131
+ | `o0`, `o1`, `o2` | **Other bases** counts (with `--save-rest`) |
132
+
133
+ **Count categories** (x0, x1, x2):
134
+ - **x0 (low quality)**: Bases failing quality filters (trim region, max-sub, min-mapq, min-baseq)
135
+ - **x1 (high conversion)**: Bases from reads with high conversion efficiency (low Zf and high Yf)
136
+ - **x2 (insufficient conversion)**: Bases from reads with insufficient conversion efficiency (high Zf or low Yf)
120
137
 
121
138
 
122
139
  &nbsp;
@@ -0,0 +1,117 @@
1
+ # CountMut
2
+
3
+ [![Pypi Releases](https://img.shields.io/pypi/v/countmut.svg)](https://pypi.python.org/pypi/countmut)
4
+ [![Downloads](https://img.shields.io/pepy/dt/countmut)](https://pepy.tech/project/countmut)
5
+ [![Development Status](https://img.shields.io/badge/status-alpha-orange.svg)](https://github.com/y9c/countmut)
6
+
7
+ > **Ultra-fast strand-aware mutation counter for bisulfite sequencing analysis**
8
+
9
+ CountMut is a high-performance tool for counting mutations from bisulfite sequencing BAM files (BS-seq, CAM-seq, GLORI-seq, eTAM-seq). It features parallel processing, quality-based mate overlap deduplication, and optimized file I/O for maximum speed.
10
+
11
+ ## Features
12
+
13
+ - 🚀 **Ultra-Fast**: Call mutation without pileup reads
14
+ - 🧬 **Bisulfite Support**: NS, Zf, Yf tag filtering for conversion analysis
15
+ - 🎯 **Accurate**: Quality-based mate overlap deduplication prevents double-counting
16
+ - ⚡ **Parallel**: Multi-threaded genomic window processing
17
+ - 🔧 **Flexible**: Configurable filtering, strand-specific processing, auto-indexing
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ pip install countmut
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```bash
28
+ # Basic usage - auto-creates indices if needed
29
+ countmut -i input.bam -r reference.fa -o mutations.tsv
30
+
31
+ # Count T→C mutations (common in bisulfite sequencing)
32
+ countmut -i input.bam -r reference.fa -o mutations.tsv --ref-base T --mut-base C
33
+
34
+ # With custom threads and filtering
35
+ countmut -i input.bam -r reference.fa -o mutations.tsv -t 8 --max-unc 5 --min-con 2
36
+ ```
37
+
38
+ ## Options
39
+
40
+ **Input/Output**
41
+ ```bash
42
+ -i, --input PATH Input BAM file (coordinate-sorted) [required]
43
+ -r, --reference PATH Reference FASTA file [required]
44
+ -o, --output PATH Output TSV file (default: stdout)
45
+ -f, --force Overwrite output without prompting
46
+ ```
47
+
48
+ **Mutation Analysis**
49
+ ```bash
50
+ --ref-base TEXT Reference base to count from [default: A]
51
+ --mut-base TEXT Mutation base to count [default: G]
52
+ --strand TEXT Strand: both/forward/reverse [default: both]
53
+ --region TEXT Genomic region (e.g., 'chr1:1000000-2000000')
54
+ ```
55
+
56
+ **Performance**
57
+ ```bash
58
+ -t, --threads INTEGER Number of parallel threads [default: auto]
59
+ -b, --bin-size INTEGER Genomic bin size in bp [default: 10000]
60
+ ```
61
+
62
+ **Quality Filters**
63
+ ```bash
64
+ --min-baseq INTEGER Min base quality (Phred score) [default: 20]
65
+ --min-mapq INTEGER Min mapping quality (MAPQ) [default: 0]
66
+ --max-sub INTEGER Max substitutions (NS tag) [default: 1]
67
+ --trim-start INTEGER Trim N bases from read 5' end (fragment orientation) [default: 2]
68
+ --trim-end INTEGER Trim N bases from read 3' end (fragment orientation) [default: 2]
69
+ --max-unc INTEGER Max unconverted (Zf tag) [default: 3]
70
+ --min-con INTEGER Min converted (Yf tag) [default: 1]
71
+ ```
72
+
73
+ **Output Records**
74
+ ```bash
75
+ -p, --pad INTEGER Motif window half-size [default: 15]
76
+ -s, --save-rest Include other bases (o0, o1, o2 columns)
77
+ ```
78
+
79
+ > **Note**: BAM files must have **NS**, **Zf**, and **Yf** tags (essential for bisulfite analysis).
80
+ > Indices (.bai, .fai) are created automatically if missing.
81
+
82
+ ## Output Format
83
+
84
+ TSV file with the following columns:
85
+
86
+ | Column | Description |
87
+ |--------|-------------|
88
+ | `chrom` | Chromosome name |
89
+ | `pos` | Genomic position (1-based) |
90
+ | `strand` | Strand (+ or -) |
91
+ | `motif` | Sequence context (2×pad+1 bp window) |
92
+ | `u0`, `u1`, `u2` | **Unconverted** (reference base) counts |
93
+ | `m0`, `m1`, `m2` | **Mutation** (mutation base only) counts |
94
+ | `o0`, `o1`, `o2` | **Other bases** counts (with `--save-rest`) |
95
+
96
+ **Count categories** (x0, x1, x2):
97
+ - **x0 (low quality)**: Bases failing quality filters (trim region, max-sub, min-mapq, min-baseq)
98
+ - **x1 (high conversion)**: Bases from reads with high conversion efficiency (low Zf and high Yf)
99
+ - **x2 (insufficient conversion)**: Bases from reads with insufficient conversion efficiency (high Zf or low Yf)
100
+
101
+
102
+ &nbsp;
103
+
104
+ <p align="center">
105
+ <img
106
+ src="https://raw.githubusercontent.com/y9c/y9c/master/resource/footer_line.svg?sanitize=true"
107
+ />
108
+ </p>
109
+ <p align="center">
110
+ Copyright &copy; 2025-present
111
+ <a href="https://github.com/y9c" target="_blank">Chang Y</a>
112
+ </p>
113
+ <p align="center">
114
+ <a href="https://github.com/y9c/countmut/blob/main/LICENSE">
115
+ <img src="https://img.shields.io/static/v1.svg?style=for-the-badge&label=License&message=MIT&logoColor=d9e0ee&colorA=282a36&colorB=c678dd" />
116
+ </a>
117
+ </p>
@@ -40,12 +40,8 @@ click.rich_click.TEXT_EMOJIS = True
40
40
  click.rich_click.OPTION_GROUPS = {
41
41
  "countmut": [
42
42
  {
43
- "name": "Required Options",
44
- "options": ["--input", "--reference"],
45
- },
46
- {
47
- "name": "Output Options",
48
- "options": ["--output", "--save-rest", "--force"],
43
+ "name": "Input/Output Options",
44
+ "options": ["--input", "--reference", "--output", "--force"],
49
45
  },
50
46
  {
51
47
  "name": "Mutation Analysis",
@@ -64,8 +60,12 @@ click.rich_click.OPTION_GROUPS = {
64
60
  ],
65
61
  },
66
62
  {
67
- "name": "Sequence Context",
68
- "options": ["--pad"],
63
+ "name": "Output Records",
64
+ "options": ["--pad", "--save-rest"],
65
+ },
66
+ {
67
+ "name": "Performance Options",
68
+ "options": ["--threads", "--bin-size"],
69
69
  },
70
70
  {
71
71
  "name": "Help & Version",
@@ -181,7 +181,7 @@ countmut -i input.bam -r reference.fa --region chr1:1000000-2000000
181
181
  "-s",
182
182
  "--save-rest",
183
183
  is_flag=True,
184
- help="[bold]Save additional statistics[/bold] including y0, y1, y2 columns",
184
+ help="[bold]Save other bases[/bold] statistics (o0, o1, o2 columns)",
185
185
  )
186
186
  @click.option(
187
187
  "--region",
@@ -202,6 +202,7 @@ countmut -i input.bam -r reference.fa --region chr1:1000000-2000000
202
202
  help="[bold]Strand processing[/bold]: 'both' (default), 'forward' (+ only), or 'reverse' (- only)",
203
203
  )
204
204
  @click.option(
205
+ "-p",
205
206
  "--pad",
206
207
  type=int,
207
208
  default=15,
@@ -213,14 +214,14 @@ countmut -i input.bam -r reference.fa --region chr1:1000000-2000000
213
214
  type=int,
214
215
  default=2,
215
216
  show_default=True,
216
- help="[bold]Trim bases[/bold] at read 5' end when counting",
217
+ help="[bold]Trim N bases[/bold] from read 5' end (fragment orientation)",
217
218
  )
218
219
  @click.option(
219
220
  "--trim-end",
220
221
  type=int,
221
222
  default=2,
222
223
  show_default=True,
223
- help="[bold]Trim bases[/bold] at read 3' end when counting",
224
+ help="[bold]Trim N bases[/bold] from read 3' end (fragment orientation)",
224
225
  )
225
226
  @click.option(
226
227
  "--max-unc",
@@ -255,14 +255,14 @@ def parse_region_worker(args: tuple) -> dict[str, Any]:
255
255
  for pos in target_sites_list:
256
256
  position_data[pos] = {
257
257
  "+": {
258
- "clean_count": Counter(),
259
- "unc_count": Counter(),
260
- "drop_count": Counter(),
258
+ "high_conversion_count": Counter(),
259
+ "insufficient_conversion_count": Counter(),
260
+ "low_quality_count": Counter(),
261
261
  },
262
262
  "-": {
263
- "clean_count": Counter(),
264
- "unc_count": Counter(),
265
- "drop_count": Counter(),
263
+ "high_conversion_count": Counter(),
264
+ "insufficient_conversion_count": Counter(),
265
+ "low_quality_count": Counter(),
266
266
  },
267
267
  }
268
268
 
@@ -310,10 +310,10 @@ def parse_region_worker(args: tuple) -> dict[str, Any]:
310
310
  ns = read.get_tag("NS")
311
311
  passes_mismatch_filter = ns <= max_sub
312
312
 
313
- # Check conversion status
313
+ # Check conversion filter (reads passing are sufficiently converted)
314
314
  zf = read.get_tag("Zf")
315
315
  yf = read.get_tag("Yf")
316
- is_converted = (zf <= max_unc) and (yf >= min_con)
316
+ passes_conversion_filter = (zf <= max_unc) and (yf >= min_con)
317
317
 
318
318
  # Process each position in the read
319
319
  query_sequence = read.query_sequence
@@ -334,11 +334,23 @@ def parse_region_worker(args: tuple) -> dict[str, Any]:
334
334
  continue
335
335
  if query_pos >= len(query_sequence):
336
336
  continue
337
- # Check if is terminal
338
- is_internal = (
339
- query_pos > trim_start
340
- and len(query_sequence) - query_pos > trim_end
341
- )
337
+
338
+ # Check if position is internal (not in trimmed regions)
339
+ # Trim based on actual strand orientation (fragment 5' to 3')
340
+ # For forward strand (+): 5' is at start (low query_pos), 3' is at end (high query_pos)
341
+ # For reverse strand (-): 5' is at end (high query_pos), 3' is at start (low query_pos)
342
+ if actual_strand == "+":
343
+ # Forward: trim_start from 5' (beginning), trim_end from 3' (end)
344
+ is_internal = (
345
+ query_pos >= trim_start
346
+ and len(query_sequence) - query_pos > trim_end
347
+ )
348
+ else:
349
+ # Reverse: trim_start from 5' (end), trim_end from 3' (beginning)
350
+ is_internal = (
351
+ query_pos >= trim_end
352
+ and len(query_sequence) - query_pos > trim_start
353
+ )
342
354
  # Get query base from read sequence (base_char is reference base, not query)
343
355
  query_base = query_sequence[query_pos].upper()
344
356
  base_qual = (
@@ -363,7 +375,7 @@ def parse_region_worker(args: tuple) -> dict[str, Any]:
363
375
  bool(passes_mismatch_filter),
364
376
  bool(passes_mapq_filter),
365
377
  bool(passes_baseq_filter),
366
- bool(is_converted),
378
+ bool(passes_conversion_filter),
367
379
  )
368
380
 
369
381
  except (KeyError, AttributeError) as e:
@@ -380,28 +392,34 @@ def parse_region_worker(args: tuple) -> dict[str, Any]:
380
392
  passes_mismatch_filter,
381
393
  passes_mapq_filter,
382
394
  passes_baseq_filter,
383
- is_converted,
395
+ passes_conversion_filter,
384
396
  ) in best_obs.items():
385
397
  # Skip positions that are not in our target sites (safety)
386
398
  if ref_pos not in position_data:
387
399
  continue
388
400
 
389
- # Count in drop_count if ANY quality filter fails
401
+ # Count in low_quality if ANY quality filter fails
390
402
  if not (
391
403
  is_internal
392
404
  and passes_mismatch_filter
393
405
  and passes_mapq_filter
394
406
  and passes_baseq_filter
395
407
  ):
396
- position_data[ref_pos][strand_symbol]["drop_count"][query_base] += 1
397
- # Count in clean_count/unc_count only if ALL quality filters pass
408
+ position_data[ref_pos][strand_symbol]["low_quality_count"][
409
+ query_base
410
+ ] += 1
411
+ # Count in high_conversion/insufficient_conversion only if ALL quality filters pass
398
412
  else:
399
- if is_converted:
400
- position_data[ref_pos][strand_symbol]["clean_count"][
413
+ if passes_conversion_filter:
414
+ # High conversion efficiency (pass quality + conversion filters)
415
+ position_data[ref_pos][strand_symbol]["high_conversion_count"][
401
416
  query_base
402
417
  ] += 1
403
418
  else:
404
- position_data[ref_pos][strand_symbol]["unc_count"][query_base] += 1
419
+ # Insufficient conversion efficiency (pass quality but fail conversion)
420
+ position_data[ref_pos][strand_symbol][
421
+ "insufficient_conversion_count"
422
+ ][query_base] += 1
405
423
 
406
424
  # Calculate total skipped
407
425
  total_skipped = (
@@ -433,9 +451,15 @@ def parse_region_worker(args: tuple) -> dict[str, Any]:
433
451
  if process_reverse_only and strand_symbol != "-":
434
452
  continue
435
453
 
436
- clean_count = position_data[pos][strand_symbol]["clean_count"]
437
- unc_count = position_data[pos][strand_symbol]["unc_count"]
438
- drop_count = position_data[pos][strand_symbol]["drop_count"]
454
+ high_conversion_count = position_data[pos][strand_symbol][
455
+ "high_conversion_count"
456
+ ]
457
+ insufficient_conversion_count = position_data[pos][strand_symbol][
458
+ "insufficient_conversion_count"
459
+ ]
460
+ low_quality_count = position_data[pos][strand_symbol][
461
+ "low_quality_count"
462
+ ]
439
463
 
440
464
  # Get motif
441
465
  motif = extended_target_seq[
@@ -448,15 +472,15 @@ def parse_region_worker(args: tuple) -> dict[str, Any]:
448
472
 
449
473
  # Calculate counts
450
474
  # u = unconverted (reference base), m = mutation (mutation base only), o = others
451
- u0 = drop_count[ref_base]
452
- m0 = drop_count[mut_base]
453
- o0 = drop_count.total() - u0 - m0
454
- u1 = clean_count[ref_base]
455
- m1 = clean_count[mut_base]
456
- o1 = clean_count.total() - u1 - m1
457
- u2 = unc_count[ref_base]
458
- m2 = unc_count[mut_base]
459
- o2 = unc_count.total() - u2 - m2
475
+ u0 = low_quality_count[ref_base]
476
+ m0 = low_quality_count[mut_base]
477
+ o0 = low_quality_count.total() - u0 - m0
478
+ u1 = high_conversion_count[ref_base]
479
+ m1 = high_conversion_count[mut_base]
480
+ o1 = high_conversion_count.total() - u1 - m1
481
+ u2 = insufficient_conversion_count[ref_base]
482
+ m2 = insufficient_conversion_count[mut_base]
483
+ o2 = insufficient_conversion_count.total() - u2 - m2
460
484
 
461
485
  if u1 + m1 + u2 + m2 > 0:
462
486
  site_info = [region_chrom, pos + 1, strand_symbol, motif]
@@ -534,8 +558,8 @@ def count_mutations(
534
558
  save_rest: Whether to save additional statistics (default: False)
535
559
  region: Genomic region to process (e.g., 'chr1:1000000-2000000')
536
560
  pad: Motif half-window padding around site (default: 15)
537
- trim_start: Trim bases at read 5' end when counting (default: 2)
538
- trim_end: Trim bases at read 3' end when counting (default: 2)
561
+ trim_start: Number of bases to trim from read 5' end (fragment orientation) (default: 2)
562
+ trim_end: Number of bases to trim from read 3' end (fragment orientation) (default: 2)
539
563
  max_unc: Max unconverted threshold (Zf) to consider converted (default: 3)
540
564
  min_con: Min converted threshold (Yf) to consider converted (default: 1)
541
565
  max_sub: Max substitutions (NS) to consider mapped (default: 1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: countmut
3
- Version: 0.0.2
3
+ Version: 0.0.4
4
4
  Summary: Ultra-fast strand-aware mutation counter
5
5
  Author-email: Ye Chang <yech1990@gmail.com>
6
6
  License-Expression: MIT
@@ -41,13 +41,13 @@ Dynamic: license-file
41
41
  [![Downloads](https://img.shields.io/pepy/dt/countmut)](https://pepy.tech/project/countmut)
42
42
  [![Development Status](https://img.shields.io/badge/status-alpha-orange.svg)](https://github.com/y9c/countmut)
43
43
 
44
- **Ultra-fast strand-aware mutation counter**
44
+ > **Ultra-fast strand-aware mutation counter for bisulfite sequencing analysis**
45
45
 
46
- CountMut counts mutations from bisulfite sequencing / CAM-seq / GLORI-seq / eTAM-seq BAM files with parallel processing, quality-based mate overlap deduplication, and optimized file I/O.
46
+ CountMut is a high-performance tool for counting mutations from bisulfite sequencing BAM files (BS-seq, CAM-seq, GLORI-seq, eTAM-seq). It features parallel processing, quality-based mate overlap deduplication, and optimized file I/O for maximum speed.
47
47
 
48
48
  ## Features
49
49
 
50
- - 🚀 **Ultra-Fast**: Direct FASTA index reading, shared file handles, BGZF multi-threading
50
+ - 🚀 **Ultra-Fast**: Call mutation without pileup reads
51
51
  - 🧬 **Bisulfite Support**: NS, Zf, Yf tag filtering for conversion analysis
52
52
  - 🎯 **Accurate**: Quality-based mate overlap deduplication prevents double-counting
53
53
  - ⚡ **Parallel**: Multi-threaded genomic window processing
@@ -72,51 +72,68 @@ countmut -i input.bam -r reference.fa -o mutations.tsv --ref-base T --mut-base C
72
72
  countmut -i input.bam -r reference.fa -o mutations.tsv -t 8 --max-unc 5 --min-con 2
73
73
  ```
74
74
 
75
- ## Key Options
75
+ ## Options
76
76
 
77
+ **Input/Output**
77
78
  ```bash
78
- Required:
79
- -i, --input PATH Input BAM file
80
- -r, --reference PATH Reference FASTA file
81
-
82
- Output:
83
- -o, --output PATH Output TSV file (default: stdout)
84
-
85
- Mutation:
86
- --ref-base TEXT Reference base [default: A]
87
- --mut-base TEXT Mutation base [default: G]
88
- --strand [both|forward|reverse] Strand processing [default: both]
89
- --region TEXT Specific region (e.g., 'chr1:1000000-2000000')
90
-
91
- Performance:
92
- -t, --threads INTEGER Number of threads [default: auto]
93
- -b, --bin-size INTEGER Genomic bin size [default: 10000]
94
-
95
- Filtering (Bisulfite):
96
- --pad INTEGER Motif window padding [default: 15]
97
- --trim-start INTEGER Trim 5' bases [default: 2]
98
- --trim-end INTEGER Trim 3' bases [default: 2]
99
- --max-unc INTEGER Max unconverted (Zf) [default: 3]
100
- --min-con INTEGER Min converted (Yf) [default: 1]
101
- --max-sub INTEGER Max substitutions (NS) [default: 1]
102
- --min-baseq INTEGER Min base quality (Phred) [default: 20]
103
- --min-mapq INTEGER Min mapping quality (MAPQ) [default: 0]
79
+ -i, --input PATH Input BAM file (coordinate-sorted) [required]
80
+ -r, --reference PATH Reference FASTA file [required]
81
+ -o, --output PATH Output TSV file (default: stdout)
82
+ -f, --force Overwrite output without prompting
104
83
  ```
105
84
 
106
- **Note**: BAM must have NS, Zf, and Yf tags for bisulfite analysis.
85
+ **Mutation Analysis**
86
+ ```bash
87
+ --ref-base TEXT Reference base to count from [default: A]
88
+ --mut-base TEXT Mutation base to count [default: G]
89
+ --strand TEXT Strand: both/forward/reverse [default: both]
90
+ --region TEXT Genomic region (e.g., 'chr1:1000000-2000000')
91
+ ```
107
92
 
108
- ## Output Format
93
+ **Performance**
94
+ ```bash
95
+ -t, --threads INTEGER Number of parallel threads [default: auto]
96
+ -b, --bin-size INTEGER Genomic bin size in bp [default: 10000]
97
+ ```
109
98
 
110
- TSV file with columns:
111
- - `chrom`, `pos`, `strand`, `motif` - Position and sequence context
112
- - `u0`, `u1`, `u2` - Unconverted (reference base) counts [drop, clean, unconverted]
113
- - `m0`, `m1`, `m2` - Mutation (mutation base only) counts [drop, clean, unconverted]
114
- - `o0`, `o1`, `o2` - Other bases counts [drop, clean, unconverted] (only with `--save-rest`)
99
+ **Quality Filters**
100
+ ```bash
101
+ --min-baseq INTEGER Min base quality (Phred score) [default: 20]
102
+ --min-mapq INTEGER Min mapping quality (MAPQ) [default: 0]
103
+ --max-sub INTEGER Max substitutions (NS tag) [default: 1]
104
+ --trim-start INTEGER Trim N bases from read 5' end (fragment orientation) [default: 2]
105
+ --trim-end INTEGER Trim N bases from read 3' end (fragment orientation) [default: 2]
106
+ --max-unc INTEGER Max unconverted (Zf tag) [default: 3]
107
+ --min-con INTEGER Min converted (Yf tag) [default: 1]
108
+ ```
109
+
110
+ **Output Records**
111
+ ```bash
112
+ -p, --pad INTEGER Motif window half-size [default: 15]
113
+ -s, --save-rest Include other bases (o0, o1, o2 columns)
114
+ ```
115
+
116
+ > **Note**: BAM files must have **NS**, **Zf**, and **Yf** tags (essential for bisulfite analysis).
117
+ > Indices (.bai, .fai) are created automatically if missing.
118
+
119
+ ## Output Format
115
120
 
116
- Where:
117
- - **drop** (x0): Bases failing quality filters (internal position, mismatch, mapq, baseq)
118
- - **clean** (x1): High-quality bases passing all filters
119
- - **unconverted** (x2): Bases in unconverted reads (high Zf or low Yf)
121
+ TSV file with the following columns:
122
+
123
+ | Column | Description |
124
+ |--------|-------------|
125
+ | `chrom` | Chromosome name |
126
+ | `pos` | Genomic position (1-based) |
127
+ | `strand` | Strand (+ or -) |
128
+ | `motif` | Sequence context (2×pad+1 bp window) |
129
+ | `u0`, `u1`, `u2` | **Unconverted** (reference base) counts |
130
+ | `m0`, `m1`, `m2` | **Mutation** (mutation base only) counts |
131
+ | `o0`, `o1`, `o2` | **Other bases** counts (with `--save-rest`) |
132
+
133
+ **Count categories** (x0, x1, x2):
134
+ - **x0 (low quality)**: Bases failing quality filters (trim region, max-sub, min-mapq, min-baseq)
135
+ - **x1 (high conversion)**: Bases from reads with high conversion efficiency (low Zf and high Yf)
136
+ - **x2 (insufficient conversion)**: Bases from reads with insufficient conversion efficiency (high Zf or low Yf)
120
137
 
121
138
 
122
139
  &nbsp;
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "countmut"
7
- version = "0.0.2"
7
+ version = "0.0.4"
8
8
  description = "Ultra-fast strand-aware mutation counter"
9
9
  readme = "README.md"
10
10
  license = "MIT"
countmut-0.0.2/README.md DELETED
@@ -1,100 +0,0 @@
1
- # CountMut
2
-
3
- [![Pypi Releases](https://img.shields.io/pypi/v/countmut.svg)](https://pypi.python.org/pypi/countmut)
4
- [![Downloads](https://img.shields.io/pepy/dt/countmut)](https://pepy.tech/project/countmut)
5
- [![Development Status](https://img.shields.io/badge/status-alpha-orange.svg)](https://github.com/y9c/countmut)
6
-
7
- **Ultra-fast strand-aware mutation counter**
8
-
9
- CountMut counts mutations from bisulfite sequencing / CAM-seq / GLORI-seq / eTAM-seq BAM files with parallel processing, quality-based mate overlap deduplication, and optimized file I/O.
10
-
11
- ## Features
12
-
13
- - 🚀 **Ultra-Fast**: Direct FASTA index reading, shared file handles, BGZF multi-threading
14
- - 🧬 **Bisulfite Support**: NS, Zf, Yf tag filtering for conversion analysis
15
- - 🎯 **Accurate**: Quality-based mate overlap deduplication prevents double-counting
16
- - ⚡ **Parallel**: Multi-threaded genomic window processing
17
- - 🔧 **Flexible**: Configurable filtering, strand-specific processing, auto-indexing
18
-
19
- ## Installation
20
-
21
- ```bash
22
- pip install countmut
23
- ```
24
-
25
- ## Quick Start
26
-
27
- ```bash
28
- # Basic usage - auto-creates indices if needed
29
- countmut -i input.bam -r reference.fa -o mutations.tsv
30
-
31
- # Count T→C mutations (common in bisulfite sequencing)
32
- countmut -i input.bam -r reference.fa -o mutations.tsv --ref-base T --mut-base C
33
-
34
- # With custom threads and filtering
35
- countmut -i input.bam -r reference.fa -o mutations.tsv -t 8 --max-unc 5 --min-con 2
36
- ```
37
-
38
- ## Key Options
39
-
40
- ```bash
41
- Required:
42
- -i, --input PATH Input BAM file
43
- -r, --reference PATH Reference FASTA file
44
-
45
- Output:
46
- -o, --output PATH Output TSV file (default: stdout)
47
-
48
- Mutation:
49
- --ref-base TEXT Reference base [default: A]
50
- --mut-base TEXT Mutation base [default: G]
51
- --strand [both|forward|reverse] Strand processing [default: both]
52
- --region TEXT Specific region (e.g., 'chr1:1000000-2000000')
53
-
54
- Performance:
55
- -t, --threads INTEGER Number of threads [default: auto]
56
- -b, --bin-size INTEGER Genomic bin size [default: 10000]
57
-
58
- Filtering (Bisulfite):
59
- --pad INTEGER Motif window padding [default: 15]
60
- --trim-start INTEGER Trim 5' bases [default: 2]
61
- --trim-end INTEGER Trim 3' bases [default: 2]
62
- --max-unc INTEGER Max unconverted (Zf) [default: 3]
63
- --min-con INTEGER Min converted (Yf) [default: 1]
64
- --max-sub INTEGER Max substitutions (NS) [default: 1]
65
- --min-baseq INTEGER Min base quality (Phred) [default: 20]
66
- --min-mapq INTEGER Min mapping quality (MAPQ) [default: 0]
67
- ```
68
-
69
- **Note**: BAM must have NS, Zf, and Yf tags for bisulfite analysis.
70
-
71
- ## Output Format
72
-
73
- TSV file with columns:
74
- - `chrom`, `pos`, `strand`, `motif` - Position and sequence context
75
- - `u0`, `u1`, `u2` - Unconverted (reference base) counts [drop, clean, unconverted]
76
- - `m0`, `m1`, `m2` - Mutation (mutation base only) counts [drop, clean, unconverted]
77
- - `o0`, `o1`, `o2` - Other bases counts [drop, clean, unconverted] (only with `--save-rest`)
78
-
79
- Where:
80
- - **drop** (x0): Bases failing quality filters (internal position, mismatch, mapq, baseq)
81
- - **clean** (x1): High-quality bases passing all filters
82
- - **unconverted** (x2): Bases in unconverted reads (high Zf or low Yf)
83
-
84
-
85
- &nbsp;
86
-
87
- <p align="center">
88
- <img
89
- src="https://raw.githubusercontent.com/y9c/y9c/master/resource/footer_line.svg?sanitize=true"
90
- />
91
- </p>
92
- <p align="center">
93
- Copyright &copy; 2025-present
94
- <a href="https://github.com/y9c" target="_blank">Chang Y</a>
95
- </p>
96
- <p align="center">
97
- <a href="https://github.com/y9c/countmut/blob/main/LICENSE">
98
- <img src="https://img.shields.io/static/v1.svg?style=for-the-badge&label=License&message=MIT&logoColor=d9e0ee&colorA=282a36&colorB=c678dd" />
99
- </a>
100
- </p>
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes