History of virus_host_db
The changes to the source code of this entry in the repository, newest first, each with the differences it made to the entry's files. They are also listed on GitLab.
[entries] New entry: virus_host_db f6e84be
diff --git a/entries/virus_host_db/__init__.py b/entries/virus_host_db/__init__.py
new file mode 100644
index 0000000..18a8716
--- /dev/null
+++ b/entries/virus_host_db/__init__.py
@@ -0,0 +1,161 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2026 Tiago de Paula Peixoto <tiago@skewed.de>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Affero General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import csv
+
+from .. import *
+
+title = "Virus-Host DB virus-host associations (2016-2026)"
+description = r"""Networks of viruses and their hosts from the Virus-Host DB of GenomeNet (Kyoto University), which covers all the viruses with complete genomes in NCBI RefSeq and in GenBank (as listed in EBI Genomes), across all domains of life (viruses of bacteria, archaea, protists, fungi, plants and animals, including humans). The hosts are collected from the annotations of RefSeq and GenBank, UniProt and ViralZone, and from a manual curation of the literature. Each virus and each host is an NCBI taxon, and an edge links a virus to a host in which it was found.
+
+Virus-Host DB is updated with every RefSeq release (about every two months), and its earlier releases are archived. There is one network for each archived release, named `release_<N>` by the number of its directory in the archive, which is the RefSeq release on which it is based, from `release_75` (2016) to `release_236` (RefSeq release 236, July 6, 2026); the RefSeq release and its date are given by the graph properties `refseq_release` and `refseq_date`. Releases 76, 97, 201, 223, 234 are missing from the archive, except that the directory 224 holds the data of RefSeq release 223. The directory 232 holds the same data as 233 (of RefSeq release 233), and the file of the directory 230 (RefSeq release 230, May 2, 2025) is empty: both are left out. The first release, `release_75`, has no KEGG identifiers, diseases, PubMed identifiers and evidence.
+
+The viruses whose genomes come from environmental samples (metagenomes) have as host the root of the taxonomy (taxon `1`, `root`), with the kind of sample (e.g. `Soil`, `Marine`, `Organismal`) as edge property: this node is kept, and it joins all these viruses. Some viruses are hosts of other viruses (e.g. satellite viruses and virophages), so that a few edges join two viruses, and the networks are not strictly bipartite. The viruses without any host given are isolated nodes. A virus and a host are joined by several edges if they are listed several times (e.g. with different kinds of samples).
+
+The networks have the following properties:
+
+- `tax_id`: the NCBI taxonomy identifier of the virus or host.
+- `is_virus`: whether the node is a virus (otherwise it is only a host).
+- `name`: the name of the virus or host.
+- `lineage`: the taxonomic lineage of the virus or host, separated by `; `.
+- `refseq_ids`: the RefSeq (or GenBank) accessions of the genome of the virus, separated by `, ` (empty for hosts).
+- `kegg_genome`, `kegg_disease`, `disease`: the KEGG GENOME and KEGG DISEASE identifiers of the virus, and the diseases it causes (empty if none, and for hosts).
+- Edge `pmids` (from `release_77`): the PubMed identifiers of the articles on the association, separated by `, `.
+- Edge `evidence` (from `release_77`): the sources of the host information (`RefSeq`, `Literature`, `UniProt`, `NCBI Virus`, ...), separated by `, `.
+- Edge `sample_type` (from `release_89`): the kind of environmental sample of the genome, if the host is the root of the taxonomy.
+- Edge `source_organism` (from `release_89`): the taxonomy identifier of the organism of the sample, for organismal samples (-1 if not given).
+- `refseq_release`, `refseq_date` (graph): the RefSeq release on which the network is based, and its date (empty if not given)."""
+tags = ['Biological', 'Ecological', 'Unweighted', 'Multigraph', 'Metadata']
+url = ['https://www.genome.jp/virushostdb/', 'https://www.genome.jp/ftp/db/virushostdb/']
+citation = [('T. Mihara, Y. Nishimura, Y. Shimizu, H. Nishiyama, G. Yoshikawa, H. Uehara, P. Hingamp, S. Goto, H. Ogata, "Linking Virus Genomes with Host Taxonomy." Viruses 8, 66 (2016)',
+ 'https://doi.org/10.3390/v8030066')]
+icon_hash = None
+upstream_license = ('Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)',
+ 'https://creativecommons.org/licenses/by-nc-sa/4.0/')
+# The archived releases (the files of the main directory are replaced at
+# every release)
+upstream_prefix = 'https://www.genome.jp/ftp/db/virushostdb/old'
+# The directory of each archived release, with the RefSeq release and its
+# date (from the file dbrel.txt of the directory; release 75 has none). The
+# directory 232 holds the same data as 233, and the file of 230 is empty:
+# both are left out.
+releases = {
+ 75: ("75", ""), 77: ("77", "June 29, 2016"), 78: ("78", "September 6, 2016"),
+ 79: ("79", "October 31, 2016"), 80: ("80", "January 9, 2017"),
+ 81: ("81", "March 6, 2017"), 82: ("82", "May 8, 2017"),
+ 83: ("83", "July, 2017"), 84: ("84", "September 11, 2017"),
+ 85: ("85", "November 6, 2017"), 86: ("86", "January 8, 2018"),
+ 87: ("87", "March 5, 2018"), 88: ("88", "May 14, 2018"),
+ 89: ("89", "July 9, 2018"), 90: ("90", "September 10, 2018"),
+ 91: ("91", "November 5, 2018"), 92: ("92", "January 4, 2019"),
+ 93: ("93", "March 13, 2019"), 94: ("94", "May 13, 2019"),
+ 95: ("95", "July 8, 2019"), 96: ("96", "September 9, 2019"),
+ 98: ("98", "January 6, 2020"), 99: ("99", "March 2, 2020"),
+ 200: ("200", "May 4, 2020"), 202: ("202", "September 8, 2020"),
+ 203: ("203", "November 2, 2020"), 204: ("204", "January 4, 2021"),
+ 205: ("205", "March 1, 2021"), 206: ("206", "May 17, 2021"),
+ 207: ("207", "July 12, 2021"), 208: ("208", "September 7, 2021"),
+ 209: ("209", "November 1, 2021"), 210: ("210", "January 3, 2022"),
+ 211: ("211", "March 7, 2022"), 212: ("212", "May 2, 2022"),
+ 213: ("213", "July 11, 2022"), 214: ("214", "September 12, 2022"),
+ 215: ("215", "November 7, 2022"), 216: ("216", "January 9, 2023"),
+ 217: ("217", "March 8, 2023"), 218: ("218", "May 1, 2023"),
+ 219: ("219", "July 10, 2023"), 220: ("220", "September 5, 2023"),
+ 221: ("221", "November 6, 2023"), 222: ("222", "January 8, 2024"),
+ 224: ("223", "March 4, 2024"), 225: ("225", "July 8, 2024"),
+ 226: ("226", "September 9, 2024"), 227: ("227", "November 4, 2024"),
+ 228: ("228", "January 3, 2025"), 229: ("229", "March 3, 2025"),
+ 231: ("231", "July 7, 2025"),
+ 233: ("233", "January 26, 2026"), 235: ("235", "May 11, 2026"),
+ 236: ("236", "July 6, 2026")}
+files = [(f"release{r}/virushostdb.tsv", f"release_{r}", None) for r in releases]
+
+def fetch_upstream(force=False):
+ return fetch_upstream_files(__name__.split(".")[-1], upstream_prefix, files,
+ force)
+
+@cache_network()
+@coerce_props()
+@annotate()
+def parse(alts=None):
+ name = __name__.split(".")[-1]
+ for rel, (refseq, date) in releases.items():
+ alt = f"release_{rel}"
+ if alts is not None and alt not in alts:
+ continue
+ vidx = {}
+ vinfo = []
+ edges = []
+ eprops = {k: [] for k in ("pmids", "evidence", "sample_type",
+ "source_organism")}
+ def vertex(tid, nm, lin):
+ v = vidx.get(tid)
+ if v is None:
+ v = vidx[tid] = len(vidx)
+ vinfo.append([tid, False, nm, lin, "", "", "", ""])
+ return v
+ with open_upstream_file(name, f"release{rel}/virushostdb.tsv", "r") as f:
+ reader = csv.reader(f, delimiter="\t", quoting=csv.QUOTE_NONE)
+ header = next(reader)
+ # (the first release, 75, has only some of the columns)
+ col = {c: i for i, c in enumerate(header)}
+ assert {"virus tax id", "virus name", "virus lineage", "refseq id",
+ "host tax id", "host name", "host lineage"} <= set(col), header
+ def get(r, c):
+ return r[col[c]] if c in col else ""
+ old = "pmid" not in col
+ new = "sample type" in col
+ for r in reader:
+ if not r:
+ continue
+ assert len(r) == len(header), r
+ u = vertex(int(get(r, "virus tax id")), get(r, "virus name"),
+ get(r, "virus lineage"))
+ vinfo[u][1] = True
+ vinfo[u][4:8] = [get(r, c) for c in ("refseq id", "KEGG GENOME",
+ "KEGG DISEASE", "DISEASE")]
+ if not get(r, "host tax id"):
+ continue
+ w = vertex(int(get(r, "host tax id")), get(r, "host name"),
+ get(r, "host lineage"))
+ edges.append((u, w))
+ eprops["pmids"].append(get(r, "pmid"))
+ eprops["evidence"].append(get(r, "evidence"))
+ if new:
+ eprops["sample_type"].append(get(r, "sample type"))
+ so = get(r, "source organism")
+ eprops["source_organism"].append(int(so) if so else -1)
+ g = Graph(directed=False)
+ g.add_vertex(len(vidx))
+ g.add_edge_list(numpy.array(edges, dtype="int64").reshape(-1, 2))
+ for i, (k, t) in enumerate([("tax_id", "int64_t"), ("is_virus", "bool"),
+ ("name", "string"), ("lineage", "string"),
+ ("refseq_ids", "string"),
+ ("kegg_genome", "string"),
+ ("kegg_disease", "string"),
+ ("disease", "string")]):
+ g.vp[k] = g.new_vp(t, vals=[x[i] for x in vinfo])
+ if not old:
+ g.ep.pmids = g.new_ep("string", vals=eprops["pmids"])
+ g.ep.evidence = g.new_ep("string", vals=eprops["evidence"])
+ if new:
+ g.ep.sample_type = g.new_ep("string", vals=eprops["sample_type"])
+ g.ep.source_organism = g.new_ep("int64_t", vals=eprops["source_organism"])
+ g.gp.refseq_release = g.new_gp("string", refseq)
+ g.gp.refseq_date = g.new_gp("string", date)
+ yield alt, g