🖥️

Command line & samtools

The shell basics plus the genomics toolkit you use daily.

Navigating & files

pwdwhere am I (print working dir)
ls -lh / ls -alist (sizes) / include hidden
cd dir / cd .. / cd ~enter / up one / home
mkdir -p a/bmake folders + parents
cp -r src dstcopy (-r for folders)
mv src dstmove / rename
rm file / rm -r dirdelete (no undo!)
chmod +x script.shmake a script executable
find . -name "*.bam"find files by pattern

Text & data wrangling

cat / less fileprint / page (q quits)
head -n 20 / tail -n 5first / last lines
wc -l filecount lines
grep "pat" filefind matching lines (-v invert, -c count)
cut -f1,3pick columns (tab-delim)
sort | uniq -csort then count duplicates
awk '{print $1}'print column 1
sed 's/a/b/g'find & replace

Pipes, redirects & survival

cmd | cmd2pipe output into next command
cmd > out / >> outwrite / append to file
cmd && cmd2run cmd2 only if cmd succeeds
*.fastq wildcardmatch all matching files
Tab / Ctrl-C / autocomplete / stop / last cmd
gzip / tar -xzvfcompress / extract archive

samtools (BAM/SAM)

samtools view -b in.sam > out.bamSAM → BAM (-b)
samtools sort in.bam -o s.bamsort by position (do before index)
samtools index s.bambuild the .bai index
samtools flagstat in.bammapping summary (% mapped…)
samtools stats in.bamdetailed QC statistics
samtools idxstats in.bamreads per chromosome
samtools view in.bam chr1:1-1000reads in a region (needs index)
samtools view -f 4 / -F 4keep / exclude by FLAG (4 = unmapped)
samtools view -q 30filter by mapping quality
samtools markdupmark PCR duplicates
samtools depth / coverageper-base depth / per-region coverage
samtools merge out.bam *.bammerge BAMs
samtools faidx ref.faindex a FASTA (.fai)

bcftools (variants)

bcftools mpileup -f ref.fa in.bampile up reads vs reference
... | bcftools call -mv -Oz -o out.vcf.gzcall variants (-m multiallelic, -v variant sites, -Oz bgzip)
bcftools view -f PASS in.vcf.gzview / filter records
bcftools norm -f ref.fanormalize & left-align indels
bcftools filter -e 'QUAL<20'exclude by expression
bcftools index in.vcf.gzindex a bgzipped VCF
bcftools stats in.vcf.gzsummary (ts/tv, counts…)
⚠️ Order matters: sort → index before any region query. You cannot index an unsorted BAM, and most tools need the matching .bai/.fai.
🧬

File formats

What each bioinformatics file holds, and the traps.

FASTA — sequences

>seq_id  optional description
ACGTACGTACGTACGT...

Header line starts with >; sequence may wrap over many lines. DNA, RNA, or protein. Index with samtools faidx.

FASTQ — reads + quality

@read_id              # 1 name (@)
ACGTACGTACGT          # 2 sequence
+                     # 3 separator (+)
IIIIFFFF;;;;          # 4 quality (1 char/base)
EncodingPhred+33 (Sanger/Illumina 1.8+); ASCII − 33 = Q
Q201 error in 100 (99%)
Q301 in 1,000 (99.9%) — common target
Q401 in 10,000 (99.99%)

GTF / GFF — annotation

9 tab-separated columns (seqname, source, feature, start, end, score, strand, frame, attributes). Maps coordinates to genes/exons/CDS. 1-based.

SAM / BAM — aligned reads

SAM = text, BAM = compressed binary. 11 mandatory columns:

QNAMEread name
FLAGbitwise (4 = unmapped, 16 = reverse, 2 = proper pair, 1024 = duplicate)
RNAME / POSreference & 1-based position
MAPQmapping quality (Phred)
CIGARM match, I insert, D delete, S soft-clip, N skip
SEQ / QUALread bases & qualities

VCF — variants

CHROM / POSlocation (1-based)
REF / ALTreference vs alternate allele
QUAL / FILTERconfidence / PASS or fail
INFOsite annotations (DP, AF…)
FORMAT + samplegenotype: 0/0 hom-ref, 0/1 het, 1/1 hom-alt

BED — regions

chr1   200   1000   name   # start is 0-based
.fai / .baiFASTA / BAM index files
.vcf.gz + .tbibgzipped VCF + tabix index
⚠️ Coordinate trap: BED is 0-based (half-open); GTF, SAM, VCF are 1-based. Mixing them shifts everything by one.
📦

conda & Git

Environments that never break, and version control you can trust.

conda — one-time setup

conda config --add channels conda-forge
conda config --add channels bioconda
conda config --set channel_priority strict

conda — daily use

conda create -n env python=3.11new environment
conda activate envturn it on
conda install -c bioconda samtoolsinstall a tool
conda list / env listpackages / all envs
conda search bwafind a package
conda deactivateturn off
conda env remove -n envdelete an environment
conda env export --from-history > environment.ymlsave a portable spec
conda env create -f environment.ymlrecreate anywhere
mamba install ...faster drop-in for conda
One env per project. Never install into base. Pin versions that affect results.

Git — the daily loop

git clone <url>copy a repo locally
git statuswhat changed
git add file / git add .stage changes
git commit -m "msg"save a snapshot
git push / git pullsend to / get from GitHub
git log --onelinehistory
git diffsee unstaged changes

Git — branches & fixes

git checkout -b featurecreate + switch branch
git merge featuremerge into current branch
git stash / stash popshelve / restore changes
git restore filediscard local edits to a file
git push -u origin mainfirst push of a branch
# .gitignore — never commit these
*.bam
*.fastq.gz
data/
.env        # secrets / API keys
__pycache__/
The loop: edit → add → commit → push. Commit small and often, with clear messages.
📊

Stats & DESeq2

The numbers you must interpret, and the workflow.

Statistics quick reference

p-valueP(data this extreme | null true). Not the chance the result is real.
padj / FDRp corrected for many tests (Benjamini-Hochberg). Filter on this, not raw p.
Bonferronistrict: significant if p < 0.05 / (number of tests)
effect sizehow big the change is (log2FC, Cohen's d). Report with the p-value.
log2FC1 = 2×, 2 = 4×, 3 = 8×; negative = down
multiple testing20,000 genes at p<0.05 ≈ 1,000 false hits by chance
neg. binomialthe count model RNA-seq uses (over-dispersed); don't t-test raw counts
A microscopic p-value with a near-zero effect size is significant but biologically boring. Use both axes (that's what a volcano plot shows).

Volcano thresholds (typical)

padj < 0.05significance cutoff (y-axis)
|log2FC| > 1at least 2-fold change (x-axis)

DESeq2 workflow (R)

# 1. load
library(DESeq2)

# 2. build: counts + sample table + design
dds <- DESeqDataSetFromMatrix(
  countData = counts,
  colData   = coldata,
  design    = ~ condition)

# 3. (optional) pre-filter low counts
dds <- dds[rowSums(counts(dds)) >= 10, ]

# 4. run the analysis
dds <- DESeq(dds)

# 5. results: treated vs control
res <- results(dds,
  contrast = c("condition","treated","control"))

# 6. shrink LFCs for ranking & plots
res <- lfcShrink(dds, coef=2, type="apeglm")

# 7. significant genes
sig <- subset(res, padj < 0.05 & abs(log2FoldChange) > 1)

# 8. handy extras
plotMA(res)                 # MA plot
vsd <- vst(dds)             # for PCA / heatmaps
counts(dds, normalized=TRUE) # normalized counts
Same idea in edgeR: DGEList → calcNormFactors → estimateDisp → glmQLFit → glmQLFTest.