Skip to content

Instantly share code, notes, and snippets.

@nitaku
Last active August 29, 2015 14:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nitaku/d8c76dfc03589e6dbf1f to your computer and use it in GitHub Desktop.
Save nitaku/d8c76dfc03589e6dbf1f to your computer and use it in GitHub Desktop.
Half matrix for symmetrical relations

An experiment on showing a weighted symmetric relation. Since the depicted relation is symmetric (and reflexive), only half of the matrix (even excluding the diagonal) can be shown without losing information. This could be useful to save screen estate.

Unfortunately, reading the half-matrix does not feel very natural, making especially difficult to see line-based patterns, since lines are often split in halves. A set of interactive techniques is put into place to ease these issues, at least in part.

In this specific example, the number of co-occurrences of characters from Les Miserables is encoded with the area of the blue dots (a different take on this classic example by Mike Bostock).

SIDE = 7
DIST = 8
svg = d3.select('svg')
width = svg.node().getBoundingClientRect().width
height = svg.node().getBoundingClientRect().height
vis = svg.append('g')
.attr
transform: "translate(#{width/2}, #{height-10})"
d3.json 'miserables.json', (data) ->
length = data.nodes.length
cellsize = d3.scale.sqrt()
.domain([0, d3.max data.links, (d) -> d.value])
.range([0,SIDE/2])
# objectify the graph
data.links.forEach (l) ->
l.source = data.nodes[l.source]
l.target = data.nodes[l.target]
# store the index of the node within its object, in order to use it when placing link cells
data.nodes.forEach (d, i) ->
d.i = i
# create the cursors
cursor_x = vis.append('line')
.style
display: 'none'
.attr
class: 'cursor x'
transform: 'rotate(-45)'
cursor_x2 = vis.append('line')
.style
display: 'none'
.attr
class: 'cursor x'
transform: 'rotate(-45)'
cursor_xcurve = vis.append('path')
.style
display: 'none'
.attr
class: 'cursor x'
d: "M0 #{-SIDE/2} C0 #{-SIDE/6} #{SIDE/6} 0 #{SIDE/2} 0"
cursor_y = vis.append('line')
.style
display: 'none'
.attr
class: 'cursor y'
transform: 'rotate(-45)'
cursor_y2 = vis.append('line')
.style
display: 'none'
.attr
class: 'cursor y'
transform: 'rotate(-45)'
cursor_ycurve = vis.append('path')
.style
display: 'none'
.attr
class: 'cursor y'
d: "M0 #{-SIDE/2} C0 #{-SIDE/6} #{SIDE/6} 0 #{SIDE/2} 0"
# draw the cells containing data
cells = vis.selectAll('.cell')
.data(data.links)
cells.enter().append('circle')
.attr
class: 'cell'
r: (d) -> cellsize(d.value)
transform: (d) -> "rotate(-45) translate(#{(d.source.i-length/2)*DIST},#{(d.target.i-length/2)*DIST})"
# draw the labels
PAD = 4
labels_a = vis.selectAll('.label_a')
.data(data.nodes.slice(1)) # first label is useless, the diagonal is not shown
labels_a.enter().append('text')
.each((d) -> d.label_a = d3.select(this))
.text((d) -> d.name)
.attr
class: 'label label_a'
dy: '0.35em'
transform: (d) -> "rotate(45) translate(#{-length/2*DIST-PAD-DIST/2},#{(-d.i+length/2)*DIST})"
.on 'mouseenter', (d) ->
d.label_a.classed('highlighted_x', true) if d.label_a?
d.label_b.classed('highlighted_x', true) if d.label_b?
cursor_x
.style
display: 'inline'
.attr
x1: (d.i-length/2)*DIST
x2: (d.i-length/2)*DIST
y1: (-length/2-0.5)*DIST
y2: (d.i-length/2-0.5)*DIST
cursor_x2
.style
display: 'inline'
.attr
x1: (d.i-length/2+0.5)*DIST
x2: (+length/2-0.5)*DIST
y1: (d.i-length/2)*DIST
y2: (d.i-length/2)*DIST
if d.label_b?
cursor_xcurve
.style
display: 'inline'
.attr
transform: "rotate(-45) translate(#{(d.i-length/2)*DIST}, #{(d.i-length/2)*DIST})"
.on 'mouseleave', (d) ->
d.label_a.classed('highlighted_x', false) if d.label_a?
d.label_b.classed('highlighted_x', false) if d.label_b?
cursor_x
.style
display: 'none'
cursor_x2
.style
display: 'none'
cursor_xcurve
.style
display: 'none'
labels_b = vis.selectAll('.label_b')
.data(data.nodes.slice(0,-1)) # last label is useless, the diagonal is not shown
labels_b.enter().append('text')
.each((d) -> d.label_b = d3.select(this))
.text((d) -> d.name)
.attr
class: 'label label_b'
dy: '0.35em'
transform: (d) -> "rotate(-45) translate(#{length/2*DIST+PAD-DIST/2},#{(d.i-length/2)*DIST})"
.on 'mouseenter', (d) ->
d.label_a.classed('highlighted_y', true) if d.label_a?
d.label_b.classed('highlighted_y', true) if d.label_b?
cursor_y
.style
display: 'inline'
.attr
x1: (d.i-length/2+0.5)*DIST
x2: (+length/2-0.5)*DIST
y1: (d.i-length/2)*DIST
y2: (d.i-length/2)*DIST
cursor_y2
.style
display: 'inline'
.attr
x1: (d.i-length/2)*DIST
x2: (d.i-length/2)*DIST
y1: (-length/2-0.5)*DIST
y2: (d.i-length/2-0.5)*DIST
if d.label_a?
cursor_ycurve
.style
display: 'inline'
.attr
transform: "rotate(-45) translate(#{(d.i-length/2)*DIST}, #{(d.i-length/2)*DIST})"
.on 'mouseleave', (d) ->
d.label_a.classed('highlighted_y', false) if d.label_a?
d.label_b.classed('highlighted_y', false) if d.label_b?
cursor_y
.style
display: 'none'
cursor_y2
.style
display: 'none'
cursor_ycurve
.style
display: 'none'
# create "interactive" cells to show the cursors
icells = []
d3.range(0, length).forEach (y) ->
d3.range(y+1, length).forEach (x) ->
icells.push {x: x, y: y, source: data.nodes[x], target: data.nodes[y]}
interactive_cells = vis.selectAll('.interactive_cell')
.data(icells)
interactive_cells.enter().append('rect')
.attr
class: 'interactive_cell'
x: -DIST/2
y: -DIST/2
width: DIST
height: DIST
transform: (d) -> "rotate(-45) translate(#{(d.x-length/2)*DIST},#{(d.y-length/2)*DIST})"
.on 'mouseenter', (d) ->
d.source.label_a.classed('highlighted_x', true) if d.source.label_a?
d.target.label_a.classed('highlighted_y', true) if d.target.label_a?
d.source.label_b.classed('highlighted_x', true) if d.source.label_b?
d.target.label_b.classed('highlighted_y', true) if d.target.label_b?
cursor_x
.style
display: 'inline'
.attr
x1: (d.x-length/2)*DIST
x2: (d.x-length/2)*DIST
y1: (-length/2-0.5)*DIST
y2: (d.x-length/2-0.5)*DIST
cursor_x2
.style
display: 'inline'
.attr
x1: (d.x-length/2+0.5)*DIST
x2: (+length/2-0.5)*DIST
y1: (d.x-length/2)*DIST
y2: (d.x-length/2)*DIST
cursor_y
.style
display: 'inline'
.attr
x1: (d.y-length/2+0.5)*DIST
x2: (+length/2-0.5)*DIST
y1: (d.y-length/2)*DIST
y2: (d.y-length/2)*DIST
cursor_y2
.style
display: 'inline'
.attr
x1: (d.y-length/2)*DIST
x2: (d.y-length/2)*DIST
y1: (-length/2-0.5)*DIST
y2: (d.y-length/2-0.5)*DIST
if d.source.label_b?
cursor_xcurve
.style
display: 'inline'
.attr
transform: "rotate(-45) translate(#{(d.x-length/2)*DIST}, #{(d.x-length/2)*DIST})"
if d.target.label_a?
cursor_ycurve
.style
display: 'inline'
.attr
transform: "rotate(-45) translate(#{(d.y-length/2)*DIST}, #{(d.y-length/2)*DIST})"
.on 'mouseleave', (d) ->
d.source.label_a.classed('highlighted_x', false) if d.source.label_a?
d.target.label_a.classed('highlighted_y', false) if d.target.label_a?
d.source.label_b.classed('highlighted_x', false) if d.source.label_b?
d.target.label_b.classed('highlighted_y', false) if d.target.label_b?
cursor_x
.style
display: 'none'
cursor_x2
.style
display: 'none'
cursor_xcurve
.style
display: 'none'
cursor_y
.style
display: 'none'
cursor_y2
.style
display: 'none'
cursor_ycurve
.style
display: 'none'
@font-face {
font-family: "Lacuna";
src: url("lacuna.ttf");
}
.label {
font-family: "Lacuna";
font-size: 8px;
}
.highlighted_x.label {
font-weight: bold;
fill: #E21975;
}
.highlighted_y.label {
font-weight: bold;
fill: #FF7100;
}
.label_a {
text-anchor: end;
}
.cell {
fill: #2272C0;
}
.interactive_cell {
fill: transparent;
}
.interactive_cell:hover {
stroke: #FF3B1C;
}
.cursor {
fill: none;
stroke-width: 0.5;
stroke-linecap: square;
}
.cursor.x {
stroke: #E21975;
}
.cursor.y {
stroke: #FF7100;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Half matrix for symmetrical relations</title>
<link type="text/css" href="index.css" rel="stylesheet"/>
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<svg height="500" width="960"></svg>
<script src="index.js"></script>
</body>
</html>
// Generated by CoffeeScript 1.4.0
(function() {
var DIST, SIDE, height, svg, vis, width;
SIDE = 7;
DIST = 8;
svg = d3.select('svg');
width = svg.node().getBoundingClientRect().width;
height = svg.node().getBoundingClientRect().height;
vis = svg.append('g').attr({
transform: "translate(" + (width / 2) + ", " + (height - 10) + ")"
});
d3.json('miserables.json', function(data) {
var PAD, cells, cellsize, cursor_x, cursor_x2, cursor_xcurve, cursor_y, cursor_y2, cursor_ycurve, icells, interactive_cells, labels_a, labels_b, length;
length = data.nodes.length;
cellsize = d3.scale.sqrt().domain([
0, d3.max(data.links, function(d) {
return d.value;
})
]).range([0, SIDE / 2]);
data.links.forEach(function(l) {
l.source = data.nodes[l.source];
return l.target = data.nodes[l.target];
});
data.nodes.forEach(function(d, i) {
return d.i = i;
});
cursor_x = vis.append('line').style({
display: 'none'
}).attr({
"class": 'cursor x',
transform: 'rotate(-45)'
});
cursor_x2 = vis.append('line').style({
display: 'none'
}).attr({
"class": 'cursor x',
transform: 'rotate(-45)'
});
cursor_xcurve = vis.append('path').style({
display: 'none'
}).attr({
"class": 'cursor x',
d: "M0 " + (-SIDE / 2) + " C0 " + (-SIDE / 6) + " " + (SIDE / 6) + " 0 " + (SIDE / 2) + " 0"
});
cursor_y = vis.append('line').style({
display: 'none'
}).attr({
"class": 'cursor y',
transform: 'rotate(-45)'
});
cursor_y2 = vis.append('line').style({
display: 'none'
}).attr({
"class": 'cursor y',
transform: 'rotate(-45)'
});
cursor_ycurve = vis.append('path').style({
display: 'none'
}).attr({
"class": 'cursor y',
d: "M0 " + (-SIDE / 2) + " C0 " + (-SIDE / 6) + " " + (SIDE / 6) + " 0 " + (SIDE / 2) + " 0"
});
cells = vis.selectAll('.cell').data(data.links);
cells.enter().append('circle').attr({
"class": 'cell',
r: function(d) {
return cellsize(d.value);
},
transform: function(d) {
return "rotate(-45) translate(" + ((d.source.i - length / 2) * DIST) + "," + ((d.target.i - length / 2) * DIST) + ")";
}
});
PAD = 4;
labels_a = vis.selectAll('.label_a').data(data.nodes.slice(1));
labels_a.enter().append('text').each(function(d) {
return d.label_a = d3.select(this);
}).text(function(d) {
return d.name;
}).attr({
"class": 'label label_a',
dy: '0.35em',
transform: function(d) {
return "rotate(45) translate(" + (-length / 2 * DIST - PAD - DIST / 2) + "," + ((-d.i + length / 2) * DIST) + ")";
}
}).on('mouseenter', function(d) {
if (d.label_a != null) {
d.label_a.classed('highlighted_x', true);
}
if (d.label_b != null) {
d.label_b.classed('highlighted_x', true);
}
cursor_x.style({
display: 'inline'
}).attr({
x1: (d.i - length / 2) * DIST,
x2: (d.i - length / 2) * DIST,
y1: (-length / 2 - 0.5) * DIST,
y2: (d.i - length / 2 - 0.5) * DIST
});
cursor_x2.style({
display: 'inline'
}).attr({
x1: (d.i - length / 2 + 0.5) * DIST,
x2: (+length / 2 - 0.5) * DIST,
y1: (d.i - length / 2) * DIST,
y2: (d.i - length / 2) * DIST
});
if (d.label_b != null) {
return cursor_xcurve.style({
display: 'inline'
}).attr({
transform: "rotate(-45) translate(" + ((d.i - length / 2) * DIST) + ", " + ((d.i - length / 2) * DIST) + ")"
});
}
}).on('mouseleave', function(d) {
if (d.label_a != null) {
d.label_a.classed('highlighted_x', false);
}
if (d.label_b != null) {
d.label_b.classed('highlighted_x', false);
}
cursor_x.style({
display: 'none'
});
cursor_x2.style({
display: 'none'
});
return cursor_xcurve.style({
display: 'none'
});
});
labels_b = vis.selectAll('.label_b').data(data.nodes.slice(0, -1));
labels_b.enter().append('text').each(function(d) {
return d.label_b = d3.select(this);
}).text(function(d) {
return d.name;
}).attr({
"class": 'label label_b',
dy: '0.35em',
transform: function(d) {
return "rotate(-45) translate(" + (length / 2 * DIST + PAD - DIST / 2) + "," + ((d.i - length / 2) * DIST) + ")";
}
}).on('mouseenter', function(d) {
if (d.label_a != null) {
d.label_a.classed('highlighted_y', true);
}
if (d.label_b != null) {
d.label_b.classed('highlighted_y', true);
}
cursor_y.style({
display: 'inline'
}).attr({
x1: (d.i - length / 2 + 0.5) * DIST,
x2: (+length / 2 - 0.5) * DIST,
y1: (d.i - length / 2) * DIST,
y2: (d.i - length / 2) * DIST
});
cursor_y2.style({
display: 'inline'
}).attr({
x1: (d.i - length / 2) * DIST,
x2: (d.i - length / 2) * DIST,
y1: (-length / 2 - 0.5) * DIST,
y2: (d.i - length / 2 - 0.5) * DIST
});
if (d.label_a != null) {
return cursor_ycurve.style({
display: 'inline'
}).attr({
transform: "rotate(-45) translate(" + ((d.i - length / 2) * DIST) + ", " + ((d.i - length / 2) * DIST) + ")"
});
}
}).on('mouseleave', function(d) {
if (d.label_a != null) {
d.label_a.classed('highlighted_y', false);
}
if (d.label_b != null) {
d.label_b.classed('highlighted_y', false);
}
cursor_y.style({
display: 'none'
});
cursor_y2.style({
display: 'none'
});
return cursor_ycurve.style({
display: 'none'
});
});
icells = [];
d3.range(0, length).forEach(function(y) {
return d3.range(y + 1, length).forEach(function(x) {
return icells.push({
x: x,
y: y,
source: data.nodes[x],
target: data.nodes[y]
});
});
});
interactive_cells = vis.selectAll('.interactive_cell').data(icells);
return interactive_cells.enter().append('rect').attr({
"class": 'interactive_cell',
x: -DIST / 2,
y: -DIST / 2,
width: DIST,
height: DIST,
transform: function(d) {
return "rotate(-45) translate(" + ((d.x - length / 2) * DIST) + "," + ((d.y - length / 2) * DIST) + ")";
}
}).on('mouseenter', function(d) {
if (d.source.label_a != null) {
d.source.label_a.classed('highlighted_x', true);
}
if (d.target.label_a != null) {
d.target.label_a.classed('highlighted_y', true);
}
if (d.source.label_b != null) {
d.source.label_b.classed('highlighted_x', true);
}
if (d.target.label_b != null) {
d.target.label_b.classed('highlighted_y', true);
}
cursor_x.style({
display: 'inline'
}).attr({
x1: (d.x - length / 2) * DIST,
x2: (d.x - length / 2) * DIST,
y1: (-length / 2 - 0.5) * DIST,
y2: (d.x - length / 2 - 0.5) * DIST
});
cursor_x2.style({
display: 'inline'
}).attr({
x1: (d.x - length / 2 + 0.5) * DIST,
x2: (+length / 2 - 0.5) * DIST,
y1: (d.x - length / 2) * DIST,
y2: (d.x - length / 2) * DIST
});
cursor_y.style({
display: 'inline'
}).attr({
x1: (d.y - length / 2 + 0.5) * DIST,
x2: (+length / 2 - 0.5) * DIST,
y1: (d.y - length / 2) * DIST,
y2: (d.y - length / 2) * DIST
});
cursor_y2.style({
display: 'inline'
}).attr({
x1: (d.y - length / 2) * DIST,
x2: (d.y - length / 2) * DIST,
y1: (-length / 2 - 0.5) * DIST,
y2: (d.y - length / 2 - 0.5) * DIST
});
if (d.source.label_b != null) {
cursor_xcurve.style({
display: 'inline'
}).attr({
transform: "rotate(-45) translate(" + ((d.x - length / 2) * DIST) + ", " + ((d.x - length / 2) * DIST) + ")"
});
}
if (d.target.label_a != null) {
return cursor_ycurve.style({
display: 'inline'
}).attr({
transform: "rotate(-45) translate(" + ((d.y - length / 2) * DIST) + ", " + ((d.y - length / 2) * DIST) + ")"
});
}
}).on('mouseleave', function(d) {
if (d.source.label_a != null) {
d.source.label_a.classed('highlighted_x', false);
}
if (d.target.label_a != null) {
d.target.label_a.classed('highlighted_y', false);
}
if (d.source.label_b != null) {
d.source.label_b.classed('highlighted_x', false);
}
if (d.target.label_b != null) {
d.target.label_b.classed('highlighted_y', false);
}
cursor_x.style({
display: 'none'
});
cursor_x2.style({
display: 'none'
});
cursor_xcurve.style({
display: 'none'
});
cursor_y.style({
display: 'none'
});
cursor_y2.style({
display: 'none'
});
return cursor_ycurve.style({
display: 'none'
});
});
});
}).call(this);
{
"nodes":[
{"name":"Myriel","group":1},
{"name":"Napoleon","group":1},
{"name":"Mlle.Baptistine","group":1},
{"name":"Mme.Magloire","group":1},
{"name":"CountessdeLo","group":1},
{"name":"Geborand","group":1},
{"name":"Champtercier","group":1},
{"name":"Cravatte","group":1},
{"name":"Count","group":1},
{"name":"OldMan","group":1},
{"name":"Labarre","group":2},
{"name":"Valjean","group":2},
{"name":"Marguerite","group":3},
{"name":"Mme.deR","group":2},
{"name":"Isabeau","group":2},
{"name":"Gervais","group":2},
{"name":"Tholomyes","group":3},
{"name":"Listolier","group":3},
{"name":"Fameuil","group":3},
{"name":"Blacheville","group":3},
{"name":"Favourite","group":3},
{"name":"Dahlia","group":3},
{"name":"Zephine","group":3},
{"name":"Fantine","group":3},
{"name":"Mme.Thenardier","group":4},
{"name":"Thenardier","group":4},
{"name":"Cosette","group":5},
{"name":"Javert","group":4},
{"name":"Fauchelevent","group":0},
{"name":"Bamatabois","group":2},
{"name":"Perpetue","group":3},
{"name":"Simplice","group":2},
{"name":"Scaufflaire","group":2},
{"name":"Woman1","group":2},
{"name":"Judge","group":2},
{"name":"Champmathieu","group":2},
{"name":"Brevet","group":2},
{"name":"Chenildieu","group":2},
{"name":"Cochepaille","group":2},
{"name":"Pontmercy","group":4},
{"name":"Boulatruelle","group":6},
{"name":"Eponine","group":4},
{"name":"Anzelma","group":4},
{"name":"Woman2","group":5},
{"name":"MotherInnocent","group":0},
{"name":"Gribier","group":0},
{"name":"Jondrette","group":7},
{"name":"Mme.Burgon","group":7},
{"name":"Gavroche","group":8},
{"name":"Gillenormand","group":5},
{"name":"Magnon","group":5},
{"name":"Mlle.Gillenormand","group":5},
{"name":"Mme.Pontmercy","group":5},
{"name":"Mlle.Vaubois","group":5},
{"name":"Lt.Gillenormand","group":5},
{"name":"Marius","group":8},
{"name":"BaronessT","group":5},
{"name":"Mabeuf","group":8},
{"name":"Enjolras","group":8},
{"name":"Combeferre","group":8},
{"name":"Prouvaire","group":8},
{"name":"Feuilly","group":8},
{"name":"Courfeyrac","group":8},
{"name":"Bahorel","group":8},
{"name":"Bossuet","group":8},
{"name":"Joly","group":8},
{"name":"Grantaire","group":8},
{"name":"MotherPlutarch","group":9},
{"name":"Gueulemer","group":4},
{"name":"Babet","group":4},
{"name":"Claquesous","group":4},
{"name":"Montparnasse","group":4},
{"name":"Toussaint","group":5},
{"name":"Child1","group":10},
{"name":"Child2","group":10},
{"name":"Brujon","group":4},
{"name":"Mme.Hucheloup","group":8}
],
"links":[
{"source":1,"target":0,"value":1},
{"source":2,"target":0,"value":8},
{"source":3,"target":0,"value":10},
{"source":3,"target":2,"value":6},
{"source":4,"target":0,"value":1},
{"source":5,"target":0,"value":1},
{"source":6,"target":0,"value":1},
{"source":7,"target":0,"value":1},
{"source":8,"target":0,"value":2},
{"source":9,"target":0,"value":1},
{"source":11,"target":10,"value":1},
{"source":11,"target":3,"value":3},
{"source":11,"target":2,"value":3},
{"source":11,"target":0,"value":5},
{"source":12,"target":11,"value":1},
{"source":13,"target":11,"value":1},
{"source":14,"target":11,"value":1},
{"source":15,"target":11,"value":1},
{"source":17,"target":16,"value":4},
{"source":18,"target":16,"value":4},
{"source":18,"target":17,"value":4},
{"source":19,"target":16,"value":4},
{"source":19,"target":17,"value":4},
{"source":19,"target":18,"value":4},
{"source":20,"target":16,"value":3},
{"source":20,"target":17,"value":3},
{"source":20,"target":18,"value":3},
{"source":20,"target":19,"value":4},
{"source":21,"target":16,"value":3},
{"source":21,"target":17,"value":3},
{"source":21,"target":18,"value":3},
{"source":21,"target":19,"value":3},
{"source":21,"target":20,"value":5},
{"source":22,"target":16,"value":3},
{"source":22,"target":17,"value":3},
{"source":22,"target":18,"value":3},
{"source":22,"target":19,"value":3},
{"source":22,"target":20,"value":4},
{"source":22,"target":21,"value":4},
{"source":23,"target":16,"value":3},
{"source":23,"target":17,"value":3},
{"source":23,"target":18,"value":3},
{"source":23,"target":19,"value":3},
{"source":23,"target":20,"value":4},
{"source":23,"target":21,"value":4},
{"source":23,"target":22,"value":4},
{"source":23,"target":12,"value":2},
{"source":23,"target":11,"value":9},
{"source":24,"target":23,"value":2},
{"source":24,"target":11,"value":7},
{"source":25,"target":24,"value":13},
{"source":25,"target":23,"value":1},
{"source":25,"target":11,"value":12},
{"source":26,"target":24,"value":4},
{"source":26,"target":11,"value":31},
{"source":26,"target":16,"value":1},
{"source":26,"target":25,"value":1},
{"source":27,"target":11,"value":17},
{"source":27,"target":23,"value":5},
{"source":27,"target":25,"value":5},
{"source":27,"target":24,"value":1},
{"source":27,"target":26,"value":1},
{"source":28,"target":11,"value":8},
{"source":28,"target":27,"value":1},
{"source":29,"target":23,"value":1},
{"source":29,"target":27,"value":1},
{"source":29,"target":11,"value":2},
{"source":30,"target":23,"value":1},
{"source":31,"target":30,"value":2},
{"source":31,"target":11,"value":3},
{"source":31,"target":23,"value":2},
{"source":31,"target":27,"value":1},
{"source":32,"target":11,"value":1},
{"source":33,"target":11,"value":2},
{"source":33,"target":27,"value":1},
{"source":34,"target":11,"value":3},
{"source":34,"target":29,"value":2},
{"source":35,"target":11,"value":3},
{"source":35,"target":34,"value":3},
{"source":35,"target":29,"value":2},
{"source":36,"target":34,"value":2},
{"source":36,"target":35,"value":2},
{"source":36,"target":11,"value":2},
{"source":36,"target":29,"value":1},
{"source":37,"target":34,"value":2},
{"source":37,"target":35,"value":2},
{"source":37,"target":36,"value":2},
{"source":37,"target":11,"value":2},
{"source":37,"target":29,"value":1},
{"source":38,"target":34,"value":2},
{"source":38,"target":35,"value":2},
{"source":38,"target":36,"value":2},
{"source":38,"target":37,"value":2},
{"source":38,"target":11,"value":2},
{"source":38,"target":29,"value":1},
{"source":39,"target":25,"value":1},
{"source":40,"target":25,"value":1},
{"source":41,"target":24,"value":2},
{"source":41,"target":25,"value":3},
{"source":42,"target":41,"value":2},
{"source":42,"target":25,"value":2},
{"source":42,"target":24,"value":1},
{"source":43,"target":11,"value":3},
{"source":43,"target":26,"value":1},
{"source":43,"target":27,"value":1},
{"source":44,"target":28,"value":3},
{"source":44,"target":11,"value":1},
{"source":45,"target":28,"value":2},
{"source":47,"target":46,"value":1},
{"source":48,"target":47,"value":2},
{"source":48,"target":25,"value":1},
{"source":48,"target":27,"value":1},
{"source":48,"target":11,"value":1},
{"source":49,"target":26,"value":3},
{"source":49,"target":11,"value":2},
{"source":50,"target":49,"value":1},
{"source":50,"target":24,"value":1},
{"source":51,"target":49,"value":9},
{"source":51,"target":26,"value":2},
{"source":51,"target":11,"value":2},
{"source":52,"target":51,"value":1},
{"source":52,"target":39,"value":1},
{"source":53,"target":51,"value":1},
{"source":54,"target":51,"value":2},
{"source":54,"target":49,"value":1},
{"source":54,"target":26,"value":1},
{"source":55,"target":51,"value":6},
{"source":55,"target":49,"value":12},
{"source":55,"target":39,"value":1},
{"source":55,"target":54,"value":1},
{"source":55,"target":26,"value":21},
{"source":55,"target":11,"value":19},
{"source":55,"target":16,"value":1},
{"source":55,"target":25,"value":2},
{"source":55,"target":41,"value":5},
{"source":55,"target":48,"value":4},
{"source":56,"target":49,"value":1},
{"source":56,"target":55,"value":1},
{"source":57,"target":55,"value":1},
{"source":57,"target":41,"value":1},
{"source":57,"target":48,"value":1},
{"source":58,"target":55,"value":7},
{"source":58,"target":48,"value":7},
{"source":58,"target":27,"value":6},
{"source":58,"target":57,"value":1},
{"source":58,"target":11,"value":4},
{"source":59,"target":58,"value":15},
{"source":59,"target":55,"value":5},
{"source":59,"target":48,"value":6},
{"source":59,"target":57,"value":2},
{"source":60,"target":48,"value":1},
{"source":60,"target":58,"value":4},
{"source":60,"target":59,"value":2},
{"source":61,"target":48,"value":2},
{"source":61,"target":58,"value":6},
{"source":61,"target":60,"value":2},
{"source":61,"target":59,"value":5},
{"source":61,"target":57,"value":1},
{"source":61,"target":55,"value":1},
{"source":62,"target":55,"value":9},
{"source":62,"target":58,"value":17},
{"source":62,"target":59,"value":13},
{"source":62,"target":48,"value":7},
{"source":62,"target":57,"value":2},
{"source":62,"target":41,"value":1},
{"source":62,"target":61,"value":6},
{"source":62,"target":60,"value":3},
{"source":63,"target":59,"value":5},
{"source":63,"target":48,"value":5},
{"source":63,"target":62,"value":6},
{"source":63,"target":57,"value":2},
{"source":63,"target":58,"value":4},
{"source":63,"target":61,"value":3},
{"source":63,"target":60,"value":2},
{"source":63,"target":55,"value":1},
{"source":64,"target":55,"value":5},
{"source":64,"target":62,"value":12},
{"source":64,"target":48,"value":5},
{"source":64,"target":63,"value":4},
{"source":64,"target":58,"value":10},
{"source":64,"target":61,"value":6},
{"source":64,"target":60,"value":2},
{"source":64,"target":59,"value":9},
{"source":64,"target":57,"value":1},
{"source":64,"target":11,"value":1},
{"source":65,"target":63,"value":5},
{"source":65,"target":64,"value":7},
{"source":65,"target":48,"value":3},
{"source":65,"target":62,"value":5},
{"source":65,"target":58,"value":5},
{"source":65,"target":61,"value":5},
{"source":65,"target":60,"value":2},
{"source":65,"target":59,"value":5},
{"source":65,"target":57,"value":1},
{"source":65,"target":55,"value":2},
{"source":66,"target":64,"value":3},
{"source":66,"target":58,"value":3},
{"source":66,"target":59,"value":1},
{"source":66,"target":62,"value":2},
{"source":66,"target":65,"value":2},
{"source":66,"target":48,"value":1},
{"source":66,"target":63,"value":1},
{"source":66,"target":61,"value":1},
{"source":66,"target":60,"value":1},
{"source":67,"target":57,"value":3},
{"source":68,"target":25,"value":5},
{"source":68,"target":11,"value":1},
{"source":68,"target":24,"value":1},
{"source":68,"target":27,"value":1},
{"source":68,"target":48,"value":1},
{"source":68,"target":41,"value":1},
{"source":69,"target":25,"value":6},
{"source":69,"target":68,"value":6},
{"source":69,"target":11,"value":1},
{"source":69,"target":24,"value":1},
{"source":69,"target":27,"value":2},
{"source":69,"target":48,"value":1},
{"source":69,"target":41,"value":1},
{"source":70,"target":25,"value":4},
{"source":70,"target":69,"value":4},
{"source":70,"target":68,"value":4},
{"source":70,"target":11,"value":1},
{"source":70,"target":24,"value":1},
{"source":70,"target":27,"value":1},
{"source":70,"target":41,"value":1},
{"source":70,"target":58,"value":1},
{"source":71,"target":27,"value":1},
{"source":71,"target":69,"value":2},
{"source":71,"target":68,"value":2},
{"source":71,"target":70,"value":2},
{"source":71,"target":11,"value":1},
{"source":71,"target":48,"value":1},
{"source":71,"target":41,"value":1},
{"source":71,"target":25,"value":1},
{"source":72,"target":26,"value":2},
{"source":72,"target":27,"value":1},
{"source":72,"target":11,"value":1},
{"source":73,"target":48,"value":2},
{"source":74,"target":48,"value":2},
{"source":74,"target":73,"value":3},
{"source":75,"target":69,"value":3},
{"source":75,"target":68,"value":3},
{"source":75,"target":25,"value":3},
{"source":75,"target":48,"value":1},
{"source":75,"target":41,"value":1},
{"source":75,"target":70,"value":1},
{"source":75,"target":71,"value":1},
{"source":76,"target":64,"value":1},
{"source":76,"target":65,"value":1},
{"source":76,"target":66,"value":1},
{"source":76,"target":63,"value":1},
{"source":76,"target":62,"value":1},
{"source":76,"target":48,"value":1},
{"source":76,"target":58,"value":1}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment