Dynamic Networks#
Dynamic, or temporal, networks are a special subset of multilayer networks that allow you to examine changes in a network over time. NetworkX currently has no functionality for dynamic networks, so you’ll use the Python library Teneto.
Creating TemporalNetwork Objects#
import networkx as nx
import pandas as pd
# Import the main Teneto object:
from teneto import TemporalNetwork
# Import the network measures you'll need:
from teneto.networkmeasures import *
/Users/jrladd/Library/Python/3.9/lib/python/site-packages/nilearn/input_data/__init__.py:23: FutureWarning: The import path 'nilearn.input_data' is deprecated in version 0.9. Importing from 'nilearn.input_data' will be possible at least until release 0.13.0. Please import from 'nilearn.maskers' instead.
warnings.warn(message, FutureWarning)
Teneto’s TemporalNetwork
objects are different from NetworkX’s Graph
objects. You need to have a third column in your edge table that indicates the timespan in which that edge exists. This isn’t a date or a pair of dates/times but rather a sequential number that indicates what “slice” that edge is in. Many edges will wind up being in more than one “time slice”.
This time column is typicall expressed as t
in your graph. If you have an edgelist that includes start and stop dates, you’ll need to convert these into sequential groups. Below is an example of how to do that using the Six Degrees of Francis Bacon Quakers network. But keep in mind that this is an idiosyncratic example and your original data may format time in a different way.
# Import edges
edges = pd.read_csv("../data/quaker_edgelist.csv")
# Import nodes
# Normally you wouldn't need to do that
# But here we need to create IDs from the name indices
nodes = pd.read_csv("../data/quaker_nodelist.csv")
all_nodes = nodes.name.to_list()
# Set time intervals, here I've chosen 20 years
intervals = range(edges["Start Year"].min(),edges["End Year"].max(),20)
new_edges = []
# Iterate through each edge
for n,row in edges.iterrows():
# Look in every time interval
for i,x in enumerate(intervals):
# See if the start and end year of the edge contain that time interval
# If so, add them to an edge list with the correct sequential number
if row["Start Year"] <= x <= row["End Year"]:
new_edges.append({'i': all_nodes.index(row['Source']), 'j': all_nodes.index(row['Target']), 't': i})
# Put those edges into a new dataframe
new_df = pd.DataFrame(new_edges)
new_df
i | j | t | |
---|---|---|---|
0 | 122 | 28 | 4 |
1 | 122 | 28 | 5 |
2 | 122 | 167 | 4 |
3 | 122 | 167 | 5 |
4 | 122 | 127 | 4 |
... | ... | ... | ... |
939 | 163 | 74 | 5 |
940 | 163 | 74 | 6 |
941 | 74 | 2 | 5 |
942 | 74 | 2 | 6 |
943 | 74 | 2 | 7 |
944 rows × 3 columns
In the dataframe above, i
and j
are the source and target of each edge, and t
is the time interval. Once you have a dataframe in this form you can load it directly into Teneto:
tnet = TemporalNetwork(from_df=new_df)
tnet.network
i | j | t | |
---|---|---|---|
0 | 122 | 28 | 4 |
1 | 122 | 28 | 5 |
2 | 122 | 167 | 4 |
3 | 122 | 167 | 5 |
4 | 122 | 127 | 4 |
... | ... | ... | ... |
939 | 163 | 74 | 5 |
940 | 163 | 74 | 6 |
941 | 74 | 2 | 5 |
942 | 74 | 2 | 6 |
943 | 74 | 2 | 7 |
944 rows × 3 columns
The display of the network looks exactly the same as the input data.
Calculating Temporal Network Measures#
There are many different functions in the Teneto documentation, but if you’ve imported the functions correctly (see above), you can now run whichever metrics you like on your new TemporalNetwork
object.
temporal_degree_centrality(tnet)
array([ 0., 4., 0., 0., 8., 16., 0., 0., 1., 0., 6., 0., 0.,
14., 0., 0., 18., 10., 0., 10., 0., 2., 0., 0., 3., 2.,
0., 0., 0., 0., 24., 0., 4., 0., 0., 3., 0., 8., 4.,
14., 0., 14., 12., 18., 6., 3., 1., 7., 0., 6., 0., 1.,
4., 0., 0., 0., 2., 0., 0., 0., 0., 0., 30., 0., 4.,
0., 0., 41., 3., 3., 0., 2., 2., 0., 20., 15., 22., 0.,
1., 0., 2., 4., 0., 22., 1., 0., 0., 0., 0., 0., 8.,
0., 0., 0., 2., 0., 3., 3., 0., 4., 3., 0., 4., 2.,
0., 14., 0., 6., 6., 0., 6., 3., 4., 9., 0., 0., 3.,
40., 3., 3., 0., 10., 41., 2., 4., 1., 0., 8., 3., 11.,
0., 3., 22., 0., 0., 5., 0., 2., 0., 0., 0., 3., 1.,
4., 0., 3., 1., 8., 0., 4., 2., 0., 7., 0., 0., 0.,
6., 4., 0., 3., 3., 2., 0., 2., 6., 0., 6., 2., 0.,
4., 3., 1., 20., 0., 2., 6., 3., 0., 0., 4., 0., 73.,
0., 94., 3., 20., 1., 6., 0., 0., 2., 0.])