Network Analysis

In this series of lessons, we’re going to learn about network analysis. Network analysis will help us better understand the complex relationships between groups of people, fictional characters, and other kinds of things.

Dataset

Game of Thrones

Lucky it might be, and red it certainly was, but Ygritte’s hair was such a tangle that Jon was tempted to ask her if she only brushed it at the changing of the seasons.

—George R. R. Martin, A Song of Ice and Fire

Network Basics

Install NetworkX

!pip install networkx

Import Libraries

import networkx
import pandas as pd
pd.set_option('max_rows', 400)
import matplotlib.pyplot as plt

Game of Thrones Network

The network data that we’re going to use in this lesson is taken from Andrew Beveridge and Jie Shan’s paper, “Network of Thrones.”

These researchers calculated how many times each Game of Thrones character appeared within 15 words of another character in A Storm of Swords, the third book in the series.

Network Element

GOT

Node

GOT character

Edge

Mutually mentioned within 15 words

Float

Decimal Numbers

Boolean

True/False

For example, the following sentence counts as an “edge” or connection between Jon Snow and Sam Tarly:

“It was the bastard Jon Snow who had taken that from him, him and his fat friend Sam Tarly.”

“Lucky it might be, and red it certainly was, but Ygritte’s hair was such a tangle that Jon was tempted to ask her if she only brushed it at the changing of the seasons.”

Arya gave Gendry a sideways look. He said it with me, like Jon used to do, back in Winterfell. She missed Jon Snow the most of all her brothers.””

got_df = pd.read_csv('../data/got-edges.csv')
got_df
Source Target Weight
0 Aemon Grenn 5
1 Aemon Samwell 31
2 Aerys Jaime 18
3 Aerys Robert 6
4 Aerys Tyrion 5
5 Aerys Tywin 8
6 Alliser Mance 5
7 Amory Oberyn 5
8 Arya Anguy 11
9 Arya Beric 23
10 Arya Bran 9
11 Arya Brynden 6
12 Arya Cersei 5
13 Arya Gendry 43
14 Arya Gregor 7
15 Arya Jaime 11
16 Arya Joffrey 6
17 Arya Jon 7
18 Arya Rickon 8
19 Arya Robert 4
20 Arya Roose 5
21 Arya Sandor 46
22 Arya Thoros 18
23 Arya Tyrion 5
24 Balon Loras 4
25 Belwas Barristan 18
26 Belwas Illyrio 10
27 Beric Anguy 4
28 Beric Gendry 4
29 Beric Thoros 21
30 Bran Hodor 96
31 Bran Jojen 46
32 Bran Jon 12
33 Bran Luwin 4
34 Bran Meera 54
35 Bran Nan 14
36 Bran Rickon 35
37 Bran Samwell 11
38 Bran Theon 11
39 Brienne Loras 7
40 Bronn Gregor 5
41 Bronn Podrick 19
42 Brynden Lothar 4
43 Brynden Walder 5
44 Catelyn Bran 4
45 Catelyn Brienne 7
46 Catelyn Brynden 8
47 Catelyn Cersei 4
48 Catelyn Edmure 16
49 Catelyn Hoster 9
50 Catelyn Jaime 19
51 Catelyn Jeyne 10
52 Catelyn Lysa 8
53 Catelyn Petyr 5
54 Catelyn Robb 43
55 Catelyn Roose 4
56 Catelyn Roslin 6
57 Catelyn Sansa 8
58 Catelyn Stannis 4
59 Catelyn Tyrion 5
60 Catelyn Walder 19
61 Cersei Brienne 5
62 Cersei Bronn 4
63 Cersei Elia 4
64 Cersei Gregor 6
65 Cersei Ilyn 7
66 Cersei Jaime 36
67 Cersei Joffrey 23
68 Cersei Meryn 10
69 Cersei Pycelle 4
70 Cersei Robert 16
71 Cersei Sandor 6
72 Cersei Shae 4
73 Cersei Tyrion 46
74 Cersei Varys 4
75 Craster Karl 6
76 Daario Drogo 4
77 Daario Irri 5
78 Daenerys Aegon 8
79 Daenerys Barristan 20
80 Daenerys Belwas 26
81 Daenerys Daario 14
82 Daenerys Drogo 18
83 Daenerys Irri 17
84 Daenerys Jorah 47
85 Daenerys Kraznys 10
86 Daenerys Missandei 26
87 Daenerys Rakharo 7
88 Daenerys Rhaegar 12
89 Daenerys Robert 5
90 Daenerys Viserys 8
91 Daenerys Worm 14
92 Davos Cressen 4
93 Davos Salladhor 16
94 Eddard Arya 18
95 Eddard Beric 8
96 Eddard Bran 15
97 Eddard Catelyn 5
98 Eddard Cersei 6
99 Eddard Jaime 6
100 Eddard Jon 8
101 Eddard Rickon 8
102 Eddard Robb 13
103 Eddard Robert 10
104 Eddard Sandor 5
105 Eddard Sansa 6
106 Eddison Grenn 8
107 Edmure Brynden 7
108 Edmure Lothar 4
109 Edmure Roslin 16
110 Edmure Walder 9
111 Gendry Thoros 7
112 Gilly Craster 17
113 Gregor Elia 9
114 Gregor Ilyn 7
115 Gregor Meryn 7
116 Gregor Oberyn 24
117 Gregor Sandor 12
118 Hodor Jojen 35
119 Hodor Meera 41
120 Hoster Edmure 9
121 Irri Drogo 7
122 Jaime Balon 6
123 Jaime Barristan 4
124 Jaime Brienne 88
125 Jaime Edmure 5
126 Jaime Elia 4
127 Jaime Gregor 6
128 Jaime Joffrey 15
129 Jaime Loras 16
130 Jaime Meryn 11
131 Jaime Qyburn 11
132 Jaime Renly 7
133 Jaime Robert 17
134 Jaime Stannis 5
135 Jaime Tommen 8
136 Jaime Tyrion 31
137 Janos Alliser 9
138 Janos Bowen 5
139 Janos Mance 4
140 Joffrey Gregor 5
141 Joffrey Ilyn 4
142 Joffrey Kevan 8
143 Joffrey Loras 7
144 Joffrey Margaery 28
145 Joffrey Meryn 5
146 Joffrey Myrcella 5
147 Joffrey Oberyn 4
148 Joffrey Sandor 14
149 Joffrey Stannis 10
150 Joffrey Tommen 9
151 Joffrey Tyrion 54
152 Jojen Meera 33
153 Jojen Samwell 11
154 Jon Aemon 30
155 Jon Alliser 15
156 Jon Craster 7
157 Jon Dalla 6
158 Jon Eddison 4
159 Jon Gilly 9
160 Jon Grenn 25
161 Jon Janos 8
162 Jon Mance 69
163 Jon Meera 4
164 Jon Melisandre 7
165 Jon Orell 6
166 Jon Qhorin 31
167 Jon Rattleshirt 20
168 Jon Robert 5
169 Jon Samwell 52
170 Jon Stannis 9
171 Jon Styr 16
172 Jon Theon 8
173 Jon Val 12
174 Jon Ygritte 54
175 Jon Arryn Lysa 5
176 Jon Arryn Robert 6
177 Jorah Barristan 11
178 Jorah Belwas 13
179 Jorah Daario 7
180 Jorah Drogo 6
181 Kevan Lancel 7
182 Kevan Varys 5
183 Loras Margaery 9
184 Loras Olenna 5
185 Lothar Roslin 4
186 Luwin Nan 4
187 Lysa Cersei 4
188 Lysa Hoster 6
189 Lysa Marillion 10
190 Lysa Petyr 29
191 Lysa Robert Arryn 9
192 Lysa Tyrion 5
193 Lysa Tywin 4
194 Mance Craster 11
195 Mance Dalla 8
196 Mance Gilly 7
197 Mance Qhorin 10
198 Mance Rattleshirt 9
199 Mance Styr 7
200 Mance Val 8
201 Mance Ygritte 12
202 Meera Samwell 7
203 Melisandre Davos 30
204 Melisandre Samwell 5
205 Meryn Ilyn 5
206 Missandei Irri 4
207 Myrcella Tommen 5
208 Myrcella Tyrion 4
209 Oberyn Ellaria 6
210 Oberyn Mace 6
211 Podrick Margaery 4
212 Rattleshirt Qhorin 6
213 Renly Loras 8
214 Renly Margaery 7
215 Renly Varys 4
216 Rhaegar Barristan 5
217 Rhaegar Elia 7
218 Rhaegar Jorah 5
219 Rhaegar Robert 6
220 Rickard Brynden 4
221 Rickon Theon 8
222 Robb Arya 15
223 Robb Balon 6
224 Robb Bran 23
225 Robb Brienne 6
226 Robb Brynden 17
227 Robb Edmure 32
228 Robb Hodor 5
229 Robb Jaime 15
230 Robb Jeyne 18
231 Robb Joffrey 10
232 Robb Jon 14
233 Robb Lothar 10
234 Robb Petyr 5
235 Robb Ramsay 4
236 Robb Rickard 7
237 Robb Rickon 15
238 Robb Roose 4
239 Robb Sansa 15
240 Robb Stannis 4
241 Robb Theon 11
242 Robb Tyrion 12
243 Robb Tywin 12
244 Robb Walder 26
245 Robert Aemon 4
246 Robert Barristan 5
247 Robert Renly 4
248 Robert Stannis 5
249 Robert Thoros 4
250 Robert Arryn Marillion 4
251 Roose Brienne 4
252 Samwell Bowen 6
253 Samwell Craster 34
254 Samwell Eddison 12
255 Samwell Gilly 36
256 Samwell Grenn 43
257 Samwell Janos 6
258 Samwell Mance 10
259 Samwell Qhorin 5
260 Sandor Beric 15
261 Sandor Gendry 5
262 Sandor Ilyn 4
263 Sandor Meryn 4
264 Sandor Robert 6
265 Sandor Thoros 10
266 Sansa Arya 22
267 Sansa Bran 10
268 Sansa Brienne 5
269 Sansa Cersei 16
270 Sansa Jaime 10
271 Sansa Joffrey 35
272 Sansa Jon 4
273 Sansa Kevan 5
274 Sansa Loras 14
275 Sansa Lysa 28
276 Sansa Margaery 36
277 Sansa Marillion 9
278 Sansa Myrcella 4
279 Sansa Olenna 7
280 Sansa Petyr 28
281 Sansa Podrick 8
282 Sansa Renly 4
283 Sansa Rickon 7
284 Sansa Robert 5
285 Sansa Robert Arryn 6
286 Sansa Sandor 6
287 Sansa Shae 8
288 Sansa Tyrion 77
289 Shae Chataya 4
290 Shae Varys 8
291 Shireen Davos 5
292 Stannis Aemon 4
293 Stannis Balon 4
294 Stannis Davos 32
295 Stannis Melisandre 20
296 Stannis Renly 15
297 Stannis Samwell 13
298 Tommen Margaery 5
299 Tyrion Balon 4
300 Tyrion Bronn 31
301 Tyrion Chataya 5
302 Tyrion Doran 5
303 Tyrion Elia 5
304 Tyrion Ellaria 4
305 Tyrion Gregor 22
306 Tyrion Ilyn 5
307 Tyrion Janos 5
308 Tyrion Kevan 11
309 Tyrion Loras 6
310 Tyrion Mace 9
311 Tyrion Margaery 7
312 Tyrion Meryn 5
313 Tyrion Oberyn 25
314 Tyrion Petyr 12
315 Tyrion Podrick 28
316 Tyrion Pycelle 11
317 Tyrion Renly 6
318 Tyrion Robert 9
319 Tyrion Sandor 4
320 Tyrion Shae 21
321 Tyrion Stannis 6
322 Tyrion Varys 18
323 Tywin Balon 5
324 Tywin Brynden 4
325 Tywin Cersei 16
326 Tywin Gregor 7
327 Tywin Jaime 13
328 Tywin Joffrey 13
329 Tywin Kevan 14
330 Tywin Mace 5
331 Tywin Oberyn 6
332 Tywin Petyr 4
333 Tywin Podrick 5
334 Tywin Pycelle 5
335 Tywin Robert 11
336 Tywin Stannis 15
337 Tywin Tommen 4
338 Tywin Tyrion 39
339 Tywin Val 4
340 Tywin Varys 6
341 Tywin Walder 4
342 Val Dalla 7
343 Varys Pycelle 4
344 Viserys Rhaegar 7
345 Viserys Tyrion 4
346 Walder Lothar 12
347 Walder Petyr 6
348 Walder Roslin 6
349 Walton Jaime 10
350 Ygritte Qhorin 7
351 Ygritte Rattleshirt 9

Create a Network From a Pandas DataFrame

G = networkx.from_pandas_edgelist(got_df, 'Source', 'Target', 'Weight')

Output a Network File

networkx.write_graphml(G, 'GOT-network.graphml')

Draw a Simple Network

networkx.draw(G)
../../_images/Network-Analysis_22_01.png
plt.figure(figsize=(8,8))
networkx.draw(G, with_labels=True, node_color='skyblue', width=.3, font_size=8)
../../_images/Network-Analysis_23_01.png

Calculate Degree

Who has the most number of connections in the network?

networkx.degree(G)
DegreeView({'Aemon': 5, 'Grenn': 4, 'Samwell': 15, 'Aerys': 4, 'Jaime': 24, 'Robert': 18, 'Tyrion': 36, 'Tywin': 22, 'Alliser': 3, 'Mance': 12, 'Amory': 1, 'Oberyn': 7, 'Arya': 19, 'Anguy': 2, 'Beric': 6, 'Bran': 14, 'Brynden': 8, 'Cersei': 20, 'Gendry': 4, 'Gregor': 12, 'Joffrey': 18, 'Jon': 26, 'Rickon': 6, 'Roose': 4, 'Sandor': 13, 'Thoros': 5, 'Balon': 6, 'Loras': 9, 'Belwas': 4, 'Barristan': 6, 'Illyrio': 1, 'Hodor': 4, 'Jojen': 4, 'Luwin': 2, 'Meera': 5, 'Nan': 2, 'Theon': 4, 'Brienne': 7, 'Bronn': 4, 'Podrick': 5, 'Lothar': 5, 'Walder': 8, 'Catelyn': 18, 'Edmure': 8, 'Hoster': 3, 'Jeyne': 2, 'Lysa': 10, 'Petyr': 7, 'Robb': 25, 'Roslin': 4, 'Sansa': 26, 'Stannis': 14, 'Elia': 5, 'Ilyn': 6, 'Meryn': 7, 'Pycelle': 4, 'Shae': 5, 'Varys': 7, 'Craster': 5, 'Karl': 1, 'Daario': 4, 'Drogo': 4, 'Irri': 4, 'Daenerys': 14, 'Aegon': 1, 'Jorah': 6, 'Kraznys': 1, 'Missandei': 2, 'Rakharo': 1, 'Rhaegar': 6, 'Viserys': 3, 'Worm': 1, 'Davos': 5, 'Cressen': 1, 'Salladhor': 1, 'Eddard': 12, 'Eddison': 3, 'Gilly': 4, 'Qyburn': 1, 'Renly': 8, 'Tommen': 5, 'Janos': 6, 'Bowen': 2, 'Kevan': 6, 'Margaery': 7, 'Myrcella': 4, 'Dalla': 3, 'Melisandre': 4, 'Orell': 1, 'Qhorin': 5, 'Rattleshirt': 4, 'Styr': 2, 'Val': 4, 'Ygritte': 4, 'Jon Arryn': 2, 'Lancel': 1, 'Olenna': 2, 'Marillion': 3, 'Robert Arryn': 3, 'Ellaria': 2, 'Mace': 3, 'Rickard': 2, 'Ramsay': 1, 'Chataya': 2, 'Shireen': 1, 'Doran': 1, 'Walton': 1})

Make the degree values a dictionary, then add it as a network “attribute” with networkx.set_node_attributes()

degrees = dict(networkx.degree(G))
networkx.set_node_attributes(G, name='degree', values=degrees)

Make a Pandas dataframe from the degree data G.nodes(data='degree'), then sort from highest to lowest

degree_df = pd.DataFrame(G.nodes(data='degree'), columns=['node', 'degree'])
degree_df = degree_df.sort_values(by='degree', ascending=False)
degree_df
node degree
6 Tyrion 36
21 Jon 26
50 Sansa 26
48 Robb 25
4 Jaime 24
7 Tywin 22
17 Cersei 20
12 Arya 19
42 Catelyn 18
5 Robert 18
20 Joffrey 18
2 Samwell 15
63 Daenerys 14
15 Bran 14
51 Stannis 14
24 Sandor 13
9 Mance 12
75 Eddard 12
19 Gregor 12
46 Lysa 10
27 Loras 9
16 Brynden 8
41 Walder 8
43 Edmure 8
79 Renly 8
37 Brienne 7
54 Meryn 7
57 Varys 7
47 Petyr 7
84 Margaery 7
11 Oberyn 7
29 Barristan 6
69 Rhaegar 6
83 Kevan 6
81 Janos 6
14 Beric 6
53 Ilyn 6
22 Rickon 6
26 Balon 6
65 Jorah 6
52 Elia 5
56 Shae 5
58 Craster 5
72 Davos 5
89 Qhorin 5
80 Tommen 5
0 Aemon 5
40 Lothar 5
39 Podrick 5
34 Meera 5
25 Thoros 5
77 Gilly 4
62 Irri 4
28 Belwas 4
93 Ygritte 4
90 Rattleshirt 4
23 Roose 4
85 Myrcella 4
31 Hodor 4
18 Gendry 4
32 Jojen 4
60 Daario 4
61 Drogo 4
3 Aerys 4
87 Melisandre 4
55 Pycelle 4
36 Theon 4
1 Grenn 4
38 Bronn 4
49 Roslin 4
92 Val 4
86 Dalla 3
44 Hoster 3
76 Eddison 3
98 Robert Arryn 3
70 Viserys 3
97 Marillion 3
100 Mace 3
8 Alliser 3
91 Styr 2
94 Jon Arryn 2
96 Olenna 2
99 Ellaria 2
101 Rickard 2
103 Chataya 2
45 Jeyne 2
13 Anguy 2
35 Nan 2
33 Luwin 2
82 Bowen 2
67 Missandei 2
10 Amory 1
66 Kraznys 1
105 Doran 1
104 Shireen 1
102 Ramsay 1
59 Karl 1
64 Aegon 1
68 Rakharo 1
95 Lancel 1
30 Illyrio 1
71 Worm 1
73 Cressen 1
74 Salladhor 1
78 Qyburn 1
88 Orell 1
106 Walton 1

Plot the nodes with the highest degree values

num_nodes_to_inspect = 10
degree_df[:num_nodes_to_inspect].plot(x='node', y='degree', kind='barh').invert_yaxis()
../../_images/Network-Analysis_32_01.png

Calculate Weighted Degree

Who has the most number of connections in the network (if you factor in edge weight)?

networkx.degree(G, weight='Weight')
DegreeView({'Aemon': 74, 'Grenn': 81, 'Samwell': 282, 'Aerys': 37, 'Jaime': 372, 'Robert': 128, 'Tyrion': 551, 'Tywin': 204, 'Alliser': 29, 'Mance': 160, 'Amory': 5, 'Oberyn': 76, 'Arya': 269, 'Anguy': 15, 'Beric': 75, 'Bran': 344, 'Brynden': 55, 'Cersei': 226, 'Gendry': 59, 'Gregor': 117, 'Joffrey': 255, 'Jon': 442, 'Rickon': 81, 'Roose': 17, 'Sandor': 137, 'Thoros': 60, 'Balon': 29, 'Loras': 76, 'Belwas': 67, 'Barristan': 63, 'Illyrio': 10, 'Hodor': 177, 'Jojen': 125, 'Luwin': 8, 'Meera': 139, 'Nan': 18, 'Theon': 38, 'Brienne': 122, 'Bronn': 59, 'Podrick': 64, 'Lothar': 34, 'Walder': 87, 'Catelyn': 184, 'Edmure': 98, 'Hoster': 24, 'Jeyne': 28, 'Lysa': 108, 'Petyr': 89, 'Robb': 342, 'Roslin': 32, 'Sansa': 383, 'Stannis': 146, 'Elia': 29, 'Ilyn': 32, 'Meryn': 47, 'Pycelle': 24, 'Shae': 45, 'Varys': 49, 'Craster': 75, 'Karl': 6, 'Daario': 30, 'Drogo': 35, 'Irri': 33, 'Daenerys': 232, 'Aegon': 8, 'Jorah': 89, 'Kraznys': 10, 'Missandei': 30, 'Rakharo': 7, 'Rhaegar': 42, 'Viserys': 19, 'Worm': 14, 'Davos': 87, 'Cressen': 4, 'Salladhor': 16, 'Eddard': 108, 'Eddison': 24, 'Gilly': 69, 'Qyburn': 11, 'Renly': 55, 'Tommen': 31, 'Janos': 37, 'Bowen': 11, 'Kevan': 50, 'Margaery': 96, 'Myrcella': 18, 'Dalla': 21, 'Melisandre': 62, 'Orell': 6, 'Qhorin': 59, 'Rattleshirt': 44, 'Styr': 23, 'Val': 31, 'Ygritte': 82, 'Jon Arryn': 11, 'Lancel': 7, 'Olenna': 12, 'Marillion': 23, 'Robert Arryn': 19, 'Ellaria': 10, 'Mace': 20, 'Rickard': 11, 'Ramsay': 4, 'Chataya': 9, 'Shireen': 5, 'Doran': 5, 'Walton': 10})

Make the weighted degree values a dictionary, then add it as a network “attribute” with networkx.set_node_attributes()

weighted_degrees = dict(networkx.degree(G, weight='Weight'))
networkx.set_node_attributes(G, name='weighted_degree', values=weighted_degrees)

Make a Pandas dataframe from the degree data G.nodes(data='weighted_degree'), then sort from highest to lowest

weighted_degree_df = pd.DataFrame(G.nodes(data='weighted_degree'), columns=['node', 'weighted_degree'])
weighted_degree_df = weighted_degree_df.sort_values(by='weighted_degree', ascending=False)
weighted_degree_df
node weighted_degree
6 Tyrion 551
21 Jon 442
50 Sansa 383
4 Jaime 372
15 Bran 344
48 Robb 342
2 Samwell 282
12 Arya 269
20 Joffrey 255
63 Daenerys 232
17 Cersei 226
7 Tywin 204
42 Catelyn 184
31 Hodor 177
9 Mance 160
51 Stannis 146
34 Meera 139
24 Sandor 137
5 Robert 128
32 Jojen 125
37 Brienne 122
19 Gregor 117
46 Lysa 108
75 Eddard 108
43 Edmure 98
84 Margaery 96
47 Petyr 89
65 Jorah 89
72 Davos 87
41 Walder 87
93 Ygritte 82
1 Grenn 81
22 Rickon 81
11 Oberyn 76
27 Loras 76
14 Beric 75
58 Craster 75
0 Aemon 74
77 Gilly 69
28 Belwas 67
39 Podrick 64
29 Barristan 63
87 Melisandre 62
25 Thoros 60
38 Bronn 59
89 Qhorin 59
18 Gendry 59
79 Renly 55
16 Brynden 55
83 Kevan 50
57 Varys 49
54 Meryn 47
56 Shae 45
90 Rattleshirt 44
69 Rhaegar 42
36 Theon 38
81 Janos 37
3 Aerys 37
61 Drogo 35
40 Lothar 34
62 Irri 33
53 Ilyn 32
49 Roslin 32
92 Val 31
80 Tommen 31
60 Daario 30
67 Missandei 30
52 Elia 29
8 Alliser 29
26 Balon 29
45 Jeyne 28
55 Pycelle 24
76 Eddison 24
44 Hoster 24
91 Styr 23
97 Marillion 23
86 Dalla 21
100 Mace 20
70 Viserys 19
98 Robert Arryn 19
35 Nan 18
85 Myrcella 18
23 Roose 17
74 Salladhor 16
13 Anguy 15
71 Worm 14
96 Olenna 12
78 Qyburn 11
82 Bowen 11
101 Rickard 11
94 Jon Arryn 11
99 Ellaria 10
106 Walton 10
30 Illyrio 10
66 Kraznys 10
103 Chataya 9
64 Aegon 8
33 Luwin 8
95 Lancel 7
68 Rakharo 7
59 Karl 6
88 Orell 6
10 Amory 5
104 Shireen 5
105 Doran 5
73 Cressen 4
102 Ramsay 4

Plot the nodes with the highest weighted degree values

num_nodes_to_inspect = 10
weighted_degree_df[:num_nodes_to_inspect].plot(x='node', y='weighted_degree', color='orange', kind='barh').invert_yaxis()
../../_images/Network-Analysis_41_01.png

Calculate Betweenness Centrality Scores

Who connects the most other nodes in the network?

networkx.betweenness_centrality(G)
{'Aemon': 0.004622508177397137,
 'Grenn': 5.989817310572027e-05,
 'Samwell': 0.03227262859511145,
 'Aerys': 0.0,
 'Jaime': 0.09994344156136246,
 'Robert': 0.2094523840293194,
 'Tyrion': 0.19791284320277353,
 'Tywin': 0.06553840423231058,
 'Alliser': 0.0,
 'Mance': 0.007275961371756524,
 'Amory': 0.0,
 'Oberyn': 0.01958014232946847,
 'Arya': 0.07960711308183888,
 'Anguy': 0.0,
 'Beric': 0.0006091214266627511,
 'Bran': 0.06301659941786954,
 'Brynden': 0.004727134574764742,
 'Cersei': 0.02674643287824232,
 'Gendry': 0.0,
 'Gregor': 0.005879915417737236,
 'Joffrey': 0.025434099443917696,
 'Jon': 0.22996466368473173,
 'Rickon': 0.0007626320968639029,
 'Roose': 0.0002629599012559427,
 'Sandor': 0.014162278987154626,
 'Thoros': 0.0019261823273239152,
 'Balon': 0.0006281164805527878,
 'Loras': 0.003780752291533962,
 'Belwas': 0.018867924528301886,
 'Barristan': 0.04011407489067884,
 'Illyrio': 0.0,
 'Hodor': 0.0021937273446681065,
 'Jojen': 0.0004064518889316733,
 'Luwin': 0.0,
 'Meera': 0.0019922569692430686,
 'Nan': 0.0,
 'Theon': 0.00047177590304274953,
 'Brienne': 0.0015376001520469229,
 'Bronn': 0.00010482180293501046,
 'Podrick': 0.0008358985235471674,
 'Lothar': 0.0006600525898733143,
 'Walder': 0.0032731813973401044,
 'Catelyn': 0.04890588265764755,
 'Edmure': 0.0063172332740347534,
 'Hoster': 0.0002426336765959408,
 'Jeyne': 0.0,
 'Lysa': 0.015215156305177053,
 'Petyr': 0.0011502139845250673,
 'Robb': 0.1269644713830151,
 'Roslin': 6.751960581039384e-05,
 'Sansa': 0.12672031668498004,
 'Stannis': 0.1026998617273264,
 'Elia': 0.002996853410630337,
 'Ilyn': 0.0,
 'Meryn': 0.00025900705642512993,
 'Pycelle': 0.0,
 'Shae': 0.0012523948930099663,
 'Varys': 0.0009094137867722772,
 'Craster': 0.018867924528301886,
 'Karl': 0.0,
 'Daario': 5.989817310572027e-05,
 'Drogo': 5.989817310572027e-05,
 'Irri': 0.00017969451931716083,
 'Daenerys': 0.15720345212054956,
 'Aegon': 0.0,
 'Jorah': 0.002329891127259132,
 'Kraznys': 0.0,
 'Missandei': 0.0,
 'Rakharo': 0.0,
 'Rhaegar': 0.007312671807481101,
 'Viserys': 0.015644391307527945,
 'Worm': 0.0,
 'Davos': 0.056064690026954175,
 'Cressen': 0.0,
 'Salladhor': 0.0,
 'Eddard': 0.019341416465723616,
 'Eddison': 0.0,
 'Gilly': 0.0,
 'Qyburn': 0.0,
 'Renly': 0.004227926858198188,
 'Tommen': 0.0006984420191967362,
 'Janos': 0.02355861153253357,
 'Bowen': 0.0,
 'Kevan': 0.019219915043818533,
 'Margaery': 0.0010973522354777933,
 'Myrcella': 0.0006235971795124951,
 'Dalla': 0.0,
 'Melisandre': 0.00860866854841803,
 'Orell': 0.0,
 'Qhorin': 0.0002635519616651692,
 'Rattleshirt': 0.0,
 'Styr': 0.0,
 'Val': 0.005898334987101542,
 'Ygritte': 0.0,
 'Jon Arryn': 0.0005764583325230229,
 'Lancel': 0.0,
 'Olenna': 0.0,
 'Marillion': 0.0,
 'Robert Arryn': 0.0,
 'Ellaria': 0.0,
 'Mace': 0.0,
 'Rickard': 0.0,
 'Ramsay': 0.0,
 'Chataya': 0.0,
 'Shireen': 0.0,
 'Doran': 0.0,
 'Walton': 0.0}
betweenness_centrality = networkx.betweenness_centrality(G)

Add betweenness_centrality (which is already a dictionary) as a network “attribute” with networkx.set_node_attributes()

networkx.set_node_attributes(G, name='betweenness', values=betweenness_centrality)

Make a Pandas dataframe from the betweenness data G.nodes(data='betweenness'), then sort from highest to lowest

betweenness_df = pd.DataFrame(G.nodes(data='betweenness'), columns=['node', 'betweenness'])
betweenness_df = betweenness_df.sort_values(by='betweenness', ascending=False)
betweenness_df
node betweenness
21 Jon 0.229965
5 Robert 0.209452
6 Tyrion 0.197913
63 Daenerys 0.157203
48 Robb 0.126964
50 Sansa 0.126720
51 Stannis 0.102700
4 Jaime 0.099943
12 Arya 0.079607
7 Tywin 0.065538
15 Bran 0.063017
72 Davos 0.056065
42 Catelyn 0.048906
29 Barristan 0.040114
2 Samwell 0.032273
17 Cersei 0.026746
20 Joffrey 0.025434
81 Janos 0.023559
11 Oberyn 0.019580
75 Eddard 0.019341
83 Kevan 0.019220
28 Belwas 0.018868
58 Craster 0.018868
70 Viserys 0.015644
46 Lysa 0.015215
24 Sandor 0.014162
87 Melisandre 0.008609
69 Rhaegar 0.007313
9 Mance 0.007276
43 Edmure 0.006317
92 Val 0.005898
19 Gregor 0.005880
16 Brynden 0.004727
0 Aemon 0.004623
79 Renly 0.004228
27 Loras 0.003781
41 Walder 0.003273
52 Elia 0.002997
65 Jorah 0.002330
31 Hodor 0.002194
34 Meera 0.001992
25 Thoros 0.001926
37 Brienne 0.001538
56 Shae 0.001252
47 Petyr 0.001150
84 Margaery 0.001097
57 Varys 0.000909
39 Podrick 0.000836
22 Rickon 0.000763
80 Tommen 0.000698
40 Lothar 0.000660
26 Balon 0.000628
85 Myrcella 0.000624
14 Beric 0.000609
94 Jon Arryn 0.000576
36 Theon 0.000472
32 Jojen 0.000406
89 Qhorin 0.000264
23 Roose 0.000263
54 Meryn 0.000259
44 Hoster 0.000243
62 Irri 0.000180
38 Bronn 0.000105
49 Roslin 0.000068
60 Daario 0.000060
61 Drogo 0.000060
1 Grenn 0.000060
97 Marillion 0.000000
102 Ramsay 0.000000
105 Doran 0.000000
100 Mace 0.000000
104 Shireen 0.000000
98 Robert Arryn 0.000000
86 Dalla 0.000000
103 Chataya 0.000000
88 Orell 0.000000
93 Ygritte 0.000000
96 Olenna 0.000000
90 Rattleshirt 0.000000
101 Rickard 0.000000
95 Lancel 0.000000
91 Styr 0.000000
99 Ellaria 0.000000
53 Ilyn 0.000000
82 Bowen 0.000000
55 Pycelle 0.000000
3 Aerys 0.000000
8 Alliser 0.000000
10 Amory 0.000000
13 Anguy 0.000000
18 Gendry 0.000000
30 Illyrio 0.000000
33 Luwin 0.000000
35 Nan 0.000000
45 Jeyne 0.000000
59 Karl 0.000000
78 Qyburn 0.000000
64 Aegon 0.000000
66 Kraznys 0.000000
67 Missandei 0.000000
68 Rakharo 0.000000
71 Worm 0.000000
73 Cressen 0.000000
74 Salladhor 0.000000
76 Eddison 0.000000
77 Gilly 0.000000
106 Walton 0.000000

Plot the nodes with the highest betweenness centrality scores

num_nodes_to_inspect = 10
betweenness_df[:num_nodes_to_inspect].plot(x='node', y='betweenness', color='green', kind='barh').invert_yaxis()
../../_images/Network-Analysis_51_01.png

Communities

Who forms distinct communities within this network?

from networkx.algorithms import community

Calculate communities with community.greedy_modularity_communities()

communities = community.greedy_modularity_communities(G)
communities
[frozenset({'Aerys',
            'Amory',
            'Balon',
            'Bronn',
            'Chataya',
            'Doran',
            'Elia',
            'Ellaria',
            'Gregor',
            'Ilyn',
            'Jaime',
            'Joffrey',
            'Kevan',
            'Lancel',
            'Loras',
            'Mace',
            'Margaery',
            'Meryn',
            'Myrcella',
            'Oberyn',
            'Olenna',
            'Podrick',
            'Pycelle',
            'Qyburn',
            'Renly',
            'Sandor',
            'Shae',
            'Tommen',
            'Tyrion',
            'Tywin',
            'Varys',
            'Walton'}),
 frozenset({'Aemon',
            'Alliser',
            'Bowen',
            'Craster',
            'Cressen',
            'Dalla',
            'Davos',
            'Eddison',
            'Gilly',
            'Grenn',
            'Hodor',
            'Janos',
            'Jojen',
            'Jon',
            'Karl',
            'Mance',
            'Meera',
            'Melisandre',
            'Orell',
            'Qhorin',
            'Rattleshirt',
            'Salladhor',
            'Samwell',
            'Shireen',
            'Stannis',
            'Styr',
            'Val',
            'Ygritte'}),
 frozenset({'Arya',
            'Bran',
            'Brienne',
            'Brynden',
            'Catelyn',
            'Cersei',
            'Eddard',
            'Edmure',
            'Hoster',
            'Jeyne',
            'Lothar',
            'Luwin',
            'Lysa',
            'Marillion',
            'Nan',
            'Petyr',
            'Ramsay',
            'Rickard',
            'Rickon',
            'Robb',
            'Robert Arryn',
            'Roose',
            'Roslin',
            'Sansa',
            'Theon',
            'Walder'}),
 frozenset({'Aegon',
            'Barristan',
            'Belwas',
            'Daario',
            'Daenerys',
            'Drogo',
            'Illyrio',
            'Irri',
            'Jon Arryn',
            'Jorah',
            'Kraznys',
            'Missandei',
            'Rakharo',
            'Rhaegar',
            'Robert',
            'Viserys',
            'Worm'}),
 frozenset({'Anguy', 'Beric', 'Gendry', 'Thoros'})]

Make a dictionary by looping through the communities and, for each member of the community, adding their community number

# Create empty dictionary
modularity_class = {}
#Loop through each community in the network
for community_number, community in enumerate(communities):
    #For each member of the community, add their community number
    for name in community:
        modularity_class[name] = community_number

Add modularity class to the network as an attribute

networkx.set_node_attributes(G, modularity_class, 'modularity_class')

Make a Pandas dataframe from modularity class network data G.nodes(data='modularity_class')

communities_df = pd.DataFrame(G.nodes(data='modularity_class'), columns=['node', 'modularity_class'])
communities_df = communities_df.sort_values(by='modularity_class', ascending=False)
communities_df
node modularity_class
25 Thoros 4
18 Gendry 4
14 Beric 4
13 Anguy 4
28 Belwas 3
94 Jon Arryn 3
65 Jorah 3
67 Missandei 3
71 Worm 3
70 Viserys 3
29 Barristan 3
30 Illyrio 3
69 Rhaegar 3
68 Rakharo 3
64 Aegon 3
63 Daenerys 3
5 Robert 3
62 Irri 3
61 Drogo 3
60 Daario 3
66 Kraznys 3
36 Theon 2
37 Brienne 2
43 Edmure 2
40 Lothar 2
42 Catelyn 2
44 Hoster 2
45 Jeyne 2
46 Lysa 2
47 Petyr 2
48 Robb 2
49 Roslin 2
50 Sansa 2
35 Nan 2
41 Walder 2
33 Luwin 2
17 Cersei 2
102 Ramsay 2
75 Eddard 2
101 Rickard 2
15 Bran 2
16 Brynden 2
12 Arya 2
98 Robert Arryn 2
22 Rickon 2
23 Roose 2
97 Marillion 2
73 Cressen 1
72 Davos 1
58 Craster 1
74 Salladhor 1
76 Eddison 1
77 Gilly 1
87 Melisandre 1
86 Dalla 1
81 Janos 1
59 Karl 1
0 Aemon 1
88 Orell 1
32 Jojen 1
2 Samwell 1
104 Shireen 1
8 Alliser 1
9 Mance 1
21 Jon 1
89 Qhorin 1
31 Hodor 1
82 Bowen 1
34 Meera 1
93 Ygritte 1
51 Stannis 1
92 Val 1
1 Grenn 1
91 Styr 1
90 Rattleshirt 1
84 Margaery 0
96 Olenna 0
99 Ellaria 0
100 Mace 0
103 Chataya 0
105 Doran 0
85 Myrcella 0
95 Lancel 0
53 Ilyn 0
83 Kevan 0
26 Balon 0
3 Aerys 0
4 Jaime 0
6 Tyrion 0
7 Tywin 0
10 Amory 0
11 Oberyn 0
19 Gregor 0
20 Joffrey 0
24 Sandor 0
27 Loras 0
80 Tommen 0
38 Bronn 0
39 Podrick 0
52 Elia 0
54 Meryn 0
55 Pycelle 0
56 Shae 0
57 Varys 0
78 Qyburn 0
79 Renly 0
106 Walton 0

Inspect each community in the network

communities_df[communities_df['modularity_class'] == 4]
communities_df[communities_df['modularity_class'] == 3]
communities_df[communities_df['modularity_class'] == 2]
communities_df[communities_df['modularity_class'] == 1]
communities_df[communities_df['modularity_class'] == 0]

Plot a sample of 40 characters with their modularity class indicated by a star

import seaborn as sns
#Set figure size
plt.figure(figsize=(4,12))

#Plot a categorical scatter plot from the dataframe communities_df.sample(40)
ax =sns.stripplot(x='modularity_class', y='node', data=communities_df.sample(40),
              hue='modularity_class', marker='*',size=15)
#Set legend outside the plot with bbox_to_anchor
ax.legend(loc='upper right',bbox_to_anchor=(1.5, 1), title='Modularity Class')
ax.set_title("GOT Characters By Modularity Class\n(Random Sample)")
plt.show()
../../_images/Network-Analysis_73_01.png

Plot all GOT characters with their modularity class indicated by a star (tak

start_time = datetime.datetime.now()

plt.figure(figsize=(4,25))

ax =sns.stripplot(x='modularity_class', y='node', data=communities_df,
              hue='modularity_class', marker='*',size=15)

ax.legend(loc='upper right',bbox_to_anchor=(1.5, 1), title='Modularity Class')
ax.set_title("GOT Characters By Modularity Class")
plt.show()
print(datetime.datetime.now() - start_time)
../../_images/Network-Analysis_75_01.png
0:01:07.423365

All Network Metrics

Create a Pandas dataframe of all network attributes by creating a dictionary of G.nodes(data=True)

dict(G.nodes(data=True))

…and then transposing it (flipping the columns and rows) with .T

nodes_df = pd.DataFrame(dict(G.nodes(data=True))).T
nodes_df
nodes_df.sort_values(by='betweenness', ascending=False)