Generate pileup data showing all reads covering each genomic position for variant calling and position-level analysis.
# samtools and bcftools
conda install -c bioconda samtools bcftools
# pysam
pip install pysam
# Reference must be indexed
samtools faidx reference.fa
Tell your AI agent what you want to do: - "Generate pileup for variant calling" - "Count alleles at a specific position" - "Call variants using bcftools" - "Check read support at position chr1:1000000"
"Generate text pileup for my BAM file"
"Create pileup for chr1:1000000-2000000 only"
"Generate pileup with quality filtering (MAPQ 20, baseQ 20)"
"Call variants using samtools mpileup and bcftools"
"Generate BCF file for efficient variant calling"
"Call variants from multiple samples together"
"Count allele frequencies at position chr1:1000000"
"Find all positions with alternative alleles above 10%"
"Check read support for known variants"
chr1 1000 A 15 ....,,..,..G... FFFFFFFGFFFFFFFF
| Column | Value | Description |
|---|---|---|
| 1 | chr1 | Chromosome |
| 2 | 1000 | Position (1-based) |
| 3 | A | Reference base |
| 4 | 15 | Depth |
| 5 | ....,,..,..G... | Read bases |
| 6 | FFF... | Base qualities (ASCII - 33) |
| Symbol | Meaning |
|---|---|
. |
Matches reference (forward strand) |
, |
Matches reference (reverse strand) |
A/C/G/T |
Mismatch (uppercase = forward) |
a/c/g/t |
Mismatch (lowercase = reverse) |
* |
Deletion |
+NNNbases |
Insertion |
-NNNbases |
Deletion |
^Q |
Start of read (Q = MAPQ) |
$ |
End of read |
samtools mpileup -f reference.fa input.bam > pileup.txt
samtools mpileup -f reference.fa -q 20 -Q 20 input.bam
-q 20: Minimum mapping quality-Q 20: Minimum base qualitysamtools mpileup -f reference.fa -r chr1:1000000-2000000 input.bam
samtools mpileup -f reference.fa -l targets.bed input.bam
samtools mpileup -g/-u (BCF output) was deprecated in samtools 1.9 and removed in 1.15; use bcftools mpileup for variant calling.
# Modern single-sample pipeline (with quality filtering and per-sample annotations)
bcftools mpileup -f reference.fa -d 1000000 -q 20 -Q 20 -a FORMAT/AD,FORMAT/DP input.bam | \
bcftools call -mv -Oz -o variants.vcf.gz
bcftools index variants.vcf.gz
# BCF intermediate (more efficient than re-piping)
bcftools mpileup -f reference.fa -d 1000000 -Ob -o raw.bcf input.bam
bcftools call -mv raw.bcf -o variants.vcf
# Multi-sample calling
bcftools mpileup -f reference.fa -d 1000000 -a FORMAT/AD,FORMAT/DP sample1.bam sample2.bam sample3.bam | \
bcftools call -mv -Oz -o variants.vcf.gz
# Limit max depth (reduces memory)
samtools mpileup -f reference.fa -d 1000 input.bam
# Parallel by chromosome
for chr in chr1 chr2 chr3; do
bcftools mpileup -f reference.fa -r "$chr" -d 1000000 input.bam | \
bcftools call -mv -Oz -o "${chr}.vcf.gz" &
done
wait
bcftools concat -Oz -o all.vcf.gz chr*.vcf.gz && bcftools index all.vcf.gz
import pysam
with pysam.AlignmentFile('input.bam', 'rb') as bam:
for pileup_column in bam.pileup('chr1', 1000000, 1001000, truncate=True):
pos = pileup_column.pos
depth = pileup_column.n
print(f'chr1:{pos+1} depth={depth}')
import pysam
from collections import Counter
def count_alleles(bam_path, chrom, pos, min_qual=20):
alleles = Counter()
with pysam.AlignmentFile(bam_path, 'rb') as bam:
for pileup_column in bam.pileup(chrom, pos, pos + 1, truncate=True, min_base_quality=min_qual):
if pileup_column.pos != pos:
continue
for pileup_read in pileup_column.pileups:
if pileup_read.is_del:
alleles['DEL'] += 1
elif pileup_read.is_refskip:
pass
else:
qpos = pileup_read.query_position
base = pileup_read.alignment.query_sequence[qpos].upper()
alleles[base] += 1
return dict(alleles)
counts = count_alleles('input.bam', 'chr1', 1000000)
total = sum(counts.values())
for base, count in sorted(counts.items(), key=lambda x: -x[1]):
print(f'{base}: {count} ({count/total*100:.1f}%)')
import pysam
from collections import Counter
def find_variants(bam_path, ref_path, chrom, start, end, min_depth=10, min_alt_freq=0.1):
variants = []
with pysam.AlignmentFile(bam_path, 'rb') as bam:
with pysam.FastaFile(ref_path) as ref:
for pileup_column in bam.pileup(chrom, start, end, truncate=True, min_base_quality=20):
pos = pileup_column.pos
ref_base = ref.fetch(chrom, pos, pos + 1).upper()
alleles = Counter()
for pileup_read in pileup_column.pileups:
if pileup_read.is_del or pileup_read.is_refskip:
continue
qpos = pileup_read.query_position
base = pileup_read.alignment.query_sequence[qpos].upper()
alleles[base] += 1
total = sum(alleles.values())
if total < min_depth:
continue
for base, count in alleles.items():
if base != ref_base:
freq = count / total
if freq >= min_alt_freq:
variants.append({'chrom': chrom, 'pos': pos + 1, 'ref': ref_base, 'alt': base, 'depth': total, 'alt_count': count, 'freq': freq})
return variants
import pysam
with pysam.AlignmentFile('input.bam', 'rb') as bam:
for pileup_column in bam.pileup('chr1', 1000000, 1000001, truncate=True):
print(f'Position {pileup_column.pos}, depth {pileup_column.n}')
for pileup_read in pileup_column.pileups:
if pileup_read.is_del:
print(f' {pileup_read.alignment.query_name}: DELETION')
continue
qpos = pileup_read.query_position
base = pileup_read.alignment.query_sequence[qpos]
qual = pileup_read.alignment.query_qualities[qpos]
strand = '-' if pileup_read.alignment.is_reverse else '+'
print(f' {pileup_read.alignment.query_name}: {base} Q{qual} {strand}')
Reference doesn't match the BAM:
samtools view -H input.bam | grep "^@SQ" # Check BAM chromosomes
grep "^>" reference.fa # Check reference chromosomes
samtools view input.bam chr1:1000-2000 | headls reference.fa.faiReduce max depth:
samtools mpileup -f reference.fa -d 500 input.bam
-l targets.bed-q 20 -Q 20) for variant callingtruncate=True in pysam for exact region boundaries-d flag for high-coverage samples