Skip to main content

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 -> b means "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:

  1. Select influencers -- top n_influencers nodes by in_degree (configurable) with deterministic tie-breaking.
  2. Build the interaction table -- one row per edge into an influencer (['influencer', 'user']), self-loops excluded.
  3. Score users -- correspondence analysis maps each user to a score in [-1, 1] on a bipolar ideology axis (the IdeologyScorer plug point; the built-in LatentIdeologyScorer is deterministic).
  4. 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 (see netpol/config.py).
  • analyze_layer(graph, config, scorer=None) -> LayerResult
  • analyze_layers(layers, config, scorer=None) -> dict[layer_id, LayerResult]
  • LatentIdeologyScorer(min_sources=2, max_sources=None) -- built-in scorer.
  • IdeologyScorer -- Protocol to 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_reason on every failure path (no silent except), directed-graph validation, min_edges guardrail.

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

netpol-0.1.0.tar.gz (11.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

netpol-0.1.0-py3-none-any.whl (14.2 kB view details)

Uploaded Python 3

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

Hashes for netpol-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c1695cab7864ead846b645e5a191e51031bcf3877871f5a50bce1a3679d3a179
MD5 c28ac644f0d8d3d593d8b8a4c28b3b02
BLAKE2b-256 3cfc66dffe710c74952568f11bcf8eb30c6f3feed1646175c9f50c33007bde8a

See more details on using hashes here.

Provenance

The following attestation bundles were made for netpol-0.1.0.tar.gz:

Publisher: publish.yml on alessiogandelli/netpol

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for netpol-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 197f010c848ccdb105e2c5d016d18156dc24e2190bac0fe39be5fa48499de998
MD5 ffb6191531868e5478caf7d10b573b92
BLAKE2b-256 08ba21a78ed2db2ced18f9bda0f2e2fd10b05a859782568c75a162ee48fb58b2

See more details on using hashes here.

Provenance

The following attestation bundles were made for netpol-0.1.0-py3-none-any.whl:

Publisher: publish.yml on alessiogandelli/netpol

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.3.0

2 files

0.2.1

2 files

0.2.0

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page