logo Netzschleuder network catalog, repository, and centrifuge

History of us_county_fb

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/us_county_fb/__init__.py b/entries/us_county_fb/__init__.py
index b01a3e1..00d0546 100644
--- a/entries/us_county_fb/__init__.py
+++ b/entries/us_county_fb/__init__.py
@@ -52,7 +52,7 @@ url = ['https://www.cs.cornell.edu/~arb/data/US-county-fb/',
        'https://data.humdata.org/dataset/social-connectedness-index']
 citation = [('J. Jia and A. R. Benson, "A Unifying Generative Model for Graph Learning Algorithms: Label Propagation, Graph Convolutions, and Combinations." SIAM Journal on Mathematics of Data Science 4(1), 100-125 (2022).', 'https://doi.org/10.1137/21M1395351'),
             ('M. Bailey, R. Cao, T. Kuchler, J. Stroebel, A. Wong, "Social Connectedness: Measurement, Determinants, and Effects." Journal of Economic Perspectives 32(3), 259-280 (2018).', 'https://doi.org/10.1257/jep.32.3.259')]
-icon_hash = None
+date_added = '2026-10-27'
 # Jia and Benson's data state no license; HDX gives the index as CC0
 upstream_license = ("CC0 (sci); none stated (strong_ties)",
                     "https://creativecommons.org/publicdomain/zero/1.0/")
[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/us_county_fb/__init__.py b/entries/us_county_fb/__init__.py
index 6f56393..b01a3e1 100644
--- a/entries/us_county_fb/__init__.py
+++ b/entries/us_county_fb/__init__.py
@@ -38,15 +38,15 @@ The `sci` network contains all 3,204 counties and county equivalents of the file
 
 The `strong_ties` network has the following properties:
 
-- `fips`: the FIPS code of the county.
-- `sh050m`, `sh100m`, `sh500m`: the share of the Facebook friends of the county's users who live within 50, 100 and 500 miles (normalized).
-- `income_<year>`, `migration_<year>`, `birth_<year>`, `death_<year>`, `education_<year>`, `unemployment_<year>` (for the years 2012 and 2016): the median household income, the net migration rate, the birth and death rates, the education level and the unemployment rate of the county (normalized).
-- `election_<year>`: the margin of the presidential election in the county, (Republican votes - Democratic votes) / (Republican votes + Democratic votes) (normalized).
+- (Node) `fips`: the FIPS code of the county.
+- (Node) `sh050m`, `sh100m`, `sh500m`: the share of the Facebook friends of the county's users who live within 50, 100 and 500 miles (normalized).
+- (Node) `income_<year>`, `migration_<year>`, `birth_<year>`, `death_<year>`, `education_<year>`, `unemployment_<year>` (for the years 2012 and 2016): the median household income, the net migration rate, the birth and death rates, the education level and the unemployment rate of the county (normalized).
+- (Node) `election_<year>`: the margin of the presidential election in the county, (Republican votes - Democratic votes) / (Republican votes + Democratic votes) (normalized).
 
 The `sci` network has the following properties:
 
-- `fips`: the FIPS code of the county.
-- Edge `weight`: the scaled Social Connectedness Index of the two counties."""
+- (Node) `fips`: the FIPS code of the county.
+- (Edge) `weight`: the scaled Social Connectedness Index of the two counties."""
 tags = ['Social', 'Online', 'Political', 'Unweighted', 'Weighted', 'Metadata']
 url = ['https://www.cs.cornell.edu/~arb/data/US-county-fb/',
        'https://data.humdata.org/dataset/social-connectedness-index']
[entries] New entry: us_county_fb by Tiago de Paula Peixoto 7f45ebd
diff --git a/entries/us_county_fb/__init__.py b/entries/us_county_fb/__init__.py
new file mode 100644
index 0000000..6f56393
--- /dev/null
+++ b/entries/us_county_fb/__init__.py
@@ -0,0 +1,136 @@
+#! /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 csv
+import pandas
+import hashlib
+import io
+
+title = "US counties linked by Facebook social connectedness"
+description = r"""Networks of counties of the United States linked by the Social Connectedness Index of Facebook (the relative probability that two users living in the two counties are friends on Facebook):
+
+- `strong_ties`: the network compiled by J. Jia and A. R. Benson as a benchmark for graph-based regression of election outcomes, with demographic and election features of the counties.
+- `sci`: the complete, unthresholded county-to-county Social Connectedness Index as published by Meta (Data for Good) on the Humanitarian Data Exchange (HDX), weighted by the index.
+
+In `strong_ties`, nodes are counties (identified by their FIPS codes), and an undirected edge connects two counties with strong social ties according to the Social Connectedness Index of Facebook (the relative probability that two users living in the two counties are friends on Facebook): each county is connected to those it is most strongly tied to, about 20 per county according to the authors. The network is not planar, since strong ties also exist between distant counties. The edge list of the data contains 584 pairs of counties twice, once in each direction; they are merged into one undirected edge. The raw values of the Social Connectedness Index are not included in this network.
+
+The counties of `strong_ties` have demographic features from the US Department of Agriculture and the results of the presidential elections of 2012 and 2016 (formatted by T. McGovern), as well as features of the Facebook friendships. All features are normalized by the authors to zero mean and unit standard deviation over the counties; the raw values are not given.
+
+The `sci` network contains all 3,204 counties and county equivalents of the file (including those of Puerto Rico and the US Virgin Islands) and an edge for every pair of them, weighted by the scaled index (from 1 to 1,000,000,000), including a self-loop for each county (the connectedness of its users among themselves). The file gives each pair in both directions, with the same value; each pair is kept once. It is a later release of the index (built from a snapshot of Facebook friendships of December 2025 to January 2026, with a revised methodology) than the one used for `strong_ties`, which Meta no longer provides: older releases are not published, as the index is not meant to be compared over time. HDX replaces the file in place with new releases; the version of May 11, 2026 is used.
+
+The `strong_ties` network has the following properties:
+
+- `fips`: the FIPS code of the county.
+- `sh050m`, `sh100m`, `sh500m`: the share of the Facebook friends of the county's users who live within 50, 100 and 500 miles (normalized).
+- `income_<year>`, `migration_<year>`, `birth_<year>`, `death_<year>`, `education_<year>`, `unemployment_<year>` (for the years 2012 and 2016): the median household income, the net migration rate, the birth and death rates, the education level and the unemployment rate of the county (normalized).
+- `election_<year>`: the margin of the presidential election in the county, (Republican votes - Democratic votes) / (Republican votes + Democratic votes) (normalized).
+
+The `sci` network has the following properties:
+
+- `fips`: the FIPS code of the county.
+- Edge `weight`: the scaled Social Connectedness Index of the two counties."""
+tags = ['Social', 'Online', 'Political', 'Unweighted', 'Weighted', 'Metadata']
+url = ['https://www.cs.cornell.edu/~arb/data/US-county-fb/',
+       'https://data.humdata.org/dataset/social-connectedness-index']
+citation = [('J. Jia and A. R. Benson, "A Unifying Generative Model for Graph Learning Algorithms: Label Propagation, Graph Convolutions, and Combinations." SIAM Journal on Mathematics of Data Science 4(1), 100-125 (2022).', 'https://doi.org/10.1137/21M1395351'),
+            ('M. Bailey, R. Cao, T. Kuchler, J. Stroebel, A. Wong, "Social Connectedness: Measurement, Determinants, and Effects." Journal of Economic Perspectives 32(3), 259-280 (2018).', 'https://doi.org/10.1257/jep.32.3.259')]
+icon_hash = None
+# Jia and Benson's data state no license; HDX gives the index as CC0
+upstream_license = ("CC0 (sci); none stated (strong_ties)",
+                    "https://creativecommons.org/publicdomain/zero/1.0/")
+# The data of Jia and Benson (Google Drive), and the county-to-county file of
+# the Social Connectedness Index on HDX, which is replaced in place by new
+# releases: the version of May 11, 2026 is used, checked by its MD5 hash
+upstream_prefix = {'strong_ties': 'gdrive://',
+                   'sci': 'https://data.humdata.org/dataset/e9988552-74e4-4ff4-943f-c782ac8bca87/resource/97dc352f-c9c5-47d6-a6ef-88709e14006c/download'}
+_id = '141_PnOln0tlM2SjP8PndRexEL45J_Jl1'
+files = [((f'{_id}:zip:US-county-fb/US-county-fb-graph.txt',
+           f'{_id}:zip:US-county-fb/US-county-fb-2012-feats.csv',
+           f'{_id}:zip:US-county-fb/US-county-fb-2016-feats.csv'), 'strong_ties', None),
+         ('us_counties.csv', 'sci', None)]
+SCI_MD5 = '186ef44f829555ecf8051cb2e7ae0284'
+
+def fetch_upstream(force=False):
+    name = __name__.split(".")[-1]
+    for f in files:
+        fetch_upstream_files(name, upstream_prefix[f[1]], [f], force)
+
+def parse_sci(name, fname):
+    with open_upstream_file(name, fname, "rb") as f:
+        data = f.read()
+    if hashlib.md5(data).hexdigest() != SCI_MD5:
+        raise ValueError(f"{fname}: unexpected version (MD5 mismatch)")
+    with io.BytesIO(data) as f:
+        d = pandas.read_csv(f, usecols=["user_region", "friend_region", "scaled_sci"],
+                            dtype={"user_region": str, "friend_region": str,
+                                   "scaled_sci": "int64"})
+    del data
+    fips = numpy.sort(d.user_region.unique())
+    idx = pandas.Series(numpy.arange(len(fips)), index=fips)
+    u = idx.loc[d.user_region].to_numpy()
+    v = idx.loc[d.friend_region].to_numpy()
+    w = d.scaled_sci.to_numpy()
+    del d
+    # The index is symmetric and given for both directions of every pair (and
+    # for each county with itself): keep each pair once
+    m = u <= v
+    g = Graph(directed=False)
+    g.add_vertex(len(fips))
+    g.vp.fips = g.new_vp("int32_t", vals=fips.astype(int))
+    g.ep.weight = g.new_ep("int64_t")
+    g.add_edge_list(numpy.column_stack((u[m], v[m], w[m])), eprops=[g.ep.weight])
+    return g
+
+@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
+        if alt == "sci":
+            yield alt, parse_sci(name, fnames)
+            continue
+        feats = {}
+        for year, fname in zip((2012, 2016), fnames[1:]):
+            with open_upstream_file(name, fname, "r") as f:
+                feats[year] = list(csv.DictReader(f))
+        N = len(feats[2012])
+        fips = [int(r["FIPS"]) for r in feats[2012]]
+        assert fips == [int(r["FIPS"]) for r in feats[2016]]
+        with open_upstream_file(name, fnames[0], "rb") as f:
+            E = numpy.loadtxt(f, dtype="int64")
+        # the nodes of the edge list are the (1-based) rows of the feature files
+        assert E.min() >= 1 and E.max() <= N
+        E = numpy.unique(numpy.sort(E - 1, axis=1), axis=0)
+        g = Graph(directed=False)
+        g.add_vertex(N)
+        g.add_edge_list(E)
+        g.vp.fips = g.new_vp("int32_t", vals=fips)
+        for k in ["sh050m", "sh100m", "sh500m"]:
+            assert all(r1[k] == r2[k] for r1, r2 in zip(feats[2012], feats[2016]))
+            g.vp[k] = g.new_vp("double", vals=[float(r[k]) for r in feats[2012]])
+        for year in (2012, 2016):
+            for k in ["income", "migration", "birth", "death", "education",
+                      "unemployment", "election"]:
+                g.vp[f"{k}_{year}"] = g.new_vp("double", vals=[float(r[k]) for r in feats[year]])
+        yield alt, g