Showing posts with label D3. Show all posts
Showing posts with label D3. Show all posts

Visualizing health movement with Chord Diagrams

Recently I have been working on a data visualization piece, to show the movements or changes taking place within a certain population of registered patients over two consecutive periods. The patients are put into five to six health status groups based on a number of clinical factors that determine their risk of illness from low to high. The periods could be anything, but more typically these are either annual, by-annual or quarterly.

Having figured out the required data set and built the data extraction part, I investigated several visualizations including, the more traditional line charts, stacked bar charts and also some Sankey and Chord diagrams.

I found chord diagrams to be the most suitable for this type of visualization as it offered more clarity than sankey diagrams which in my opinion are more suitable to visualize flow changing status over time in one direction. Whereas the chords are more flexible and can easily show bi-directional movement of statuses.

D3 has the Chord Layout which simplifies the development of chord diagrams by taking care of all the mathematics involved mapping the data to the visual elements. Essentially what is required is to provide it with a two dimensional matrix consisting of the groups and their values like so.















The above code in javascript is a six by six array of arrays consisting of the number of patients in 
each group in the consecutive periods represented by the past in columns (horizontal) and present in rows (vertical).


For example in the above data set, the array element [1] [2] (row, column zero based) contains the 514 patients who have moved from a Low status in the past period to a Healthy status in the present period, and element [2] [1] contains the 53 patients in reverse who have moved from a Healthy status to a Low status. Similarly element [1] [3] contains the 1373 patients who have moved from a Moderate status to Healthy status, and element [3] [1] contains the 143 patients in reverse who have moved from a Healthy status to a Moderate status. On the other hand element [0] [0] contain the 488 patients who have remained in the Non-users status. Similarly elements such as [1] [1] and [3] [3] contain the number of patients who have remained in the same status between the two periods.

The D3 Chord Layout maps the above matrix data into the representative 6 groups which can then be plotted as arcs at the outer radius of the circle. The length of the arc is the group total representing the total number of patients in that health status group. Each arc can be given a specific distinguishing colour as in this instance, I have chosen from blue, green, yellow, orange, purple and red for the 6 health status groups. Then chords are plotted between the arcs which conveys the movement of a health status in both directions. The width of the chord at each end represent the number of patients who have moved from one to another. 
























Moving the mouse over an arc displays a tool-tip with the total number of patients in that heath status group. Moving the mouse over a chord displays the number of patients who have moved in both directions between the respective health status groups that is connected by that chord.



















A running version of the above diagram along with the code is published on http://bl.ocks.org/rarpal/3dd7f3950d3d8c570eb6 or to view only http://rarpal.github.io/LabD3Js/

D3.js, understanding Enter, Update and Exit

Enter, Update and Exit are essentially a way that D3 binds data to DOM elements such as SVG elements or any other HTML elements. 

Enter

Enter attempts to append a DOM element (such as an SVG element or any other HTML element specified in the selection), a number of times that matches a given number of data items, so that each data items gets assigned a representative DOM element. The number of DOM elements created by the action will be = (number of data items - number of existing DOM elements selected).

Update

Update, simply updates a number of DOM elements specified in the selection, that matches the number of supplied data items. Please note that Update does not have a named method, but is an operation you perform on the existing data.

Exit

In opposite to Enter, Exits attempts to remove any existing DOM elements specified in the selection such that the number of remaining DOM elements always matches the number of specified data items. Which means that if no data items are available or supplied, and one or more DOM elements specified in the selection are present, then Exits will remove all of the elements.

Hence Enter, Exits and Update when used together is the mechanism by which D3 binds DOM elements and their corresponding data items so that the DOM elements and data items are kept in sync as the number of data items enters and exists the HTML documents which D3 manages.

The following javascript code is an example that demonstrates this behaviour. The function named render is called a number of times with a varying number of data items as the first parameter and a colour as the second parameter. There is a time delay set between each call, so that you can see how D3 manipulates the DOM elements to match the data items.

<script src="~/Scripts/d3.min.js"></script>

<script language="javascript">

    var scale = d3.scale.linear()
      .domain([1, 5])   // Data space
      .range([0, 200]); // Pixel space

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

    function render(data, color) {

        var rects = svg.selectAll("rect").data(data);

        // Enter
        rects.enter().append("rect")
          .attr("y", 50)
          .attr("width", 20)
          .attr("height", 20);

        // Update
        rects
          .attr("x", scale)
          .attr("fill", color);

        // Exit
        rects.exit().remove();

    }

    setTimeout(function () { render([1, 2, 2.5], "red"); }, 1000);
    setTimeout(function () { render([1, 2, 3, 4, 5], "blue"); }, 2000);
    setTimeout(function () { render([1, 2], "green"); }, 3000);
    setTimeout(function () { render([3, 4, 5], "cyan"); }, 4000);
    setTimeout(function () { render([4, 5], "magenta"); }, 5000);

</script>
Run the code on JSFiddle or Github Gist

The above code will render a set of SVG rectangle DOM elements as follows.


















Note that each render will replace the previous render, so only one set of rectangles will be displayed at a time as they transit from render 1 to render 5. The data items at the top is to help in identifying the association between the DOM elements and the data items. Also note that although the shapes are squares, they are still SVG rectangle DOM elements with both the width and height set to 20 pixels.

The first render([1, 2, 2.5], "red"), will append 3 rectangles via the ENTER method to be bound to the 3 data elements supplied in the first parameter. The UPDATE will then update all available rectangles with red. The EXIT method as no effect since the number of data elements and the number of DOM elements are now in sync.

Notice how the number of data elements supplied is associated with the number of rectangles appended and the value of the data element is associated with the pixel space between the rectangles which was configured in the first line in the code. i.e the data domain 1 to 5 has been associated with the pixel range 0 to 200. Therefore each data element is worth 40 pixel spaces and half of any data element is worth 20 pixel spaces.

The second render([1, 2, 3, 4, 5], "blue"), will first append 2 rectangles via the ENTER method and then update all the 5 rectangle elements to blue via the UPDATE. Since the number of data elements and number of DOM elements are in sync there is no effect by the EXIT method.

The third render([1, 2], "green"), will have no impact by the ENTER method no new data elements have entered. Now there are altogether 5 rectangles, so the UPDATE will colour all of then green. However now the EXIT method have an impact as only 2 data elements are supplied but there are 5 rectangles available, hence the 3 additional once will be removed in order to keep the number of data elements and number of DOM elements in sync. Notice that the rectangles removed are the once that were not associated with the supplied data elements, i.e. 3, 4 and 5.

Now only 2 green rectangles associated with data elements 1 and 2 remain. Then the fourth render([3, 4, 5], "cyan"), will first append the 3 new data elements supplied via the ENTER method. The UPDATE will then colour all available rectangles with cyan. The EXIT method will then remove all rectangles other than the once associated with the newly supplied data elements 3, 4 and 5.

The fifth and final render([4, 5], "magenta"), will have no impact by the ENTER method as the rectangles associated with the supplied data elements 4 and 5 are already present as a result of the previous render. The UPDATE will as usual update all available rectangles with the supplied colour in the second parameter which in this instance is magenta. And finally the EXIT method will remove all rectangles not associated with the supplied data elements 4 and 5 leaving only the 2 magenta rectangles at the end of execution.