{ "cells": [ { "cell_type": "markdown", "id": "dee32b11-7922-44f4-a587-e118c0e1b793", "metadata": {}, "source": [ "# Dynamic Networks\n", "\n", "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](https://teneto.readthedocs.io/en/latest/).\n", "\n", "## Creating TemporalNetwork Objects" ] }, { "cell_type": "code", "execution_count": 1, "id": "2976a937-0352-4d5d-aaac-6a1ff7f474ed", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/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.\n", " warnings.warn(message, FutureWarning)\n" ] } ], "source": [ "import networkx as nx\n", "import pandas as pd\n", "\n", "# Import the main Teneto object:\n", "from teneto import TemporalNetwork\n", "# Import the network measures you'll need:\n", "from teneto.networkmeasures import *" ] }, { "cell_type": "markdown", "id": "15824835-76a5-4ccf-b0ec-a4828bf7fc7a", "metadata": {}, "source": [ "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\".\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": 13, "id": "c6870e8e-feb5-45dc-89bf-4810c3aee461", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ijt
0122284
1122285
21221674
31221675
41221274
............
939163745
940163746
9417425
9427426
9437427
\n", "

944 rows × 3 columns

\n", "
" ], "text/plain": [ " i j t\n", "0 122 28 4\n", "1 122 28 5\n", "2 122 167 4\n", "3 122 167 5\n", "4 122 127 4\n", ".. ... ... ..\n", "939 163 74 5\n", "940 163 74 6\n", "941 74 2 5\n", "942 74 2 6\n", "943 74 2 7\n", "\n", "[944 rows x 3 columns]" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Import edges\n", "edges = pd.read_csv(\"../data/quaker_edgelist.csv\")\n", "\n", "# Import nodes\n", "# Normally you wouldn't need to do that\n", "# But here we need to create IDs from the name indices\n", "nodes = pd.read_csv(\"../data/quaker_nodelist.csv\")\n", "all_nodes = nodes.name.to_list()\n", "\n", "# Set time intervals, here I've chosen 20 years\n", "intervals = range(edges[\"Start Year\"].min(),edges[\"End Year\"].max(),20)\n", "\n", "new_edges = []\n", "# Iterate through each edge\n", "for n,row in edges.iterrows():\n", " # Look in every time interval\n", " for i,x in enumerate(intervals):\n", " # See if the start and end year of the edge contain that time interval\n", " # If so, add them to an edge list with the correct sequential number\n", " if row[\"Start Year\"] <= x <= row[\"End Year\"]:\n", " new_edges.append({'i': all_nodes.index(row['Source']), 'j': all_nodes.index(row['Target']), 't': i})\n", "\n", "# Put those edges into a new dataframe\n", "new_df = pd.DataFrame(new_edges)\n", "new_df" ] }, { "cell_type": "markdown", "id": "aace1228-cea7-44e1-8f04-53d18abb3481", "metadata": {}, "source": [ "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:" ] }, { "cell_type": "code", "execution_count": 14, "id": "acda0a1b-c97d-45d5-beaf-8f4fb835599e", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ijt
0122284
1122285
21221674
31221675
41221274
............
939163745
940163746
9417425
9427426
9437427
\n", "

944 rows × 3 columns

\n", "
" ], "text/plain": [ " i j t\n", "0 122 28 4\n", "1 122 28 5\n", "2 122 167 4\n", "3 122 167 5\n", "4 122 127 4\n", ".. ... ... ..\n", "939 163 74 5\n", "940 163 74 6\n", "941 74 2 5\n", "942 74 2 6\n", "943 74 2 7\n", "\n", "[944 rows x 3 columns]" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tnet = TemporalNetwork(from_df=new_df)\n", "tnet.network" ] }, { "cell_type": "markdown", "id": "cbffdc53-94eb-44fe-bb12-424959055829", "metadata": {}, "source": [ "The display of the network looks exactly the same as the input data." ] }, { "cell_type": "markdown", "id": "adec6c9e-134f-49d0-8e96-3c6dd2c5f121", "metadata": {}, "source": [ "## Calculating Temporal Network Measures\n", "\n", "There are many different functions in the [Teneto documentation](https://teneto.readthedocs.io/en/latest/tutorial.html), but if you've imported the functions correctly (see above), you can now run whichever metrics you like on your new `TemporalNetwork` object." ] }, { "cell_type": "code", "execution_count": 15, "id": "03216b3d-60a0-4356-ac57-84ad6bacb924", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 0., 4., 0., 0., 8., 16., 0., 0., 1., 0., 6., 0., 0.,\n", " 14., 0., 0., 18., 10., 0., 10., 0., 2., 0., 0., 3., 2.,\n", " 0., 0., 0., 0., 24., 0., 4., 0., 0., 3., 0., 8., 4.,\n", " 14., 0., 14., 12., 18., 6., 3., 1., 7., 0., 6., 0., 1.,\n", " 4., 0., 0., 0., 2., 0., 0., 0., 0., 0., 30., 0., 4.,\n", " 0., 0., 41., 3., 3., 0., 2., 2., 0., 20., 15., 22., 0.,\n", " 1., 0., 2., 4., 0., 22., 1., 0., 0., 0., 0., 0., 8.,\n", " 0., 0., 0., 2., 0., 3., 3., 0., 4., 3., 0., 4., 2.,\n", " 0., 14., 0., 6., 6., 0., 6., 3., 4., 9., 0., 0., 3.,\n", " 40., 3., 3., 0., 10., 41., 2., 4., 1., 0., 8., 3., 11.,\n", " 0., 3., 22., 0., 0., 5., 0., 2., 0., 0., 0., 3., 1.,\n", " 4., 0., 3., 1., 8., 0., 4., 2., 0., 7., 0., 0., 0.,\n", " 6., 4., 0., 3., 3., 2., 0., 2., 6., 0., 6., 2., 0.,\n", " 4., 3., 1., 20., 0., 2., 6., 3., 0., 0., 4., 0., 73.,\n", " 0., 94., 3., 20., 1., 6., 0., 0., 2., 0.])" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "temporal_degree_centrality(tnet)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" } }, "nbformat": 4, "nbformat_minor": 5 }