ganon2 hands-on - 2026-06-17:)
This tutorial introduces practical approaches to creating and utilising databases for metagenomics classification with Ganon2. It will cover both basic and advanced options, as well as common pitfalls in metagenomics classification. Topics include selecting databases and taxonomies, choosing parameters and thresholds, and interpreting results.
By the end, you should have acquired the knowledge required to create your own databases and implement suitable classification strategies to improve your metagenomics analyses.
The tutorial uses real human skin metagenomics samples and enhances their default classification using metagenome-assembled genomes. Reports and a final table of counts are created, and the results are visualised using taxonomic bars and a heatmap.
Prepare files and tools:)
Download files:)
Download prepared files and unpack in a working directory of choice:
mkdir ganon_handson_20260617
cd ganon_handson_20260617
wget https://zenodo.org/records/20671089/files/ganon_handson_files_20260617.tar.gz
tar xf ganon_handson_files_20260617.tar.gz
Install tools:)
conda create -c conda-forge -c bioconda -n ganonenv ganon
- GRIMER (for visualization)
conda create -n grimerenv -c conda-forge "python=3.9" "biom-format=2.1.16" "numpy=1.23.5"
conda activate grimerenv
conda install "grimer=1.1.0"
conda deactivate
Useful alias to check tsv files:)
# c: like cat but with aligned tabs for tsv files
alias c="column -s$'\t' -t"
# cless: like less with c function above
cless() {
if [ -z "$1" ]; then
c | less -SN
else
c "$1" | less -SN
fi
}
If you find them useful, you can add those commands to your ~/.bashrc to have it available at all times.
Check installed version and files:)
Ganon:
$ conda activate ganonenv
$ ganon --version
version: ganon 2.4.2
$ genome_updater.sh | grep -m 1 "v" | tr -d ' '
v0.8.1
$ python -c "import multitax; print(multitax.__version__)"
1.6.0
GRIMER:
$ conda activate grimerenv
$ grimer --version | grep "grimer"
grimer 1.1.0
Files:
$ ls -1 *
ganon_handson_files_20260617.tar.gz
files:
aux
gcmeta
mgnify
reads
rs_bac_cg_ncbi_t1f_files
rs_bac_gtdb_t1f_files
Note
To run commands faster, set then number of threads to the max possible in your machine: threads=$(nproc --all) or to a specific number threads=4. Double-check the value set with echo $threads.
Default databases:)
NCBI:)
conda activate ganonenv
ganon build --help
Please refer to the documentation for more information.
Build default database based on Bacterial RefSeq complete genomes, NCBI Taxonomy, limiting number of assemblies to 1 per family:
ganon build --source refseq \
--organism-group bacteria \
--complete-genomes \
--genome-updater "-A 'family:1'" \
--db-prefix rs_bac_cg_ncbi_t1f \
--threads ${threads:-8}
Offline
rm -rf rs_bac_cg_ncbi_t1f_files/
cp -r files/rs_bac_cg_ncbi_t1f_files .
ganon build --source refseq \
--organism-group bacteria \
--complete-genomes \
--genome-updater "-A 'family:1'" \
--db-prefix rs_bac_cg_ncbi_t1f \
--threads ${threads:-8} \
--taxonomy-files files/aux/taxdump.tar.gz \
--genome-size-files files/aux/species_genome_size.txt.gz
- Check: logs printed after execution in the terminal -> taxonomy distribution of entries at "Validating and converting taxonomy".
- Always check logs for missing information, errors, warnings, etc.
- Check: generated files
rs_bac_cg_ncbi_t1f_files/,rs_bac_cg_ncbi_t1f.taxandrs_bac_cg_ncbi_t1f.hibf. More infos. - Extra: run with a higher
--max-fp(e.g.0.1) and check the diference in index size. More infos - Extra: see possible extra filters for
--genome-updaterwithgenome_updater.sh -hMore infos
Warning
"-A 'family:1'" is not recommended for general use - we use it here to get a very small sub-set to run examples fast. "-A 'genus:1'", "-A 'species:1'", --top 1 (for leaves in taxonomy) or no top assemblies are more commonly used.
Note
ganon build default target taxonomic rank is species (--level species)
Update (optional):)
After some days, the database can be update with the newest sequences with the following command:
ganon update --db-prefix rs_bac_cg_ncbi_t1f
GTDB:)
Build default database based on Bacterial RefSeq, GTDB, limiting number of assemblies to 1 per family:
ganon build --source refseq \
--organism-group bacteria \
--taxonomy gtdb \
--genome-updater "-A 'family:1'" \
--db-prefix rs_bac_gtdb_t1f \
--threads ${threads:-8}
Offline
rm -rf rs_bac_gtdb_t1f_files/
cp -r files/rs_bac_gtdb_t1f_files .
ganon build --source refseq \
--organism-group bacteria \
--taxonomy gtdb \
--genome-updater "-A 'family:1'" \
--db-prefix rs_bac_gtdb_t1f \
--threads ${threads:-8} \
--taxonomy-files files/aux/bac120_taxonomy_r232.tsv.gz \
--genome-size-files files/aux/bac120_metadata_r232.tsv.gz \
files/aux/ar53_metadata_r232.tsv.gz
- Check: logs printed after execution in the terminal -> taxonomy distribution of entries at "Validating and converting taxonomy".
- Check: generated files
rs_bac_gtdb_t1f_files/,rs_bac_gtdb_t1f.taxandrs_bac_gtdb_t1f.hibf. More infos. - Extra: instead of building by organism group, one can select one or more taxonomic nodes with
--taxidMore infos
Warning
When building gtdb databases, always use --source refseq genbank to get the complete set of GTDB Genomes. Here we use only refseq to reduce the data for our toy example database. Example ganon build --source refseq genbank --organism-group archaea bacteria --threads 48 --taxonomy gtdb --db-prefix ab_gtdb will build a database will all GTDB genomes from the latest release.
Classification:)
ganon classify --help
Please refer to the documentation for more information.
Samples:)
- 3 small samples from human skin study, around 2M reads each
$ zcat files/reads/SampleA_1.fq.gz | wc -l
8751928
$ zcat files/reads/SampleA_2.fq.gz | wc -l
8751928
$ zcat files/reads/SampleB_1.fq.gz | wc -l
8270608
$ zcat files/reads/SampleB_2.fq.gz | wc -l
8270608
$ zcat files/reads/SampleC_1.fq.gz | wc -l
8084424
$ zcat files/reads/SampleC_2.fq.gz | wc -l
8084424
Classify paired-end reads against the default database (NCBI):
ganon classify --db-prefix rs_bac_cg_ncbi_t1f \
--paired-reads files/reads/SampleA_1.fq.gz files/reads/SampleA_2.fq.gz \
--output-prefix sampleA_default_ncbi \
--threads ${threads:-8} \
--skip-report --multiple-matches skip
- Check: stats printed after execution in the terminal. More infos
- Check: the
sampleA_default_ncbi.repfile. More infos - Obs: results are very bad due to famility top 1 filter in the build step
- Obs: Several paired-end (
--paired-reads) and single-end (--single-reads) sequences/reads can be clasified in one run. More infos
Explaining the stats:
ganon-classify processed 2187982 sequences (656.395 Mbp) with 77492114 k-mers in 16.3083 seconds (2414.95 Mbp/m)
22578 sequences classified (1.03191%) <--| QUANTITY |
22159 with unique matches (1.01276%)
419 with multiple matches (0.0191501%) <--| UNIQUE/SHARED |
2165404 sequences unclassified (98.9681%)
matches: 23617 (avg. 1.04602 reference/sequence), 2577 discarded (--rel-filter), 0 discarded (--fpr-query)
k-mers: 696028/796398 k-mers matched/k-mers from classified sequences (87.397%) <--| QUALITY |
- Extra: Run the same command again with
--rel-cutoff 0.2(the default value is0.75) and evaluate the changes in quantity and quality in the stats.
Same for the GTDB database:
ganon classify --db-prefix rs_bac_gtdb_t1f \
--paired-reads files/reads/SampleA_1.fq.gz files/reads/SampleA_2.fq.gz \
--output-prefix sampleA_default_gtdb \
--threads ${threads:-8} \
--skip-report --multiple-matches skip
RECAP 1
Custom databases:)
ganon build-custom --help
Please refer to the documentation for more information.
gcMeta:)
Website -> Download -> Find Human Skin MAGs
Build:)
Generate custom input file linking files and taxonomy (More infos):
- Check original metadata file
cless files/gcmeta/metadata.tsv - Check pre-downloaded files
ls -1 files/gcmeta/Human_Skin/Human_Skin_species-level_representative_MAGs/ | head
tail -n+2 files/gcmeta/metadata.tsv |
cut -f 1,9 |
tr ';' '\t' |
awk -F"\t" -v path="$(pwd)/files/gcmeta/Human_Skin/Human_Skin_species-level_representative_MAGs/" '{for(i=2;i<=8;i++){if(length($i)==3){break}else{leave=$i}}; print path $1".fa.gz\t"$1"\t"leave}' > gcmeta_ganon_input_file.tsv
- Obs: this command extracts the sequence identifier and links it with the file and taxonomy
- Check: file
cless gcmeta_ganon_input_file.tsv - Obs: not all genomes are linked to a species (
--level leaves)
Build custom database with the generated file, using GTDB:
ganon build-custom --input-file gcmeta_ganon_input_file.tsv \
--db-prefix gcmeta_human_skin \
--taxonomy gtdb \
--level leaves \
--threads ${threads:-8}
Offline
rm -rf gcmeta_human_skin_files/
ganon build-custom --input-file gcmeta_ganon_input_file.tsv \
--db-prefix gcmeta_human_skin \
--taxonomy gtdb \
--level leaves \
--threads ${threads:-8} \
--taxonomy-files files/aux/bac120_taxonomy_r232.tsv.gz \
files/aux/ar53_taxonomy_r232.tsv.gz \
--genome-size-files files/aux/bac120_metadata_r232.tsv.gz \
files/aux/ar53_metadata_r232.tsv.gz
- Obs:
--level leaveswill generate the database at the available taxonomy entry provided (multiple levels) - Check: log printed after execution in the terminal -> look for "Validating and converting taxonomy"
-INVALID- 173- 10% of our MAGs were discarded
- Extra: try to change
--levelto a specific taxonomic rank (e.g.genus) and see what it happens. If--levelis omitted, eachfileis used as a target providing a genome level (strain) classification.
Build custom database with the generated file, using the correct GTDB version:
ganon build-custom --input-file gcmeta_ganon_input_file.tsv \
--db-prefix gcmeta_human_skin \
--taxonomy gtdb-220 \
--level leaves \
--threads ${threads:-8}
Offline
rm -rf gcmeta_human_skin_files/
ganon build-custom --input-file gcmeta_ganon_input_file.tsv \
--db-prefix gcmeta_human_skin \
--taxonomy gtdb-220 \
--level leaves \
--threads ${threads:-8} \
--taxonomy-files files/aux/bac120_taxonomy_r220.tsv.gz \
files/aux/ar53_taxonomy_r220.tsv.gz \
--genome-size-files files/aux/bac120_metadata_r220.tsv.gz \
files/aux/ar53_metadata_r220.tsv.gz
- Check: log printed after execution in the terminal -> look for "Validating and converting taxonomy"
-INVALID- 0
Important
Always verify which version of GTDB was used to generate the classification in the metadata file.
Classify:)
Classify reads with 2 databases:
- The default database (GTDB)
rs_bac_gtdb_t1f - The gcMeta custom database
gcmeta_human_skin
ganon classify --db-prefix rs_bac_gtdb_t1f gcmeta_human_skin \
--paired-reads files/reads/SampleA_1.fq.gz files/reads/SampleA_2.fq.gz \
--output-prefix sampleA_2dbs \
--threads ${threads:-8} \
--skip-report --multiple-matches skip
- Check: stats printed after execution in the terminal. More infos
- Changes in quantity and quality.
- Extra: Run the same command again with a lower
--rel-cutoff(the default value is0.75) and evaluate the quantity and quality changes in the stats.
MGnify:)
Website -> Browse data -> Genomes -> Find human skin -> FTP Site
Build:)
Once again: generate custom input file linking files and taxonomy (More infos):
- Check original metadata file
cless files/mgnify/metadata.tsv - Check pre-downloaded files
ls -1 files/mgnify/mags/ | head
Warning
Only 100 random MAGs are used in this example
tail -n+2 files/mgnify/metadata.tsv |
cut -f 1,15 | tr ';' '\t' |
awk -F"\t" '{leave="1";for(i=2;i<=8;i++){if(length($i)==3){break}else{leave=$i}}; print $1"\t"leave}' > mgnify_leaves.txt
join <(find $(pwd)/files/mgnify/mags/ -name "*.fna.gz" |
awk '{n = split($1, f, "/"); print substr(f[n],0, length(f[n])-7)"\t"$1}' |
sort -t$'\t' -k 2,2) \
<(sort -t$'\t' -k 1,1 mgnify_leaves.txt) -t$'\t' -o "1.2,2.1,2.2" > mgnify_ganon_input_file.tsv
- Check: file
cless mgnify_ganon_input_file.tsv
Build custom database with previously generated file, already using the correct GTDB version:
ganon build-custom --input-file mgnify_ganon_input_file.tsv \
--db-prefix mgnify_human_skin \
--taxonomy gtdb-226 \
--level leaves \
--threads ${threads:-8}
Offline
rm -rf mgnify_human_skin_files/
ganon build-custom --input-file mgnify_ganon_input_file.tsv \
--db-prefix mgnify_human_skin \
--taxonomy gtdb-226 \
--level leaves \
--threads ${threads:-8} \
--taxonomy-files files/aux/bac120_taxonomy_r226.tsv.gz \
files/aux/ar53_taxonomy_r226.tsv.gz \
--genome-size-files files/aux/bac120_metadata_r226.tsv.gz \
files/aux/ar53_metadata_r226.tsv.gz
Classify:)
Classify reads with 3 databases:
- The default database (GTDB)
rs_bac_gtdb_t1f - The gcMeta custom database
gcmeta_human_skin - The MGnify custom database
mgnify_human_skin
ganon classify --db-prefix rs_bac_gtdb_t1f gcmeta_human_skin mgnify_human_skin \
--paired-reads files/reads/SampleA_1.fq.gz files/reads/SampleA_2.fq.gz \
--output-prefix sampleA_3dbs \
--threads ${threads:-8} \
--skip-report --multiple-matches skip
- Check: stats printed after execution in the terminal. More infos
- Changes in quantity and quality.
- sequences classified: 0.103931% -> 18.5039% -> 21.8342%
- k-mer ratio: 86.7768% -> 92.2388% -> 93.489%
GTDB -> GTDB conversion:)
Taxonomy version are incompatible:
- The default database (GTDB)
rs_bac_gtdb_t1f->v232(latest) - The gcMeta custom database
gcmeta_human_skin->v220 - The MGnify custom database
mgnify_human_skin->v226
Problem: may generate inconsistent results!
Solution: convert all to the newest GTDB
Re-build databases converting to the one single taxonomy version (v232, latest):
Default:
ganon build-custom --input-file gcmeta_ganon_input_file.tsv \
--db-prefix gcmeta_human_skin \
--taxonomy gtdb-220 \
--convert-taxonomy gtdb-232 \
--level leaves \
--threads ${threads:-8}
ganon build-custom --input-file mgnify_ganon_input_file.tsv \
--db-prefix mgnify_human_skin \
--taxonomy gtdb-226 \
--convert-taxonomy gtdb-232 \
--level leaves \
--threads ${threads:-8}
Offline
rm -rf gcmeta_human_skin_files/
ganon build-custom --input-file gcmeta_ganon_input_file.tsv \
--db-prefix gcmeta_human_skin \
--taxonomy gtdb-220 \
--convert-taxonomy gtdb-232 \
--level leaves \
--threads ${threads:-8} \
--taxonomy-files files/aux/bac120_taxonomy_r220.tsv.gz \
files/aux/ar53_taxonomy_r220.tsv.gz \
--convert-taxonomy-files files/aux/bac120_taxonomy_r232.tsv.gz \
files/aux/ar53_taxonomy_r232.tsv.gz \
--convert-gtdb-files files/aux/220_acc_rep_lin_ncbi.tsv.gz \
files/aux/232_acc_rep_lin_ncbi.tsv.gz \
--genome-size-files files/aux/bac120_metadata_r232.tsv.gz \
files/aux/ar53_metadata_r232.tsv.gz
rm -rf mgnify_human_skin_files/
ganon build-custom --input-file mgnify_ganon_input_file.tsv \
--db-prefix mgnify_human_skin \
--taxonomy gtdb-226 \
--convert-taxonomy gtdb-232 \
--level leaves \
--threads ${threads:-8} \
--taxonomy-files files/aux/bac120_taxonomy_r226.tsv.gz \
files/aux/ar53_taxonomy_r226.tsv.gz \
--convert-taxonomy-files files/aux/bac120_taxonomy_r232.tsv.gz \
files/aux/ar53_taxonomy_r232.tsv.gz \
--convert-gtdb-files files/aux/226_acc_rep_lin_ncbi.tsv.gz \
files/aux/232_acc_rep_lin_ncbi.tsv.gz \
--genome-size-files files/aux/bac120_metadata_r232.tsv.gz \
files/aux/ar53_metadata_r232.tsv.gz
- Check: logs printed after execution in the terminal -> taxonomy distribution of entries at "Validating and converting taxonomy".
- There is some resolution lost
Re-classify against 3 fixed databases:
ganon classify --db-prefix rs_bac_gtdb_t1f gcmeta_human_skin mgnify_human_skin \
--paired-reads files/reads/SampleA_1.fq.gz files/reads/SampleA_2.fq.gz \
--output-prefix sampleA_3dbs \
--threads ${threads:-8} \
--skip-report --multiple-matches skip
GTDB -> NCBI conversion:)
Translation rates for each rank from GTDB genomes (R232) to the NCBI taxonomy (2025-09-07):
root domain phylum class order family genus species
domain 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
phylum 18.27 74.11 7.61 0.00 0.00 0.00 0.00 0.00
class 10.54 64.03 20.16 5.27 0.00 0.00 0.00 0.00
order 5.06 47.94 25.56 15.91 5.53 0.00 0.00 0.00
family 2.71 39.77 29.38 13.98 9.68 4.49 0.00 0.00
genus 0.70 20.53 29.70 16.53 12.54 12.84 7.15 0.00
species 0.13 3.16 3.66 2.73 2.14 2.41 5.07 80.70
The translation from NCBI (2025-09-07) considering only archaeal and bacterial taxa to GTDB (R232):
missing root domain phylum class order family genus species
domain 0.00 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
phylum 6.90 14.22 31.03 47.84 0.00 0.00 0.00 0.00 0.00
class 8.70 13.83 32.41 7.91 37.15 0.00 0.00 0.00 0.00
order 8.92 9.48 24.54 4.28 9.85 42.94 0.00 0.00 0.00
family 9.14 4.74 20.14 3.81 6.18 10.91 45.09 0.00 0.00
genus 13.74 1.11 6.81 1.44 2.81 2.79 16.82 54.48 0.00
species 82.80 0.03 0.20 0.05 0.09 0.11 0.31 0.82 15.58
ganon build-custom --input-file gcmeta_ganon_input_file.tsv \
--db-prefix gcmeta_human_skin_NCBI \
--taxonomy gtdb-220 \
--convert-taxonomy ncbi-latest \
--level leaves \
--threads ${threads:-8}
ganon build-custom --input-file mgnify_ganon_input_file.tsv \
--db-prefix mgnify_human_skin_NCBI \
--taxonomy gtdb-226 \
--convert-taxonomy ncbi-latest \
--level leaves \
--threads ${threads:-8}
Offline
rm -rf gcmeta_human_skin_NCBI/
ganon build-custom --input-file gcmeta_ganon_input_file.tsv \
--db-prefix gcmeta_human_skin_NCBI \
--taxonomy gtdb-220 \
--convert-taxonomy ncbi-latest \
--level leaves \
--threads ${threads:-8} \
--taxonomy-files files/aux/bac120_taxonomy_r220.tsv.gz \
files/aux/ar53_taxonomy_r220.tsv.gz \
--convert-taxonomy-files files/aux/taxdump.tar.gz \
--convert-gtdb-files files/aux/220_acc_rep_lin_ncbi.tsv.gz \
--genome-size-files files/aux/species_genome_size.txt.gz
rm -rf mgnify_human_skin_NCBI/
ganon build-custom --input-file mgnify_ganon_input_file.tsv \
--db-prefix mgnify_human_skin_NCBI \
--taxonomy gtdb-226 \
--convert-taxonomy ncbi-latest \
--level leaves \
--threads ${threads:-8} \
--taxonomy-files files/aux/bac120_taxonomy_r226.tsv.gz \
files/aux/ar53_taxonomy_r226.tsv.gz \
--convert-taxonomy-files files/aux/taxdump.tar.gz \
--convert-gtdb-files files/aux/226_acc_rep_lin_ncbi.tsv.gz \
--genome-size-files files/aux/species_genome_size.txt.gz
- Check: log printed after execution in the terminal -> look for "Validating and converting taxonomy"
- Read can be re-assigned to lower tax. ranks at classification level, but this will introduce more errors
Re-classify against 3 NCBI databases:
ganon classify --db-prefix rs_bac_cg_ncbi_t1f gcmeta_human_skin_NCBI mgnify_human_skin_NCBI \
--paired-reads files/reads/SampleA_1.fq.gz files/reads/SampleA_2.fq.gz \
--output-prefix sampleA_3dbs_NCBI \
--threads ${threads:-8} \
--skip-report --multiple-matches skip
- Check: classification stats are similar (even more reads classfied)
- Differences will be clear in the taxonomic reports
RECAP 2
Classification in depth:)
Classification with read re-assignment and report generation:
ganon classify --db-prefix rs_bac_gtdb_t1f gcmeta_human_skin mgnify_human_skin \
--paired-reads files/reads/SampleA_1.fq.gz files/reads/SampleA_2.fq.gz \
--output-prefix sampleA_reassign_report \
--threads ${threads:-8} \
--multiple-matches em \
--report-type abundance \
--min-count 5e-05
- Check: log in the terminal consists of 3 parts:
ganon classify-> sequence classificationganon reassign-> EM algorithmganon report-> taxonomic stats
- Check:
sampleA_reassign_report.trefile. - Extra: generate other outputs with
--output-all,--output-one,--output-unc,--output-stats - Extra: classify using the NCBI default database and two NCBI converted databases, check taxonomic stats
Generate an alternative report based on same base results (sampleA_reassign_report.rep):
ganon report --db-prefix rs_bac_gtdb_t1f gcmeta_human_skin mgnify_human_skin \
--input sampleA_reassign_report.rep \
--output-prefix sampleA_alt_report \
--sort lineage \
--output-format bioboxes \
--min-count 1e-03
Classification thresholds:)
Two main parameters to control matches, in summary:
--rel-cutoffcontrols the strictness of the matching algorithm.- lower values -> more read matches
- higher values -> less read matches
--rel-filtercontrols how many matches each read will have, from best to worst- lower values -> more unique matching reads
- higher values -> more multi-matching reads
Please refer to the documentation for more information.
Effects of --rel-cutoff were already presented.
Changing the --rel-filter:
ganon classify --db-prefix rs_bac_gtdb_t1f gcmeta_human_skin mgnify_human_skin \
--paired-reads files/reads/SampleA_1.fq.gz files/reads/SampleA_2.fq.gz \
--output-prefix sampleA_lowcutoff \
--threads ${threads:-8} --skip-report --multiple-matches skip \
--rel-cutoff 0.25 \
--rel-filter 0.7
Classify with high cutoff and low filter:
ganon classify --db-prefix rs_bac_gtdb_t1f gcmeta_human_skin mgnify_human_skin \
--paired-reads files/reads/SampleA_1.fq.gz files/reads/SampleA_2.fq.gz \
--output-prefix sampleA_lowfilter \
--threads ${threads:-8} --skip-report --multiple-matches skip \
--rel-cutoff 0.25 \
--rel-filter 0.1
- Check:
discarded (--rel-filter)numbers and effects on unique/multiple matches- Quantity and quality remains the same
Multiple-matching reads:)
Reads with more than one match to a reference can be "solved" -> re-assigned to an unique taxonomic node with --multiple-matches:
- Expectation-maximization (
em) algorithm and/or - Lowest-common-ancestor (
lca) algorithm
LCA:
ganon classify --db-prefix rs_bac_gtdb_t1f gcmeta_human_skin mgnify_human_skin \
--paired-reads files/reads/SampleA_1.fq.gz files/reads/SampleA_2.fq.gz \
--output-prefix sampleA_results_lca \
--threads ${threads:-8} \
--multiple-matches lca
EM:
ganon classify --db-prefix rs_bac_gtdb_t1f gcmeta_human_skin mgnify_human_skin \
--paired-reads files/reads/SampleA_1.fq.gz files/reads/SampleA_2.fq.gz \
--output-prefix sampleA_results_em \
--threads ${threads:-8} \
--multiple-matches em
- Check: taxonomy report summary and look at the differences in the "shared" col. More infos
# LCA
unique shared children total
root 0% 0.02505% 21.82% 21.84%
domain 0.004296% 1.741% 20.07% 21.82%
phylum 0% 0.572% 19.49% 20.07%
class 0.01892% 4.164% 15.3% 19.49%
order 0.03967% 0.3714% 14.87% 15.28%
family 0.08707% 0.2023% 14.52% 14.81%
genus 1.824% 2.04% 10.49% 14.36%
species 10.17% 0% 0% 10.17%
# EM
unique shared children total
root 0% 0% 21.84% 21.84%
domain 0.004296% 0.002102% 21.84% 21.84%
phylum 0% 0% 21.83% 21.83%
class 0.01892% 0.001097% 21.8% 21.82%
order 0.03967% 0.009278% 21.73% 21.78%
family 0.08707% 0.02313% 21.55% 21.66%
genus 1.829% 1.573% 17.99% 21.39%
species 10.19% 7.471% 0% 17.66%
- Obs: not all
sharedare re-distributed to species because the database was based on available taxonomic leaves. - Obs: EM + LCA can be done in one classify run More infos
Classification in batches:)
files/reads/read_batches.tsv:
sA files/reads/SampleA_1.fq.gz files/reads/SampleA_2.fq.gz
sB files/reads/SampleB_1.fq.gz files/reads/SampleB_2.fq.gz
sC files/reads/SampleC_1.fq.gz files/reads/SampleC_2.fq.gz
Classify all samples in one run using --batch-reads files/reads/read_batches.tsv:
ganon classify --db-prefix rs_bac_gtdb_t1f gcmeta_human_skin mgnify_human_skin \
--batch-reads files/reads/read_batches.tsv \
--output-prefix ./ \
--threads ${threads:-8} \
--rel-cutoff 0.2
- Check: stats in terminal for all 3 samples.
- Obs: files were created based on
--output-prefix+ identifier of the first col onfiles/reads/read_batches.tsv - Obs: load database(s) only once → may save several hours for large databases and many samples
Hierarchical classification:)
Classify in multiple databases at the same time. The databases can also be provided in a hierarchical order. Reads unclassified in the first level be carried over to the next.
First gcmeta, then mgnify:
ganon classify --db-prefix gcmeta_human_skin mgnify_human_skin \
--hierarchy-labels 1-GCMETA 2-MGNIFY \
--paired-reads files/reads/SampleA_1.fq.gz files/reads/SampleA_2.fq.gz \
--output-prefix sampleA_gcmeta_mgnify \
--threads ${threads:-8}
First mgnify, then gcmeta:
ganon classify --db-prefix gcmeta_human_skin mgnify_human_skin \
--hierarchy-labels 2-GCMETA 1-MGNIFY \
--paired-reads files/reads/SampleA_1.fq.gz files/reads/SampleA_2.fq.gz \
--output-prefix sampleA_mgnify_gcmeta \
--threads ${threads:-8}
- Obs:
--hierarchy-labelsare sorted
Several databases can be user per level, with different thresholds:
ganon classify --db-prefix rs_bac_gtdb_t1f gcmeta_human_skin mgnify_human_skin \
--hierarchy-labels 1-base 2-mags 2-mags \
--rel-cutoff 0.5 0.7 0.8 \
--rel-filter 0.2 0.5 \
--paired-reads files/reads/SampleA_1.fq.gz files/reads/SampleA_2.fq.gz \
--output-prefix sampleA_hierarchy_test \
--threads ${threads:-8}
- Check: stats for each database
- Check: level is reported in the
.repfile -> can be used later in reports (keep, skip) - Obs: If
--hierarchy-labelsis not provided, all databases are run in the same level (same as one big database) - Obs: Useful to remove host reads, contaminants, prioritize better quality databases, ...
- Extra: classify all 3 samples in on run with
--read-batchusing several database in an hierarchical order.
RECAP 3
Report:)
Generate a base result (sampleA_base_results.rep):
ganon classify --db-prefix rs_bac_gtdb_t1f gcmeta_human_skin mgnify_human_skin \
--paired-reads files/reads/SampleA_1.fq.gz files/reads/SampleA_2.fq.gz \
--output-prefix sampleA_base_results \
--threads ${threads:-8} \
--skip-report
Abundance - taxonomic profiling:
ganon report --db-prefix rs_bac_gtdb_t1f gcmeta_human_skin mgnify_human_skin \
--input sampleA_base_results.rep \
--output-prefix sampleA_report_abundance \
--min-count 5e-05 --report-type abundance
Reads - read binning:
ganon report --db-prefix rs_bac_gtdb_t1f gcmeta_human_skin mgnify_human_skin \
--input sampleA_base_results.rep \
--output-prefix sampleA_report_reads \
--report-type reads
- Check: taxonomic summary differences
--min-countremove many entries, but has little effect on total percentage- mostly removing the long tail of low abundance, usually false positives
- Check top 10 species for read and tax. profiling:
grep -m 5 "genus" sampleA_report_abundance.tre | cut -f 2,9 | cgrep -m 5 "genus" sampleA_report_reads.tre | cut -f 2,9 | c
Matches:
ganon report --db-prefix rs_bac_gtdb_t1f gcmeta_human_skin mgnify_human_skin \
--input sampleA_base_results.rep \
--output-prefix sampleA_report_matches \
--report-type matches
Table:)
Put together several tree-like reports (.tre) into a table of counts. More infos
From the batch classification output:
ganon table --input sA.tre sB.tre sC.tre \
--output-file table_top_sample10.tsv
Selecting the top 10 most abundant taxa for each sample, using lineage as header:
ganon table --input sA.tre sB.tre sC.tre \
--output-file table_top_sample10.tsv \
--header lineage \
--top-sample 10
Selecting the top 10 most abundant species for each sample:
ganon table --input sA.tre sB.tre sC.tre \
--output-file table_top_sample10_species.tsv \
--header lineage \
--rank species \
--top-sample 10
Selecting the top 10 most abundant species among all samples:
ganon table --input sA.tre sB.tre sC.tre \
--output-file table_top_all10.tsv \
--rank species \
--top-all 10
- Check: table files
Visualization:)
Generate "parsable" table for GRIMER:
ganon table --input sA.tre sB.tre sC.tre \
--output-file table.tsv \
--filtered-label unassigned --unclassified-label unassigned \
--header taxid
Toy metadata file (files/reads/metadata.tsv):
sample_name age group date disease
sA.tre 12 A 2026-05-01 true
sB.tre 77 B 2025-01-01 false
sC.tre 21 A 2020-12-03 true
conda activate grimerenv
grimer --input table.tsv \
--metadata-file files/reads/metadata.tsv \
--ranks domain phylum class order family genus species \
--taxonomy gtdb \
--taxonomy-files files/aux/bac120_taxonomy_r226.tsv.gz files/aux/ar53_taxonomy_r226.tsv.gz \
--unassigned-header unassigned
- Check:
output.htmlin the browser. - A more complete visualization example
RECAP 4
I hope you enjoy it and learned something new to improve your metagenomics analysis. Feel free to contact me and open issues on the GitHub repo if you have questions, suggestions or find bugs!
Obs: if you want to keep this tutorial, please save this page locally. Soon this will turn into a tutorial for metagenomics analysis and some commands/sections will change.