Star Wars Character Network

Introduction

In the Star Wars legendarium, knowledge and training in the Force passes down through master/apprentice relationships. So, plotting the relationship network between them could help to understand how Star Wars characters are connected and how they became the Jedi or Sith that they are.

All the info is gathered from Wookiepedia (http://starwars.wikia.com). Wookieepedia is a Star Wars encyclopedia written collaboratively by its readers. Wookieepedia strives to be the Internet's foremost comprehensive resource for Star Wars canon information. To this end, Wookieepedia adheres to the canon doctrines set forth by Lucasfilm Ltd. and its subsidiaries.

The plot will be created with the Python package PyGraphviz (http://pygraphviz.github.io/). This is a Python interface to the Graphviz graph layout and visualization package. With PyGraphviz you can create, edit, read, write, and draw graphs using Python to access the Graphviz graph data structure and layout algorithms.

The starting point will be the jedi master Yoda. Every canon character found in the relationship network will be plotted.

Objective

The objective is drawing the master/apprentice relationship network between the Star Wars characters with the PyGraphviz package of Python.

Coding

Database

Character info has been formatted into a plain txt database.


In [1]:
fpath = "./database/swr_char_info-canon.txt"
fhand = open(fpath, 'rb')

for i in fhand:
    print i.strip()

fhand.close()


# Star wars character network
# Last update: July 25, 2014
# Information extracted from Wookiepedia: http://starwars.wikia.com
# "Star Wars", its characters and creations are the property of Lucasfilm. All other trademarks are property of the respective trademark owners.
# Legend definition:
#> NAM: Name.
#> MAS: Known Masters.
#> APP: Known apprentices.
#> BSF: Wether the character has been aligned to the bright side of the force (value 1) or not (value 0).
#> DSF: Wether the character has been aligned to the dark side of the force (value 1) or not (value 0).
#> MTS: Wether the training MTS[n] received from the master MAS[n] was in the bright (value 1) or in the dark (value 0) side of the force.
#> ATS: Wether the training ATS[n] given to the apprentice APP[n] was in the bright (value 1) or in the dark (value 0) side of the force.
#NAM	MAS	APP	BSF	DSF	MTS	ATS
Yoda	Qui-Gon_Jinn	Darth_Tyranus;Obi-Wan_Kenobi;Luke_Skywalker	1	0	1	1;1;1
Qui-Gon_Jinn	Darth_Tyranus	Obi-Wan_Kenobi;Darth_Vader;Yoda	1	0	1	1;1;1
Darth_Tyranus	Yoda;Darth_Sidious	Qui-Gon_Jinn;Grievous;Asajj_Ventress;Savage_Opress	1	1	1;0	1;0;0;0
Obi-Wan_Kenobi	Yoda;Qui-Gon_Jinn	Darth_Vader;Luke_Skywalker	1	0	1;1	1;1
Luke_Skywalker	Obi-Wan_Kenobi;Yoda	.	1	0	1;1	.
Darth_Vader	Qui-Gon_Jinn;Obi-Wan_Kenobi;Darth_Sidious	Ashoka_Tano	1	1	1;1;0	1
Darth_Sidious	Darth_Plagueis	Darth_Maul;Darth_Tyranus;Darth_Vader	0	1	0	0;0;0
Grievous	Darth_Tyranus	.	0	0	0	.
Asajj_Ventress	Darth_Tyranus	Savage_Opress	0	1	0	0
Savage_Opress	Darth_Tyranus;Darth_Maul	.	0	1	0;0	.
Ashoka_Tano	Darth_Vader	.	1	0	1	.
Darth_Plagueis	.	Darth_Sidious	0	1	.	0
Darth_Maul	Darth_Sidious	Savage_Opress	0	1	0	0

Parsing database

Character info and training info are obtained separately.

  • chars_info: contains the master/apprentices names and the force alignment (bright or dark side) of the character. Eg.:

      {
          "Luke Skywalker": {
              "apprentices": [
                  "."
              ], 
              "bright_side_force": "1", 
              "dark_side_force": "0", 
              "masters": [
                  "Obi-Wan Kenobi", 
                  "Yoda"
              ]
          },
         "Darth Sidious": {
              "apprentices": [
                  "Darth Maul", 
                  "Darth Tyranus", 
                  "Darth Vader"
              ], 
              "bright_side_force": "0", 
              "dark_side_force": "1", 
              "masters": [
                  "Darth Plagueis"
              ]
          }
      }
  • training_info: contains the force alignment of the given training between characters

      [
          ['Yoda', 'Luke Skywalker', '1'],
          ['Qui-Gon Jinn', 'Darth Vader', '1'], 
          ['Darth Sidious', 'Darth Vader', '0']
       ]

In [2]:
import re
import json

def parse_network_file(fpath):
    '''It parses the file containing the network information'''

    fhand = open(fpath,'rb')

    chars_info = {}
    training_info = []
    for line in fhand:
        line = line.strip()

        if not line.startswith('#'): # Skipping header
            char_data = re.sub('_', ' ', line).split('\t')

            # Extracting fields
            name = char_data[0]
            masters = char_data[1].split(';')
            apprentices = char_data[2].split(';')
            bright_side_force = char_data[3]
            dark_side_force = char_data[4]
            training_master = char_data[5].split(';')
            training_apprentice = char_data[6].split(';')

            chars_info[name] = {}
            chars_info[name]['masters'] = masters
            chars_info[name]['apprentices'] = apprentices
            chars_info[name]['bright_side_force'] = bright_side_force
            chars_info[name]['dark_side_force'] = dark_side_force

            for i, apprentice in enumerate(apprentices):
                if not apprentice == '.':
                    training_relationship = [name, apprentice, training_apprentice[i]]
                    training_info.append(training_relationship)

    fhand.close()
    return chars_info, training_info

chars_info, training_info = parse_network_file(fpath)

Data structure for plotting

The Python package "PyGraphviz" can use a dictionary of dictionaries as data structure for plotting the network graph. Eg:

{
'Darth Sidious': {
    'Darth Maul': None,
    'Darth Tyranus': None,
    'Darth Vader': None
    }, 
'Yoda': {
    'Darth Tyranus': None,
    'Luke Skywalker': None, 
    'Obi-Wan Kenobi': None
    }, 
}


In [3]:
def create_dictionary_for_plotting(chars_info):
    '''It populates dictionary "d" for plotting'''
    d = {}
    master_list = []
    apprentice_list = []
    for char in chars_info:
        master_list.append(char)
        apprentices = chars_info[char]['apprentices']
        d[char] = {}
        for apprentice in apprentices:
            if not apprentice == '.':
                apprentice_list.append(apprentice)
                d[char][apprentice] = None

    # Remove chars with no apprentice (empty element from 'd')
    d = dict((k, v) for k, v in d.iteritems() if v)
    return d

d = create_dictionary_for_plotting(chars_info)

Plotting

A directed graphs is created to show the relationship network.


In [4]:
import pygraphviz as pgv   

def plotting_graphs(d, training_info):
    # Plotting the graphs
    G = pgv.AGraph(d,
                   strict = False,
                   directed = True,
                   overlap = False, # Avoid overlapping nodes
                   splines = True) # Curvy edges   
    
               
    for training in training_info:
        edge = G.get_edge(training[0], training[1])
        edge.attr['len'] = 1
        if int(training[2]):
            edge.attr['color'] = 'green'
        else:
            edge.attr['color'] = 'red'
    
    for char in chars_info:
        node = G.get_node(char)
        node.attr['style'] = 'filled'
        bsf = int(chars_info[char]['bright_side_force'])
        dsf = int(chars_info[char]['dark_side_force'])
        if bsf and dsf:
            continue
        elif bsf:
            node.attr['fillcolor'] = 'green'
        elif dsf:
            node.attr['fillcolor'] = 'red'    

    
    G.layout(prog = 'fdp')
    G.draw('./images/file1.png')


# Plotting
plotting_graphs(d, training_info)

# Showing the image in the ipython notebook
from IPython.display import Image, display
i = Image(filename='./images/file1.png')
display(i)