Treemap: Transition Between 2 Data Sets

1,008 views
Skip to first unread message

JoeH

unread,
Jul 28, 2011, 11:15:57 AM7/28/11
to d3-js
Hello All,

Sorry if this is a simple question but I'm brand new to d3, but I like
the look of its possibilities and want to persevere (I've done the
tutorials and now I'm trying something a bit harder). I'm trying to
create a treemap rather like: http://mbostock.github.com/d3/ex/treemap.html.
However, I would like to be able to click on the button and rather
than switch from 'value' to 'count' switch to a new set of values. To
clarify within my hierarchy each lowest level node has two values
associated with it say a number of apples and a number of oranges. I
would like a smooth transition between a treemap based on the number
of apples and one based on the number of oranges. I have tried storing
the two sets of values as two separate .json files but the first one I
load always gets 'stuck in memory' i.e. even if I remove the
visualization ( I tried div.selectAll("div").remove() ) when I
'redraw' in theory referencing the other file the data from the
original file is plotted again.

I would really appreciate any help or just a smarter way of doing it!?

Thank you in anticipation.

Regards,
Joe

Code Snippet:
function jsonLoad(FileName) {
d3.json(FileName, function(json) {

div.data(d3.entries(json)).selectAll("div")
.data(treemap)
.enter().append("div")
.attr("class", "cell")
.style("background", function(d) { return d.children ?
color(d.data.key) : null; })
.call(cell)
.text(function(d) { return d.children ? null : d.data.key; });
});
}

function Clear() {
div.selectAll("div").remove();
}

d3.select("#size").on("click", function() {
Clear()
div.selectAll("div")
jsonLoad("./Data/flare.json");

d3.select("#size").classed("active", true);
d3.select("#count").classed("active", false);
});

d3.select("#count").on("click", function() {
Clear()
jsonLoad("./Data/flare2.json");

d3.select("#size").classed("active", false);
d3.select("#count").classed("active", true);
});

Mike Bostock

unread,
Jul 28, 2011, 3:42:14 PM7/28/11
to d3...@googlegroups.com
Since the hierarchy is the same across your two data sets, the easiest
thing is to have a single JSON tree, and store your two values in the
leaf nodes. Then, just change the layout's value function from
function(d) { return d.apples; } to function(d) { return d.oranges; }.
In the JSON file, something like this:

{"name": "AgglomerativeCluster", "apples": 42, "oranges": 140}

Mike

JoeH

unread,
Jul 29, 2011, 6:04:41 AM7/29/11
to d3-js
Thanks Mike.

This makes more sense and thanks for the JSON example as I wasn't sure
as to the way to assign two values to a single leaf.

Keep up the good work and I'll keep trying to spread the word!

Joe

Ivan

unread,
Apr 23, 2017, 1:30:40 PM4/23/17
to d3-js
Dear colleagues,

I have a follow-up question on the transitioning between two data sets in a treemap. I am aware the previous posts are rather old, but I think my question relates to them. I hope it reaches someone...

Here is my problem: I have a csv data set of the form:

region,country,iso,data2010,data2015
Africa,Angola,ao,19158141.5, 20081534.88
Arab States,Algeria,dz,3261528.6, 25192747.68
...

I want to transition between two data sets, one with the data for 2010 (field data2010) and the other with the data for 2015 (field data2015).

I managed to do that by creating two separate treemaps using the code below (full code available here).

However, by doing this, even if I define the treemap with a tile "d3.treemapResquarify" in the transition between the two data sets the rectangles of each country get reshuffled and that makes it confusing, as nothing major actually happens to most of the countries changing positions.

I would like to have a transition in which only the sizes of the rectangles in the treemap change.

The hierarchy is the same across my two data sets, only the leave values should change. I thought the answer given by Mike to Joe would apply to my case, but I have not managed to get anything working by using just one treemap and then changing the sum function.

Actually, I did not manage to go from a nest structure involving more than one value in the roll-up to a treemap hierarchy. Either I use two separate nests rolling up the 2010 and 2015 values respectively or I get all sorts of errors if I try to roll-up the two of them at once and then apply d3.hierarchy and treemap().

Any help on this would be much appreciated. Anyways, thanks very much for developing and sharing these d3 libraries, it is impressive work and great for the community.

Best,
Ivan

code extract----------------------------

var nest1 = d3.nest()
    .key(function(d) { return d.region; })
    .key(function(d) { return d.iso; })
    .key(function(d) { return d.country; })
    .rollup(function(d) { return d3.sum(d, function(d) { return parseFloat(d.data2010); }); });

var nest2 = d3.nest()
    .key(function(d) { return d.region; })
    .key(function(d) { return d.iso; })
    .key(function(d) { return d.country; })
    .rollup(function(d) { return d3.sum(d, function(d) { return parseFloat(d.data2015); }); });

// read the data
d3.csv("Non_Int_users.csv", function(error, csv_data) {
  if (error) throw error;

  data = csv_data;

// create hierarchies from the data
var root1 = d3.hierarchy({values: nest1.entries(data)}, function(d) { return d.values; })
    .sum(function(d) { return d.value; })
    .sort(function(a, b) { return b.value - a.value; });

var root2 = d3.hierarchy({values: nest2.entries(data)}, function(d) { return d.values; })
    .sum(function(d) { return d.value; })
    .sort(function(a, b) { return b.value - a.value; });

// create trees
treemap(root1);
treemap(root2);

// draw initial treemap
var cell = svg.selectAll("g")
  .data(root1.leaves())
  .enter().append("g")
...

Seemant Kulleen

unread,
Apr 23, 2017, 2:08:23 PM4/23/17
to d3-js
Happy Sunday Ivan,

This is certainly tricky.  Since your two nests only differ in their rollups, perhaps you can consider using one nest, with a rollup that looks like:

.rollup(function(d) { 
    return { 
          sum2015: d3.sum(d, function(d) { return parseFloat(d.data2015); })
        , sum2010: d3.sum(d, function(d) { return parseFloat(d.data2010); }) 
    };
});

Giving your leaves both sums at once.  Now, you can set a variable like:
var YEAR = 2010; 

Your hierarchy stuff can then look something like:
    .sum(function(d) { return d.value["sum" + YEAR; })
    .sort(function(a, b) { return b.value["sum" + YEAR] - a.value["sum" + YEAR]; });

The "sum" + YEAR stuff will become a string "sum2010", which keys to the object that you rolled up earlier.

This is likely very buggy as I haven't tested any of it, and it's a Sunday, but I hope it can help a bit.

Cheers,
Seemant

--
Oakland Finish Up Weekend
Be Amazed.  Be Amazing.
Get Mentored | Get Inspired | Finish Up
http://oaklandfinishup.com


--
You received this message because you are subscribed to the Google Groups "d3-js" group.
To unsubscribe from this group and stop receiving emails from it, send an email to d3-js+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Ivan Vallejo Vall

unread,
Apr 24, 2017, 6:30:38 AM4/24/17
to d3...@googlegroups.com
Hi Seemant,

Thanks for your prompt reply!

I tried the code you suggested (see below) but still the rolling up seems not to work with the two values. The following error is thrown: "Cannot read property 'sum2010' of undefined"

I looked into what is happening when I call the sum function and the problem seems to be the following:

- For the leaves (country names), it works ok, because they have a value object with the "sum2010" and "sum2015" elements which I can call:

  1. Object {key: "Angola", value: Object}
    1. key: "Angola"
    2. value: Object
      1. sum2010: 19158141.52
      2. sum2015: 20081534.88

- When we go up the tree (i.e. iso codes and regions), it does not work, because they do not have any value object, so the call to "d.value" inside the sum function fails (see first for ISO code, 2nd for regions):

  1. Object {key: "ao", values: Array[1]}
    1. key: "ao"
    2. values: Array[1]
      1. 0: Object
        1. key: "Angola"
        2. value: Object
          1. sum2010: 19158141.52
          2. sum2015: 20081534.88


  1. Object {key: "Africa", values: Array[44]}
    1. key: "Africa"
    2. values: Array[44]
      1. 0: Object
        1. key: "Angola"
        2. value: Object
          1. sum2010: 19158141.52
          2. sum2015: 20081534.88
          3. __proto__: Object
        3. __proto__: Object
      2. 1: Object
        1. key: "Benin"
        2. value: Object
          1. sum2010: 9224504.06
          2. sum2015: 10227038.32
        ...
(to illustrate the structure for regions, I deleted the iso layer, otherwise it throws the error at the iso level before getting to the region level)

Actually, I noticed that the sum function also has the same problem when I use two separate trees, but it does not really matter because I do not call the sum function for the trees. Plus it does not throw an error: d.value of undefined just returns undefined, d.value["sum2010"] of undefined returns an error.

Any ideas on how to go about this?

Thanks again,
Ivan

code ---

var nest = d3.nest()

    .key(function(d) { return d.region; })
    .key(function(d) { return d.iso; })
    .key(function(d) { return d.country; })
    .rollup(function(d) { return{

            sum2015: d3.sum(d, function(d) { return parseFloat(d.data2015); }),
            sum2010: d3.sum(d, function(d) { return parseFloat(d.data2010); })
        };
    });

var root = d3.hierarchy({values: nest.entries(data)}, function(d){ return d.values})
                        .sum(function(d) { return d.value["sum2010"]; })
                        .sort(function(a, b) { return b.value["sum2010"] - a.value["sum2010"]; });
 


--
You received this message because you are subscribed to a topic in the Google Groups "d3-js" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/d3-js/U9pNjufmRvs/unsubscribe.
To unsubscribe from this group and all its topics, send an email to d3-js+unsubscribe@googlegroups.com.

Seemant Kulleen

unread,
Apr 24, 2017, 11:43:06 AM4/24/17
to d3-js
Happy Monday Ivan!

Thank you for trying my suggestion.  While I have no immediate insight to the error you're seeing, I can tell you how I would start to debug this.  Changing your " var root" code to be like:
var root = d3.hierarchy({values: nest.entries(data)}, function(d){ return d.values})
                        .sum(function(d) { console.log(d); return d.value["sum2010"]; })

                        .sort(function(a, b) { return b.value["sum2010"] - a.value["sum2010"]; });

That should spit out (onto the dev console) the datum that is being seen, which might provide some insight.

Hope that helps...

Cheers,
Seemant


--
Oakland Finish Up Weekend
Be Amazed.  Be Amazing.
Get Mentored | Get Inspired | Finish Up
http://oaklandfinishup.com


Ivan Vallejo Vall

unread,
Apr 24, 2017, 2:20:58 PM4/24/17
to d3...@googlegroups.com
Hi Seemant,

Thanks for the tip, that is exactly what I did.

From what I get on the console (I am copying it again below), I see that the error is thrown because only the leaves have a value field.

The roll-up does not seem to work or at least it only creates a value field at the lowest level, not at the higher level of the tree. In higher-level nodes, there is a values array and within it, I can go down to reach the leaves and then find the value.

Nothing seems to be rolled-up in terms of the value (i.e. country values are not summed up to produce the region totals higher up in the hierarchy).

So I see why it throws an error, but I do not know how to fix it. Again, any help would be much appreciated.

Have a good Monday,
Ivan

Console.log(d) for leaves:

  1. Object {key: "Angola", value: Object}
    1. key: "Angola"
    2. value: Object
      1. sum2010: 19158141.52
      2. sum2015: 20081534.88

Console.log(d) for higher level nodes (no value field, you need to go n-levels down to the leaves to find it):

 
  1. Object {key: "ao", values: Array[1]}
    1. key: "ao"
    2. values: Array[1]
      1. 0: Object
        1. key: "Angola"
        2. value: Object
          1. sum2010: 19158141.52
          2. sum2015: 20081534.88

Seemant Kulleen

unread,
Apr 24, 2017, 4:55:28 PM4/24/17
to d3-js
Ivan,

Thank you.  I have some thoughts, but  think it would be more productive if you can share your code with me, so that we can address things within context.  Can you possibly put up your code at blockbuilder.org (which will put it at bl.ocks.org) or codepen or so?

Cheers,
Seemant


--
Oakland Finish Up Weekend
Be Amazed.  Be Amazing.
Get Mentored | Get Inspired | Finish Up
http://oaklandfinishup.com


Ivan Vallejo Vall

unread,
Apr 26, 2017, 5:59:37 AM4/26/17
to d3...@googlegroups.com
Hi Seemant,

I managed to upload a simplified version of my treemap at bl.ocks.org: https://bl.ocks.org/ivan-vallejo/1e92db10504b6115b37db398a93e2d9f

In lines 112-138 is where I create the two trees. As discussed, I tried to modify these lines to get the two datasets into the same tree by rolling up the two of them together, but did not manage to make it happen.

Thanks so much for your help!
Ivan

M Brown

unread,
May 31, 2017, 5:00:18 PM5/31/17
to d3-js
Hi Ivan,

I had the same issue and managed to get it working (transitioning between 2 rolled up values) with the following bits of code

var nest = d3.nest()
    .key(function(d){ return d.organization})
    .key(function(d){ return d.sub_org})
    .key(function(d){ return d.month})
    .rollup(function(d) {
     return {"visits": d3.sum(d, function(d) {
        return  parseFloat(d.visits_tot)}), "downloads": d3.sum(d, function(d) {
            return  parseFloat(d.downloads_tot)})} });
  

        var root = d3.hierarchy({values: nest.entries(data)},function(d){
            return d.values;
        })
        root.sum(sumByDownload)
        .sort(function(a, b) {
         return b.height - a.height || b.value - a.value; });

        treemap(root);

    function sumByDownload(d) {
            try{
                return d.value.downloads;
            }catch(e){}
             }


    function sumByVisits(d) {
        try{
                return d.value.visits;
            }catch(e){}
             }

    }
To unsubscribe from this group and stop receiving emails from it, send an email to d3-js+un...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to a topic in the Google Groups "d3-js" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/d3-js/U9pNjufmRvs/unsubscribe.
To unsubscribe from this group and all its topics, send an email to d3-js+un...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "d3-js" group.
To unsubscribe from this group and stop receiving emails from it, send an email to d3-js+un...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to a topic in the Google Groups "d3-js" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/d3-js/U9pNjufmRvs/unsubscribe.
To unsubscribe from this group and all its topics, send an email to d3-js+un...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "d3-js" group.
To unsubscribe from this group and stop receiving emails from it, send an email to d3-js+un...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to a topic in the Google Groups "d3-js" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/d3-js/U9pNjufmRvs/unsubscribe.
To unsubscribe from this group and all its topics, send an email to d3-js+un...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages