Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3 heatmap without explicitly storing row and column

I have a 2d array of data that I'd like to visualize as a heatmap in D3.

From the examples I've seen, including:

https://stackoverflow.com/a/12925448/1647819

http://bl.ocks.org/2605010

it looks like you have to encode the row and column as attributes of each data element. Is there a way to take advantage of the structure of the data (i.e., the fact that it's a 2D array indexable with [i][j] notation) to avoid redundantly encoding the position information?

If not, is there a concise and/or efficient way to generate the data structure that encodes the position information? By that I mean something other than a nested for loop:

myData = [[1, 2], [3, 4]]
myDataWithPos = []

nRow = myData.length
nCol = myData[0].length

for (i = 0; i < nRow; i++){
    for(j = 0; j < nCol; j++){
        myDataWithPos.push({val: myData[i][j], row: i, col: j})
    }
}
like image 715
DMack Avatar asked Oct 25 '12 19:10

DMack


1 Answers

Sure, you can use the indices of a 2d array to draw a heatmap. I put together a quick example at http://jsfiddle.net/uArga/4/.

Assuming that your data looks like this:

var data = [[3,5],[9,2]];

First I set up some basic scales to calculate the x and y offsets based on the size of the data and the size of the chart. This makes it easy to resize the chart by changing the height and width without having to manually recalculate how big each of the cells should be.

var x = d3.scale.linear()
    .range([0, width])
    .domain([0,data[0].length]);

var y = d3.scale.linear()
    .range([0, height])
    .domain([0,data.length]);

You can then add each row to the heat map

var row = svg.selectAll(".row")
             .data(data)
           .enter().append("svg:g")
             .attr("class", "row");

And then add each cell to the row using a subselect. There might be a cleaner way to do this but I basically just capture the row number alongside the data for each cell. I then use the scales to figure out the x, y, height, and width for each cell.

var col = row.selectAll(".cell")
    .data(function (d,i) {return d.map(function(a){return {value: a, row: i};})})
  .enter().append("svg:rect")
    .attr("class", "cell")
    .attr("x", function(d, i) { return x(i); })
    .attr("y", function(d, i) { return y(d.row); })
    .attr("width", x(1))
    .attr("height", y(1))
    .style("fill", function(d) { return colorScale(d.value); });
like image 164
Bill Avatar answered Sep 30 '22 13:09

Bill