History of humap_complexes
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] Add the date when each entry was added, and remove icon_hash beff7c9
The dates are those of the commits that first added the entries' files (2020-08-11 for the entries of the initial commit); renamed entries keep the date of their first name. Changed: none
diff --git a/entries/humap_complexes/__init__.py b/entries/humap_complexes/__init__.py
index 9d24ecb..2c457f1 100644
--- a/entries/humap_complexes/__init__.py
+++ b/entries/humap_complexes/__init__.py
@@ -54,7 +54,7 @@ citation = [('K. Drew, C. Lee, R. L. Huizar, F. Tu, B. Borgeson, C. D. McWhite,
'https://doi.org/10.15252/msb.202010016'),
('S. N. Fischer, E. R. Claussen, S. Kourtis, S. Sdelci, S. Orchard, H. Hermjakob, G. Kustatscher, K. Drew, "hu.MAP3.0: atlas of human protein complexes by integration of >25,000 proteomic experiments." Molecular Systems Biology 21 (2025)',
'https://doi.org/10.1038/s44320-025-00121-5')]
-icon_hash = None
+date_added = '2026-10-31'
# the sites of hu.MAP 1.0, 2.0 and 3.0 state CC0 (with credit requested); the
# Zenodo record of the files of hu.MAP 2.0 states CC BY 4.0
upstream_license = ('Creative Commons Zero (CC0), credit requested (hu.MAP 2.0 files on Zenodo: CC BY 4.0)',
[entries] Prefix the properties in the descriptions with (Node), (Edge) or (Graph) bc3dbbd
The lists of properties at the end of the descriptions marked edge properties with "Edge" but left node properties unmarked, or grouped them in "Nodes: `a`, ...; `b`, ..." items, so the kind of a property was often unclear. Every item now starts with (Node), (Edge) or (Graph), followed by the property names, a colon and the explanation, one item per property or group of properties; qualifications (kinds of nodes, networks) follow the names in parentheses. Properties that were only named got a short explanation, and a few items that named the wrong kind of property were corrected.
diff --git a/entries/humap_complexes/__init__.py b/entries/humap_complexes/__init__.py
index 0b193d2..9d24ecb 100644
--- a/entries/humap_complexes/__init__.py
+++ b/entries/humap_complexes/__init__.py
@@ -38,12 +38,12 @@ The training and test sets of complexes and interactions, the feature matrices,
The networks have the following properties:
-- `uniprot`: the UniProt accession of the protein (empty for complexes, and for the proteins of hu.MAP 1.0 without one; in 2.0 and 3.0, the identifier given instead of an accession for a few proteins, e.g. an NCBI Gene identifier).
-- `gene_id` (`v1_*`): the identifier of the protein in hu.MAP 1.0: its NCBI Gene identifier (an Ensembl gene identifier for 40 proteins).
-- `name`: the gene name of the protein (several names separated by `; ` in 3.0; empty if not given, e.g. for the proteins of the `all_pairs` networks that are in no complex), or the identifier of the complex (e.g. `HuMAP2_00000`, `huMAP3_00000.1`; in 1.0, the number of the complex, from 0, in the order of the file).
-- `is_protein` (`*_complexes`): whether the node is a protein (otherwise it is a complex).
-- `confidence` (`v2_complexes`, `v3_complexes`): the confidence of the complex: the number of the most stringent of the clusterings in which it was found, from 1 (extremely high) to 5 (medium) in 2.0, and to 6 (medium) in 3.0 (0 for proteins).
-- Edge `probability` (`*_pairs`): the probability that the two proteins interact, according to the classifier."""
+- (Node) `uniprot`: the UniProt accession of the protein (empty for complexes, and for the proteins of hu.MAP 1.0 without one; in 2.0 and 3.0, the identifier given instead of an accession for a few proteins, e.g. an NCBI Gene identifier).
+- (Node) `gene_id` (`v1_*`): the identifier of the protein in hu.MAP 1.0: its NCBI Gene identifier (an Ensembl gene identifier for 40 proteins).
+- (Node) `name`: the gene name of the protein (several names separated by `; ` in 3.0; empty if not given, e.g. for the proteins of the `all_pairs` networks that are in no complex), or the identifier of the complex (e.g. `HuMAP2_00000`, `huMAP3_00000.1`; in 1.0, the number of the complex, from 0, in the order of the file).
+- (Node) `is_protein` (`*_complexes`): whether the node is a protein (otherwise it is a complex).
+- (Node) `confidence` (`v2_complexes`, `v3_complexes`): the confidence of the complex: the number of the most stringent of the clusterings in which it was found, from 1 (extremely high) to 5 (medium) in 2.0, and to 6 (medium) in 3.0 (0 for proteins).
+- (Edge) `probability` (`*_pairs`): the probability that the two proteins interact, according to the classifier."""
tags = ['Biological', 'Protein interactions', 'Weighted', 'Bipartite', 'Multigraph', 'Metadata']
url = ['https://humap3.proteincomplexes.org/', 'https://humap3.proteincomplexes.org/download',
'https://humap2.proteincomplexes.org/download', 'https://zenodo.org/records/15293715',
[entries] New entry: humap_complexes faefe90
diff --git a/entries/humap_complexes/__init__.py b/entries/humap_complexes/__init__.py
new file mode 100644
index 0000000..0b193d2
--- /dev/null
+++ b/entries/humap_complexes/__init__.py
@@ -0,0 +1,281 @@
+#! /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
+
+import pyarrow
+import pyarrow.csv
+import pyarrow.compute as pc
+
+from .. import *
+
+title = "hu.MAP human protein complexes, versions 1.0, 2.0 and 3.0 (2017-2024)"
+description = r"""Networks of human protein complexes and protein interactions from the three versions of hu.MAP, a map of human protein complexes obtained by integrating mass spectrometry proteomic experiments: hu.MAP 1.0 (2017; more than 9,000 experiments: co-fractionation, and affinity purification of tagged proteins of BioPlex and of Hein et al.), hu.MAP 2.0 (2020/2021; more than 15,000 experiments, adding proximity labeling (BioID), RNA pull-down and further affinity purification data) and hu.MAP 3.0 (2024; more than 25,000 experiments). In each version, a machine learning classifier, trained on known complexes (CORUM in 1.0 and 2.0, the Complex Portal in 3.0), gives the probability that two proteins interact (are in the same complex); the network of these interactions was then clustered, in two stages and with several parameters, to identify the complexes.
+
+The networks are named by the version (`v1_`, `v2_`, `v3_`) and the kind of network:
+
+- `v1_complexes`, `v2_complexes`, `v3_complexes`: bipartite network of proteins and the complexes in which they are members (a protein can be in several complexes): 4,659 complexes in 1.0, 6,965 in 2.0 and 15,326 in 3.0, with the confidence of each complex in 2.0 and 3.0.
+- `v1_complex_pairs`, `v3_complex_pairs`: network of the pairs of proteins of the same complexes, weighted by the predicted probability that they interact. (hu.MAP 2.0 does not distribute this network as a file, only within its Cytoscape session; it is left out.)
+- `v2_all_pairs`, `v3_all_pairs`: network of all the pairs of proteins for which the classifier gave a prediction (of August 2020 and May 2023), weighted by the predicted probability that they interact; most pairs have a low probability. (hu.MAP 1.0 does not distribute these predictions, only the feature matrix.)
+
+The training and test sets of complexes and interactions, the feature matrices, the enrichment tables and the Cytoscape sessions are left out. The data of hu.MAP 2.0 are kept as given, with their repetitions, which come from proteins (gene identifiers) mapped to the same UniProt accession: 34 complexes of `v2_complexes` list a protein twice (parallel edges), and in `v2_all_pairs` 66,458 pairs of proteins appear twice, with different probabilities, and 21 pairs join a protein to itself (self-loops).
+
+The networks have the following properties:
+
+- `uniprot`: the UniProt accession of the protein (empty for complexes, and for the proteins of hu.MAP 1.0 without one; in 2.0 and 3.0, the identifier given instead of an accession for a few proteins, e.g. an NCBI Gene identifier).
+- `gene_id` (`v1_*`): the identifier of the protein in hu.MAP 1.0: its NCBI Gene identifier (an Ensembl gene identifier for 40 proteins).
+- `name`: the gene name of the protein (several names separated by `; ` in 3.0; empty if not given, e.g. for the proteins of the `all_pairs` networks that are in no complex), or the identifier of the complex (e.g. `HuMAP2_00000`, `huMAP3_00000.1`; in 1.0, the number of the complex, from 0, in the order of the file).
+- `is_protein` (`*_complexes`): whether the node is a protein (otherwise it is a complex).
+- `confidence` (`v2_complexes`, `v3_complexes`): the confidence of the complex: the number of the most stringent of the clusterings in which it was found, from 1 (extremely high) to 5 (medium) in 2.0, and to 6 (medium) in 3.0 (0 for proteins).
+- Edge `probability` (`*_pairs`): the probability that the two proteins interact, according to the classifier."""
+tags = ['Biological', 'Protein interactions', 'Weighted', 'Bipartite', 'Multigraph', 'Metadata']
+url = ['https://humap3.proteincomplexes.org/', 'https://humap3.proteincomplexes.org/download',
+ 'https://humap2.proteincomplexes.org/download', 'https://zenodo.org/records/15293715',
+ 'https://hu1.proteincomplexes.org/download']
+citation = [('K. Drew, C. Lee, R. L. Huizar, F. Tu, B. Borgeson, C. D. McWhite, Y. Ma, J. B. Wallingford, E. M. Marcotte, "Integration of over 9,000 mass spectrometry experiments builds a global map of human protein complexes." Molecular Systems Biology 13, 932 (2017)',
+ 'https://doi.org/10.15252/msb.20167490'),
+ ('K. Drew, J. B. Wallingford, E. M. Marcotte, "hu.MAP 2.0: integration of over 15,000 proteomic experiments builds a global compendium of human multiprotein assemblies." Molecular Systems Biology 17, e10016 (2021)',
+ 'https://doi.org/10.15252/msb.202010016'),
+ ('S. N. Fischer, E. R. Claussen, S. Kourtis, S. Sdelci, S. Orchard, H. Hermjakob, G. Kustatscher, K. Drew, "hu.MAP3.0: atlas of human protein complexes by integration of >25,000 proteomic experiments." Molecular Systems Biology 21 (2025)',
+ 'https://doi.org/10.1038/s44320-025-00121-5')]
+icon_hash = None
+# the sites of hu.MAP 1.0, 2.0 and 3.0 state CC0 (with credit requested); the
+# Zenodo record of the files of hu.MAP 2.0 states CC BY 4.0
+upstream_license = ('Creative Commons Zero (CC0), credit requested (hu.MAP 2.0 files on Zenodo: CC BY 4.0)',
+ 'https://creativecommons.org/publicdomain/zero/1.0/')
+
+# hu.MAP 3.0
+upstream_prefix = 'https://humap3.proteincomplexes.org/static/downloads/humap3'
+COMPLEXES = "hu.MAP3.0_complexes_wConfidenceScores_total15326_wGenenames_20240922.csv"
+PAIRS = "humap3_complex_pairs_20240929.pairsWprob"
+# the same pairs as PAIRS, in the same order, with gene names
+GENE_PAIRS = "hu.MAP3.0_complex_pairs_genenames_20240930.pairsWprob"
+v3_files = [((COMPLEXES, PAIRS, GENE_PAIRS), "v3_complexes", None),
+ ((GENE_PAIRS, PAIRS), "v3_complex_pairs", None),
+ ((PAIRS, GENE_PAIRS, "humap3_all_ppis_202305.pairsWprob.gz"), "v3_all_pairs", None)]
+
+# hu.MAP 2.0 (the files of its download page, on Zenodo)
+v2_prefix = 'https://zenodo.org/records/15293715/files'
+V2_COMPLEXES = "humap2_complexes_20200809.txt"
+V2_PAIRS = "humap2_ppis_ACC_20200821.pairsWprob.gz"
+V2_GENE_PAIRS = "humap2_ppis_genename_20200821.pairsWprob.gz"
+v2_files = [((V2_COMPLEXES, V2_PAIRS, V2_GENE_PAIRS), "v2_complexes", None),
+ ((V2_PAIRS, V2_GENE_PAIRS), "v2_all_pairs", None)]
+
+# hu.MAP 1.0
+v1_prefix = 'https://hu1.proteincomplexes.org/static'
+V1_COMPLEXES = "downloads/clusters.txt"
+# the same complexes, with gene names
+V1_GENE_COMPLEXES = "downloads/genename_clusters.txt"
+V1_PAIRS = "downloads/pairsWprob.txt"
+# the UniProt accessions
+V1_NODES = "downloads/nodeTable.txt"
+v1_files = [((V1_COMPLEXES, V1_GENE_COMPLEXES, V1_NODES), "v1_complexes", None),
+ ((V1_PAIRS, V1_COMPLEXES, V1_GENE_COMPLEXES, V1_NODES), "v1_complex_pairs", None)]
+
+files = v1_files + v2_files + v3_files
+
+def fetch_upstream(force=False):
+ name = __name__.split(".")[-1]
+ fetch_upstream_files(name, v1_prefix, v1_files, force)
+ fetch_upstream_files(name, v2_prefix, v2_files, force)
+ return fetch_upstream_files(name, upstream_prefix, v3_files, force)
+
+# for checks: stop after this many lines of each file of pairs (None: all)
+_check_rows = None
+
+def read_complexes(name, fname, header):
+ "The rows (id, confidence, accessions) of a complexes file of 2.0 or 3.0."
+ rows = []
+ with open_upstream_file(name, fname, "r") as f:
+ h = next(f).strip().split(",")
+ assert h == header, h
+ for l in f:
+ cid, conf, accs, genes = l.rstrip("\r\n").split(",")
+ rows.append((cid, int(conf), accs.split(" ")))
+ return rows
+
+def read_v1_complexes(name):
+ """The complexes of hu.MAP 1.0 (lists of gene identifiers), and the gene
+ names (from the version of the file with gene names, whose lines and
+ columns correspond) and UniProt accessions (from the node table) of the
+ proteins."""
+ complexes = []
+ gene = {}
+ with open_upstream_file(name, V1_COMPLEXES, "r") as f, \
+ open_upstream_file(name, V1_GENE_COMPLEXES, "r") as fg:
+ for l, lg in zip(f, fg, strict=True):
+ ids = l.split()
+ names = lg.rstrip("\r\n").split("\t")
+ assert len(ids) == len(names), (l, lg)
+ for x, n in zip(ids, names):
+ assert gene.setdefault(x, n) == n, (x, n)
+ complexes.append(ids)
+ uniprot = {}
+ with open_upstream_file(name, V1_NODES, "r") as f:
+ for r in csv.DictReader(f):
+ assert uniprot.setdefault(r["key"], r["acc"]) == r["acc"], r
+ return complexes, gene, uniprot
+
+def read_pair_lines(name, pairs, gene_pairs):
+ "The lines (a, b, name of a or b, name of b or a) of the two files of pairs."
+ with open_upstream_file(name, pairs, "r") as f, \
+ open_upstream_file(name, gene_pairs, "r") as fg:
+ for i, (l, lg) in enumerate(zip(f, fg, strict=True)):
+ if _check_rows is not None and i >= _check_rows:
+ break
+ a, b, p = l.rstrip("\n").split("\t")
+ r = lg.rstrip("\n").split("\t")
+ if len(r) == 2:
+ # a protein without a name
+ r = [r[0], "", r[1]]
+ ga, gb, pg = r
+ assert p == pg, (l, lg)
+ yield a, b, ga, gb
+
+def read_gene_names(name, pairs, gene_pairs):
+ """The gene names of the proteins, from the two versions of the file of
+ pairs (with UniProt accessions and with gene names), whose lines
+ correspond, but not always the order of the two proteins in them: the
+ name of a protein is the one common to all the lines in which it appears
+ (and, if it is ambiguous, the one not taken by its partner). The names of
+ the complexes files cannot be matched to the accessions, since some
+ proteins lack a name there. (The files are read again for the ambiguous
+ ones, instead of being kept in memory.)"""
+ cand = {}
+ for a, b, ga, gb in read_pair_lines(name, pairs, gene_pairs):
+ for x in (a, b):
+ if x in cand:
+ cand[x] &= {ga, gb}
+ else:
+ cand[x] = {ga, gb}
+ gene = {x: next(iter(c)) for x, c in cand.items() if len(c) == 1}
+ changed = True
+ while changed and len(gene) < len(cand):
+ changed = False
+ for a, b, ga, gb in read_pair_lines(name, pairs, gene_pairs):
+ for x, y in ((a, b), (b, a)):
+ if x not in gene and y in gene and len(cand[x]) == 2:
+ rest = cand[x] - {gene[y]}
+ if len(rest) == 1:
+ gene[x] = next(iter(rest))
+ changed = True
+ return gene
+
+def read_pairs(name, fname):
+ "A table (a, b, p) of a file of pairs with probabilities."
+ with open_upstream_file(name, fname, "rb") as f:
+ r = pyarrow.csv.open_csv(
+ f,
+ read_options=pyarrow.csv.ReadOptions(column_names=["a", "b", "p"]),
+ parse_options=pyarrow.csv.ParseOptions(delimiter="\t",
+ quote_char=False),
+ convert_options=pyarrow.csv.ConvertOptions(
+ column_types={"a": pyarrow.string(), "b": pyarrow.string(),
+ "p": pyarrow.float64()}))
+ batches = []
+ n = 0
+ for batch in r:
+ batches.append(batch)
+ n += batch.num_rows
+ if _check_rows is not None and n >= _check_rows:
+ break
+ t = pyarrow.Table.from_batches(batches, schema=r.schema)
+ if _check_rows is not None:
+ t = t.slice(0, _check_rows)
+ return t
+
+def complexes_graph(prots, complexes, confidence=None):
+ """Bipartite network of the proteins (a dict of the ids to their indices)
+ and the complexes (lists of protein ids)."""
+ g = Graph(directed=False)
+ N = len(prots)
+ g.add_vertex(N + len(complexes))
+ g.add_edge_list([(prots[a], N + i) for i, accs in enumerate(complexes)
+ for a in accs])
+ g.vp.is_protein = g.new_vp("bool", vals=[True] * N + [False] * len(complexes))
+ if confidence is not None:
+ g.vp.confidence = g.new_vp("int", vals=[0] * N + confidence)
+ return g
+
+def pairs_graph(t):
+ "Network of the pairs of a table (a, b, p); returns it and the ids."
+ g = Graph(directed=False)
+ ids = pc.unique(pyarrow.chunked_array(t.column("a").chunks +
+ t.column("b").chunks))
+ g.add_vertex(len(ids))
+ g.add_edge_list(numpy.column_stack(
+ [pc.index_in(t.column("a"), value_set=ids).to_numpy(),
+ pc.index_in(t.column("b"), value_set=ids).to_numpy()]))
+ g.ep.probability = g.new_ep("double", vals=t.column("p").to_numpy())
+ return g, ids.to_pylist()
+
+@cache_network()
+@coerce_props()
+@annotate()
+def parse(alts=None):
+ name = __name__.split(".")[-1]
+ for fnames, alt, fmt in files:
+ if alts is not None and alt not in alts:
+ continue
+ version, kind = alt.split("_", 1)
+ if version == "v1":
+ complexes, gene, uniprot = read_v1_complexes(name)
+ if kind == "complexes":
+ prots = {}
+ for ids in complexes:
+ for a in ids:
+ prots.setdefault(a, len(prots))
+ g = complexes_graph(prots, complexes)
+ ids = list(prots)
+ cnames = [str(i) for i in range(len(complexes))]
+ else:
+ g, ids = pairs_graph(read_pairs(name, V1_PAIRS))
+ cnames = []
+ g.vp.uniprot = g.new_vp("string", vals=[uniprot.get(a, "") for a in ids] +
+ [""] * len(cnames))
+ g.vp.gene_id = g.new_vp("string", vals=ids + [""] * len(cnames))
+ g.vp.name = g.new_vp("string", vals=[gene.get(a, "") for a in ids] + cnames)
+ else:
+ if version == "v2":
+ cfile, pairs, gene_pairs = V2_COMPLEXES, V2_PAIRS, V2_GENE_PAIRS
+ header = ["HuMAP2_ID", "Confidence", "Uniprot_ACCs", "genenames"]
+ else:
+ cfile, pairs, gene_pairs = COMPLEXES, PAIRS, GENE_PAIRS
+ header = ["HuMAP3_ID", "ComplexConfidence", "Uniprot_ACCs",
+ "genenames"]
+ gene = read_gene_names(name, pairs, gene_pairs)
+ if kind == "complexes":
+ complexes = read_complexes(name, cfile, header)
+ prots = {}
+ for cid, conf, accs in complexes:
+ for a in accs:
+ prots.setdefault(a, len(prots))
+ g = complexes_graph(prots, [c[2] for c in complexes],
+ [c[1] for c in complexes])
+ ids = list(prots)
+ cnames = [c[0] for c in complexes]
+ else:
+ fname = {"v2_all_pairs": V2_PAIRS, "v3_complex_pairs": PAIRS,
+ "v3_all_pairs": "humap3_all_ppis_202305.pairsWprob.gz"}[alt]
+ g, ids = pairs_graph(read_pairs(name, fname))
+ cnames = []
+ g.vp.uniprot = g.new_vp("string", vals=ids + [""] * len(cnames))
+ g.vp.name = g.new_vp("string", vals=[gene.get(a, "") for a in ids] + cnames)
+ yield alt, g