Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Mark and remove PCR/optical duplicates using samtools fixmate and markdup. Use when preparing alignments for variant calling or when duplicate reads would bias analysis.
.claude/skills/bio-duplicate-handling/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 63% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 16% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 87% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 64% | 0% |
| case-22 | ✓→✓ | = Same ✓ | 105% | 0% |
<!--
#
#
-->
Mark and remove PCR/optical duplicates using samtools.
PCR duplicates are identical copies of the same original molecule, created during library preparation. They:
Optical duplicates are clusters read multiple times due to their proximity on the flowcell.
The standard samtools workflow requires multiple steps:
bash# 1. Sort by name (required for fixmate) samtools sort -n -o namesort.bam input.bam # 2. Add mate information with fixmate samtools fixmate -m namesort.bam fixmate.bam # 3. Sort by coordinate (required for markdup) samtools sort -o coordsort.bam fixmate.bam # 4. Mark duplicates samtools markdup coordsort.bam marked.bam # 5. Index result samtools index marked.bam
bashsamtools sort -n input.bam | \ samtools fixmate -m - - | \ samtools sort - | \ samtools markdup - marked.bam samtools index marked.bam
Adds mate information required by markdup. Must be run on name-sorted BAM.
bashsamtools fixmate namesorted.bam fixmate.bam
bash# Required for markdup to work correctly samtools fixmate -m namesorted.bam fixmate.bam
bashsamtools fixmate -m -@ 4 namesorted.bam fixmate.bam
bashsamtools fixmate -r -m namesorted.bam fixmate.bam
Marks or removes duplicate alignments. Requires coordinate-sorted BAM with mate tags from fixmate.
bashsamtools markdup input.bam marked.bam
bashsamtools markdup -r input.bam deduped.bam
bashsamtools markdup -s input.bam marked.bam 2> markdup_stats.txt
bash# Set pixel distance for optical duplicate detection (default: 100) samtools markdup -d 2500 input.bam marked.bam
bashsamtools markdup -@ 4 input.bam marked.bam
bashsamtools markdup -f stats.txt input.bam marked.bam
bashsamtools flagstat marked.bam # Look for "duplicates" line
bash# Count reads with duplicate flag samtools view -c -f 1024 marked.bam
bashtotal=$(samtools view -c marked.bam) dups=$(samtools view -c -f 1024 marked.bam) echo "scale=2; $dups * 100 / $total" | bc
pythonimport pysam # Sort by name pysam.sort('-n', '-o', 'namesort.bam', 'input.bam') # Fixmate pysam.fixmate('-m', 'namesort.bam', 'fixmate.bam') # Sort by coordinate pysam.sort('-o', 'coordsort.bam', 'fixmate.bam') # Mark duplicates pysam.markdup('coordsort.bam', 'marked.bam') # Index pysam.index('marked.bam')
pythonimport pysam with pysam.AlignmentFile('marked.bam', 'rb') as bam: total = 0 duplicates = 0 for read in bam: total += 1 if read.is_duplicate: duplicates += 1 print(f'Total: {total}') print(f'Duplicates: {duplicates}') print(f'Rate: {duplicates/total*100:.2f}%')
pythonimport pysam with pysam.AlignmentFile('marked.bam', 'rb') as infile: with pysam.AlignmentFile('nodup.bam', 'wb', header=infile.header) as outfile: for read in infile: if not read.is_duplicate: outfile.write(read)
pythonimport pysam from collections import defaultdict def simple_markdup(input_bam, output_bam): seen = defaultdict(set) with pysam.AlignmentFile(input_bam, 'rb') as infile: with pysam.AlignmentFile(output_bam, 'wb', header=infile.header) as outfile: for read in infile: if read.is_unmapped: outfile.write(read) continue key = (read.reference_id, read.reference_start, read.is_reverse, read.next_reference_id, read.next_reference_start) if key in seen: read.is_duplicate = True else: seen[key].add(read.query_name) outfile.write(read) simple_markdup('sorted.bam', 'marked.bam')
Some aligners can mark duplicates directly:
bashbwa-mem2 mem ref.fa R1.fq R2.fq | \ samblaster | \ samtools sort -o marked.bam
bashjava -jar picard.jar MarkDuplicates \ I=input.bam \ O=marked.bam \ M=metrics.txt
| Task | Command | |------|---------| | Full workflow | sort -n \| fixmate -m \| sort \| markdup | | Mark duplicates | samtools markdup in.bam out.bam | | Remove duplicates | samtools markdup -r in.bam out.bam | | Count duplicates | samtools view -c -f 1024 marked.bam | | View non-duplicates | samtools view -F 1024 marked.bam | | Get stats | samtools markdup -s in.bam out.bam |
| Flag | Value | Meaning | |------|-------|---------| | 0x400 | 1024 | PCR or optical duplicate |
bash# View only duplicates samtools view -f 1024 marked.bam # View non-duplicates only samtools view -F 1024 marked.bam # Count non-duplicates samtools view -c -F 1024 marked.bam
| Error | Cause | Solution | |-------|-------|----------| | mate not found | Input not name-sorted | Run samtools sort -n first | | no MC tag | fixmate not run with -m | Re-run fixmate with -m flag | | not coordinate sorted | Input to markdup not sorted | Run samtools sort after fixmate |
<!-- AUTHOR_SIGNATURE: 9a7f3c2e-MD-BABU-MIA-2026-MSSM-SECURE -->
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-22 | pass→pass | 7,136 | 3,410 | -52% | 1 | 1 | 0% | 1,237 | 2,537 | +105% | 0 | 0 | — |
case-20 | pass→pass | 3,456 | 4,709 | +36% | 1 | 1 | 0% | 614 | 2,782 | +353% | 0 | 0 | — |
case-21 | pass→pass | 8,030 | 5,851 | -27% | 1 | 1 | 0% | 1,566 | 3,054 | +95% | 0 | 0 | — |
case-01 | fail→pass | 8,235 | 4,019 | -51% | 1 | 1 | 0% | 1,714 | 2,791 | +63% | 0 | 0 | — |
case-02 | pass→pass | 3,901 | 2,508 | -36% | 1 | 1 | 0% | 663 | 2,321 | +250% | 0 | 0 | — |
case-03 | pass→pass | 8,505 | 5,475 | -36% | 1 | 1 | 0% | 1,600 | 3,111 | +94% | 0 | 0 | — |
case-04 | pass→pass | 6,352 | 2,472 | -61% | 1 | 1 | 0% | 1,074 | 2,366 | +120% | 0 | 0 | — |
case-05 | pass→pass | 4,630 | 2,599 | -44% | 1 | 1 | 0% | 858 | 2,392 | +179% | 0 | 0 | — |
case-06 | pass→pass | 5,098 | 2,738 | -46% | 1 | 1 | 0% | 929 | 2,398 | +158% | 0 | 0 | — |
case-07 | pass→pass | 7,204 | 5,703 | -21% | 1 | 1 | 0% | 1,357 | 3,072 | +126% | 0 | 0 | — |
case-08 | fail→pass | 14,280 | 6,802 | -52% | 1 | 1 | 0% | 2,885 | 3,346 | +16% | 0 | 0 | — |
case-09 | pass→pass | 14,012 | 8,999 | -36% | 1 | 1 | 0% | 2,373 | 3,630 | +53% | 0 | 0 | — |
case-10 | fail→pass | 9,097 | 11,257 | +24% | 1 | 1 | 0% | 1,830 | 3,414 | +87% | 0 | 0 | — |
case-11 | pass→pass | 6,658 | 4,026 | -40% | 1 | 1 | 0% | 1,329 | 2,691 | +102% | 0 | 0 | — |
case-12 | pass→pass | 8,778 | 4,587 | -48% | 1 | 1 | 0% | 1,632 | 2,889 | +77% | 0 | 0 | — |
case-17 | fail→pass | 14,358 | 13,714 | -4% | 1 | 1 | 0% | 2,763 | 4,533 | +64% | 0 | 0 | — |
case-18 | pass→pass | 3,342 | 1,880 | -44% | 1 | 1 | 0% | 419 | 2,244 | +436% | 0 | 0 | — |
case-19 | pass→pass | 2,961 | 2,546 | -14% | 1 | 1 | 0% | 569 | 2,426 | +326% | 0 | 0 | — |
case-13 | pass→pass | 7,635 | 4,150 | -46% | 1 | 1 | 0% | 1,422 | 2,721 | +91% | 0 | 0 | — |
case-14 | pass→pass | 4,526 | 2,802 | -38% | 1 | 1 | 0% | 776 | 2,326 | +200% | 0 | 0 | — |
case-15 | pass→pass | 10,395 | 2,067 | -80% | 1 | 1 | 0% | 1,166 | 2,246 | +93% | 0 | 0 | — |
case-16 | pass→pass | 7,828 | 3,967 | -49% | 1 | 1 | 0% | 1,269 | 2,649 | +109% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +18 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
| Model | Method | Date | Lift |
|---|---|---|---|
| gemini-3.6-flash | verified | 7/26/2026 | +9% |
Other measured skills in the registry, with their headline benchmark lift.