EzDevInfo.com

d3.js interview questions

Top d3.js frequently asked interview questions

Adding new nodes to Force-directed layout

First question on Stack Overflow, so bear with me! I am new to d3.js, but have been consistently amazed by what others are able to accomplish with it... and almost as amazed by how little headway I've been able to make with it myself! Clearly I'm not grokking something, so I hope that the kind souls here can show me the light.

My intention is to make a reusable javascript function which simply does the following:

  • Creates a blank force-directed graph in a specified DOM element
  • Allows you to add and delete labeled, image-bearing nodes to that graph, specifying connections between them

I've taken http://bl.ocks.org/950642 as a starting point, since that's essentially the kind of layout I want to be able to create:

enter image description here

Here's what my code looks like:

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="jquery.min.js"></script>
    <script type="text/javascript" src="underscore-min.js"></script>
    <script type="text/javascript" src="d3.v2.min.js"></script>
    <style type="text/css">
        .link { stroke: #ccc; }
        .nodetext { pointer-events: none; font: 10px sans-serif; }
        body { width:100%; height:100%; margin:none; padding:none; }
        #graph { width:500px;height:500px; border:3px solid black;border-radius:12px; margin:auto; }
    </style>
</head>
<body>
<div id="graph"></div>
</body>
<script type="text/javascript">

function myGraph(el) {

    // Initialise the graph object
    var graph = this.graph = {
        "nodes":[{"name":"Cause"},{"name":"Effect"}],
        "links":[{"source":0,"target":1}]
    };

    // Add and remove elements on the graph object
    this.addNode = function (name) {
        graph["nodes"].push({"name":name});
        update();
    }

    this.removeNode = function (name) {
        graph["nodes"] = _.filter(graph["nodes"], function(node) {return (node["name"] != name)});
        graph["links"] = _.filter(graph["links"], function(link) {return ((link["source"]["name"] != name)&&(link["target"]["name"] != name))});
        update();
    }

    var findNode = function (name) {
        for (var i in graph["nodes"]) if (graph["nodes"][i]["name"] === name) return graph["nodes"][i];
    }

    this.addLink = function (source, target) {
        graph["links"].push({"source":findNode(source),"target":findNode(target)});
        update();
    }

    // set up the D3 visualisation in the specified element
    var w = $(el).innerWidth(),
        h = $(el).innerHeight();

    var vis = d3.select(el).append("svg:svg")
        .attr("width", w)
        .attr("height", h);

    var force = d3.layout.force()
        .nodes(graph.nodes)
        .links(graph.links)
        .gravity(.05)
        .distance(100)
        .charge(-100)
        .size([w, h]);

    var update = function () {

        var link = vis.selectAll("line.link")
            .data(graph.links);

        link.enter().insert("line")
            .attr("class", "link")
            .attr("x1", function(d) { return d.source.x; })
            .attr("y1", function(d) { return d.source.y; })
            .attr("x2", function(d) { return d.target.x; })
            .attr("y2", function(d) { return d.target.y; });

        link.exit().remove();

        var node = vis.selectAll("g.node")
            .data(graph.nodes);

        node.enter().append("g")
            .attr("class", "node")
            .call(force.drag);

        node.append("image")
            .attr("class", "circle")
            .attr("xlink:href", "https://d3nwyuy0nl342s.cloudfront.net/images/icons/public.png")
            .attr("x", "-8px")
            .attr("y", "-8px")
            .attr("width", "16px")
            .attr("height", "16px");

        node.append("text")
            .attr("class", "nodetext")
            .attr("dx", 12)
            .attr("dy", ".35em")
            .text(function(d) { return d.name });

        node.exit().remove();

        force.on("tick", function() {
          link.attr("x1", function(d) { return d.source.x; })
              .attr("y1", function(d) { return d.source.y; })
              .attr("x2", function(d) { return d.target.x; })
              .attr("y2", function(d) { return d.target.y; });

          node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
        });

        // Restart the force layout.
        force
          .nodes(graph.nodes)
          .links(graph.links)
          .start();
    }

    // Make it all go
    update();
}

graph = new myGraph("#graph");

// These are the sort of commands I want to be able to give the object.
graph.addNode("A");
graph.addNode("B");
graph.addLink("A", "B");

</script>
</html>

Every time I add a new node, it re-labels all of the existing nodes; these pile on top of each other and things start to get ugly. I understand why this is: because when I call the update() function function upon adding a new node, it does a node.append(...) to the entire data set. I can't figure out how to do this for only the node I'm adding... and I can only apparently use node.enter() to create a single new element, so that doesn't work for the additional elements I need bound to the node. How can I fix this?

Thank you for any guidance that you're able to give on any of this issue!

Edited because I quickly fixed a source of several other bugs that were previously mentioned


Source: (StackOverflow)

Is there a way to zoom into a D3 force layout graph?

D3 has a force directed layout here. Is there a way to add zooming to this graph? Currently, I was able to capture the mouse wheel event but am not really sure how to write the redraw function itself. Any suggestions?

    var vis = d3.select("#graph")
        .append("svg:svg")
        .call(d3.behavior.zoom().on("zoom", redraw)) // <-- redraw function
        .attr("width", w)
        .attr("height", h);

Source: (StackOverflow)

Advertisements

A good book for learning D3.js [closed]

I saw d3js via this Skinny!

I was interested and bought this book!, I may be wrong, however found it un-fulfilling.

does anyone have prime material for mastering d3js for data visualisation?

I can see alot of potential for d3js and am very keen and interested.

thanks in advance!

update: I just completed reading Interactive Data Visualization for the Web by Scott Murray, great book! try it, its free.


Source: (StackOverflow)

Python equivalent of D3.js

Can anyone recommend a Python library that can do interactive graph visualization?

I specifically want something like d3.js but for python and ideally it would be 3D as well.

I have looked at:

  • NetworkX - it only does Matplotlib plots and those seem to be 2D. I didn't see any sort of interactiveness, like one that d3.js gives, such as pulling nodes around.
  • Graph-tool - it does only 2D plots and has very slow interactive graphs.

Source: (StackOverflow)

d3 axis labeling

How do I add text labels to axes in d3?

For instance, I have a simple line graph with an x and y axis.

On my x-axis, I have ticks from 1 to 10. I want the word "days" to appear underneath it so people know the x axis is counting days.

Similarly, on the y-axis, I have the numbers 1-10 as ticks, and I want the words "sandwiches eaten" to appear sideways.

Is there a simple way to do this?


Source: (StackOverflow)

What is the difference D3 datum vs. data?

Can someone please explain the difference between datum() and data() in D3.js? I see both being used and I am not sure why you should choose one over the other?


Source: (StackOverflow)

Fast and responsive interactive charts/graphs: SVG, Canvas, other?

I am trying to choose the right technology to use for updating a project that basically renders thousands of points in a zoomable, pannable graph. The current implementation, using Protovis, is underperformant. Check it out here:

http://www.planethunters.org/classify

There are about 2000 points when fully zoomed out. Try using the handles on the bottom to zoom in a bit, and drag it to pan around. You will see that it is quite choppy and your CPU usage probably goes up to 100% on one core unless you have a really fast computer. Each change to the focus area calls a redraw to protovis which is pretty darn slow and is worse with more points drawn.

I would like to make some updates to the interface as well as change the underlying visualization technology to be more responsive with animation and interaction. From the following article, it seems like the choice is between another SVG-based library, or a canvas-based one:

http://www.sitepoint.com/how-to-choose-between-canvas-and-svg/

d3.js, which grew out of Protovis, is SVG-based and is supposed to be better at rendering animations. However, I'm dubious as to how much better and what its performance ceiling is. For that reason, I'm also considering a more complete overhaul using a canvas-based library like KineticJS. However, before I get too far into using one approach or another, I'd like to hear from someone who has done a similar web application with this much data and get their opinion.

The most important thing is performance, with a secondary focus on ease of adding other interaction features and programming the animation. There will probably be no more than 2000 points at once, with those small error bars on each one. Zooming in, out, and panning around need to be smooth. If the most recent SVG libraries are decent at this, then perhaps the ease of using d3 will outweigh the increased setup for KineticJS, etc. But if there is a huge performance advantage to using a canvas, especially for people with slower computers, then I would definitely prefer to go that way.

Example of app made by the NYTimes that uses SVG, but still animates acceptably smoothly: http://www.nytimes.com/interactive/2012/05/17/business/dealbook/how-the-facebook-offering-compares.html . If I can get that performance and not have to write my own canvas drawing code, I would probably go for SVG.

I noticed that some users have used a hybrid of d3.js manipulation combined with canvas rendering. However, I can't find much documentation about this online or get in contact with the OP of that post. If anyone has any experience doing this kind of DOM-to-Canvas (demo, code) implementation, I would like to hear from you as well. It seems to be a good hybrid of being able to manipulate data and having custom control over how to render it (and therefore performance), but I'm wondering if having to load everything into the DOM is still going to slow things down.

I know that there are some existing questions that are similar to this one, but none of them exactly ask the same thing. Thanks for your help.

Follow-up: the implementation I ended up using is at https://github.com/zooniverse/LightCurves


Source: (StackOverflow)

How can I remove or replace SVG content?

I have a piece of JavaScript code which creates (using D3.js) an svg element which contains a chart. I want to update the chart based on new data coming from a web service using AJAX, the problem is that each time I click on the update button, it generates a new svg, so I want to remove the old one or update its content.

Here is a snippet from the JavaScript function where I create the svg:

var svg = d3.select("body")
        .append("svg")
        .attr("width", w)
        .attr("height", h);

How can I remove the old svg element or at least replace its content?


Source: (StackOverflow)

Whats the best way to make a d3.js visualisation layout responsive?

Assume I have a histogram script that builds a 960 500 svg graphic. How do I make this responsive so on resize the graphic widths and heights are dynamic?

<script> 

var n = 10000, // number of trials
    m = 10,    // number of random variables
    data = [];

// Generate an Irwin-Hall distribution.
for (var i = 0; i < n; i++) {
  for (var s = 0, j = 0; j < m; j++) {
    s += Math.random();
  }
  data.push(s);
}

var histogram = d3.layout.histogram()
    (data);

var width = 960,
    height = 500;

var x = d3.scale.ordinal()
    .domain(histogram.map(function(d) { return d.x; }))
    .rangeRoundBands([0, width]);

var y = d3.scale.linear()
    .domain([0, d3.max(histogram.map(function(d) { return d.y; }))])
    .range([0, height]);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

svg.selectAll("rect")
    .data(histogram)
  .enter().append("rect")
    .attr("width", x.rangeBand())
    .attr("x", function(d) { return x(d.x); })
    .attr("y", function(d) { return height - y(d.y); })
    .attr("height", function(d) { return y(d.y); });

svg.append("line")
    .attr("x1", 0)
    .attr("x2", width)
    .attr("y1", height)
    .attr("y2", height);

</script> 

Full example histogram gist is: https://gist.github.com/993912


Source: (StackOverflow)

D3: show data on mouseover of circle

I have a set of data that I am plotting in a scatter. When I mouseover one of the circles I would like it to popup with data (like x, y values, maybe more). Here is what I tried using:

vis.selectAll("circle")
   .data(datafiltered).enter().append("svg:circle")
   .attr("cx", function(d) { return x(d.x);})
   .attr("cy", function(d) {return y(d.y)})
   .attr("fill", "red").attr("r", 15)
   .on("mouseover", function() {
        d3.select(this).enter().append("text")
            .text(function(d) {return d.x;})
            .attr("x", function(d) {return x(d.x);})
            .attr("y", function (d) {return y(d.y);}); });

I suspect I need to be more informative about what data to enter?


Source: (StackOverflow)

Center a map in d3 given a geoJSON object

Currently in d3 if you have a geoJSON object that you are going to draw you have to scale it and translate it in order to get it to the size that one wants and translate it in order to center it. This is a very tedious task of trial and error, and I was wondering if anyone knew a better way to obtain these values?

So for instance if I have this code

var path, vis, xy;
xy = d3.geo.mercator().scale(8500).translate([0, -1200]);

path = d3.geo.path().projection(xy);

vis = d3.select("#vis").append("svg:svg").attr("width", 960).attr("height", 600);

d3.json("../../data/ireland2.geojson", function(json) {
  return vis.append("svg:g")
    .attr("class", "tracts")
    .selectAll("path")
    .data(json.features).enter()
    .append("svg:path")
    .attr("d", path)
    .attr("fill", "#85C3C0")
    .attr("stroke", "#222");
});

How the hell do I obtain .scale(8500) and .translate([0, -1200]) without going little by little?


Source: (StackOverflow)

Protovis vs D3.js

TLDR: Does anyone have experience of both protovis & D3.js to illuminate the differences between the two?

I've been playing with protovis for the last 2 weeks and it's been great so far. Except now I seem to have hit a bit of a brick wall with animation.

protovis: http://vis.stanford.edu/protovis/

I want to do some quite simple animation but with protovis it feels less than intuitive - I'm starting to think that protovis was never really meant for animation. So, I started looking at D3.js:

http://mbostock.github.com/d3/ex/stack.html

It looks very similar, but:

  • Seems more lightweight
  • Seems geared to interacting with other DOM elements as well as SVG
  • Seems geared to adding animations

Can anyone illuminate any other differences?

I'd be very grateful for any and all input


Source: (StackOverflow)

x >= x pattern in JavaScript

When reading source of D3.js I saw x >= x pattern. If it is for detecting NaNs among numbers, why not just isNaN(x) or x == x?

Source, where I encountered it:

d3.min = function(array, f) {
  var i = -1, n = array.length, a, b;
  if (arguments.length === 1) {
    while (++i < n) if ((b = array[i]) != null && b >= b) {
      a = b;
      break;
    }
    while (++i < n) if ((b = array[i]) != null && a > b) a = b;
  } else {
    while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
      a = b;
      break;
    }
    while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b;
  }
  return a;
};

Source: (StackOverflow)

Resize svg when window is resized in d3.js

I'm drawing a scatterplot with d3.js. With the help of this question :
how to get web page size, browser window size, screen size in a cross-browser way?

I'm using this answer :

var w = window,
    d = document,
    e = d.documentElement,
    g = d.getElementsByTagName('body')[0],
    x = w.innerWidth || e.clientWidth || g.clientWidth,
    y = w.innerHeight|| e.clientHeight|| g.clientHeight;

So I'm able to fit my plot to the user's window like this :

var svg = d3.select("body").append("svg")
        .attr("width", x)
        .attr("height", y)
        .append("g");

Now I'd like that something takes care of resizing the plot when the user resize the window.

PS : I'm not using jQuery in my code.


Source: (StackOverflow)

What is the difference between D3 and jQuery?

Referring to this example:

http://vallandingham.me/stepper_steps.html

it seems that the D3 and jQuery libraries are very similar in the sense that they both do DOM manipulation in an object-chaining way.

I'm curious as to know what functions D3 makes easier than jQuery and vice versa. There are plenty of graphing and visualization libraries that use jQuery as a basis (e.g., , , ).

Please give specific examples of how they are different.


Source: (StackOverflow)