block by mbostock 4343153

Graph Rollup

Full Screen

The d3.rollup plugin implements the “PivotGraph” technique for collapsing large graphs along categorical dimensions, as described by Martin Wattenberg in Visual Exploration of Multivariate Graphs.

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<title>PivotGraph (Rollup) Layout</title>
<style>

body {
  font-family: sans-serif;
  font-size: 10px;
}

.link {
  fill: none;
  stroke: #000;
  stroke-opacity: .25;
}

.node {
  fill: #fff;
  stroke: steelblue;
}

.axis path,
.axis line {
  display: none;
}

</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="d3.rollup.min.js"></script>
<script>

var margin = {top: 90, right: 240, bottom: 90, left: 240},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

function fx(d) { return d.gender; }
function fy(d) { return d.group; }

var x = d3.scale.ordinal()
    .rangePoints([6, width - 6]);

var y = d3.scale.ordinal()
    .rangePoints([6, height - 6]);

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("top");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");

var rollup = d3.rollup()
    .x(function(d) { return x(fx(d)); })
    .y(function(d) { return y(fy(d)); });

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

d3.json("social.json", function(error, social) {
  if (error) throw error;

  x.domain(social.nodes.map(fx));
  y.domain(social.nodes.map(fy));
  var graph = rollup(social);

  svg.selectAll(".link")
      .data(graph.links)
    .enter().append("path")
      .attr("class", "link")
      .attr("d", function(d) {
        var sx = d.source.x, sy = d.source.y,
            tx = d.target.x, ty = d.target.y,
            dx = tx - sx, dy = ty - sy,
            dr = 2 * Math.sqrt(dx * dx + dy * dy);
        return "M" + sx + "," + sy + "A" + dr + "," + dr + " 0 0,1 " + tx + "," + ty;
      })
      .style("stroke-width", function(d) { return d.value * 4; });

  svg.selectAll(".node")
      .data(graph.nodes)
    .enter().append("circle")
      .attr("class", "node")
      .attr("r", function(d) { return Math.sqrt(d.nodes.length * 40); })
      .attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; });

  svg.append("g")
      .attr("class", "x axis")
      .call(xAxis);

  svg.append("g")
      .attr("class", "y axis")
      .call(yAxis);
});

</script>

d3.rollup.min.js

d3.rollup=function(){function n(n,t){function e(n){return o[n]+","+m[n]}for(var r=f.call(this,n,t),u=g.call(this,n,t),i=r.length,l=u.length,t=-1,o=[],m=[],k=0,v={},p={};i>++t;){(n=r[t]).index=t,o[t]=c.call(this,n,t),m[t]=a.call(this,n,t);var y=e(t),b=v[y];b||(b=v[y]={index:k++,x:o[t],y:m[t],nodes:[]}),b.nodes.push(n)}for(t=-1;l>++t;){var S=d.call(this,n=u[t],t),T=h.call(this,n,t),V=x.call(this,n,t),j=v[e("number"==typeof T?T:T.index)],q=v[e("number"==typeof V?V:V.index)],w=!s&&j.index>q.index?q.index+","+j.index:j.index+","+q.index,z=p[w];z||(z=p[w]={source:j,target:q,value:0,links:[]}),z.links.push(u[t]),z.value+=S}return{nodes:d3.values(v),links:d3.values(p)}}function t(n){return n.x}function e(n){return n.y}function r(n){return n.nodes}function u(n){return n.links}function i(){return 1}function l(n){return n.source}function o(n){return n.target}var s=!0,c=t,a=e,f=r,g=u,d=i,h=l,x=o;return n.x=function(t){return arguments.length?(c=t,n):c},n.y=function(t){return arguments.length?(a=t,n):a},n.nodes=function(t){return arguments.length?(f=t,n):f},n.links=function(t){return arguments.length?(g=t,n):g},n.linkSource=function(t){return arguments.length?(h=t,n):h},n.linkTarget=function(t){return arguments.length?(x=t,n):x},n.linkValue=function(t){return arguments.length?(d=t,n):d},n.directed=function(t){return arguments.length?(s=t,n):s},n};

social.json

{
  "nodes": [
    {"gender": "M", "group": "Boston"},
    {"gender": "F", "group": "San Francisco"},
    {"gender": "M", "group": "Boston"},
    {"gender": "M", "group": "San Francisco"},
    {"gender": "F", "group": "San Francisco"},
    {"gender": "M", "group": "Boston"},
    {"gender": "F", "group": "Boston"},
    {"gender": "M", "group": "San Francisco"},
    {"gender": "F", "group": "San Francisco"},
    {"gender": "F", "group": "San Francisco"}
  ],
  "links": [
    {"source": 0, "target": 1},
    {"source": 1, "target": 2},
    {"source": 2, "target": 3},
    {"source": 2, "target": 4},
    {"source": 2, "target": 5},
    {"source": 2, "target": 6},
    {"source": 2, "target": 7},
    {"source": 5, "target": 6},
    {"source": 6, "target": 7},
    {"source": 5, "target": 8},
    {"source": 8, "target": 6},
    {"source": 6, "target": 9}
  ]
}