History of celegans_contactome
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: celegans_contactome 9612d8a
diff --git a/entries/celegans_contactome/__init__.py b/entries/celegans_contactome/__init__.py
new file mode 100644
index 0000000..7da1f38
--- /dev/null
+++ b/entries/celegans_contactome/__init__.py
@@ -0,0 +1,126 @@
+#! /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 xml.etree.ElementTree as ET
+
+title = "C. elegans nerve ring membrane contacts (2021)"
+description = r"""Membrane contact networks ("contactomes") of the nerve ring, the main neuropil of the brain of the nematode *Caenorhabditis elegans*, reconstructed by Brittin et al. from the serial-section electron microscopy (EM) images of two animals, an L4 larva (series JSH) and an adult hermaphrodite (series N2U), originally imaged by White et al. (1986). The neurites of the cells were segmented in each EM section, and the membrane contacts between them were measured. Nodes are cells (neurons, identified by their standard names, e.g. `AVAL`), and an edge indicates that the membranes of two cells are adjacent.
+
+There are two kinds of networks:
+
+- `l4`, `adult`: the membrane contacts of each animal, aggregated over the EM sections (410 sections for the L4 animal, 302 for the adult). These are undirected.
+- `reference_*`: the authors' graphs of the connections classified by their reproducibility across the four available nerve ring sides (left and right of the two animals, with left and right cells mapped onto each other): `*_delta1` to `*_delta4` contain the connections found in exactly $\delta$ = 1 to 4 of the four sides:
+ - `reference_contact_delta*`: membrane contacts, after removing the contacts below the 35th percentile of contact area (`reference_contact_delta4_classes`: the same for $\delta=4$, with the left and right cells of each class merged into a single node);
+ - `reference_chemical_delta*`: chemical synapses (directed), computed by the authors on the membrane contacts with $\delta=4$; `reference_chemical_delta*_pruned` leaves out the C1 polyadic synapses (see the paper);
+ - `reference_gap_delta*`: gap junctions (undirected), likewise.
+
+The reference graphs include the nodes `PVR`, `PVNL`, `HSNL`, `PVR.`, `HSNR`, `SABD`, `VB02`, `PLNL`, `PLNR` and `PVNR`, which the authors remove for their "restricted" data set (they are kept here, as in the upstream files). The membrane contacts of individual animals with their synapses are also available upstream as SQL databases, which are not included here (the synapses of both animals are in the `celegans_2019` entry).
+
+The networks have the following properties:
+
+- `name`: the name of the cell (or of the cell class, in `reference_contact_delta4_classes`).
+- `weight` (edges): in `l4` and `adult`, the total membrane contact area in μm² (the number of adjacent pixels, scaled by $4.5\times 10^{-4}$); in the reference graphs, the average over the $\delta$ sides where the connection is found of the membrane contact in pixels (contacts) or of the number of EM sections with synapses (chemical synapses and gap junctions), as given by the authors (whose documentation describes it as the total).
+- `pixels` (edges, `l4` and `adult` only): the total membrane contact in pixels.
+- `sections` (edges, `l4` and `adult` only): the number of EM sections in which the two cells are in contact."""
+tags = ['Biological', 'Connectome', 'Weighted', 'Metadata']
+url = 'https://doi.org/10.5281/zenodo.4763083'
+citation = [('C. A. Brittin, S. J. Cook, D. H. Hall, S. W. Emmons, N. Cohen, "A multi-scale brain map derived from whole-brain volumetric reconstructions." Nature 591, 105-110 (2021)', 'https://doi.org/10.1038/s41586-021-03284-x')]
+icon_hash = None
+upstream_license = ("CC BY 4.0", "https://creativecommons.org/licenses/by/4.0/")
+# Zenodo record 4763083 (version 1.1.0)
+upstream_prefix = 'https://zenodo.org/api/records/4763083/files'
+_adj = 'adjacency.tar.bz2/content:tar.bz2:adjacency'
+_ref = 'reference_graphs.tar.bz2/content:tar.bz2:reference_graphs'
+files = [(f"{_adj}/adjacency_l4.xml", "l4", None),
+ (f"{_adj}/adjacency_adult.xml", "adult", None)]
+files += [(f"{_ref}/reference_graph_adj_l35_delta{d}.graphml",
+ f"reference_contact_delta{d}", None) for d in range(1, 5)]
+files += [(f"{_ref}/reference_graph_adj_l35_delta4_cl.graphml",
+ "reference_contact_delta4_classes", None)]
+files += [(f"{_ref}/reference_graph_chem_delta{d}{p}.graphml",
+ f"reference_chemical_delta{d}{p}", None)
+ for d in range(1, 5) for p in ["", "_pruned"]]
+files += [(f"{_ref}/reference_graph_gap_delta{d}.graphml",
+ f"reference_gap_delta{d}", None) for d in range(1, 5)]
+
+def fetch_upstream(force=False):
+ return fetch_upstream_files(__name__.split(".")[-1], upstream_prefix, files,
+ force)
+
+# pixels to μm²
+pixel_area = 4.5e-4
+
+@cache_network()
+@coerce_props()
+@annotate()
+def parse(alts=None):
+ name = __name__.split(".")[-1]
+ for fname, alt, fmt in files:
+ if alts is not None and alt not in alts:
+ continue
+ g = Graph(directed="chemical" in alt)
+ vs = {}
+ def vertex(c):
+ if c not in vs:
+ vs[c] = g.add_vertex()
+ return vs[c]
+ g.vp.name = g.new_vp("string")
+ with open_upstream_file(name, fname, "rb") as f:
+ if alt in ["l4", "adult"]:
+ pixels = {}
+ sections = {}
+ layer = None
+ for ev, el in ET.iterparse(f, events=("start", "end")):
+ if ev == "start":
+ if el.tag == "layer":
+ layer = el.get("name")
+ continue
+ if el.tag == "area":
+ c1 = el.findtext("cell1")
+ c2 = el.findtext("cell2")
+ e = tuple(sorted((c1, c2)))
+ pixels[e] = pixels.get(e, 0) + int(el.findtext("adjacency"))
+ sections.setdefault(e, set()).add(layer)
+ elif el.tag == "layer":
+ el.clear()
+ g.ep.weight = g.new_ep("double")
+ g.ep.pixels = g.new_ep("int64_t")
+ g.ep.sections = g.new_ep("int")
+ for (c1, c2), p in pixels.items():
+ e = g.add_edge(vertex(c1), vertex(c2))
+ g.ep.weight[e] = p * pixel_area
+ g.ep.pixels[e] = p
+ g.ep.sections[e] = len(sections[(c1, c2)])
+ else:
+ ns = "{http://graphml.graphdrawing.org/xmlns}"
+ root = ET.parse(f).getroot()
+ for n in root.iter(f"{ns}node"):
+ vertex(n.get("id"))
+ g.ep.weight = g.new_ep("double")
+ wkey = [k.get("id") for k in root.iter(f"{ns}key")
+ if k.get("attr.name") == "weight"][0]
+ for el in root.iter(f"{ns}edge"):
+ e = g.add_edge(vertex(el.get("source")),
+ vertex(el.get("target")))
+ for d in el.iter(f"{ns}data"):
+ if d.get("key") == wkey:
+ g.ep.weight[e] = float(d.text)
+ for c, v in vs.items():
+ g.vp.name[v] = c
+ yield alt, g