Tuesday, October 6, 2015

Learn D3 by "Official" examples -- Treemap

Explanation of D3 clickable "pack" layout. click to see official example. "elements" in this article means HTML elements. In treemap layou, we are using rectangular as basic units while in pack layout, we are using circles as basic units.

<style>
body {
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  margin: auto;
  position: relative;    /*CSS positioning: http://www.w3schools.com/css/css_positioning.asp*/
  width: 960px;
}
form {
  position: absolute;
  right: 10px;
  top: 10px;
}
.node {
  border: solid 1px white;
  font: 10px sans-serif;
  line-height: 12px;
  overflow: hidden;
  position: absolute;
  text-indent: 2px;
}
</style>
<form>
  <label><input type="radio" name="mode" value="size" checked> Size</label>
  <label><input type="radio" name="mode" value="count"> Count</label>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
var margin = {top: 40, right: 10, bottom: 10, left: 10},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;
var color = d3.scale.category20c();
var treemap = d3.layout.treemap()
    .size([width, height])
    .sticky(true)
    .value(function(d) { return d.size; });  /*size of rect*/
var div = d3.select("body").append("div")
    .style("position", "relative")
    .style("width", (width + margin.left + margin.right) + "px")
    .style("height", (height + margin.top + margin.bottom) + "px")
    .style("left", margin.left + "px")
    .style("top", margin.top + "px");
d3.json("flare.json", function(error, root) {
  if (error) throw error;
  var node = div.datum(root).selectAll(".node").data(treemap.nodes) //same as div.selectAll(".node").data(treemap.nodes(root))
      .enter().append("div")
      .attr("class", "node")
      .call(position)
      .style("background", function(d) { return d.children ? color(d.name) : null; }) /* "backgournd:null" means transparent.*/
      .text(function(d) { return d.children ? null : d.name; });
  d3.selectAll("input").on("change", function change() {
    var value = this.value === "count" ? function() { return 1; } : function(d) { return d.size; };
    node.data(treemap.value(value).nodes)
        .transition()
        .duration(1500)
        .call(position);
  });
});
/*compute the boundary of rectangular*/
function position() {
  this.style("left", function(d) { return d.x + "px"; })  /*(d.x,d.y) is the top left point of the rect.*/
      .style("top", function(d) { return d.y + "px"; })
      .style("width", function(d) { return Math.max(0, d.dx - 1) + "px"; }) /*d.dx is width of rect.*/
      .style("height", function(d) { return Math.max(0, d.dy - 1) + "px"; }); /*d.dy is eight of rect.*/
}
</script>

Treemap nodes:
  • parent - the parent node, or null for the root.
  • children - the array of child nodes, or null for leaf nodes.
  • value - the node value, as returned by the value accessor.
  • depth - the depth of the node, starting at 0 for the root.
  • x - the minimum x-coordinate of the node position.
  • y - the minimum y-coordinate of the node position.
  • dx - the x-extent of the node position.
  • dy - the y-extent of the node position.
For each rectangular, top left point is (x,y) and the width is "dx" and the height is "dy". "value" is the size of rect.

Friday, October 2, 2015

Learn D3 by "Official" Examples -- "Pack" layout II

Explanation of D3 clickable "pack" layout. click to see official example. "elements" in this article means HTML elements.
<head>
<style>
.node {
  cursor: pointer;
}
/*CSS selector ".node:hover" means select elements with "class=node" when mouse is over the element. */
.node:hover {
  stroke: #000;
  stroke-width: 1.5px;
}
/*CSS selector ".node--leaf" means select elements with "class=node--leaf". "node--leaf" is a class name. */
.node--leaf {
  fill: white;
}
.label {
  font: 11px "Helvetica Neue", Helvetica, Arial, sans-serif;
  text-anchor: middle;
  text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff, 0 -1px 0 #fff;
}
.label,
.node--root,
.node--leaf {
  pointer-events: none; /*no mouse event response on elements with these classes*/
}
</style>
</head>
<body>
<script>
var margin = 20,
    diameter = 960;
/*define the color. d3.scale transforms the input from .domain to .range using .interpolate. In this example, it means the input to the function "color" will be in [-1,5] and the output will be in ["hsl(152,80%,80%)", "hsl(228,30%,40%)"]. Basically, it is a mapping from [-1,5] to ["hsl(152,80%,80%)", "hsl(228,30%,40%)"]. How to compute which value in [-1,5] corresponds to which value in ["hsl(152,80%,80%)", "hsl(228,30%,40%)"]? We use interpolate "d3.interpolateHcl".*/
var color = d3.scale.linear()
    .domain([-1, 5])
    .range(["hsl(152,80%,80%)", "hsl(228,30%,40%)"])
    .interpolate(d3.interpolateHcl);
/*Here is how we use pack layout. We define a size of the entire pack and a value for each of its elements.*/
var pack = d3.layout.pack()
    .padding(2)
    .size([diameter - margin, diameter - margin])
    .value(function(d) { return d.size; })     /*d.size is defined in the given dataset in file "flare.json". Just a heads up, if you do not return anything, the default return value is 0.*/
var svg = d3.select("body").append("svg")
    .attr("width", diameter)
    .attr("height", diameter)
  .append("g")
    .attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
d3.json("flare.json", function(error, root) {
  if (error) throw error;
  var focus = root,
      nodes = pack.nodes(root), /*"nodes" is an array of objects which are extracted from Json file.*/
      view;
  var circle = svg.selectAll("circle")
      .data(nodes)   /*each element of circle is associate with an object stored in "nodes".*/
      .enter().append("circle")
      /*determine the classes for different HTML elements. "node node--leaf" means that the html element with two attributes "class=node" and "class=node--leaf" which further means the CSS styles defined for ".node" and ".node--leaf" will be applied to this element. However, in this example, "node" is not needed for "node--leaf" and "node--root". There will be no differences if we write like this:
      .attr("class", function(d) { return d.parent ? d.children ? "node" : "node--leaf" : "node--root"; })  */
      .attr("class", function(d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; })
      .style("fill", function(d) { return d.children ? color(d.depth) : null; }) /*d.depth is level of the node in hierarchy. automatically computed.*/
      .on("click", function(d) { if (focus !== d) zoom(d), d3.event.stopPropagation(); }); /*when we click a node and it is different from previous clicked node, we "zoom" to that node. d3.event.stopPropagation() stops the click event propagated to its parent node. what does this mean? Suppose you have two circles A and A_sub. A_sub is inside A. If you click A_sub, it will trigger the actions for A_sub first and then the actions for A. If you have d3.event.stopPropagation() put in A_sub's actions, then you click A_sub, only A_sub's actions will be triggered. See an example here: http://bl.ocks.org/jasondavies/3186840*/
  var text = svg.selectAll("text")  /*select all elements with tag name "text"*/
      .data(nodes)
    .enter().append("text")
      .attr("class", "label")       /*define a class "label" for tag "text"*/
      .style("fill-opacity", function(d) { return d.parent === root ? 1 : 0; }) /*Opacity can be used if you want to create transparency or fade effect. without this line, you won't see the fade effect.*/
      .style("display", function(d) { return d.parent === root ? null : "none"; }) /*display the elements when they are the children of root module.*/
      .text(function(d) { return d.name; });
  var node = svg.selectAll("circle,text"); /*all elements with "class="circle" and "class="text" are selected.*/
  d3.select("body")
      .style("background", color(-1))      /*the parameter of color is in [-1,5]*/
      .on("click", function() { zoom(root); }); /*if you click the background, it will zoom to root.*/
  zoomTo([root.x, root.y, root.r * 2 + margin]);     /*initial display of the entire data from json file.*/
  function zoom(d) {
    var focus0 = focus; focus = d;/*update the current focus. focus0 is not used.*/
    /*simply put, d3.transition performs the transition procedure from one point to another point. During these two points, there are many "frames" (think it as film frame). ".tween" is used to control how to display each frame. The tween function is called repeatedly, being passed the current normalized time t in [0, 1].*/
    var transition = d3.transition()
        .duration(d3.event.altKey ? 7500 : 750)     /*specify the length of the transition.*/
        .tween("zoom", function(d) {                
          var i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2 + margin]); /*compute each frame between "view" and "focus", where "view" is a global variable and set in function "zoomto".*/
          return function(t) { zoomTo(i(t)); }; /*"i" is a function, t is passed to the 2nd parameter of tween function. i(t) is the view of frame.*/
        });
    transition.selectAll("text")
      .filter(function(d) { return d.parent === focus || this.style.display === "inline"; })
        .style("fill-opacity", function(d) { return d.parent === focus ? 1 : 0; })        /*fade effect*/
        .each("start", function(d) { if (d.parent === focus) this.style.display = "inline"; }) /*beginning of frame.*/
        .each("end", function(d) { if (d.parent !== focus) this.style.display = "none"; });    /*end of frame.*/
  }
  function zoomTo(v) {
    /*room from current  "view" to "v" which is computed by d3.interpolateZoom.*/
    var k = diameter / v[2]; view = v; /*v[2] is the new radius of the clicked circle.*/
    node.attr("transform", function(d) { return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")"; });
    circle.attr("r", function(d) { return d.r * k; }); /*update the circle size*/
    /*note, all nodes ("circle" and "text" on Line 66) will be updated.*/
  }
});
d3.select(self.frameElement).style("height", diameter + "px");
</script>
</body>

Saturday, September 26, 2015

Learn D3 by "Official" Examples -- "Pack" layout

Explanation of D3 "pack" layout. click to see official example
<head>
<style>
circle {
  fill: rgb(31, 119, 180);
  fill-opacity: .25;
  stroke: rgb(31, 119, 180);
  stroke-width: 1px;
}
.leaf circle {
  fill: #ff7f0e;
  fill-opacity: 1;
}
text {
  font: 10px sans-serif;
}
</style>
<!--
Line 2-16 defines styles for tag selector "circle" and class selector ".leaf circle" and tag selector "text". Priority of CSS selectors: id selector > class selector > tag selector. Wait a second, what does Line 9 mean by ".leaf circle"? ".leaf" means all elements with "class="leaf"". ".leaf circle" means all elements with tag "<circle>" inside "class="leaf"" should follow the specified rules between Line 9 and Line 12. See the following example, "c1" and "c2" are elements belonging to ".leaf circle".
<p class="leaf">
    <circle id="c1"> Circle 1 is shown here.</circle>
    <p>
        <circle id="c2"> Circle 2 is shown here.</circle>
    </p>
</p>
<p class="leaf node">double classes</p> means this element has two classes associated with it. The way to specify the style:
<style>
.leaf.node {color:red}
</style>
-->
<!--the official CDN of D3 -->
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<script>
var diameter = 960,
    /*d3.format is a function converting a number to a string. See: https://github.com/mbostock/d3/wiki/Formatting#d3_format.  Variable "format" is a function that converts integer as string with a comma for a thousands separator. If the number is not an integer it will be ignored. Try console.log(format(123456)) and console.log(format(123.456)) for details.*/
    format = d3.format(",d");
var pack = d3.layout.pack()                    //This is the way to use pack layout.*/
    .size([diameter - 4, diameter - 4])        //Specify size of the layout.*/
    .value(function(d) { return d.size; });    //".value" determines the size of each node in the pack layout. Where is "d"? "d" will be associated when the pack connects to dataset.  
                                               
var svg = d3.select("body").append("svg")      //append a svg which is like a whiteboard to draw.
    .attr("width", diameter)                   //specify width of svg     
    .attr("height", diameter)                  //specify height of svg
    .append("g")                               //define a group
    .attr("transform", "translate(2,2)");      //Translate will move the entire svg by 2 along x-axis and by 2 along y-axis. 
d3.json("flare.json", function(error, root) {  //read in a json file and store it to variable "root".
  if (error) throw error;
  var node = svg.datum(root)     //connect the retrieved data "root" to "svg". Similar to "d3.selection.data". 
      .selectAll(".node")        //select the "virtual" elements with <class="node">. These elements are not created yet but will be created later.
      .data(pack.nodes)          //pack.nodes comes from its parent node "svg.datum(root)". then pack.nodes are assigned to elements with <class="node">. From now on, you can believe each node in pack corresponds to some data (an object in the given json) from pack.nodes. 
      .enter().append("g")     
      /*for each node, we assign css attribute to it. Here the node's class depends on whether it has children.*/  
      .attr("class", function(d) { return d.children ? "node" : "leaf node"; })
      /*assign location for each node. d.x and d.y are automatically computed by pack layout.*/
      .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
  node.append("title")           //assign a title to each node
      .text(function(d) { return d.name + (d.children ? "" : ": " + format(d.size)); });
  node.append("circle")          //assign a shape to each node
      .attr("r", function(d) { return d.r; });  //where is d.r from? pack layout automatically computes it. I guess it is computed based on node.value (Line 36).
  node.filter(function(d) { return !d.children; }) //choose all leaf nodes and assign text to them. 
      .append("text")
      .attr("dy", ".3em")
      .style("text-anchor", "middle")
      .text(function(d) { return d.name.substring(0, d.r / 3); });
});
d3.select(self.frameElement).style("height", diameter + "px");
</script>
</body>

The concept of "node" in a pack layout is important to understand how pack layout works.
Each node has the following attributes:

  • parent - the parent node, or null for the root. Automatically computed!
  • children - the array of child nodes, or null for leaf nodes.
  • value - the node value, as returned by the value accessor.Line 40. If not existing, it is the sum of its children's size.
  • depth - the depth of the node, starting at 0 for the root.Automatically computed!
  • x - the computed x-coordinate of the node position. Automatically computed!
  • y - the computed y-coordinate of the node position.Automatically computed!
  • r - the computed node radius.Automatically computed!

So only "children" and "value" are assigned from the given input dataset. I will use an example to explain the concept.
{
 "name": "flare",
 "children": [
      {"name": "sub1", "size": 3938},
      {
          "name": "sub2", 
          "children": [
                  {"name": "sub2_sub1", "size": 6714},
                  {"name": "sub2_sub2", "size": 743}
           ]
      }
 ]
}
We have 5 objects in the json file:
Object { name: "flare", children: Array[2], depth: 0, value: 11395, y: 478, x: 478, r: 478 } 
Object { name: "sub1", size: 3938, parent: Object, depth: 1, value: 3938, r: 174.4465055101499, x: 174.4465055101499, y: 478 } 
Object { name: "sub2", children: Array[2], parent: Object, depth: 1, value: 7457, r: 303.5534944898501, x: 652.4465055101499, y: 478 } 
Object { name: "sub2_sub1", size: 6714, parent: Object, depth: 2, value: 6714, r: 227.7797367518277, x: 728.2202632481724, y: 478 }
Object { name: "sub2_sub2", size: 743, parent: Object, depth: 2, value: 743, r: 75.77375773802244, x: 424.6667687583222, y: 478 }
"Node" of the pack corresponds to each object. So we have 5 nodes in this pack layout. "x", "y", "r" and "parent" are already there which means they are computed by pack layout. "name" and "children" are assigned by the dataset in the given json file. The size of each node is controlled by pack.value (Line 40). Note object "flare" does not have a "size" attribute, so its size is the sum of its children's sizes.

Tuesday, September 15, 2015

Parallelism vs. Concurrency

I always had a perception that parallelism and concurrency are interchangeable and they convey the same concept.
But that was a wrong perception! 

Here is my understanding: 
Parallelism is using multiple threads to compute the same problem such that this single problem can be divided into many sub-problems which then be computed simultaneously).
Concurrency is more like a concept used in distributed system, i.e., rapid IO interaction combined with callbacks to be triggered on certain events.

An explicit example is web server. Suppose a web server can have 100 requests per second. Then these requests will be handled by this single server which will be running concurrently. If there are 100 web servers to serve the 100 requests, then it is a parallel handling.