logo Netzschleuder network catalog, repository, and centrifuge

History of fedivertex

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 by Tiago de Paula Peixoto 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/fedivertex/__init__.py b/entries/fedivertex/__init__.py
index 8534b71..4b0c0c1 100644
--- a/entries/fedivertex/__init__.py
+++ b/entries/fedivertex/__init__.py
@@ -45,7 +45,7 @@ citation = [('M. Damie, E. Cyffers, "Fedivertex: a Graph Dataset based on Decent
              'https://doi.org/10.1145/3774904.3792868')]
 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/')
-icon_hash = None
+date_added = '2026-10-31'
 
 # Zenodo record of the complete dataset, version 2026.04 (3.75 GB zip); the
 # reduced variant (19705753) leaves out the Mastodon federation graphs
[entries] Prefix the properties in the descriptions with (Node), (Edge) or (Graph) by Tiago de Paula Peixoto 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/fedivertex/__init__.py b/entries/fedivertex/__init__.py
index 9eddd61..8534b71 100644
--- a/entries/fedivertex/__init__.py
+++ b/entries/fedivertex/__init__.py
@@ -33,9 +33,12 @@ The dataset contains 572 weekly graphs; this entry keeps, for each kind of graph
 
 The networks have the following properties:
 
-- Nodes: `host`, the host name of the instance; `has_record`, whether it is in the file of instances; and the attributes of the instances given by their APIs, which depend on the software, e.g. `version`, `users`, `statuses` or `posts`, `comments`, `communities`, `languages`, `description_language`, `registration_enabled`, `users_active_day`, `users_active_week`, `users_active_month` and `users_active_half_year` (Lemmy), `captcha_enabled` and `require_email_verification` (Lemmy), `users_count` and `posts_count` (Misskey), `categories`, `totalUsers`, `totalDailyActiveUsers`, `totalWeeklyActiveUsers`, `totalMonthlyActiveUsers`, `totalLocalVideos`, `totalVideos`, `totalLocalPlaylists`, `totalVideoComments`, `totalLocalVideoComments`, `totalLocalVideoViews` and `serverVersion` (PeerTube), and `error`, the error of the crawler for the instance, if any (numerical values are NaN if unknown).
-- Edges: `weight`, as described above (1 for `federation` and `follow`).
-- Graph: `date`, the date of the crawl (YYYY-MM-DD)."""
+- (Node) `host`: the host name of the instance.
+- (Node) `has_record`: whether it is in the file of instances.
+- (Node) Further properties: the attributes of the instances given by their APIs, which depend on the software, e.g. `version`, `users`, `statuses` or `posts`, `comments`, `communities`, `languages`, `description_language`, `registration_enabled`, `users_active_day`, `users_active_week`, `users_active_month` and `users_active_half_year` (Lemmy), `captcha_enabled` and `require_email_verification` (Lemmy), `users_count` and `posts_count` (Misskey), `categories`, `totalUsers`, `totalDailyActiveUsers`, `totalWeeklyActiveUsers`, `totalMonthlyActiveUsers`, `totalLocalVideos`, `totalVideos`, `totalLocalPlaylists`, `totalVideoComments`, `totalLocalVideoComments`, `totalLocalVideoViews` and `serverVersion` (PeerTube) (numerical values are NaN if unknown).
+- (Node) `error`: the error of the crawler for the instance, if any.
+- (Edge) `weight`: as described above (1 for `federation` and `follow`).
+- (Graph) `date`: the date of the crawl (YYYY-MM-DD)."""
 tags = ['Social', 'Online', 'Technological', 'Weighted', 'Signed', 'Metadata']
 url = ['https://doi.org/10.5281/zenodo.19705211', 'https://pypi.org/project/fedivertex/']
 citation = [('M. Damie, E. Cyffers, "Fedivertex: a Graph Dataset based on Decentralized Social Media." Proceedings of the ACM Web Conference 2026, 8393-8396 (2026)',
[entries] Remove unneeded code from the entries by Tiago de Paula Peixoto 2c69f19
Unneeded "global files" statements, unused imports and variables,
f-strings without placeholders, duplicate keys in foodweb_little_rock
(keeping the order and the values used), and the second definition of
get_lines() in twitter_higgs. Nothing changes in the networks or
descriptions.

Changed: none
diff --git a/entries/fedivertex/__init__.py b/entries/fedivertex/__init__.py
index 3a51645..9eddd61 100644
--- a/entries/fedivertex/__init__.py
+++ b/entries/fedivertex/__init__.py
@@ -100,7 +100,6 @@ def column_prop(g, n, cname, vals):
 @coerce_props()
 @annotate()
 def parse(alts=None):
-    global files
     name = __name__.split(".")[-1]
     for (finst, fint), alt, fmt in files:
         if alts is not None and alt not in alts:
[entries] New entry: fedivertex by Tiago de Paula Peixoto 092cc80
diff --git a/entries/fedivertex/__init__.py b/entries/fedivertex/__init__.py
new file mode 100644
index 0000000..3a51645
--- /dev/null
+++ b/entries/fedivertex/__init__.py
@@ -0,0 +1,137 @@
+#! /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/>.
+
+from .. import *
+import pandas
+
+title = "Fedivertex: networks of Fediverse instances (2025-2026)"
+description = """Networks of the instances (servers) of seven decentralized social networks of the Fediverse (Mastodon, Misskey, Pleroma, Friendica, Lemmy, PeerTube and BookWyrm), from the Fedivertex dataset of Damie and Cyffers, who crawled the public APIs of the instances every week from February 2025 to April 2026 (some kinds of graphs only until July 2025). Each node is an instance, identified by its host name, and the kinds of networks are:
+
+- `federation` (BookWyrm, Friendica, Lemmy, Misskey, Mastodon, Pleroma): an edge from instance X to instance Y means that X lists Y among its known peers (instances with which it communicates). The authors treat these graphs as undirected; the edges are kept as listed (many in both directions).
+- `follow` (PeerTube): an edge from X to Y means that X follows Y (its users see the videos posted on Y).
+- `federation_with_blocks` (Lemmy): the federation graph, with negative edges (weight -1) from X to Y when X blocks Y.
+- `cross_instance` (Lemmy): two instances are connected if users of both posted in the same thread (possibly on a third instance), weighted by the number of such interactions; undirected for the authors, listed in both directions.
+- `intra_instance` (Lemmy): an edge from X to Y means that users of X posted on Y, weighted by their number.
+- `active_user` (Mastodon, Misskey, Pleroma): for the 10,000 most recently active users of each instance X, the accounts they follow, counted by the instance Y of the followed account: an edge from X to Y with this count as weight (with self-loops).
+
+The dataset contains 572 weekly graphs; this entry keeps, for each kind of graph and each month, the last weekly graph of the month (141 networks): from February 2025 to April 2026 for the graphs collected until 2026 (the last one of March 2026 is of March 9 for Friendica), and from February to July 2025 for the others. The networks are named `<software>_<kind>_<year>_<month>`, e.g. `mastodon_federation_2025_07`, and the date of each graph is the graph property `date`. The host names are given as in the dataset, with dots written as `[DOT]`. Instances referenced in the edges but missing from the file of instances are included without attributes (`has_record` false). The crawl of the Fediverse of 2018 is the entry `fediverse`.
+
+The networks have the following properties:
+
+- Nodes: `host`, the host name of the instance; `has_record`, whether it is in the file of instances; and the attributes of the instances given by their APIs, which depend on the software, e.g. `version`, `users`, `statuses` or `posts`, `comments`, `communities`, `languages`, `description_language`, `registration_enabled`, `users_active_day`, `users_active_week`, `users_active_month` and `users_active_half_year` (Lemmy), `captcha_enabled` and `require_email_verification` (Lemmy), `users_count` and `posts_count` (Misskey), `categories`, `totalUsers`, `totalDailyActiveUsers`, `totalWeeklyActiveUsers`, `totalMonthlyActiveUsers`, `totalLocalVideos`, `totalVideos`, `totalLocalPlaylists`, `totalVideoComments`, `totalLocalVideoComments`, `totalLocalVideoViews` and `serverVersion` (PeerTube), and `error`, the error of the crawler for the instance, if any (numerical values are NaN if unknown).
+- Edges: `weight`, as described above (1 for `federation` and `follow`).
+- Graph: `date`, the date of the crawl (YYYY-MM-DD)."""
+tags = ['Social', 'Online', 'Technological', 'Weighted', 'Signed', 'Metadata']
+url = ['https://doi.org/10.5281/zenodo.19705211', 'https://pypi.org/project/fedivertex/']
+citation = [('M. Damie, E. Cyffers, "Fedivertex: a Graph Dataset based on Decentralized Social Media." Proceedings of the ACM Web Conference 2026, 8393-8396 (2026)',
+             'https://doi.org/10.1145/3774904.3792868')]
+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/')
+icon_hash = None
+
+# Zenodo record of the complete dataset, version 2026.04 (3.75 GB zip); the
+# reduced variant (19705753) leaves out the Mastodon federation graphs
+upstream_prefix = 'https://zenodo.org/records/19705211/files'
+ZIP = 'Fedivertex-Complete-2026-04.zip'
+DIR = 'Fedivertex-Complete-2026-04'
+# The last weekly graph of each month, for each kind (from the listing of
+# the archive)
+snapshots = {
+    ("bookwyrm", "federation"): ["20250224", "20250331", "20250429", "20250527", "20250624", "20250721", "20250825", "20250929", "20251027", "20251124", "20251229", "20260126", "20260223", "20260330", "20260413"],
+    ("friendica", "federation"): ["20250224", "20250331", "20250429", "20250527", "20250623", "20250721", "20250825", "20250929", "20251027", "20251124", "20251229", "20260126", "20260223", "20260309", "20260414"],
+    ("lemmy", "cross_instance"): ["20250224", "20250331", "20250428", "20250526", "20250630", "20250714"],
+    ("lemmy", "federation"): ["20250224", "20250331", "20250428", "20250526", "20250630", "20250722", "20250825", "20250929", "20251027", "20251124", "20251229", "20260126", "20260223", "20260330", "20260413"],
+    ("lemmy", "federation_with_blocks"): ["20250224", "20250331", "20250428", "20250526", "20250630", "20250722", "20250826", "20250929", "20251027", "20251124", "20251229", "20260126", "20260223", "20260330", "20260413"],
+    ("lemmy", "intra_instance"): ["20250224", "20250331", "20250428", "20250526", "20250630", "20250714"],
+    ("mastodon", "active_user"): ["20250225", "20250325", "20250429", "20250528", "20250624", "20250715"],
+    ("mastodon", "federation"): ["20250225", "20250325", "20250429", "20250527", "20250624", "20250715"],
+    ("misskey", "active_user"): ["20250224", "20250331", "20250429", "20250527", "20250624", "20250715"],
+    ("misskey", "federation"): ["20250224", "20250331", "20250429", "20250527", "20250624", "20250715", "20250826", "20250929", "20251027", "20251124", "20251229", "20260126", "20260223", "20260330", "20260413"],
+    ("peertube", "follow"): ["20250224", "20250331", "20250428", "20250526", "20250630", "20250722", "20250826", "20250929", "20251027", "20251124", "20251229", "20260126", "20260223", "20260330", "20260413"],
+    ("pleroma", "active_user"): ["20250224", "20250331", "20250429", "20250527", "20250624", "20250715"],
+    ("pleroma", "federation"): ["20250224", "20250331", "20250429", "20250527", "20250624", "20250715", "20250826", "20250929", "20251027", "20251124", "20251229", "20260126", "20260223", "20260330", "20260413"],
+}
+files = [((f"{ZIP}:{DIR}/{sw}/{kind}/{d}/instances.csv",
+           f"{ZIP}:{DIR}/{sw}/{kind}/{d}/interactions.csv"),
+          f"{sw}_{kind}_{d[:4]}_{d[4:6]}", None)
+         for (sw, kind), dates in snapshots.items() for d in dates]
+
+# Number of edges read (None for all; set only for checks)
+_check_rows = None
+
+def fetch_upstream(force=False):
+    return fetch_upstream_files(__name__.split(".")[-1], upstream_prefix, files,
+                                force)
+
+def column_prop(g, n, cname, vals):
+    "Set a vertex property from the column values (strings) of the first n nodes."
+    s = pandas.Series(vals)
+    nonempty = s[s != ""]
+    if len(nonempty) and nonempty.isin(["True", "False"]).all():
+        p = g.new_vp("int16_t", val=-1)
+        p.a[:n] = s.map({"True": 1, "False": 0}).fillna(-1).astype("int16").values
+    else:
+        x = pandas.to_numeric(nonempty, errors="coerce")
+        if len(nonempty) and x.notna().all():
+            p = g.new_vp("double", val=numpy.nan)
+            p.a[:n] = pandas.to_numeric(s.replace("", numpy.nan)).values
+        else:
+            p = g.new_vp("string")
+            for i, x in enumerate(vals):
+                p[i] = x
+    g.vp[cname] = p
+
+@cache_network()
+@coerce_props()
+@annotate()
+def parse(alts=None):
+    global files
+    name = __name__.split(".")[-1]
+    for (finst, fint), alt, fmt in files:
+        if alts is not None and alt not in alts:
+            continue
+        with open_upstream_file(name, finst, "rb") as f:
+            inst = pandas.read_csv(f, dtype=str, keep_default_na=False)
+        with open_upstream_file(name, fint, "rb") as f:
+            d = pandas.read_csv(f, dtype={"Source": str, "Target": str,
+                                          "Weight": "int64"},
+                                keep_default_na=False, nrows=_check_rows)
+        inst = inst.drop(columns=[c for c in ("Id", "Label") if c in inst.columns])
+        inst = inst.drop_duplicates("host")
+        hosts = list(inst.host)
+        idx = {h: i for i, h in enumerate(hosts)}
+        n = len(hosts)
+        for h in pandas.concat([d.Source, d.Target]):
+            if h not in idx:
+                idx[h] = len(hosts)
+                hosts.append(h)
+        g = Graph(directed=True)
+        d8 = fint.split("/")[-2]
+        g.gp.date = g.new_gp("string", val=f"{d8[:4]}-{d8[4:6]}-{d8[6:]}")
+        g.add_vertex(len(hosts))
+        g.vp.host = g.new_vp("string", vals=hosts)
+        g.vp.has_record = g.new_vp("bool")
+        g.vp.has_record.a[:n] = True
+        for c in inst.columns:
+            if c != "host":
+                column_prop(g, n, c, list(inst[c]) + [""] * (len(hosts) - n))
+        g.ep.weight = g.new_ep("int64_t")
+        e = numpy.column_stack((d.Source.map(idx).values, d.Target.map(idx).values,
+                                d.Weight.values)).astype("int64")
+        g.add_edge_list(e, eprops=[g.ep.weight])
+        yield alt, g