block by nitaku 1f2dd2ec370bc1a64a84f0fe9210f06b

Rectangular adjacency matrix V

Full Screen

Another rectangular adjacency matrix as in the previous example, this time using a color scale to convey the magnitude of a link’s weight. The example random data shows the energy consumption of a hypotetical office, according to time of day and day of week.

index.js

// Generated by CoffeeScript 1.10.0
(function() {
  var a_ids, a_labels, a_n, all_a_labels, all_b_labels, all_cells, b_ids, b_labels, b_n, cells, cellsize, en_a_labels, en_b_labels, en_cells, height, links, matrix_h, matrix_w, max_weight, svg, vis, weight2color, width, x, y;

  a_ids = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'];

  b_ids = ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'];

  links = [];

  a_ids.forEach(function(a) {
    return b_ids.forEach(function(b) {
      return links.push({
        a: a,
        b: b,
        w: (Math.random() + (b === '08' || b === '09' || b === '10' || b === '11' || b === '12' || b === '13' || b === '14' || b === '15' || b === '16' || b === '17' || b === '18' ? 0.4 : 0)) / (a === 'Sa' || a === 'Su' ? 3 : 1)
      });
    });
  });

  max_weight = d3.max(links, function(d) {
    return d.w;
  });

  matrix_w = 800;

  matrix_h = 400;

  a_n = a_ids.length;

  b_n = b_ids.length;

  cellsize = Math.min(matrix_w / b_n, matrix_h / a_n);

  x = d3.scaleBand().domain(b_ids).range([-cellsize * b_n / 2, cellsize * b_n / 2]);

  y = d3.scaleBand().domain(a_ids).range([-cellsize * a_n / 2, cellsize * a_n / 2]);

  weight2color = d3.scaleSequential(d3.interpolateViridis).domain([0, max_weight]);

  svg = d3.select('svg');

  width = svg.node().getBoundingClientRect().width;

  height = svg.node().getBoundingClientRect().height;

  vis = svg.append('g').attrs({
    transform: "translate(" + (width / 2) + "," + (height / 2) + ")"
  });

  cells = vis.selectAll('.cell').data(links, function(d) {
    return d.a + "--" + d.b;
  });

  en_cells = cells.enter().append('rect').attrs({
    "class": 'cell'
  });

  all_cells = en_cells.merge(cells);

  all_cells.attrs({
    x: function(d) {
      return x(d.b);
    },
    y: function(d) {
      return y(d.a);
    },
    width: x.bandwidth(),
    height: y.bandwidth(),
    fill: function(d) {
      return weight2color(d.w);
    }
  });

  cells.exit().remove();

  a_labels = vis.selectAll('.a_label').data(a_ids);

  en_a_labels = a_labels.enter().append('text').attrs({
    "class": 'a_label'
  });

  all_a_labels = en_a_labels.merge(a_labels);

  all_a_labels.text(function(d) {
    return d;
  }).attrs({
    dy: '0.35em',
    x: x.range()[0] - 8,
    y: function(d) {
      return y(d) + y.bandwidth() / 2;
    }
  });

  a_labels.exit().remove();

  b_labels = vis.selectAll('.b_label').data(b_ids);

  en_b_labels = b_labels.enter().append('text').attrs({
    "class": 'b_label'
  });

  all_b_labels = en_b_labels.merge(b_labels);

  all_b_labels.text(function(d) {
    return d;
  }).attrs({
    x: function(d) {
      return x(d) + x.bandwidth() / 2;
    },
    y: y.range()[0] - 8
  });

  b_labels.exit().remove();

}).call(this);

index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Rectangular adjacency matrix V</title>
  <link type="text/css" href="index.css" rel="stylesheet"/>
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src="https://d3js.org/d3-selection-multi.v0.4.min.js"></script>
</head>
<body>
  <svg></svg>
  <script src="index.js"></script>
</body>
</html>

index.coffee

# DATA

a_ids = ['Mo','Tu','We','Th','Fr','Sa','Su']
b_ids = ['00','01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20','21','22','23']

links = []

a_ids.forEach (a) ->
  b_ids.forEach (b) ->
    links.push {
      a: a,
      b: b,
      w: ( Math.random() + (if b in ['08','09','10','11','12','13','14','15','16','17','18'] then 0.4 else 0) ) / (if a in ['Sa','Su'] then 3 else 1)
    }

max_weight = d3.max links, (d) -> d.w

# LAYOUT

# try to fit this rectangle with the matrix
matrix_w = 800
matrix_h = 400

a_n = a_ids.length
b_n = b_ids.length

cellsize = Math.min matrix_w/b_n, matrix_h/a_n # beware! a is y and b is x!

x = d3.scaleBand()
  .domain b_ids # beware! a is y and b is x!
  .range [-cellsize*b_n/2, cellsize*b_n/2] # centered in (0,0)
  
y = d3.scaleBand()
  .domain a_ids
  .range [-cellsize*a_n/2, cellsize*a_n/2]
  
weight2color = d3.scaleSequential(d3.interpolateViridis)
  .domain [0, max_weight]

# VIS

svg = d3.select 'svg'
width = svg.node().getBoundingClientRect().width
height = svg.node().getBoundingClientRect().height

# center the vis on (0,0)
vis = svg.append 'g'
  .attrs
    transform: "translate(#{width/2},#{height/2})"

# draw cells
cells = vis.selectAll '.cell'
  .data links, (d) -> "#{d.a}--#{d.b}"
  
en_cells = cells.enter().append 'rect'
  .attrs
    class: 'cell'
      
all_cells = en_cells.merge cells

all_cells
  .attrs
    x: (d) -> x(d.b)
    y: (d) -> y(d.a)
    width: x.bandwidth()
    height: y.bandwidth()
    fill: (d) -> weight2color(d.w)
    
cells.exit().remove()

# draw row labels
a_labels = vis.selectAll '.a_label'
  .data a_ids
  
en_a_labels = a_labels.enter().append 'text'
  .attrs
    class: 'a_label'
    
all_a_labels = en_a_labels.merge a_labels

all_a_labels
  .text (d) -> d
  .attrs
    dy: '0.35em'
    x: x.range()[0] - 8
    y: (d) -> y(d) + y.bandwidth()/2
  
a_labels.exit().remove()

# draw column labels
b_labels = vis.selectAll '.b_label'
  .data b_ids
  
en_b_labels = b_labels.enter().append 'text'
  .attrs
    class: 'b_label'
    
all_b_labels = en_b_labels.merge b_labels

all_b_labels
  .text (d) -> d
  .attrs
    x: (d) -> x(d) + x.bandwidth()/2
    y: y.range()[0] - 8
  
b_labels.exit().remove()

index.css

body, html {
  padding: 0;
  margin: 0;
  height: 100%;
}
svg {
  width: 100%;
  height: 100%;
  background: white;
}

.cell {
  shape-rendering: crispEdges;
}

.a_label, .b_label {
  font-family: sans-serif;
  font-size: 14px;
}
.a_label {
  text-anchor: end;
}
.b_label {
  text-anchor: middle;
}