Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

⬆️ 🎨 Allow the use of gtdb taxonomy in Autometa #284

Merged
merged 50 commits into from
Dec 16, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
9633568
:arrow_up: :art: Add database.py and gtdb.py
Sidduppal Jul 16, 2022
e9d25c5
:art: Set default dbtype as ncbi in autometa-summary
Sidduppal Jul 18, 2022
8eb37da
:art::shell::snake::memo: Add GTDB database handling
Sidduppal Jul 20, 2022
7cc9fbd
:fire: Reset unnecessary file changes
Sidduppal Jul 20, 2022
fc43bf8
:bug: :memo: Fix read the docs build bug
Sidduppal Jul 21, 2022
13fc07c
Upgrade python version to 3.8 in pytest_codecov.yml
Sidduppal Jul 21, 2022
52ccffa
Add gtdb workflow
Sidduppal Jul 21, 2022
4103cf1
:bug: Rename ncbi to taxa_db
Sidduppal Jul 21, 2022
11bdb83
:white_check_mark::snake::art::bug: Fix NCBI kwarg
evanroyrees Jul 21, 2022
62d1a01
:art::bug: Fix bug in download_gtdb_files(...) config.databases method
evanroyrees Jul 21, 2022
47ab03f
:bug::art: Fix bug in autometa-update-databases --update-gtdb. Now on…
evanroyrees Jul 22, 2022
5b7266c
:art: Reformat the files to use taxonkit instead of gtdb-taxdump
Sidduppal Sep 17, 2022
2eb67bc
Apply suggestions from code review
Sidduppal Sep 22, 2022
b1de92e
:art: Apply Evan's suggestions
Sidduppal Sep 23, 2022
55945c3
:art: Incorporate Evan's comments
Sidduppal Sep 23, 2022
a7acf67
Reduce the version of scipy and joblib
Sidduppal Sep 23, 2022
3191c99
:art: Address Evan's comments
Sidduppal Oct 11, 2022
2970e58
Apply suggestions from code review
Sidduppal Oct 11, 2022
510eafe
:art: Address Evan's comments
Sidduppal Oct 11, 2022
7b29936
Apply suggestions from code review
Sidduppal Oct 11, 2022
941403c
:memo: Update docs
Sidduppal Oct 12, 2022
04720eb
Merge branch 'dev' of github.com:KwanLab/Autometa into gtdb_to_autometa
Sidduppal Oct 12, 2022
3f9e9fe
:memo: Update docs
Sidduppal Oct 13, 2022
4bab399
Update workflow
Sidduppal Oct 13, 2022
194ea88
Update workflows
Sidduppal Oct 13, 2022
d514143
:art: Update workflows
Sidduppal Oct 16, 2022
a83dae1
Added gtdb sub-workflow to autometa.sh
Sidduppal Oct 18, 2022
ab7818e
Apply suggestions from code review
Sidduppal Oct 18, 2022
12684da
Update workflow
Sidduppal Oct 18, 2022
d2f15f8
Merge branch 'gtdb_to_autometa' of github.com:KwanLab/Autometa into g…
Sidduppal Oct 18, 2022
51abf55
Added check for taxa_rountine
Sidduppal Oct 18, 2022
9133028
:fire: Delete unnneccesary file
Sidduppal Oct 20, 2022
119bf3d
:fire: Remove workflow files
evanroyrees Oct 21, 2022
e6df890
:fire: Remove old GTDB workflow from autometa-large-data-mode.sh
Sidduppal Oct 23, 2022
c6f0d5d
:art::shell: Change camelCase to snake_case
evanroyrees Oct 23, 2022
64d6064
:art::shell: Add $dbtype variable to reduce redundancy
evanroyrees Oct 23, 2022
ed1cba8
:art::shell: Update large-data-mode variable assignments
evanroyrees Oct 24, 2022
b207159
:arrow_up::green_heart: pin pytest dependencies (scipy, joblib)
evanroyrees Oct 24, 2022
e1b7648
Update workflows
Sidduppal Oct 25, 2022
5fd5f46
Fix typo in cache directory path
Sidduppal Oct 25, 2022
55c8014
:bug::snake: Create cache directory when it does not exist
evanroyrees Oct 25, 2022
535ea97
Fix typo in large_data_mode.py
Sidduppal Oct 25, 2022
4463f1d
Pulling
Sidduppal Oct 25, 2022
d777d0e
Fix typo in workflows/autometa-large-data-mode.sh
Sidduppal Oct 25, 2022
12a7bcd
:art::bug: Fix checking file size on a non-existent file error
evanroyrees Nov 3, 2022
8a0d784
:bug::snake: Check cache prior to making outdir path
evanroyrees Nov 4, 2022
9a328c0
Address comments in the PR
Sidduppal Nov 4, 2022
684aee4
Address comments in the PR
Sidduppal Nov 4, 2022
7f86c81
:art: Rename contigs_ids to orf_prefix
Sidduppal Nov 12, 2022
54c168c
:fire: Remove redundant dbtype var
evanroyrees Dec 16, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pytest_codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
python-version: [3.7]
python-version: [3.8]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the python version for tests being changed here? Is this necessary for gtdb_to_taxdump installation?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was giving an error with 3.7, link. from typing import Union, List, Literal is only supported in versions >=3.8

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's needed else one of the tests is failing.

env:
OS: ${{ matrix.os }}
PYTHON: ${{ matrix.python-version }}
Expand Down
10 changes: 7 additions & 3 deletions autometa/binning/large_data_mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
from autometa.common import kmers

from autometa.common.exceptions import TableFormatError, BinningError
from autometa.taxonomy.ncbi import NCBI
from autometa.taxonomy.database import TaxonomyDatabase
from autometa.binning.recursive_dbscan import get_clusters
from autometa.binning.utilities import (
write_results,
Expand Down Expand Up @@ -312,11 +312,15 @@ def cluster_by_taxon_partitioning(
"""
if reverse_ranks:
# species, genus, family, order, class, phylum, superkingdom
canonical_ranks = [rank for rank in NCBI.CANONICAL_RANKS if rank != "root"]
canonical_ranks = [
rank for rank in TaxonomyDatabase.CANONICAL_RANKS if rank != "root"
]
else:
# superkingdom, phylum, class, order, family, genus, species
canonical_ranks = [
rank for rank in reversed(NCBI.CANONICAL_RANKS) if rank != "root"
rank
for rank in reversed(TaxonomyDatabase.CANONICAL_RANKS)
if rank != "root"
]
# if stage is cached then we can first look to the cache before we begin subsetting main...
clustered_contigs = set()
Expand Down
10 changes: 7 additions & 3 deletions autometa/binning/recursive_dbscan.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from autometa.common.markers import load as load_markers

from autometa.common.exceptions import TableFormatError, BinningError
from autometa.taxonomy.ncbi import NCBI
from autometa.taxonomy.database import TaxonomyDatabase
from autometa.binning.utilities import (
write_results,
read_annotations,
Expand Down Expand Up @@ -628,10 +628,14 @@ def taxon_guided_binning(
logger.info(f"Using {method} clustering method")
if reverse_ranks:
# species, genus, family, order, class, phylum, superkingdom
ranks = [rank for rank in NCBI.CANONICAL_RANKS if rank != "root"]
ranks = [rank for rank in TaxonomyDatabase.CANONICAL_RANKS if rank != "root"]
else:
# superkingdom, phylum, class, order, family, genus, species
ranks = [rank for rank in reversed(NCBI.CANONICAL_RANKS) if rank != "root"]
ranks = [
rank
for rank in reversed(TaxonomyDatabase.CANONICAL_RANKS)
if rank != "root"
]
starting_rank_index = ranks.index(starting_rank)
ranks = ranks[starting_rank_index:]
logger.debug(f"Using ranks: {', '.join(ranks)}")
Expand Down
40 changes: 28 additions & 12 deletions autometa/binning/summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@

from Bio import SeqIO

from autometa.taxonomy.database import TaxonomyDatabase
from autometa.taxonomy.ncbi import NCBI
from autometa.taxonomy.gtdb import GTDB
from autometa.taxonomy import majority_vote
from autometa.common.markers import load as load_markers

Expand Down Expand Up @@ -226,16 +228,16 @@ def get_metabin_stats(


def get_metabin_taxonomies(
bin_df: pd.DataFrame, ncbi: NCBI, cluster_col: str = "cluster"
bin_df: pd.DataFrame, taxa_db: TaxonomyDatabase, cluster_col: str = "cluster"
) -> pd.DataFrame:
"""Retrieve taxonomies of all clusters recovered from Autometa binning.

Parameters
----------
bin_df : pd.DataFrame
Autometa binning table. index=contig, cols=['cluster','length','taxid', *canonical_ranks]
ncbi : autometa.taxonomy.ncbi.NCBI instance
Autometa NCBI class instance
taxa_db : autometa.taxonomy.ncbi.TaxonomyDatabase instance
Autometa NCBI or GTDB class instance
cluster_col : str, optional
Clustering column by which to group metabins

Expand All @@ -246,7 +248,9 @@ def get_metabin_taxonomies(
Indexed by cluster
"""
logger.info(f"Retrieving metabin taxonomies for {cluster_col}")
canonical_ranks = [rank for rank in NCBI.CANONICAL_RANKS if rank != "root"]
canonical_ranks = [
rank for rank in TaxonomyDatabase.CANONICAL_RANKS if rank != "root"
]
is_clustered = bin_df[cluster_col].notnull()
bin_df = bin_df[is_clustered]
outcols = [cluster_col, "length", "taxid", *canonical_ranks]
Expand Down Expand Up @@ -277,11 +281,13 @@ def get_metabin_taxonomies(
taxonomies[cluster][canonical_rank].update({taxid: length})
else:
taxonomies[cluster][canonical_rank][taxid] += length
cluster_taxonomies = majority_vote.rank_taxids(taxonomies, ncbi)
cluster_taxonomies = majority_vote.rank_taxids(taxonomies, taxa_db=taxa_db)
# With our cluster taxonomies, let's place these into a dataframe for easy data accession
cluster_taxa_df = pd.Series(data=cluster_taxonomies, name="taxid").to_frame()
# With the list of taxids, we'll retrieve their complete canonical-rank information
lineage_df = ncbi.get_lineage_dataframe(cluster_taxa_df.taxid.tolist(), fillna=True)
lineage_df = taxa_db.get_lineage_dataframe(
cluster_taxa_df.taxid.tolist(), fillna=True
)
# Now put it all together
cluster_taxa_df = pd.merge(
cluster_taxa_df, lineage_df, how="left", left_on="taxid", right_index=True
Expand Down Expand Up @@ -323,11 +329,18 @@ def main():
required=True,
)
parser.add_argument(
"--ncbi",
help="Path to user NCBI databases directory (Required for retrieving metabin taxonomies)",
"--dbdir",
help="Path to user taxonomy database directory (Required for retrieving metabin taxonomies)",
metavar="dirpath",
required=False,
)
parser.add_argument(
"--dbtype",
help="Taxonomy database type to use (NOTE: must correspond to the same database type used during contig taxon assignment.)",
choices=["ncbi", "gtdb"],
required=False,
default="ncbi",
)
parser.add_argument(
"--binning-column",
help="Binning column to use for grouping metabins",
Expand Down Expand Up @@ -377,14 +390,17 @@ def main():
logger.info(f"Wrote metabin stats to {args.output_stats}")
# Finally if taxonomy information is available then write out each metabin's taxonomy by modified majority voting method.
if "taxid" in bin_df.columns:
if not args.ncbi:
if not args.dbdir:
logger.warn(
"taxid found in dataframe. --ncbi argument is required to retrieve metabin taxonomies. Skipping..."
"taxid found in dataframe. --dbdir argument is required to retrieve metabin taxonomies. Skipping..."
)
else:
ncbi = NCBI(dirpath=args.ncbi)
if args.dbtype == "ncbi":
taxa_db = NCBI(dbdir=args.dbdir)
elif args.dbtype == "gtdb":
taxa_db = GTDB(dbdir=args.dbdir)
Comment on lines +398 to +401
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this should be a dispatcher routine or to simply keep a list of if/elif

Suggested change
if args.dbtype == "ncbi":
taxa_db = NCBI(dbdir=args.dbdir)
elif args.dbtype == "gtdb":
taxa_db = GTDB(dbdir=args.dbdir)
taxa_dbs = {"ncbi": NCBI, "gtdb": GTDB} # i.e. --dbtype choices
taxa_db = taxa_dbs[args.dbtype](args.dbdir)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least for me the if/else is easier to read and understand, hence I'm leaving it that way for now. Let me know if you still want me to change it.

taxa_df = get_metabin_taxonomies(
bin_df=bin_df, ncbi=ncbi, cluster_col=args.binning_column
bin_df=bin_df, taxa_db=taxa_db, cluster_col=args.binning_column
)
taxa_df.to_csv(args.output_taxonomy, sep="\t", index=True, header=True)

Expand Down
10 changes: 7 additions & 3 deletions autometa/binning/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

from typing import Iterable, Tuple

from autometa.taxonomy.ncbi import NCBI
from autometa.taxonomy.database import TaxonomyDatabase


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -98,7 +98,7 @@ def filter_taxonomy(df: pd.DataFrame, rank: str, name: str) -> pd.DataFrame:
Provided `name` not found in `rank` column.
"""
# First clean the assigned taxa by broadcasting lowercase and replacing any whitespace with underscores
for canonical_rank in NCBI.CANONICAL_RANKS:
for canonical_rank in TaxonomyDatabase.CANONICAL_RANKS:
if canonical_rank not in df.columns:
continue
df[canonical_rank] = df[canonical_rank].map(
Expand Down Expand Up @@ -395,7 +395,11 @@ def write_results(
outcols.extend(annotation_cols)
# Add in taxonomy columns if taxa are present
# superkingdom, phylum, class, order, family, genus, species
taxa_cols = [rank for rank in reversed(NCBI.CANONICAL_RANKS) if rank != "root"]
taxa_cols = [
rank
for rank in reversed(TaxonomyDatabase.CANONICAL_RANKS)
if rank != "root"
]
taxa_cols.append("taxid")
# superkingdom, phylum, class, order, family, genus, species, taxid
for taxa_col in taxa_cols:
Expand Down
50 changes: 45 additions & 5 deletions autometa/config/databases.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import logging
import os
import requests
import sys
import subprocess
import tempfile

Expand All @@ -32,6 +33,7 @@
from autometa.config.utilities import DEFAULT_CONFIG
from autometa.config.utilities import AUTOMETA_DIR
from autometa.config.utilities import put_config, get_config
from autometa.taxonomy.gtdb import create_gtdb_db


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -401,6 +403,30 @@ def download_ncbi_files(self, options: Iterable) -> None:
if "nr" in options:
self.format_nr()

def download_gtdb_files(self) -> None:
proteins_aa_reps_url = self.config.get("database_urls", "proteins_aa_reps")
gtdb_taxdmp_url = self.config.get("database_urls", "gtdb_taxdmp")
Sidduppal marked this conversation as resolved.
Show resolved Hide resolved

# User path:
proteins_aa_reps_filepath = self.config.get("gtdb", "proteins_aa_reps")
gtdb_taxdmp_filepath = self.config.get("gtdb", "gtdb_taxdmp")

urls = [proteins_aa_reps_url, gtdb_taxdmp_url]
Sidduppal marked this conversation as resolved.
Show resolved Hide resolved
filepaths = [proteins_aa_reps_filepath, gtdb_taxdmp_filepath]
Sidduppal marked this conversation as resolved.
Show resolved Hide resolved

logger.debug(f"starting GTDB databases download")
for url, filepath in zip(urls, filepaths):
cmd = ["wget", url, "-O", filepath]
full_path = os.path.abspath(filepath)
dir_path = os.path.dirname(full_path)
if not os.path.exists(dir_path):
os.makedirs(dir_path)
logger.debug(f"Created missing database directory: {dir_path}")
logger.debug(" ".join(cmd))
subprocess.run(
cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True
)

def press_hmms(self) -> None:
"""hmmpress markers hmm database files.

Expand Down Expand Up @@ -714,7 +740,7 @@ def main():
)
parser.add_argument(
"--update-all",
help="Update all out-of-date databases.",
help="Update all out-of-date databases. (NOTE: Does not update GTDB)",
action="store_true",
default=False,
)
Expand All @@ -730,6 +756,12 @@ def main():
action="store_true",
default=False,
)
parser.add_argument(
"--update-gtdb",
help="Download and format the user-configured GTDB release databases",
action="store_true",
default=False,
)
parser.add_argument(
"--check-dependencies",
help="Check database dependencies are satisfied.",
Expand Down Expand Up @@ -771,6 +803,18 @@ def main():
section = "markers"
elif args.update_ncbi:
section = "ncbi"
elif args.update_gtdb:
# Download, generate and format GTDB amino acid database
gtdb_combined = create_gtdb_db(
reps_faa=dbs.config.get("gtdb", "proteins_aa_reps"),
dbdir=dbs.config.get("databases", "gtdb"),
)
diamond.makedatabase(
fasta=gtdb_combined,
database=gtdb_combined.replace(".faa", ".dmnd"),
cpus=args.cpus,
)
evanroyrees marked this conversation as resolved.
Show resolved Hide resolved
sys.exit(0)
else:
section = None

Expand All @@ -779,15 +823,11 @@ def main():
section=section, compare_checksums=compare_checksums
)
logger.info(f"Database dependencies satisfied: {dbs_satisfied}")
import sys

sys.exit(0)

config = dbs.configure(section=section, no_checksum=args.no_checksum)

if not args.out:
import sys

sys.exit(0)
put_config(config, args.out)
logger.info(f"{args.out} written.")
Expand Down
10 changes: 10 additions & 0 deletions autometa/config/default.config
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ bedtools = None
[databases]
base = ${common:home_dir}/autometa/databases
ncbi = ${databases:base}/ncbi
gtdb = ${databases:base}/gtdb
markers = ${databases:base}/markers

[database_urls]
Expand All @@ -60,6 +61,9 @@ bacteria_single_copy = https://${markers:host}/KwanLab/Autometa/main/autometa/da
bacteria_single_copy_cutoffs = https://${markers:host}/KwanLab/Autometa/main/autometa/databases/markers/bacteria.single_copy.cutoffs
archaea_single_copy = https://${markers:host}/KwanLab/Autometa/main/autometa/databases/markers/archaea.single_copy.hmm
archaea_single_copy_cutoffs = https://${markers:host}/KwanLab/Autometa/main/autometa/databases/markers/archaea.single_copy.cutoffs
proteins_aa_reps = https://${gtdb:host}/releases/${gtdb:release}/genomic_files_reps/gtdb_proteins_aa_reps.tar.gz
gtdb_taxdmp = https://${markers:host}/shenwei356/gtdb-taxdump/releases/latest/download/gtdb-taxdump.tar.gz
Sidduppal marked this conversation as resolved.
Show resolved Hide resolved


[checksums]
taxdump = ftp://${ncbi:host}/pub/taxonomy/taxdump.tar.gz.md5
Expand All @@ -79,6 +83,12 @@ merged = ${databases:ncbi}/merged.dmp
accession2taxid = ${databases:ncbi}/prot.accession2taxid.gz
nr = ${databases:ncbi}/nr.gz

[gtdb]
host = data.gtdb.ecogenomic.org
release = latest
proteins_aa_reps = ${databases:gtdb}/gtdb_proteins_aa_reps.tar.gz
gtdb_taxdmp = ${databases:gtdb}/gtdb-taxdump.tar.gz
evanroyrees marked this conversation as resolved.
Show resolved Hide resolved

[markers]
host = raw.githubusercontent.com
bacteria_single_copy = ${databases:markers}/bacteria.single_copy.hmm
Expand Down
Loading