History of trophich
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: trophich a97df55
diff --git a/entries/trophich/__init__.py b/entries/trophich/__init__.py
new file mode 100644
index 0000000..0a53849
--- /dev/null
+++ b/entries/trophich/__init__.py
@@ -0,0 +1,105 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2020-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/>.
+
+from .. import *
+import numpy as np
+import pandas as pd
+
+title = "trophiCH Swiss multi-trophic metaweb (2025)"
+description = r"""A metaweb of the trophic interactions among the terrestrial species of Switzerland (vertebrates, invertebrates and plants), "trophiCH", compiled by Reji Chacko et al. from the literature (over 700 sources: field studies, books, databases, expert knowledge). Nodes are taxa, mostly species, and a directed edge $(i, j)$ indicates that taxon $i$ consumes (preys on, feeds on, pollinates, parasitizes) taxon $j$. Interactions recorded in the literature at a coarser taxonomic level (e.g. a predator feeding on a genus or family) were assigned by the authors to the Swiss species of that taxon, and species without recorded diet were assigned the interactions of related species in some special cases.
+
+There are three networks, from the files of the data set (version 1.0):
+
+- `empirical`: the interactions as recorded in the sources (442,624 interactions), between taxa of various ranks.
+- `inferred`: the empirical interactions together with those inferred by assigning the targets recorded at the genus or family level to their Swiss species (1,181,686 interactions).
+- `final`: the final metaweb, which also includes the inferences for the special cases (1,188,049 interactions).
+
+Self-loops are cannibalism or feeding within the same taxon. A pair of taxa can be connected by more than one edge, with different life stages. Nodes are identified by their name and rank; only the taxa that take part in interactions are included (the authors' checklist has 24,167 taxa).
+
+The networks have the following properties:
+
+- `name`: the name of the taxon (e.g. `Abax parallelepipedus`, or a feeding group).
+- `rank`: the taxonomic rank of the taxon (e.g. `Species`, `Genus`, `Family`; empty for some non-taxonomic nodes, e.g. detritus).
+- `kingdom`, `phylum`, `class`, `order`, `family`, `genus`: the taxonomy of the taxon, from the authors' checklist of Swiss taxa; empty if the taxon is not in the checklist.
+- `habitat`: the habitats where the taxon occurs (e.g. `Forest; Grassland`), from the checklist; empty if not known.
+- `zone`: the vertical zones where the taxon occurs (e.g. `In ground; On ground or in leaf litter`), from the checklist; empty if not known.
+- `red_list`: the Swiss Red List category of the taxon (e.g. `LC`, `VU`, `NE` for not evaluated), from the checklist; empty if not known.
+- `interaction` (edges): the type of interaction (`Predation`, `Herbivory`, `Pollination`, `Parasitoidism`, `Parasitism`, `Kleptoparasitism`; empty if not given).
+- `source_life_stage`, `target_life_stage` (edges): the life stages of the consumer and of the resource to which the interaction applies (e.g. `Adult`, `Young`, `Young and Adult`); empty if not specified.
+- `citation` (edges): the identifier of the source of the interaction in the authors' list of references.
+- `inference` (edges, `inferred` and `final` only): how the interaction was inferred (e.g. `Target_Genus`: from an interaction recorded with the genus of the target); empty for the empirical interactions."""
+tags = ['Biological', 'Food web', 'Unweighted', 'Multigraph', 'Metadata']
+url = 'https://doi.org/10.16904/envidat.467'
+citation = [('M. Reji Chacko et al., "A species-level multi-trophic metaweb for Switzerland." Scientific Data 12, 1164 (2025)', 'https://doi.org/10.1038/s41597-025-05487-7')]
+icon_hash = None
+upstream_license = ("CC BY 4.0", "https://creativecommons.org/licenses/by/4.0/")
+# EnviDat doi:10.16904/envidat.467, resource "trophiCH 1.0" (the resource of
+# version 0.1 is no longer downloadable)
+upstream_prefix = 'https://www.envidat.ch/dataset/4f9547a1-3018-46f1-a399-09d65a3223f3/resource/6d7f57cc-a98f-4513-9c65-c0230a15bba7/download'
+_checklist = "trophich.zip:trophiCH/data/02_taxa_checklist.csv"
+files = [((f"trophich.zip:trophiCH/data/{f}.csv", _checklist), alt, None)
+ for f, alt in [("00_empirical_metaweb", "empirical"),
+ ("00_inferred_metaweb", "inferred"),
+ ("01_final_metaweb", "final")]]
+
+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 (fweb, fcheck), alt, fmt in files:
+ if alts is not None and alt not in alts:
+ continue
+ # the metawebs are encoded in Latin-1 (e.g. "Salix ×fragilis"), the
+ # checklist in UTF-8
+ with open_upstream_file(name, fweb, "rb") as f:
+ web = pd.read_csv(f, keep_default_na=False, dtype=str,
+ encoding="latin-1")
+ with open_upstream_file(name, fcheck, "rb") as f:
+ check = pd.read_csv(f, keep_default_na=False, dtype=str)
+ check = check.set_index("Taxon")
+
+ src = list(zip(web["Source_Name"], web["Source_Rank"]))
+ tgt = list(zip(web["Target_Name"], web["Target_Rank"]))
+ nodes = sorted(set(src) | set(tgt))
+ idx = {n: i for i, n in enumerate(nodes)}
+ g = Graph()
+ g.add_vertex(len(nodes))
+ g.vp.name = g.new_vp("string", vals=[n for n, r in nodes])
+ g.vp.rank = g.new_vp("string", vals=[r for n, r in nodes])
+ info = check.reindex([n for n, r in nodes]).fillna("")
+ for col, p in [("Kingdom", "kingdom"), ("Phylum", "phylum"),
+ ("Class", "class"), ("Order", "order"),
+ ("Family", "family"), ("Genus", "genus"),
+ ("Habitat", "habitat"), ("Zone", "zone"),
+ ("CAT", "red_list")]:
+ g.vp[p] = g.new_vp("string", vals=info[col].tolist())
+
+ g.add_edge_list(np.array([[idx[s], idx[t]] for s, t in zip(src, tgt)],
+ dtype="int64").reshape(-1, 2))
+ for col, p in [("Interaction_Type", "interaction"),
+ ("Source_Life_Stage", "source_life_stage"),
+ ("Target_Life_Stage", "target_life_stage"),
+ ("Citation", "citation"), ("Inference", "inference")]:
+ if col in web.columns:
+ g.ep[p] = g.new_ep("string", vals=web[col].tolist())
+ yield alt, g