Installation

The core-periphery implementations are available in Python on Github.

pip install git+https://github.com/ryanjgallagher/core_periphery_sbm

Running Core-Periphery Models

The models can be run in Python with any NetworkX graph. To infer a hub-and-spoke structure:

import networkx as nx
from core_periphery_sbm import core_periphery as cp

# Load Les Mis graph from NetworkX
G = nx.les_miserables_graph()

# Initialize hub-and-spoke model and infer structure
hubspoke = cp.HubSpokeCorePeriphery(n_gibbs=100, n_mcmc=10*len(G))
hubspoke.infer(G)

Similarly, we can infer a layered structure with n_layers:

layered = cp.LayeredCorePeriphery(n_layers=3, n_gibbs=100, n_mcmc=10*len(G))
layered.infer(G)

The models are inferred through an MCMC-within-Gibbs sampling procedure. There are n_gibbs samples that are taken, and during each Gibbs sample, an MCMC chain of n_mcmc steps is run on the labels of the network.

From the Gibbs samples, we can infer the core-periphery assignment of each node. Lower indices indicate more core positions. For example, in the hub-and-spoke model, 0 indicates the core and 1 indicates the periphery. In the layered model, 0 is the innermost layer.

# Get core and periphery assignments from hub-and-spoke model
node2label_hs = hubspoke.get_labels(last_n_samples=50)

# Get layer assignments from the layered model
node2label_l = layered.get_labels(last_n_samples=50)

Core-Periphery Model Selection

Given core-periphery labels inferred from the hub-and-spoke and layered models, we want to determine which partition best describes the core-periphery structure of a network. We can do this by comparing the description length of the models. The smaller the description length, the better the model fit. We can approximate the description length for each core-periphery model using n_samples:

from core_periphery_sbm import model_fit as mf

# Get description length of hub-and-spoke model
inf_labels_hs = hubspoke.get_labels(last_n_samples=50, prob=False, return_dict=False)
mdl_hubspoke = mf.mdl_hubspoke(G, inf_labels_hs, n_samples=100000)

# Get the description length of layered model
inf_labels_l = layered.get_labels(last_n_samples=50, prob=False, return_dict=False)
mdl_layered = mf.mdl_layered(G, inf_labels_l, n_layers=3, n_samples=100000)