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
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.tax and rs_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-updater with genome_updater.sh -h More 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:)

More infos

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.tax and rs_bac_gtdb_t1f.hibf. More infos.
  • Extra: instead of building by organism group, one can select one or more taxonomic nodes with --taxid More 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:)

$ 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.rep file. 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 is 0.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 leaves will 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 --level to a specific taxonomic rank (e.g. genus) and see what it happens. If --level is omitted, each file is 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 is 0.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:)

More infos

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 classification
    • ganon reassign -> EM algorithm
    • ganon report -> taxonomic stats
  • Check: sampleA_reassign_report.tre file.
  • 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-cutoff controls the strictness of the matching algorithm.
    • lower values -> more read matches
    • higher values -> less read matches
  • --rel-filter controls 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

More info

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 shared are 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 on files/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-labels are 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 .rep file -> can be used later in reports (keep, skip)
  • Obs: If --hierarchy-labels is 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-batch using 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-count remove 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 | c
    • grep -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.html in 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.