netpol
Measure polarization in (multilayer) social networks.
Given a network -- or a dict of per-layer networks -- netpol selects the
top influencers, scores every user on a bipolar latent-ideology axis via
correspondence analysis, and tests whether the resulting score distribution is
multimodal (polarized) using Hartigan's dip test, with optional
Benjamini-Hochberg FDR correction across layers.
The method follows Falkenberg et al. (2021) and Flamino et al. (2021).
Install
pip install netpol
For local development:
git clone https://github.com/alessiogandelli/netpol && cd netpol
poetry install
poetry run pytest
Edge convention
Fixed and non-negotiable:
a -> bmeans "a retweets/endorses b".
Pass networkx.DiGraphs only (undirected graphs and MultiDiGraphs raise
TypeError). A multilayer network is just dict[layer_id, DiGraph].
Quickstart
import networkx as nx
from netpol import LatentIdeologyScorer, PolarizationConfig, analyze_layers
def layer(): # two camps, each retweeting one influencer
g = nx.DiGraph()
for i in range(100):
g.add_edge(f"c1_{i}", "inf_1")
g.add_edge(f"c2_{i}", "inf_2")
return g
config = PolarizationConfig(n_influencers=2, min_edges=1)
results = analyze_layers({"l1": layer()}, config, LatentIdeologyScorer(min_sources=1))
print(results["l1"].is_polarized) # True
See examples/quickstart.py for a runnable version.
How it works
Per layer:
- Select influencers -- top
n_influencersnodes byin_degree(configurable) with deterministic tie-breaking. - Build the interaction table -- one row per edge into an influencer
(
['influencer', 'user']), self-loops excluded. - Score users -- correspondence analysis maps each user to a score in
[-1, 1]on a bipolar ideology axis (theIdeologyScorerplug point; the built-inLatentIdeologyScoreris deterministic). - Test for polarization -- Hartigan's dip test on the score distribution.
Across layers, analyze_layers applies Benjamini-Hochberg FDR correction to
the per-layer p-values and re-evaluates is_polarized against the adjusted
values.
API
PolarizationConfig-- frozen config dataclass (seenetpol/config.py).analyze_layer(graph, config, scorer=None)->LayerResultanalyze_layers(layers, config, scorer=None)->dict[layer_id, LayerResult]LatentIdeologyScorer(min_sources=2, max_sources=None)-- built-in scorer.IdeologyScorer--Protocolto plug in your own scoring.LayerResult--layer_id,n_nodes,n_edges,influencers,scores,dip_statistic,p_value,adjusted_p_value,is_polarized,skip_reason.
What this does / doesn't do (yet)
Does:
- Faithful, deterministic implementation of the latent-ideology + dip-test pipeline (single-layer and multilayer).
- FDR correction, explicit
skip_reasonon every failure path (no silentexcept), directed-graph validation,min_edgesguardrail.
Does not do yet (see docs/DEBATES.md for the open
questions and [REVISIT] items):
- Effect-size / separation measure paired with the dip test.
- Score normalization across layers for comparison.
- Multivariate modality testing for
ideology_dimensions > 1. - Influencer-selection scope beyond per-layer (global/hybrid), adaptive pool sizing, or authority/HITS ranking.
References
- M. Falkenberg et al., "Growing polarisation around climate change on social media", arXiv:2112.12137 (2021).
- J. Flamino et al., "Shifting polarization and Twitter news influencers between two US presidential elections", arXiv:2111.02505 (2021).
License
MIT. See LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file netpol-0.1.0.tar.gz.
File metadata
- Download URL: netpol-0.1.0.tar.gz
- Upload date:
- Size: 11.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c1695cab7864ead846b645e5a191e51031bcf3877871f5a50bce1a3679d3a179
|
|
| MD5 |
c28ac644f0d8d3d593d8b8a4c28b3b02
|
|
| BLAKE2b-256 |
3cfc66dffe710c74952568f11bcf8eb30c6f3feed1646175c9f50c33007bde8a
|
Provenance
The following attestation bundles were made for netpol-0.1.0.tar.gz:
Publisher:
publish.yml on alessiogandelli/netpol
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
netpol-0.1.0.tar.gz -
Subject digest:
c1695cab7864ead846b645e5a191e51031bcf3877871f5a50bce1a3679d3a179 - Sigstore transparency entry: 2605774501
- Sigstore integration time:
-
Permalink:
alessiogandelli/netpol@fb9eca3bc0410b0dd5a16e8c93c9c0385b2cb534 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/alessiogandelli
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fb9eca3bc0410b0dd5a16e8c93c9c0385b2cb534 -
Trigger Event:
push
-
Statement type:
File details
Details for the file netpol-0.1.0-py3-none-any.whl.
File metadata
- Download URL: netpol-0.1.0-py3-none-any.whl
- Upload date:
- Size: 14.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
197f010c848ccdb105e2c5d016d18156dc24e2190bac0fe39be5fa48499de998
|
|
| MD5 |
ffb6191531868e5478caf7d10b573b92
|
|
| BLAKE2b-256 |
08ba21a78ed2db2ced18f9bda0f2e2fd10b05a859782568c75a162ee48fb58b2
|
Provenance
The following attestation bundles were made for netpol-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on alessiogandelli/netpol
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
netpol-0.1.0-py3-none-any.whl -
Subject digest:
197f010c848ccdb105e2c5d016d18156dc24e2190bac0fe39be5fa48499de998 - Sigstore transparency entry: 2605774517
- Sigstore integration time:
-
Permalink:
alessiogandelli/netpol@fb9eca3bc0410b0dd5a16e8c93c9c0385b2cb534 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/alessiogandelli
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fb9eca3bc0410b0dd5a16e8c93c9c0385b2cb534 -
Trigger Event:
push
-
Statement type: