In [1]:
# show plots inside the notebook
%matplotlib inline
This notebook shows a step by step example of how to construct the Multiplex Markov Chain from longitudinal data on a multiplex network with two layers. The Multiplex Markov chain helps to detect "dynamical spillover". See the following paper for details:
In our dataset, the nodes are countries in the world and the two layers are alliance and trade. The data starts in 1950 and ends in 2003. The input data has alliance as the first layer and trade as the second layer. Here are the first few lines of the file that stores the data about edges present in the network.
In [33]:
with open("groom_status_edges.csv","r") as f:
for i in range(6):
print((f.readline()).rstrip())
The multiplex network has two layers. Therefore, every edge in this network has four possible states: no alliance or trade (state:0 {labelled at}), only alliance (state:1 {labelled At}), only trade (state:2 {labelled aT}) or both alliance and trade (state:3 {labelled AT}). Therefore, from one year to the next, there are sixteen possible transitions. The transition from state $S_t = \{0,1,2,3\}$ to state $S_{t+1} = \{0,1,2,3\}$ is denoted as transition $i = 4S_t + S_{t+1}$.
In [28]:
# first convert the multiplex network data into transition counts of a Markov chain
from extract_counts import *
The following gives us a dictionary with the index as the year. The entry corresponding to the year gives us the counts associated with that year to the next, say $y \to y+1$. There are several instances in the dataset where a country exists in year $y$ but not in year $y+1$ and vice versa. The parameter "method" decides how we handle such cases. The parameter accepts two values: "union", "intersection". When we choose "union" the node is included in the counts if it is present in at least one of the timesteps. If we choose "intersection" the node is included in the counts only if it is present in both years.
In [35]:
counts_by_year = compute_counts_from_file("./agg_status_edges.csv", "./ags_nodes.csv", method="intersection")
An array (of size $16$) which contains the counts of each of the $16$ possible transitions is associated with each year.
In [15]:
# check how the counts look
counts_by_year["0"]
Out[15]:
Having obtained the counts, we can now construct the Multiplex Markov chain, compute the null model and check for transitions that are significantly different due to interaction between the two layers. Since our purpose here is to illustrate how to use the code, we will accumulate all the counts from 1950 to 2003. We do a more careful analysis of whether such an aggregation is justified in the article ().
In [16]:
# make np array of dictionary and sum counts
counts = np.sum(np.array(list(counts_by_year.values())),axis=0)
In [17]:
print(counts)
The above counts are what we will use to construct our Multiplex Markov chain and the corresponding null model. We import the corresponding class and construct the Multiplex Markov chain.
In [18]:
from MultiplexMarkovChain import *
In [19]:
mmc = MultiplexMarkovChain(counts)
In [20]:
# print the parameters of the Markov chain associated with the transitions (this is the probability of transition i)
mmc.get_parameters()
Out[20]:
In [21]:
#print the corresponding null probability
mmc.get_null_prob()
Out[21]:
In [22]:
# print state totals
mmc.get_state_totals()
Out[22]:
In order determine if spillover exists between the alliance and trade layer we need to check if the transition parameters of the Multiplex Markov chain are "substantially" different from those of the null model. We can do this by checking if the confidence interval for the Multiplex Markov chain and the null model overlap. In this example (and in the article) we choose the 99% confidence interval. Since our state totals are of the order of $10^4$, we can construct the confidence interval using a Gaussian approximation.
Note: For a Gaussian distribution the 99% confidence interval is given by $\mu \pm 2.58 \sigma$, where $\mu$ is the mean and $\sigma$ is the standard deviation.
In [23]:
import matplotlib.pylab as plt
In [24]:
z_alpha = 2.58 # change to alter level of confidence
In [26]:
# Plot to check for overlap in confidence intervals
plt.clf()
fig = plt.figure(figsize=(16, 8))
y = mmc.get_parameters()
y_std = z_alpha*mmc.get_std_dev()
y_null = mmc.get_null_prob()
y_null_std = yerr=z_alpha*mmc.get_null_std_dev()
state_labels = {0:'gs', 1:'Gs', 2:'gS', 3:'GS'}
xlabels = []
# prepare transition label
for i in range(4):
for j in range(4):
index = 4*i + j
ax = plt.subplot2grid((4,4),(i,j))
ax.errorbar([1], y[index], yerr=y_std[index], fmt="o", label="MMC")
ax.errorbar([1], y_null[index], yerr=y_null_std[index], fmt="s", color="r", label="Null")
ttext = state_labels[i] + "-->" + state_labels[j]
ax.set_title(ttext)
ax.tick_params(axis='x', which='both', bottom='off', top='off', labelbottom='off') #don't display xticks
fig.subplots_adjust(wspace=0.4)
From the above panel of figures we can determine which of these transitions exhibit dynamical spillover. We discuss the implications of the spillover in the article that can be found
In [15]: