Skip to content
import pandas as pd
import numpy as np

# Read the CSV file into a pandas DataFrame
df = pd.read_csv('hero_network.csv', usecols=['hero1', 'hero2'])

# Get unique heroes
unique_heroes = sorted(set(df['hero1']).union(set(df['hero2'])))
heroes_dict = {hero: i for i, hero in enumerate(unique_heroes)}

# Create an empty matrix filled with zeros using numpy for better performance
matrix = np.zeros((len(unique_heroes), len(unique_heroes)), dtype=int)

# Fill in the matrix based on the relationships in the DataFrame
for _, row in df.iterrows():
    hero1_index = heroes_dict[row['hero1']]
    hero2_index = heroes_dict[row['hero2']]
    matrix[hero1_index, hero2_index] += 1
    matrix[hero2_index, hero1_index] += 1

# Display the matrix for a chord diagram
matrix
matrix.shape
from IPython.display import display, HTML
import json
import numpy as np

# Convert the numpy ndarray into a list of lists, which is JSON serializable
matrix_list = matrix.tolist()
matrix_json = json.dumps(matrix_list)

# HTML template for the Jupyter notebook
html_template = f"""
<!DOCTYPE html>
<meta charset="utf-8">
<style>
  body {{
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    margin: auto;
    text-align: center;
  }}
  .chord-diagram {{
    fill-opacity: .67;
  }}
  .chord-diagram path {{
    stroke: #000;
    stroke-width: .25px;
  }}
  .chord-diagram text {{
    font-size: 10px;
  }}
</style>
<svg class="chord-diagram"></svg>
<script src="https://d3js.org/d3.v6.min.js"></script>
<script>
  // Chord chart data
  var matrix = {matrix_json};

  var width = 500,
      height = 500,
      outerRadius = Math.min(width, height) / 2 - 10,
      innerRadius = outerRadius - 24;

  var svg = d3.select(".chord-diagram")
      .attr("width", width)
      .attr("height", height)
    .append("g")
      .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

  var chord = d3.chord()
      .padAngle(0.05)
      .sortSubgroups(d3.descending)
      .sortChords(d3.descending);

  var arc = d3.arc()
      .innerRadius(innerRadius)
      .outerRadius(outerRadius);

  var ribbon = d3.ribbon()
      .radius(innerRadius);

  var color = d3.scaleOrdinal(d3.schemeCategory10);

  var g = svg.selectAll(".group")
    .data(chord(matrix).groups)
    .enter().append("g")
      .attr("class", "group");

  g.append("path")
      .style("fill", function(d) {{ return color(d.index); }})
      .style("stroke", function(d) {{ return d3.rgb(color(d.index)).darker(); }})
      .attr("d", arc);

  g.append("text")
      .each(function(d) {{ d.angle = (d.startAngle + d.endAngle) / 2; }})
      .attr("dy", ".35em")
      .attr("transform", function(d) {{
        return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
            + "translate(" + (innerRadius + 26) + ")"
            + (d.angle > Math.PI ? "rotate(180)" : "");
      }})
      .style("text-anchor", function(d) {{ return d.angle > Math.PI ? "end" : null; }})
      .text(function(d) {{ return "Hero " + d.index; }});

  svg.selectAll(".chord")
    .data(chord(matrix).chords)
    .enter().append("path")
      .attr("class", "chord")
      .style("fill", function(d) {{ return color(d.source.index); }})
      .attr("d", ribbon);

</script>
"""

# Display the HTML template in the notebook
display(HTML(html_template))
from IPython.display import display, HTML

# HTML template for the Jupyter notebook
html_template = """
<!DOCTYPE html>
<meta charset="utf-8">

<!-- Load d3.js -->
<script src="https://d3js.org/d3.v6.js"></script>

<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>

<script>

// create the svg area
const svg = d3.select("#my_dataviz")
  .append("svg")
    .attr("width", 600)
    .attr("height", 600)
  .append("g")
    .attr("transform", "translate(300,300)")

// create a matrix
const matrix = [
[0,0,0,17,8,4,0,15,3,0,0], //TOS
[19,0,0,0,0,0,0,3,1,0,0], //TAS
[2,0,0,5,0,0,0,0,0,0,0], //TNG
[12,0,17,0,3,0,0,0,0,0,0], //DS9
[3,0,8,4,0,0,0,0,0,0,0], //VOY
[4,0,4,0,0,0,0,0,0,0,0], //ENT
[8,0,0,0,0,0,0,0,0,0,0], //DIS
[4,3,15,7,5,1,0,0,0,0,0], //LD
[13,1,0,0,0,0,1,5,0,0,0], //SNW
[1,0,3,1,2,0,0,0,0,0,0], //PRO
[0,0,19,1,4,0,0,0,0,0,0] //PIC
];

// 10 groups, so create a vector of 10 colors
const colors = [ "#ff595e", "#ff924c", "#ffca3a", "#c5ca30","#8ac926", "#36949d", "#1982c4", "#4267ac","#565aa0", "#6a4c93", "#2379"
]

const names = ["TOS", "TAS", "TNG", "DS9", "VOY", "ENT", "DIS", "LD","SNW","PRO","PIC"];

// give this matrix to d3.chord(): it will calculates all the info we need to draw arc and ribbon
const res = d3.chord()
    .padAngle(0.05)
    .sortSubgroups(d3.descending)
    (matrix)

// add the groups on the outer part of the circle
svg
  .datum(res)
  .append("g")
  .selectAll("g")
  .data(function(d) { return d.groups; })
  .join("g")
  .append("path")
    .style("fill", (d,i) => colors[i])
    .style("stroke", "black")
    .attr("d", d3.arc()
      .innerRadius(200)
      .outerRadius(210)
    )

// Add the labels around the chord diagram
svg.append("g")
  .selectAll("text")
  .data(res.groups)
  .join("text")
    .each(function(d) { d.angle = (d.startAngle + d.endAngle) / 2; })
    .attr("dy", ".35em")
    .attr("transform", d => `
      rotate(${(d.angle * 180 / Math.PI - 90)})
      translate(${210 + 10})
      ${d.angle > Math.PI ? "rotate(180)" : ""}
    `)
    .attr("text-anchor", d => d.angle > Math.PI ? "end" : null)
    .text((d, i) => names[i])
    .style("font-family", "sans-serif")
    .style("font-size", 10);

// Add the links between groups
svg
  .datum(res)
  .append("g")
  .selectAll("path")
  .data(d => d)
  .join("path")
    .attr("d", d3.ribbon()
      .radius(200)
    )
    .style("fill", d => colors[d.source.index]) // colors depend on the source group. Change to target otherwise.
    .style("stroke", "none"); // Removed the black outline on chords

</script>
"""

# Display the HTML template in the notebook
display(HTML(html_template))