CAVIAR Network Analysis¶

In this case study, we will go through and understand a time-varying criminal network that was repeatedly disrupted by police forces, and how the criminal network reoriented in response to the seizures of product by the police forces.


Context¶


Here is some information on the CAVIAR project and the role of certain individuals arrested following the investigation. The investigation lasted two years and ran from 1994 to 1996. The operation was brought together by investigation units of the Montreal police and the Royal Canadian Mounted Police of Canada. During these two years, 11 wiretap warrants, valid for about two months each, were obtained (11 matrices match these phases).

This case is rather unique because, unlike other investigative strategies, the mandate of the CAVIAR project was to seize the drugs without arresting criminals. During this period, imports of the trafficking network were hit and seized by the police on eleven occasions. The arrests took place only at the end of the investigation. Monetary losses for traffickers were estimated at 32 million dollars. Some phases included no seizures, and others included multiple. Here is what they represent in terms of the amount of money:

  • Phase 4 - 1 seizure, 2,500,000 Dollars, 300 kg of marijuana
  • Phase 6 - 3 seizures, 1,300,000 Dollars, 2 x 15 kg of marijuana + 1 x 2 kg of cocaine
  • Phase 7 - 1 seizure, 3,500,000 Dollars, 401 kg of marijuana
  • Phase 8 - 1 seizure, 360,000 Dollars, 9 kg of cocaine
  • Phase 9 - 2 seizures, 4,300,000 Dollars, 2 kg of cocaine + 1 x 500 kg marijuana
  • Phase 10 - 1 seizure, 18,700,000 Dollars, 2200 kg of marijuana
  • Phase 11 - 2 seizures, 1,300,000 Dollars, 12 kg of cocaine + 11 kg of cocaine

As you can see, this case study offers a rare opportunity to study a criminal network in response to upheaval by police forces. This allows us to analyze changes in the network structure and to survey the reaction and adaptation of the participants while they were subjected to an increasing number of distressing constraints.


About the network¶


The network consists of 110 (numbered) players. Players 1-82 are the traffickers. Players 83-110 are the non-traffickers (financial investors, accountants, owners of various importation businesses, etc.). Initially, the investigation targeted Daniel Serero, the alleged mastermind of a drug network in downtown Montreal, attempting to import marijuana to Canada from Morocco, transiting through Spain. After the first seizure, happening in phase 4, traffickers reoriented to cocaine import from Colombia, transiting through the United States.

According to the police, the role of the players of the "Serero organization" under investigation were the following:

  • Daniel Serero (N1) - Mastermind of the network
  • Pierre Perlini (N3) - Principal lieutenant of Serero, executes instructions
  • Alain (N83) and G'erard (N86) Levy - Investors and transporters of money
  • Wallace Lee (N85) - Takes care of financial affairs (accountant)
  • Gaspard Lino (N6) - Broker in Spain
  • Samir Rabbat (N11) - Provider in Morocco
  • Lee Gilbert (N88) - Trusted man of Wallace Lee (became an informer after the arrest)
  • Beverly Ashton (N106) - Spouse of Lino, transports money and documents
  • Antonio Iannacci (N89) - Investor
  • Mohammed Echouafni (N84) - Moroccan investor
  • Richard Gleeson (N5), Bruno de Quinzio (N8), and Gabrielle Casale (N76) - Charged with recuperating the marijuana
  • Roderik Janouska (N77) - Individual with airport contacts
  • Patrick Lee (N87) - Investor
  • Salvatore Panetta (N82) - Transport arrangements manager
  • Steve Cunha (N96) - Transport manager, owner of a legitimate import company (became an informer after the arrest)
  • Ernesto Morales (N12) - Principal organizer of the cocaine import, an intermediary between the Colombians and the Serero organization
  • Oscar Nieri (N17) - The handyman of Morales
  • Richard Brebner (N80) - Transporting the cocaine from the US to Montreal
  • Ricardo Negrinotti (N33) - Taking possession of the cocaine in the US to hand it to Brebner
  • Johnny Pacheco (N16) - Cocaine provider

Objective¶


  • The objective of the case study is to understand, create, and visualize the data in phases.
  • Later on, we will apply different network centrality measures and understand the important nodes of the network.
  • We will visualize the centrality measures of the important nodes across phases.

Importing the required libraries¶

In [1]:
%matplotlib inline

import networkx as nx

from decorator import decorator

from networkx.utils import create_random_state, create_py_random_state

import numpy as np

import pandas as pd

import matplotlib.pyplot as plt

import seaborn as sns

import warnings

# To get rid of warning messages
warnings.filterwarnings('ignore') 

# Remove scientific notations and display numbers with 2 decimal points instead
pd.options.display.float_format = '{ : , .2f}'.format        

# Update default background style of plots
sns.set_style(style = 'darkgrid')

Loading the data from 11 phases¶

In [2]:
P1 = pd.read_csv("CAVIAR_Phases - Notebook/phase1.csv")

P2 = pd.read_csv("CAVIAR_Phases - Notebook/phase2.csv")

P3 = pd.read_csv("CAVIAR_Phases - Notebook/phase3.csv")

P4 = pd.read_csv("CAVIAR_Phases - Notebook/phase4.csv")

P5 = pd.read_csv("CAVIAR_Phases - Notebook/phase5.csv")

P6 = pd.read_csv("CAVIAR_Phases - Notebook/phase6.csv")

P7 = pd.read_csv("CAVIAR_Phases - Notebook/phase7.csv")

P8 = pd.read_csv("CAVIAR_Phases - Notebook/phase8.csv")

P9 = pd.read_csv("CAVIAR_Phases - Notebook/phase9.csv")

P10 = pd.read_csv("CAVIAR_Phases - Notebook/phase10.csv")

P11 = pd.read_csv("CAVIAR_Phases - Notebook/phase11.csv")
In [3]:
P1.head()
Out[3]:
Unnamed: 0 1 4 89 83 3 5 88 85 90 2 7 54 6 64 8
0 1 0 1 4 0 4 2 2 9 1 2 0 2 0 1 1
1 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
2 89 1 0 0 0 0 0 0 0 0 0 3 0 0 0 0
3 83 1 0 0 0 0 0 0 0 0 0 0 0 5 0 0
4 3 2 0 0 0 0 0 1 0 0 0 0 0 0 0 0

So, we have the data in the form of an adjacency matrix represented through a DataFrame.

Observations:

  • The first column (Unnamed: 0) needs to be set as the index.
  • The column names and the indices should have the same data type.

Let us check the points above.

In [4]:
# Setting first column as the index to achieve the adjacency matrix for each phase
# Defining a list with the phases
phases = [P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11]

# Now, let us set the first column as the index for all the DataFrames
for p in phases:
    p.set_index(p.columns[0], inplace = True)
In [5]:
P1
Out[5]:
1 4 89 83 3 5 88 85 90 2 7 54 6 64 8
Unnamed: 0
1 0 1 4 0 4 2 2 9 1 2 0 2 0 1 1
4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
89 1 0 0 0 0 0 0 0 0 0 3 0 0 0 0
83 1 0 0 0 0 0 0 0 0 0 0 0 5 0 0
3 2 0 0 0 0 0 1 0 0 0 0 0 0 0 0
5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
88 1 0 0 0 1 0 0 3 0 0 0 0 1 0 0
85 1 0 0 0 0 0 2 0 0 0 0 0 5 0 0
90 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
7 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0
54 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
6 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
In [6]:
# Now, that we have set the index, check for the consistency in column names and indices data type
P1.index, P1.columns
Out[6]:
(Int64Index([1, 4, 89, 83, 3, 5, 88, 85, 90, 2, 7, 54, 6, 64, 8], dtype='int64', name='Unnamed: 0'),
 Index(['1', '4', '89', '83', '3', '5', '88', '85', '90', '2', '7', '54', '6',
        '64', '8'],
       dtype='object'))

Observation:

  • The above output shows that the indices are of integer data type and the column names are of object data type.
  • Let's convert the column names to integer data types.
In [7]:
# Let's convert the column names to integer types
phases = [P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11]

col = []

for p in phases:                 # Running the loop for each phase (11)
    for i in p.columns:          # Running the loop for each column of each phase
        col.append(int(i))       # Appending column names to the list 'col'
    p.columns = col              # Updating the column names for each phase
    col = []                     # Reinitiating the list 'col' as an empty list 
In [8]:
# Let's check if the transformation worked
P1.index, P1.columns
Out[8]:
(Int64Index([1, 4, 89, 83, 3, 5, 88, 85, 90, 2, 7, 54, 6, 64, 8], dtype='int64', name='Unnamed: 0'),
 Int64Index([1, 4, 89, 83, 3, 5, 88, 85, 90, 2, 7, 54, 6, 64, 8], dtype='int64'))

Observation:

  • We observe that the transformation has worked. Both, indices as well as column names, are of integer data type.

Generating Graphs¶

Now, that we have created the adjacency matrices, let us now try to create graphs out of these adjacency matrices.

  • We will use the from_pandas_adjacency function and pass the adjacency matrix for each phase as a DataFrame.
In [9]:
graphs = []

for i, p in enumerate(phases):
    g = 'graph' + str(i + 1) 
    
    print(g)
    
    g = nx.from_pandas_adjacency(p)
    
    graphs.append(g)
    
    print(g.nodes())
graph1
[5, 83, 3, 90, 88, 85, 89, 4, 8, 64, 6, 54, 7, 2, 1]
graph2
[3, 85, 83, 90, 64, 6, 2, 7, 86, 88, 89, 12, 11, 9, 76, 98, 47, 97, 56, 10, 55, 8, 5, 1]
graph3
[5, 83, 88, 48, 89, 3, 7, 6, 52, 2, 90, 86, 85, 4, 12, 51, 13, 99, 50, 107, 49, 84, 32, 11, 35, 34, 9, 76, 56, 10, 55, 8, 1]
graph4
[5, 83, 88, 90, 85, 3, 76, 47, 6, 106, 8, 7, 86, 89, 4, 15, 52, 14, 12, 31, 109, 63, 51, 13, 107, 49, 84, 53, 11, 35, 9, 2, 1]
graph5
[5, 88, 83, 6, 86, 76, 89, 3, 32, 100, 34, 11, 8, 15, 55, 2, 85, 4, 19, 82, 25, 17, 18, 108, 12, 31, 13, 84, 9, 47, 7, 1]
graph6
[3, 8, 11, 13, 84, 12, 85, 82, 15, 83, 5, 19, 25, 31, 76, 2, 4, 20, 87, 77, 78, 18, 14, 9, 6, 1]
graph7
[3, 18, 83, 81, 2, 34, 9, 14, 88, 8, 55, 17, 5, 11, 69, 12, 77, 19, 85, 4, 15, 28, 75, 16, 68, 61, 79, 74, 22, 20, 87, 78, 76, 6, 62, 1]
graph8
[3, 8, 18, 59, 84, 80, 78, 35, 14, 39, 23, 73, 12, 17, 87, 34, 83, 85, 37, 33, 16, 19, 25, 11, 76, 2, 4, 36, 91, 38, 67, 81, 28, 22, 20, 77, 82, 13, 9, 6, 86, 1]
graph9
[3, 13, 17, 29, 30, 76, 83, 82, 11, 2, 18, 12, 7, 90, 89, 41, 101, 105, 46, 96, 36, 37, 59, 81, 16, 79, 87, 78, 14, 8, 6, 85, 88, 1]
graph10
[3, 65, 40, 87, 37, 16, 84, 85, 105, 73, 70, 58, 44, 81, 22, 82, 18, 9, 4, 45, 71, 42, 93, 95, 27, 104, 103, 21, 41, 46, 96, 24, 38, 19, 17, 14, 12, 76, 8, 86, 83, 1]
graph11
[24, 96, 61, 79, 82, 46, 26, 94, 87, 18, 17, 14, 88, 83, 65, 66, 92, 72, 102, 43, 42, 93, 27, 41, 101, 58, 36, 37, 59, 81, 16, 78, 12, 13, 84, 11, 76, 86, 85, 3, 1]

Visualizing the Graphs¶

Let us now visualize the graphs that we have created above.

Note: In case you face an error while running the below code, please upgrade the decorator library, by running the following code, to resolve the error.¶

!pip install --upgrade decorator

In [10]:
for i, g in enumerate(graphs):
    print("****************************************************************************************") #"*"*50
    
    print("Graph for phase:", i + 1)
    
    nx.draw(g, with_labels = True)
    
    plt.title(str(g))
    
    plt.show()
****************************************************************************************
Graph for phase: 1
****************************************************************************************
Graph for phase: 2
****************************************************************************************
Graph for phase: 3
****************************************************************************************
Graph for phase: 4
****************************************************************************************
Graph for phase: 5
****************************************************************************************
Graph for phase: 6
****************************************************************************************
Graph for phase: 7
****************************************************************************************
Graph for phase: 8
****************************************************************************************
Graph for phase: 9
****************************************************************************************
Graph for phase: 10
****************************************************************************************
Graph for phase: 11

Centrality Measures¶

Let us now explore the various centrality measures for the above graphs.

a. Degree Centrality¶

  • Let us now calculate the degree centrality
In [11]:
# Let us store the degree centralities for each node of a graph in a dictionary
deg_cen = {}

for g in graphs:
    deg_cen[g] = nx.degree_centrality(g)
In [12]:
# Let us look at the deg_cen dictionary
deg_cen
Out[12]:
{<networkx.classes.graph.Graph at 0x12393809340>: {5: 0.07142857142857142,
  83: 0.14285714285714285,
  3: 0.14285714285714285,
  90: 0.07142857142857142,
  88: 0.2857142857142857,
  85: 0.21428571428571427,
  89: 0.14285714285714285,
  4: 0.07142857142857142,
  8: 0.07142857142857142,
  64: 0.07142857142857142,
  6: 0.21428571428571427,
  54: 0.07142857142857142,
  7: 0.07142857142857142,
  2: 0.07142857142857142,
  1: 0.8571428571428571},
 <networkx.classes.graph.Graph at 0x123938095b0>: {3: 0.13043478260869565,
  85: 0.08695652173913043,
  83: 0.08695652173913043,
  90: 0.043478260869565216,
  64: 0.08695652173913043,
  6: 0.043478260869565216,
  2: 0.043478260869565216,
  7: 0.043478260869565216,
  86: 0.043478260869565216,
  88: 0.13043478260869565,
  89: 0.13043478260869565,
  12: 0.043478260869565216,
  11: 0.08695652173913043,
  9: 0.043478260869565216,
  76: 0.08695652173913043,
  98: 0.043478260869565216,
  47: 0.043478260869565216,
  97: 0.043478260869565216,
  56: 0.043478260869565216,
  10: 0.08695652173913043,
  55: 0.043478260869565216,
  8: 0.13043478260869565,
  5: 0.043478260869565216,
  1: 0.8260869565217391},
 <networkx.classes.graph.Graph at 0x12393809850>: {5: 0.0625,
  83: 0.25,
  88: 0.0625,
  48: 0.03125,
  89: 0.0625,
  3: 0.28125,
  7: 0.0625,
  6: 0.125,
  52: 0.09375,
  2: 0.09375,
  90: 0.0625,
  86: 0.125,
  85: 0.125,
  4: 0.03125,
  12: 0.0625,
  51: 0.03125,
  13: 0.0625,
  99: 0.03125,
  50: 0.03125,
  107: 0.0625,
  49: 0.15625,
  84: 0.125,
  32: 0.0625,
  11: 0.09375,
  35: 0.03125,
  34: 0.0625,
  9: 0.15625,
  76: 0.0625,
  56: 0.03125,
  10: 0.03125,
  55: 0.03125,
  8: 0.0625,
  1: 0.84375},
 <networkx.classes.graph.Graph at 0x12393809970>: {5: 0.03125,
  83: 0.21875,
  88: 0.0625,
  90: 0.0625,
  85: 0.15625,
  3: 0.21875,
  76: 0.0625,
  47: 0.03125,
  6: 0.03125,
  106: 0.0625,
  8: 0.125,
  7: 0.03125,
  86: 0.09375,
  89: 0.1875,
  4: 0.03125,
  15: 0.03125,
  52: 0.0625,
  14: 0.03125,
  12: 0.03125,
  31: 0.09375,
  109: 0.03125,
  63: 0.03125,
  51: 0.03125,
  13: 0.0625,
  107: 0.09375,
  49: 0.0625,
  84: 0.0625,
  53: 0.03125,
  11: 0.03125,
  35: 0.03125,
  9: 0.125,
  2: 0.03125,
  1: 0.71875},
 <networkx.classes.graph.Graph at 0x123937d6bb0>: {5: 0.06451612903225806,
  88: 0.03225806451612903,
  83: 0.06451612903225806,
  6: 0.06451612903225806,
  86: 0.03225806451612903,
  76: 0.06451612903225806,
  89: 0.0967741935483871,
  3: 0.16129032258064516,
  32: 0.03225806451612903,
  100: 0.03225806451612903,
  34: 0.03225806451612903,
  11: 0.03225806451612903,
  8: 0.06451612903225806,
  15: 0.03225806451612903,
  55: 0.03225806451612903,
  2: 0.03225806451612903,
  85: 0.0967741935483871,
  4: 0.03225806451612903,
  19: 0.03225806451612903,
  82: 0.03225806451612903,
  25: 0.03225806451612903,
  17: 0.03225806451612903,
  18: 0.03225806451612903,
  108: 0.06451612903225806,
  12: 0.25806451612903225,
  31: 0.12903225806451613,
  13: 0.03225806451612903,
  84: 0.03225806451612903,
  9: 0.06451612903225806,
  47: 0.03225806451612903,
  7: 0.03225806451612903,
  1: 0.7096774193548387},
 <networkx.classes.graph.Graph at 0x1239381a2e0>: {3: 0.56,
  8: 0.16,
  11: 0.08,
  13: 0.04,
  84: 0.08,
  12: 0.36,
  85: 0.2,
  82: 0.04,
  15: 0.12,
  83: 0.08,
  5: 0.12,
  19: 0.12,
  25: 0.04,
  31: 0.04,
  76: 0.28,
  2: 0.08,
  4: 0.04,
  20: 0.08,
  87: 0.08,
  77: 0.08,
  78: 0.04,
  18: 0.04,
  14: 0.08,
  9: 0.08,
  6: 0.04,
  1: 0.72},
 <networkx.classes.graph.Graph at 0x1239381a6a0>: {3: 0.2857142857142857,
  18: 0.02857142857142857,
  83: 0.08571428571428572,
  81: 0.05714285714285714,
  2: 0.05714285714285714,
  34: 0.02857142857142857,
  9: 0.08571428571428572,
  14: 0.02857142857142857,
  88: 0.05714285714285714,
  8: 0.02857142857142857,
  55: 0.02857142857142857,
  17: 0.02857142857142857,
  5: 0.02857142857142857,
  11: 0.05714285714285714,
  69: 0.02857142857142857,
  12: 0.14285714285714285,
  77: 0.08571428571428572,
  19: 0.11428571428571428,
  85: 0.11428571428571428,
  4: 0.02857142857142857,
  15: 0.02857142857142857,
  28: 0.02857142857142857,
  75: 0.02857142857142857,
  16: 0.02857142857142857,
  68: 0.02857142857142857,
  61: 0.02857142857142857,
  79: 0.05714285714285714,
  74: 0.05714285714285714,
  22: 0.02857142857142857,
  20: 0.05714285714285714,
  87: 0.08571428571428572,
  78: 0.05714285714285714,
  76: 0.14285714285714285,
  6: 0.02857142857142857,
  62: 0.02857142857142857,
  1: 0.6857142857142857},
 <networkx.classes.graph.Graph at 0x1239381a430>: {3: 0.3170731707317073,
  8: 0.07317073170731708,
  18: 0.024390243902439025,
  59: 0.024390243902439025,
  84: 0.024390243902439025,
  80: 0.024390243902439025,
  78: 0.024390243902439025,
  35: 0.024390243902439025,
  14: 0.07317073170731708,
  39: 0.024390243902439025,
  23: 0.024390243902439025,
  73: 0.04878048780487805,
  12: 0.24390243902439024,
  17: 0.024390243902439025,
  87: 0.21951219512195122,
  34: 0.04878048780487805,
  83: 0.024390243902439025,
  85: 0.07317073170731708,
  37: 0.04878048780487805,
  33: 0.024390243902439025,
  16: 0.024390243902439025,
  19: 0.04878048780487805,
  25: 0.024390243902439025,
  11: 0.07317073170731708,
  76: 0.14634146341463417,
  2: 0.0975609756097561,
  4: 0.024390243902439025,
  36: 0.024390243902439025,
  91: 0.024390243902439025,
  38: 0.024390243902439025,
  67: 0.024390243902439025,
  81: 0.04878048780487805,
  28: 0.024390243902439025,
  22: 0.04878048780487805,
  20: 0.04878048780487805,
  77: 0.024390243902439025,
  82: 0.04878048780487805,
  13: 0.024390243902439025,
  9: 0.04878048780487805,
  6: 0.024390243902439025,
  86: 0.04878048780487805,
  1: 0.4878048780487805},
 <networkx.classes.graph.Graph at 0x1239381a1c0>: {3: 0.33333333333333337,
  13: 0.030303030303030304,
  17: 0.030303030303030304,
  29: 0.030303030303030304,
  30: 0.06060606060606061,
  76: 0.15151515151515152,
  83: 0.030303030303030304,
  82: 0.18181818181818182,
  11: 0.030303030303030304,
  2: 0.030303030303030304,
  18: 0.030303030303030304,
  12: 0.24242424242424243,
  7: 0.06060606060606061,
  90: 0.030303030303030304,
  89: 0.030303030303030304,
  41: 0.030303030303030304,
  101: 0.030303030303030304,
  105: 0.030303030303030304,
  46: 0.06060606060606061,
  96: 0.12121212121212122,
  36: 0.030303030303030304,
  37: 0.030303030303030304,
  59: 0.030303030303030304,
  81: 0.030303030303030304,
  16: 0.030303030303030304,
  79: 0.09090909090909091,
  87: 0.24242424242424243,
  78: 0.06060606060606061,
  14: 0.030303030303030304,
  8: 0.06060606060606061,
  6: 0.030303030303030304,
  85: 0.09090909090909091,
  88: 0.030303030303030304,
  1: 0.30303030303030304},
 <networkx.classes.graph.Graph at 0x1239381a9d0>: {3: 0.024390243902439025,
  65: 0.024390243902439025,
  40: 0.024390243902439025,
  87: 0.2682926829268293,
  37: 0.21951219512195122,
  16: 0.024390243902439025,
  84: 0.04878048780487805,
  85: 0.07317073170731708,
  105: 0.04878048780487805,
  73: 0.024390243902439025,
  70: 0.024390243902439025,
  58: 0.04878048780487805,
  44: 0.024390243902439025,
  81: 0.04878048780487805,
  22: 0.024390243902439025,
  82: 0.12195121951219512,
  18: 0.024390243902439025,
  9: 0.024390243902439025,
  4: 0.024390243902439025,
  45: 0.024390243902439025,
  71: 0.04878048780487805,
  42: 0.024390243902439025,
  93: 0.024390243902439025,
  95: 0.024390243902439025,
  27: 0.024390243902439025,
  104: 0.024390243902439025,
  103: 0.024390243902439025,
  21: 0.024390243902439025,
  41: 0.04878048780487805,
  46: 0.04878048780487805,
  96: 0.04878048780487805,
  24: 0.07317073170731708,
  38: 0.024390243902439025,
  19: 0.024390243902439025,
  17: 0.024390243902439025,
  14: 0.07317073170731708,
  12: 0.17073170731707318,
  76: 0.07317073170731708,
  8: 0.04878048780487805,
  86: 0.024390243902439025,
  83: 0.04878048780487805,
  1: 0.3170731707317073},
 <networkx.classes.graph.Graph at 0x1239381a760>: {24: 0.025,
  96: 0.05,
  61: 0.025,
  79: 0.125,
  82: 0.1,
  46: 0.025,
  26: 0.025,
  94: 0.025,
  87: 0.1,
  18: 0.025,
  17: 0.05,
  14: 0.05,
  88: 0.025,
  83: 0.025,
  65: 0.025,
  66: 0.025,
  92: 0.025,
  72: 0.025,
  102: 0.07500000000000001,
  43: 0.025,
  42: 0.025,
  93: 0.05,
  27: 0.1,
  41: 0.225,
  101: 0.05,
  58: 0.05,
  36: 0.025,
  37: 0.07500000000000001,
  59: 0.05,
  81: 0.025,
  16: 0.025,
  78: 0.025,
  12: 0.30000000000000004,
  13: 0.05,
  84: 0.05,
  11: 0.025,
  76: 0.17500000000000002,
  86: 0.025,
  85: 0.07500000000000001,
  3: 0.025,
  1: 0.17500000000000002}}

b. Eigenvector, Betweenness, and Closeness Centrality measures¶

In [13]:
# Similarily, we can generate other centrality measures
# Let us try all the measures that we learnt

# Eigenvector Centrality
eig_cen = {}

for g in graphs:
    eig_cen[g] = nx.eigenvector_centrality(g)

# Betweenness Centrality
betw_cen = {}

for g in graphs:
    betw_cen[g] = nx.betweenness_centrality(g)

# Closeness Centrality
clo_cen = {}

for g in graphs:
    clo_cen[g] = nx.closeness_centrality(g)

Observations:

  • We have now created the dictionaries with the centrality measures
  • The dictionaries are deg_cen, eig_cen, betw_cen, and clo_cen.
  • We can sort the dictionaries, in descending order, to obtain the nodes that are the most important.
In [14]:
# Let us now sort the degree centrality measure and identify the important nodes

for ix, g in enumerate(graphs):
    temp_dict = {}
    for w in sorted(deg_cen[g], key = deg_cen[g].get, reverse = True):
        temp_dict[w] = deg_cen[g][w]
    print("Sorted importance of nodes in terms of deg_cen for Phase {} is {}".format(ix + 1, list(temp_dict.keys())[:5]))
    print()
Sorted importance of nodes in terms of deg_cen for Phase 1 is [1, 88, 85, 6, 83]

Sorted importance of nodes in terms of deg_cen for Phase 2 is [1, 3, 88, 89, 8]

Sorted importance of nodes in terms of deg_cen for Phase 3 is [1, 3, 83, 49, 9]

Sorted importance of nodes in terms of deg_cen for Phase 4 is [1, 83, 3, 89, 85]

Sorted importance of nodes in terms of deg_cen for Phase 5 is [1, 12, 3, 31, 89]

Sorted importance of nodes in terms of deg_cen for Phase 6 is [1, 3, 12, 76, 85]

Sorted importance of nodes in terms of deg_cen for Phase 7 is [1, 3, 12, 76, 19]

Sorted importance of nodes in terms of deg_cen for Phase 8 is [1, 3, 12, 87, 76]

Sorted importance of nodes in terms of deg_cen for Phase 9 is [3, 1, 12, 87, 82]

Sorted importance of nodes in terms of deg_cen for Phase 10 is [1, 87, 37, 12, 82]

Sorted importance of nodes in terms of deg_cen for Phase 11 is [12, 41, 76, 1, 79]

  • We generated the degree centrality based top 5 most important nodes in each phase.
  • We can go ahead and repeat the same exercise for other centrality measures.

Understanding the variation of node importance across phases¶

  • From the above analysis, we figure out that some nodes appear to be more important than others, and we will focus our attention on analyzing those nodes.

  • Let us take out Node1, Node3, and Node12 and visualize their importance across phases. We will look at the betweenness and degree centrality only. You are encouraged to try out the same for other measures.

In [15]:
# Let us first start with node 1

# Node 1
node1_deg = []

phases = []

for ix, g in enumerate(graphs):
    node1_deg.append(deg_cen[g][1]*100)
    phases.append(ix + 1)

node1_bet = []

for ix, g in enumerate(graphs):
    node1_bet.append(betw_cen[g][1]*100)

# Now, let us calculate the same for node 3 and 12 as well

# Node 3
node3_deg = []

for ix, g in enumerate(graphs):
    node3_deg.append(deg_cen[g][3]*100)

node3_bet = []

for ix, g in enumerate(graphs):
    node3_bet.append(betw_cen[g][3]*100)

# Node 12
node12_deg = []

for ix, g in enumerate(graphs):
    if (12 in deg_cen[g].keys()):
        node12_deg.append(deg_cen[g][12]*100)
    else:
        node12_deg.append(None)

node12_bet = []

for ix, g in enumerate(graphs):
    if (12 in betw_cen[g].keys()):
        node12_bet.append(betw_cen[g][12]*100)
    else:
        node12_bet.append(None)
In [16]:
# Now, let us plot the importance of nodes across phases

# Ploting Degree centrality for nodes 1, 3, and 12
plt.figure(figsize = (10, 7))

plt.plot(phases, node1_deg, label = 'Node 1')

plt.plot(phases, node3_deg, label = 'Node 3')

plt.plot(phases, node12_deg, label = 'Node 12')

plt.ylabel('Degree centrality(*100)')

plt.xlabel('Phases')

plt.legend()

plt.show()
In [17]:
# Plotting the Betweenness Centrality for nodes 1, 3, and 12

plt.figure(figsize = (10, 7))

plt.plot(phases, node1_bet,  label = 'Node 1')

plt.plot(phases, node3_bet, label = 'Node 3')

plt.plot(phases, node12_bet, label = 'Node 12')

plt.ylabel('Betweenness centrality(*100)')

plt.xlabel('Phases')

plt.legend()

plt.show()

Conclusion:¶

  • We carried out the analysis on the network and figured out techniques to read adjacency matrices into graphs.
  • We later visualized the graphs, created centrality measures, and identified the important nodes - N1 (Daniel Serero), N3 (Pierre Perlini), N12 (Ernesto Morales).
  • We studied and plotted the variation in the centrality of the important nodes across phases in a bid to understand the affect of disruption in the network.

Bonus: We can highlight the important nodes in graphs¶

  • Let's have a look at the phase 2 graph: graph2
In [18]:
graph2 = nx.from_pandas_adjacency(P2)

color = []

for node in graph2:
    if (node == 1 or node == 12  or node == 3):
        color.append('red')
    else:
        color.append('green')

nx.draw_spring(graph2, node_color = color, with_labels = True)
  • The important nodes, i.e., nodes 1, 3, and 12 have been marked as red.