Modeling relationships and interactions between entities as a graph of nodes and edges.
A network represents the relationships (links) between entities (nodes). These links can be weighted (weighted network) or unweighted (binary network), directed (directed network) or undirected (undirected network). Regardless of their type, networks generate links shared by nodes, leading to data dependency when modeling the network. One proposed solution is to model network links with random intercepts and slopes. By adding such parameters to the model, we can account for the correlations between node link relationships.
Considerations
Caution
The particularity here is that varying intercepts and slopes are generated for both nodal effects 🛈 and dyadic effects 🛈. These varying intercepts and slopes are identical to those described in previous chapters and will therefore not be detailed further. Only the random-centered version of the varying slopes will be described here.
Example
Below is an example code snippet demonstrating a Bayesian network model with a sender-receiver effect. This example is based on Ross, McElreath, and Redhead (2024).
# Setup device------------------------------------------------from BI import biimport jax.numpy as jnp# Setup device------------------------------------------------m = bi(platform='cpu')# Simulate data ------------------------------------------------N =50individual_predictor = m.dist.normal(0,1, shape = (N,1), sample =True)kinship = m.dist.bernoulli(0.3, shape = (N,N), sample =True)kinship = kinship.at[jnp.diag_indices(N)].set(0)def sim_network(kinship, individual_predictor):# Intercept alpha = m.dist.normal(0,1, sample =True)# SR sr = m.net.sender_receiver(individual_predictor, individual_predictor, s_mu =0.4, r_mu =-0.4, sample =True)# D DR = m.net.dyadic_effect(kinship, d_sd=2.5, sample =True)return m.dist.bernoulli(logits = alpha + sr + DR, sample =True)network = sim_network(m.net.mat_to_edgl(kinship), individual_predictor)# Predictive model ------------------------------------------------m.data_on_model =dict( network = network, dyadic_predictors = m.net.mat_to_edgl(kinship), focal_individual_predictors = individual_predictor, target_individual_predictors = individual_predictor)def model(network, dyadic_predictors, focal_individual_predictors, target_individual_predictors): N_id = network.shape[0]# Block --------------------------------------- alpha = m.dist.normal(0,1, sample =True)## SR shape = N individuals--------------------------------------- sr = m.net.sender_receiver( focal_individual_predictors, target_individual_predictors, s_mu =0.4, r_mu =-0.4 )# Dyadic shape = N dyads-------------------------------------- dr = m.net.dyadic_effect(dyadic_predictors, d_sd=2.5) # Diadic effect intercept only m.dist.bernoulli(logits = alpha + sr + dr, obs=network)m.fit(model, progress_bar=False)
/home/sosa/work/3.12venv/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning:
IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
BI v 0.0.45 package loaded
jax.local_device_count 32
library(BayesianInference)# Setup platform------------------------------------------------m=importBI(platform='cpu')# Import data ------------------------------------------------load(paste(system.file(package ="BayesianInference"),'/data/STRAND sim sr only.Rdata', sep =''))m$data_on_model =list()m$data_on_model$N_id =length(ids)m$data_on_model$network = m$net$mat_to_edgl(model_dat$outcomes[,,1])m$data_on_model$N_dyads = m$net$mat_to_edgl(model_dat$outcomes[,,1])$shape[[1]]m$data_on_model$focal_individual_predictors = jnp$array(model_dat$individual_predictors)m$data_on_model$target_individual_predictors =jnp$array(model_dat$individual_predictors)# Define model ------------------------------------------------model <-function( N_id, N_dyads, network, focal_individual_predictors, target_individual_predictors){## Block --------------------------------------- B =bi.dist.normal(0, 2.5, shape=c(1), name ='block')#SR --------------------------------------- sr = m$net$sender_receiver(focal_individual_predictors,target_individual_predictors)### Dyadic-------------------------------------- #dr, dr_raw, dr_sigma, dr_L = m.net.dyadic_random_effects(idx.shape[0], cholesky_density = 2)# shape = n dyads dr = m$net$dyadic_effect(shape =c(N_dyads))## SR --------------------------------------- m$dist$poisson(jnp$exp(B + sr + dr), obs=network) }# Run MCMC ------------------------------------------------m$fit(model) # Optimize model parameters through MCMC sampling# Summary ------------------------------------------------summary =m$summary()
# Setup device------------------------------------------------usingBayesianInference# Setup device------------------------------------------------m =importBI(platform="cpu")# Simulate data ------------------------------------------------N =50individual_predictor = m.dist.normal(0,1, shape = (N,1), sample =true)kinship = m.dist.bernoulli(0.3, shape = (N,N), sample =true)kinship = kinship.at[jnp.diag_indices(N)].set(0)functionsim_network(kinship, individual_predictor)# Intercept alpha = m.dist.normal(0,1, sample =true)# SR sr = m.net.sender_receiver(individual_predictor, individual_predictor, s_mu =0.4, r_mu =-0.4, sample =true)# D DR = m.net.dyadic_effect(kinship, d_sd=2.5, sample =true)return m.dist.bernoulli(logits = alpha + sr + DR, sample =true)endnetwork =sim_network(m.net.mat_to_edgl(kinship), individual_predictor)# Predictive model ------------------------------------------------m.data_on_model =pydict( network = network, dyadic_predictors = m.net.mat_to_edgl(kinship), focal_individual_predictors = individual_predictor, target_individual_predictors = individual_predictor)@BIfunctionmodel(network, dyadic_predictors, focal_individual_predictors, target_individual_predictors) N_id = network.shape[0]# Block --------------------------------------- alpha = m.dist.normal(0,1, name ="alpha")## SR shape = N individuals--------------------------------------- sr = m.net.sender_receiver( focal_individual_predictors, target_individual_predictors, s_mu =0.4, r_mu =-0.4 )# Dyadic shape = N dyads-------------------------------------- dr = m.net.dyadic_effect(dyadic_predictors, d_sd=2.5) # Diadic effect intercept only m.dist.bernoulli(logits = alpha + sr + dr, obs=network)endm.fit(model, num_samples =500, num_warmup =500, num_chains =1, thinning =1)
Caution
Event if you don’t have dyadic effect, or block model effect, they need to be define to create intercepts (means) for those effects
Mathematical Details
Main Formula
The simple model that can be built to model link weights between nodes i and j can be defined using a Poisson distribution:
\beta_1 is the effect of an individuals i level feature on the emission of a link (i.e., out-strength).
\beta_2 is the effect of an individuals j level feature on the receiving a link (i.e., in-strength).
\beta_3 is the effect of an dyadic characteristic between i and j on the likelihood of a tie.
Defining formula sub-equations and prior distributions
The sender and receiver random effects are similar to those described in chapter 15: Varying intercepts, but they are defined here using a joint prior so as to estimate the correlation within individuals to emit and receive a link:
Note that any additional covariates can be summed with a regression coefficient to \lambda_i, \pi_j and \delta_{ij}. Of course, for \lambda_i and \pi_j, as they represent nodal effects, these covariates need to be nodal characteristics (e.g., sex, age), whereas for \delta_{ij}, as it represents dyadic effects, these covariates need to be dyadic characteristics (e.g., genetic distances). Considering the previous example, given a vector of nodal characteristics, individual_predictors, and a matrix of dyadic characteristics, kinship, we can incorporate these covariates into the sender-receiver and dyadic effects, respectively.
Network links can be modeled using Bernoulli (for proportions), Binomial (for unweighted network), Poisson or zero-inflated Poisson distributions (for count). In BI, you just need to set the correct likelihood distributions. For example, if you want to model the number of interactions between nodes, you can use the Poisson distribution. If you want to model the existence or absence of a link, you can use the Bernoulli distribution.
If the network is undirected, then accounting for the correlation between the propensity to emit and receive links is not necessary, and the terms \lambda_i, \pi_j, and \delta_{ij} are no longer required. (Is it correct?)
To account for exposure on a poisson model treat exposure as a nodal characteristic with its own parameter effect (i.e., regression coefficient). Their is several function that will help you to convert vectors or matrices in edge list format to have compatible data structure for the model (see API reference for bi.net.vec_to_edgl and bi.net.mat_to_edgl). F
In the following chapters, we will see how to incorporate additional network effects into the model to account for network structural properties (e.g., clusters, assortativity, triadic closure, etc.).
Reference(s)
Ross, Cody T, Richard McElreath, and Daniel Redhead. 2024. “Modelling Animal Network Data in r Using STRAND.”Journal of Animal Ecology 93 (3): 254–66.