
Bio Workflows Microbiome Pipeline
- 5 installs
- 1.1k repo stars
- Updated July 25, 2026
- gptomics/bioskills
Run an end-to-end 16S/ITS amplicon workflow from FASTQ through DADA2 ASV inference to differential abundance.
About
Orchestrates DADA2 ASV inference, taxonomy assignment, diversity analysis, and compositional testing with ALDEx2. A developer uses it to process 16S/ITS amplicon data from reads to differential abundance in R.
- DADA2 amplicon processing and taxonomy assignment
- Diversity analysis and ALDEx2 compositional differential abundance
Bio Workflows Microbiome Pipeline by the numbers
- 5 all-time installs (skills.sh)
- Ranked #1,598 of 2,064 Data Science & ML skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/gptomics/bioskills --skill bio-workflows-microbiome-pipelineAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 5 |
|---|---|
| repo stars | ★ 1.1k |
| Last updated | July 25, 2026 |
| Repository | gptomics/bioskills ↗ |
What it does
Run an end-to-end 16S/ITS amplicon workflow from FASTQ through DADA2 ASV inference to differential abundance.
Files
Version Compatibility
Reference examples tested with: DADA2 1.30+, ggplot2 3.5+, phyloseq 1.46+, scanpy 1.10+, vegan 2.6+
Before using code patterns, verify installed versions match. If versions differ:
- R:
packageVersion('<pkg>')then?function_nameto verify parameters
If code throws ImportError, AttributeError, or TypeError, introspect the installed package and adapt the example to match the actual API rather than retrying.
Microbiome Pipeline
"Run end-to-end 16S microbiome analysis from FASTQ" -> Orchestrate DADA2 denoising, taxonomy assignment, alpha/beta diversity (phyloseq), differential abundance (ALDEx2/ANCOM-BC2), and functional prediction (PICRUSt2).
Pipeline Overview
Paired-End FASTQ (16S V4)
│
▼
┌──────────────────────────────────────────────────┐
│ microbiome-pipeline │
├──────────────────────────────────────────────────┤
│ 1. Quality Filtering (DADA2 filterAndTrim) │
│ 2. Error Learning & Denoising │
│ 3. Merge Pairs & Remove Chimeras │
│ 4. Taxonomy Assignment (SILVA) │
│ 5. Create phyloseq Object │
│ 6. Alpha/Beta Diversity │
│ 7. Differential Abundance (ALDEx2) │
│ 8. Visualization & Export │
└──────────────────────────────────────────────────┘
│
▼
ASV Table + Taxonomy + Diversity Plots + Differential TaxaComplete R Workflow
library(dada2)
library(phyloseq)
library(ALDEx2)
library(vegan)
library(ggplot2)
# === CONFIGURATION ===
path <- 'raw_reads'
silva_train <- 'silva_nr99_v138.1_train_set.fa.gz'
silva_species <- 'silva_species_assignment_v138.1.fa.gz'
metadata_file <- 'sample_metadata.csv'
# === 1. READ FILES ===
fnFs <- sort(list.files(path, pattern = '_R1_001.fastq.gz', full.names = TRUE))
fnRs <- sort(list.files(path, pattern = '_R2_001.fastq.gz', full.names = TRUE))
sample_names <- sapply(strsplit(basename(fnFs), '_'), `[`, 1)
# Setup filtered files
filtFs <- file.path('filtered', paste0(sample_names, '_F_filt.fastq.gz'))
filtRs <- file.path('filtered', paste0(sample_names, '_R_filt.fastq.gz'))
# === 2. FILTER & TRIM ===
out <- filterAndTrim(fnFs, filtFs, fnRs, filtRs,
truncLen = c(240, 160), maxN = 0, maxEE = c(2, 2),
truncQ = 2, rm.phix = TRUE, compress = TRUE, multithread = TRUE)
# === 3. LEARN ERRORS & DENOISE ===
errF <- learnErrors(filtFs, multithread = TRUE)
errR <- learnErrors(filtRs, multithread = TRUE)
dadaFs <- dada(filtFs, err = errF, multithread = TRUE)
dadaRs <- dada(filtRs, err = errR, multithread = TRUE)
# === 4. MERGE & CHIMERAS ===
mergers <- mergePairs(dadaFs, filtFs, dadaRs, filtRs, verbose = TRUE)
seqtab <- makeSequenceTable(mergers)
seqtab_nochim <- removeBimeraDenovo(seqtab, method = 'consensus', multithread = TRUE)
# === 5. ASSIGN TAXONOMY ===
taxa <- assignTaxonomy(seqtab_nochim, silva_train, multithread = TRUE)
taxa <- addSpecies(taxa, silva_species)
# === 6. BUILD PHYLOGENETIC TREE (for UniFrac) ===
library(DECIPHER)
library(phangorn)
seqs <- getSequences(seqtab_nochim)
names(seqs) <- paste0('ASV', seq_along(seqs))
alignment <- AlignSeqs(DNAStringSet(seqs), anchor = NA, processors = NULL)
phang_align <- phyDat(as(alignment, 'matrix'), type = 'DNA')
dm <- dist.ml(phang_align)
tree <- NJ(dm)
tree <- midpoint(ladderize(tree))
# === 7. CREATE PHYLOSEQ ===
metadata <- read.csv(metadata_file, row.names = 1)
ps <- phyloseq(otu_table(seqtab_nochim, taxa_are_rows = FALSE),
tax_table(taxa), sample_data(metadata), phy_tree(tree))
taxa_names(ps) <- paste0('ASV', seq(ntaxa(ps)))
# === 8. DIVERSITY ===
# Alpha diversity (including Faith's PD with tree)
library(picante)
alpha_div <- estimate_richness(ps, measures = c('Observed', 'Shannon', 'Simpson'))
faith_pd <- pd(t(otu_table(ps)), phy_tree(ps), include.root = TRUE)
alpha_div$PD <- faith_pd$PD
alpha_div$Group <- sample_data(ps)$Group
# Beta diversity (Bray-Curtis and UniFrac)
bray_dist <- phyloseq::distance(ps, method = 'bray')
unifrac_dist <- UniFrac(ps, weighted = TRUE)
pcoa_bray <- ordinate(ps, method = 'PCoA', distance = bray_dist)
pcoa_unifrac <- ordinate(ps, method = 'PCoA', distance = unifrac_dist)
# PERMANOVA on both metrics
meta_df <- data.frame(sample_data(ps))
permanova_bray <- adonis2(bray_dist ~ Group, data = meta_df, permutations = 999)
permanova_unifrac <- adonis2(unifrac_dist ~ Group, data = meta_df, permutations = 999)
# === 9. DIFFERENTIAL ABUNDANCE ===
# Filter low-abundance taxa
ps_filt <- filter_taxa(ps, function(x) sum(x > 0) > 0.1 * nsamples(ps), TRUE)
# ALDEx2
otu <- as.data.frame(t(otu_table(ps_filt)))
groups <- as.character(sample_data(ps_filt)$Group)
aldex_results <- aldex(otu, groups, mc.samples = 128, test = 'welch', effect = TRUE)
aldex_results$significant <- aldex_results$we.eBH < 0.05 & abs(aldex_results$effect) > 1
# === 10. OUTPUT ===
cat('Pipeline complete!\n')
cat(' ASVs:', ntaxa(ps), '\n')
cat(' Samples:', nsamples(ps), '\n')
cat(' PERMANOVA R2:', round(permanova$R2[1], 3), 'p =', permanova$`Pr(>F)`[1], '\n')
cat(' Differential taxa:', sum(aldex_results$significant), '\n')QC Checkpoints
| Stage | Check | Expected | Action if Failed |
|---|---|---|---|
| Filter | >70% reads pass | >70% | Adjust truncLen/maxEE |
| Merge | >80% pairs merge | >80% | Check amplicon length |
| Chimera | <25% chimeras | <25% | Check PCR cycles |
| Taxonomy | >80% genus assigned | >80% | Try different database |
| Rarefaction | Curves plateau | Plateau | Increase depth |
| PERMANOVA | p < 0.05 | p < 0.05 | Check experimental design |
Output Files
microbiome_results/
├── phyloseq_object.rds # Complete phyloseq
├── asv_table.csv # ASV counts
├── taxonomy.csv # Taxonomic assignments
├── alpha_diversity.csv # Per-sample metrics
├── aldex2_results.csv # Differential taxa
├── read_tracking.csv # Reads per pipeline stage
├── plots/
│ ├── quality_profiles.pdf
│ ├── alpha_diversity.pdf
│ ├── beta_diversity_pcoa.pdf
│ ├── taxonomic_barplot.pdf
│ └── aldex2_effect_plot.pdfWorkflow Variants
ITS Fungal Workflow
# Key differences for ITS:
# 1. No truncLen (variable length amplicons)
out <- filterAndTrim(fnFs, filtFs, fnRs, filtRs, maxN = 0, maxEE = c(2, 2),
truncQ = 2, minLen = 50, rm.phix = TRUE, multithread = TRUE)
# 2. Use UNITE database
taxa <- assignTaxonomy(seqtab_nochim, 'sh_general_release_dynamic_25.07.2023.fasta',
multithread = TRUE)Different 16S Regions
# V3-V4 (~460bp): truncLen = c(280, 200)
# V4 (~253bp): truncLen = c(240, 160)
# V1-V3 (~500bp): truncLen = c(260, 220)GTDB Taxonomy
# For environmental samples, GTDB may be more accurate
taxa <- assignTaxonomy(seqtab_nochim, 'GTDB_bac120_arc53_ssu_r214_fullTaxo.fa.gz',
multithread = TRUE)Related Skills
- microbiome/amplicon-processing - DADA2 details
- microbiome/taxonomy-assignment - Database options, IDTAXA
- microbiome/diversity-analysis - Diversity metrics, Faith's PD
- microbiome/differential-abundance - ALDEx2, ANCOM-BC2
- microbiome/functional-prediction - PICRUSt2 functional analysis
# Reference: DADA2 1.30+, ggplot2 3.5+, phyloseq 1.46+, scanpy 1.10+, vegan 2.6+ | Verify API if version differs
# Complete 16S microbiome workflow: FASTQ to differential abundance
library(dada2)
library(phyloseq)
library(ALDEx2)
library(vegan)
library(ggplot2)
# === CONFIGURATION ===
path <- 'raw_reads'
silva_train <- 'silva_nr99_v138.1_train_set.fa.gz'
silva_species <- 'silva_species_assignment_v138.1.fa.gz'
metadata_file <- 'sample_metadata.csv'
output_dir <- 'microbiome_results'
dir.create(output_dir, showWarnings = FALSE)
dir.create(file.path(output_dir, 'plots'), showWarnings = FALSE)
cat('=== Microbiome Pipeline ===\n')
# === 1. READ FILES ===
cat('\n1. Reading input files...\n')
fnFs <- sort(list.files(path, pattern = '_R1_001.fastq.gz', full.names = TRUE))
fnRs <- sort(list.files(path, pattern = '_R2_001.fastq.gz', full.names = TRUE))
sample_names <- sapply(strsplit(basename(fnFs), '_'), `[`, 1)
cat(' Samples:', length(fnFs), '\n')
# Quality profiles
pdf(file.path(output_dir, 'plots/quality_profiles.pdf'))
plotQualityProfile(fnFs[1:min(2, length(fnFs))])
plotQualityProfile(fnRs[1:min(2, length(fnRs))])
dev.off()
# === 2. FILTER & TRIM ===
cat('\n2. Filtering and trimming...\n')
dir.create('filtered', showWarnings = FALSE)
filtFs <- file.path('filtered', paste0(sample_names, '_F_filt.fastq.gz'))
filtRs <- file.path('filtered', paste0(sample_names, '_R_filt.fastq.gz'))
names(filtFs) <- sample_names
names(filtRs) <- sample_names
out <- filterAndTrim(fnFs, filtFs, fnRs, filtRs, truncLen = c(240, 160),
maxN = 0, maxEE = c(2, 2), truncQ = 2, rm.phix = TRUE,
compress = TRUE, multithread = TRUE)
cat(' Reads passing filter:', round(100 * sum(out[, 2]) / sum(out[, 1]), 1), '%\n')
# === 3. LEARN ERRORS & DENOISE ===
cat('\n3. Learning errors and denoising...\n')
errF <- learnErrors(filtFs, multithread = TRUE)
errR <- learnErrors(filtRs, multithread = TRUE)
dadaFs <- dada(filtFs, err = errF, multithread = TRUE)
dadaRs <- dada(filtRs, err = errR, multithread = TRUE)
# === 4. MERGE & CHIMERAS ===
cat('\n4. Merging pairs and removing chimeras...\n')
mergers <- mergePairs(dadaFs, filtFs, dadaRs, filtRs, verbose = FALSE)
seqtab <- makeSequenceTable(mergers)
cat(' ASVs before chimera removal:', ncol(seqtab), '\n')
seqtab_nochim <- removeBimeraDenovo(seqtab, method = 'consensus', multithread = TRUE, verbose = FALSE)
cat(' ASVs after chimera removal:', ncol(seqtab_nochim), '\n')
cat(' Reads retained:', round(100 * sum(seqtab_nochim) / sum(seqtab), 1), '%\n')
# Track reads
getN <- function(x) sum(getUniques(x))
track <- cbind(out, sapply(dadaFs, getN), sapply(dadaRs, getN), sapply(mergers, getN), rowSums(seqtab_nochim))
colnames(track) <- c('input', 'filtered', 'denoisedF', 'denoisedR', 'merged', 'nonchim')
write.csv(track, file.path(output_dir, 'read_tracking.csv'))
# === 5. ASSIGN TAXONOMY ===
cat('\n5. Assigning taxonomy...\n')
taxa <- assignTaxonomy(seqtab_nochim, silva_train, multithread = TRUE)
if (file.exists(silva_species)) taxa <- addSpecies(taxa, silva_species)
cat(' Genus assignment rate:', round(100 * sum(!is.na(taxa[, 'Genus'])) / nrow(taxa), 1), '%\n')
# === 6. CREATE PHYLOSEQ ===
cat('\n6. Creating phyloseq object...\n')
metadata <- read.csv(metadata_file, row.names = 1)
ps <- phyloseq(otu_table(seqtab_nochim, taxa_are_rows = FALSE), tax_table(taxa), sample_data(metadata))
taxa_names(ps) <- paste0('ASV', seq(ntaxa(ps)))
cat(' Final ASVs:', ntaxa(ps), '\n')
cat(' Final samples:', nsamples(ps), '\n')
saveRDS(ps, file.path(output_dir, 'phyloseq_object.rds'))
# === 7. DIVERSITY ===
cat('\n7. Calculating diversity...\n')
ps_rare <- rarefy_even_depth(ps, sample.size = min(sample_sums(ps)), rngseed = 42, verbose = FALSE)
alpha_div <- estimate_richness(ps_rare, measures = c('Observed', 'Shannon', 'Simpson'))
alpha_div <- cbind(alpha_div, sample_data(ps_rare))
write.csv(alpha_div, file.path(output_dir, 'alpha_diversity.csv'))
p_alpha <- ggplot(alpha_div, aes(x = Group, y = Shannon, fill = Group)) +
geom_boxplot(alpha = 0.7) + geom_jitter(width = 0.2, size = 2, alpha = 0.5) +
theme_minimal() + labs(title = 'Shannon Diversity') + theme(legend.position = 'none')
ggsave(file.path(output_dir, 'plots/alpha_diversity.pdf'), p_alpha, width = 6, height = 5)
bray_dist <- phyloseq::distance(ps_rare, method = 'bray')
meta_df <- data.frame(sample_data(ps_rare))
permanova <- adonis2(bray_dist ~ Group, data = meta_df, permutations = 999)
cat(' PERMANOVA R2:', round(permanova$R2[1], 3), 'p =', permanova$`Pr(>F)`[1], '\n')
pcoa <- ordinate(ps_rare, method = 'PCoA', distance = bray_dist)
p_beta <- plot_ordination(ps_rare, pcoa, color = 'Group') + stat_ellipse(level = 0.95) +
theme_minimal() + labs(title = sprintf('PCoA (PERMANOVA R2=%.2f, p=%.3f)', permanova$R2[1], permanova$`Pr(>F)`[1]))
ggsave(file.path(output_dir, 'plots/beta_diversity_pcoa.pdf'), p_beta, width = 7, height = 6)
# === 8. DIFFERENTIAL ABUNDANCE ===
cat('\n8. Differential abundance testing...\n')
ps_filt <- filter_taxa(ps, function(x) sum(x > 0) > 0.1 * nsamples(ps), TRUE)
otu <- as.data.frame(t(otu_table(ps_filt)))
groups <- as.character(sample_data(ps_filt)$Group)
aldex_out <- aldex(otu, groups, mc.samples = 128, test = 'welch', effect = TRUE, include.sample.summary = FALSE)
aldex_out$ASV <- rownames(aldex_out)
aldex_out$significant <- aldex_out$we.eBH < 0.05 & abs(aldex_out$effect) > 1
tax_df <- as.data.frame(tax_table(ps_filt))
aldex_out <- merge(aldex_out, tax_df, by.x = 'ASV', by.y = 'row.names', all.x = TRUE)
write.csv(aldex_out, file.path(output_dir, 'aldex2_results.csv'), row.names = FALSE)
cat(' Differential taxa:', sum(aldex_out$significant), '\n')
p_effect <- ggplot(aldex_out, aes(x = effect, y = -log10(we.eBH))) +
geom_point(aes(color = significant), alpha = 0.6, size = 2) +
geom_hline(yintercept = -log10(0.05), linetype = 'dashed') +
geom_vline(xintercept = c(-1, 1), linetype = 'dashed') +
scale_color_manual(values = c('grey60', 'firebrick')) +
theme_minimal() + labs(title = 'ALDEx2 Effect Plot', x = 'Effect Size', y = '-log10(Adjusted P)')
ggsave(file.path(output_dir, 'plots/aldex2_effect_plot.pdf'), p_effect, width = 7, height = 6)
# === SUMMARY ===
cat('\n=== Pipeline Complete ===\n')
cat('Results saved to:', output_dir, '\n')
Microbiome Pipeline Usage Guide
Overview
Complete 16S rRNA amplicon sequencing workflow from raw FASTQ reads to differential abundance testing using compositionally-aware methods.
Prerequisites
BiocManager::install(c('dada2', 'phyloseq', 'ALDEx2'))
install.packages(c('vegan', 'ggplot2'))Quick Start
Tell your AI agent what you want to do:
- "Run the microbiome pipeline on my 16S FASTQ files"
- "Process my amplicon data with DADA2 and run diversity analysis"
- "Find differentially abundant taxa between my groups"
Example Prompts
Basic Analysis
"I have 16S rRNA sequencing data, run the full pipeline"
"Process my paired-end amplicon reads and assign taxonomy"
Diversity Analysis
"Calculate alpha and beta diversity for my microbiome samples"
"Run PERMANOVA to test if microbial communities differ between groups"
Differential Abundance
"Find taxa that differ between treatment and control using ALDEx2"
"Run compositional differential abundance analysis on my microbiome data"
Pipeline Stages
1. Quality Filtering (DADA2)
- Inspect quality profiles
- Trim to quality thresholds
- Remove PhiX contamination
2. ASV Inference
- Learn error rates from data
- Denoise to exact sequences
- Merge forward/reverse pairs
3. Chimera Removal
- Consensus-based detection
- Remove bimeric sequences
4. Taxonomy Assignment
- SILVA 138 classifier
- Species-level matching
- Confidence filtering
5. Diversity Analysis
- Alpha: Richness, Shannon, Simpson
- Beta: Bray-Curtis, UniFrac
- PERMANOVA for group testing
6. Differential Abundance
- ALDEx2 for compositionality
- Effect size + FDR filtering
- Visualize significant taxa
Input Requirements
FASTQ Files
raw_reads/
├── Sample1_R1_001.fastq.gz
├── Sample1_R2_001.fastq.gz
├── Sample2_R1_001.fastq.gz
└── ...Metadata
SampleID,Group,Subject,Timepoint
Sample1,Control,S1,T0
Sample2,Treatment,S2,T0Reference Databases
- SILVA 138.1 training set
- SILVA species assignment
- Download from: https://zenodo.org/record/4587955
Parameter Selection
truncLen
Based on quality profiles:
- V4 (515F-806R): c(240, 160) typical
- V3-V4: c(280, 200) typical
- Ensure 20bp overlap for merging
maxEE
Maximum expected errors:
- Strict: c(2, 2)
- Permissive: c(5, 5)
Expected Results
| Metric | Typical Range |
|---|---|
| ASVs | 500-5000 |
| Reads/sample | 10k-100k |
| Genus assignment | 70-90% |
| Differential taxa | 5-20% |
Common Issues
Low merge rate
- Amplicon too long for read length
- Poor reverse read quality
- Solution: Adjust truncLen or use longer reads
Many unassigned
- Novel taxa not in database
- Poor amplification
- Solution: Try GTDB database
No differential taxa
- Insufficient replication
- High inter-individual variation
- Solution: Increase n, reduce FDR stringency
Tips
- Primer trimming: Remove primer sequences before DADA2 processing
- truncLen: Set based on quality profiles, ensure 20bp overlap for merging
- Replicates: Microbiome studies typically need more replicates (n>=5) due to high variability
- Compositionality: Use ALDEx2 or ANCOM-BC, not standard differential tests
- Reference database: SILVA 138 or GTDB for taxonomy assignment