Quinta & Unzila
2026-10-27
This presentation demonstrates how network analysis can be used to visualize character interactions as a political system.
The analysis uses an interaction dataset from A Song of Ice and Fire Book 1 and turns it into an interactive node-link graph.
Game of Thrones is a fantasy series by George R.R. Martin set in the fictional continents of Westeros and Essos.
Fictional universes with large casts and political structures can serve as controlled simulations of social systems.
Network analysis helps us study systems through relationships rather than isolated units.
Instead of asking only who the important actors are, it asks:
This analysis uses two main R packages:
igraph for graph creation and network analysis.visNetwork for interactive visualization.The dataset is loaded from character interactions in Book 1.
library(igraph)
library(visNetwork)
url <- "https://raw.githubusercontent.com/mathbeveridge/asoiaf/master/data/asoiaf-book1-edges.csv"
got_data <- read.csv(url)
head(got_data)## Source Target Type weight book
## 1 Addam-Marbrand Jaime-Lannister Undirected 3 1
## 2 Addam-Marbrand Tywin-Lannister Undirected 6 1
## 3 Aegon-I-Targaryen Daenerys-Targaryen Undirected 5 1
## 4 Aegon-I-Targaryen Eddard-Stark Undirected 4 1
## 5 Aemon-Targaryen-(Maester-Aemon) Alliser-Thorne Undirected 4 1
## 6 Aemon-Targaryen-(Maester-Aemon) Bowen-Marsh Undirected 4 1
Node criterion: Each node represents a character from Book 1. A character is included if their name appears in the text.
Edge criterion: An edge is created between two characters whenever their names appear within 15 words of one another. Edge weight corresponds to the number of co-occurrences.
Filtering criterion: We filtered to keep only characters with degree ≥ 3 (interacting with at least 3 others) to keep the visualization readable.
The edge list is converted into an undirected graph and filtered for core characters.
g <- graph_from_data_frame(got_data, directed = FALSE)
g_filtered <- induced_subgraph(g, v = V(g)[degree(g) >= 3])
cat("Full graph — Nodes:", vcount(g), " Edges:", ecount(g), "\n")## Full graph — Nodes: 187 Edges: 684
## Filtered graph — Nodes: 113 Edges: 587
## Number of nodes: 113
## Number of edges: 587
## Average degree: 10.39
## Network density: 0.0928
## Average clustering coefficient: 0.7002
## Average path length: 12.07
## Diameter: 29
Real social networks tend to follow a power law - a few central actors have many connections, while most have few (Barabási & Albert, 1999).
par(mfrow = c(1, 2))
hist(degree(g_filtered),
main = "Degree Distribution",
xlab = "Degree", ylab = "Frequency",
col = "steelblue", border = "white")
dd <- degree_distribution(g_filtered)
plot(log(1:length(dd)), log(dd),
main = "Degree Distribution (Log-Log)",
xlab = "Log(Degree)", ylab = "Log(Frequency)",
pch = 19, col = "steelblue")To reveal political or social clusters, we group the graph into communities.
communities <- cluster_louvain(g_filtered)
cat("Number of communities detected:", length(unique(membership(communities))), "\n")## Number of communities detected: 6
We test different resolution values to check if communities are stable.
communities <- cluster_louvain(g_filtered)
communities_default <- communities
communities_high <- cluster_louvain(g_filtered, resolution = 1.5)
communities_low <- cluster_louvain(g_filtered, resolution = 0.5)
cat("Default resolution — Communities:", length(unique(membership(communities_default))), "\n")## Default resolution — Communities: 6
## High resolution (1.5) — Communities: 7
## Low resolution (0.5) — Communities: 3
We check whether the detected communities correspond to known houses.
for (i in 1:length(unique(membership(communities_default)))) {
chars <- names(membership(communities_default)[membership(communities_default) == i])
cat("\nCommunity", i, ":\n")
cat(paste(head(chars, 10), collapse = ", "), "... \n")
}##
## Community 1 :
## Aemon-Targaryen-(Maester-Aemon), Albett, Alliser-Thorne, Benjen-Stark, Bowen-Marsh, Chett, Gared, Grenn, Halder, Jaremy-Rykker ...
##
## Community 2 :
## Aerys-II-Targaryen, Alyn, Barristan-Selmy, Brandon-Stark, Cayn, Cersei-Lannister, Eddard-Stark, Gerold-Hightower, Harwin, Janos-Slynt ...
##
## Community 3 :
## Aggo, Cohollo, Daenerys-Targaryen, Doreah, Drogo, Haggo, Illyrio-Mopatis, Irri, Jhiqui, Jhogo ...
##
## Community 4 :
## Arya-Stark, Beric-Dondarrion, Boros-Blount, Gregor-Clegane, Ilyn-Payne, Jeyne-Poole, Joffrey-Baratheon, Loras-Tyrell, Meryn-Trant, Mordane ...
##
## Community 5 :
## Bran-Stark, Galbart-Glover, Hallis-Mollen, Hodor, Hullen, Jon-Umber-(Greatjon), Luwin, Nan, Osha, Rickard-Karstark ...
##
## Community 6 :
## Bronn, Brynden-Tully, Catelyn-Stark, Chiggen, Conn, Danwell-Frey, Edmure-Tully, Eon-Hunter, Hoster-Tully, Jaime-Lannister ...
We define the visual properties of our characters (nodes).
nodes <- data.frame(
id = V(g_filtered)$name,
label = V(g_filtered)$name,
value = degree(g_filtered), # Size based on degree
group = membership(communities_default), # Color based on community
title = paste0("<p><b>", V(g_filtered)$name, "</b><br>Connections: ", degree(g_filtered), "</p>")
)
head(nodes)## id
## Aemon-Targaryen-(Maester-Aemon) Aemon-Targaryen-(Maester-Aemon)
## Aerys-II-Targaryen Aerys-II-Targaryen
## Aggo Aggo
## Albett Albett
## Alliser-Thorne Alliser-Thorne
## Alyn Alyn
## label value group
## Aemon-Targaryen-(Maester-Aemon) Aemon-Targaryen-(Maester-Aemon) 6 1
## Aerys-II-Targaryen Aerys-II-Targaryen 6 2
## Aggo Aggo 6 3
## Albett Albett 3 1
## Alliser-Thorne Alliser-Thorne 10 1
## Alyn Alyn 5 2
## title
## Aemon-Targaryen-(Maester-Aemon) <p><b>Aemon-Targaryen-(Maester-Aemon)</b><br>Connections: 6</p>
## Aerys-II-Targaryen <p><b>Aerys-II-Targaryen</b><br>Connections: 6</p>
## Aggo <p><b>Aggo</b><br>Connections: 6</p>
## Albett <p><b>Albett</b><br>Connections: 3</p>
## Alliser-Thorne <p><b>Alliser-Thorne</b><br>Connections: 10</p>
## Alyn <p><b>Alyn</b><br>Connections: 5</p>
We define the visual properties of character interactions.
edges <- data.frame(
from = as_edgelist(g_filtered)[,1],
to = as_edgelist(g_filtered)[,2],
width = E(g_filtered)$weight / 5,
color = list(color = "rgba(150, 150, 150, 0.5)")
)
head(edges)## from to width color
## 1 Aemon-Targaryen-(Maester-Aemon) Alliser-Thorne 0.8 rgba(150, 150, 150, 0.5)
## 2 Aemon-Targaryen-(Maester-Aemon) Bowen-Marsh 0.8 rgba(150, 150, 150, 0.5)
## 3 Aemon-Targaryen-(Maester-Aemon) Chett 1.8 rgba(150, 150, 150, 0.5)
## 4 Aemon-Targaryen-(Maester-Aemon) Jeor-Mormont 2.6 rgba(150, 150, 150, 0.5)
## 5 Aemon-Targaryen-(Maester-Aemon) Jon-Snow 6.8 rgba(150, 150, 150, 0.5)
## 6 Aemon-Targaryen-(Maester-Aemon) Samwell-Tarly 1.0 rgba(150, 150, 150, 0.5)
Real sociopolitical networks have well-documented structural properties (Newman, 2003).
| Property | Real Social Networks | Game of Thrones |
|---|---|---|
| Community structure | Clear clusters | Matches houses/factions |
| Clustering coefficient | Typically 0.1-0.6 | 0.7 |
| Small-world property | Short path lengths | 12.07 steps |
The network exhibits properties consistent with real sociopolitical networks:
What did we find? The fictional political system in Game of Thrones mirrors the structure of real social systems.
Conclusion: Even in a fictional universe, the patterns of human political interaction follow recognizable rules of connectivity and influence.