Treemap
Treemaps are a great way to visualize relative proportions within a hierarchy. Most Treemap algorithms require recursion so are hard to do entirely within NodeBox. I implemented this version using a custom node.
The treemap subnetwork takes a list of values and a bounding rectangle. It outputs the rectangles which comprise the treetop. The rectangles are colored but you will generally want to re-color them to express categories or some other dimension like intensity.
The list of values can be unordered and unnormalized, but will be sorted in descending order to create the treemap. Therefor you will generally want to sort them in descending order yourself beforehand so that you can correlate them to associated information like labels.
The attached demo shows two examples. The first, consisting of only three nodes, plots a set of random numbers. Adjust the number of random values and the size of the rectangle to see how the treemap adapts.
The second example shows how to make a compound colored treemap with labels and a legend. The top-level treemap shows the relative proportion of instruments in four stages of the evolution of orchestras. Those four rectangles are colored with a thicker border. The rectangles are then fed into another treemap subnet so that each is further subdivided. I also provide a labels subnet that automatically sizes each minor box label, rotating when necessary.
Inside the treemap subnet is the make_map custom node which does the actual work, using the squarify algorithm. You will need to add the treemap.py file to your code library whenever using this node. If you want thicker borders around a top level treemap you can change the function call (in the node's Metadata under Settings) from treemap/squarify to treemap/padded_squarify.
If you make anything interesting with this subnet please share it on the forum. Enjoy!
John
- Treemap_Screenshot.png 474 KB
- treemap.zip 6.91 KB
Keyboard shortcuts
Generic
? | Show this help |
---|---|
ESC | Blurs the current field |
Comment Form
r | Focus the comment reply box |
---|---|
^ + ↩ | Submit the comment |
You can use Command ⌘
instead of Control ^
on Mac
Support Staff 1 Posted by john on 27 Dec, 2017 07:25 AM
Could someone please try downloading this demo and see if it works?
A fellow Nodeboxer keeps getting an error which prevents him from opening the demo network, but when I try it, it seems fine. If someone else could try it we could figure out whether the problem is at my end or his.
Thanks!
John
Support Staff 2 Posted by lucasnijs on 27 Dec, 2017 08:58 AM
Hello John,
works fine with me.
Is he using 3.0.50? Else the make_table node will not exist and cause an error.
What error does he get?
grtz,
best wishes for 2018!
Lucas
Support Staff 3 Posted by john on 27 Dec, 2017 11:52 PM
Thanks, Lucas.
Your hunch was correct. He was getting an error about make_table and apparently had not downloaded 3.0.50.
Glad to hear that my custom node is working OK. Best wishes back to you and everyone at NodeBox central.
John
4 Posted by Jen on 07 Jun, 2022 02:26 AM
Hi John
I'm now working on a treemap type project, but with a twist. Let's say I want a treemap that shows the proportionate number of notes played by each type of instrument for a specific song. Now I would like to make several more treemaps in the same manner for more songs, BUT I do not want them arrange from largest to smallest proportions. Instead I would like to always keep guitar in the upper left corner regardless of size, keyboard to the right of guitar, french horn below keyboard etc. The idea is that each instrument has an assigned color with alpha turned down. Then all of the treemaps are laid on top of one another to create a colorful abstract image. I know that the code for the treemap node is not designed that way, so would you recommend using calculations in excel and then arranging the rectangles, or is there a workaround you recommend, besides altering your code? Or should I find another program more suitable?
Thank you,
Jennifer
Support Staff 5 Posted by john on 07 Jun, 2022 08:11 AM
Hi Jen,
I'm not sure I fully understand what you are trying to do.
You can certainly make what is known in the data viz business as a trellis of treemaps. That is you can make a treemap for each song and then simply use a grid control to arrange them all to make a poster with a song title for each map. You might need a little extra code to make sure all the instruments are consistently colored across all the treemaps, but that shouldn't be too hard.
Controlling the *position" of each instrument block within a treemap, however, is sort of impossible: a bit like asking for a square circle. If you have a bunch of blocks in one treemap it's not even clear what "same position" even means. You could maybe arrange each type of block at the same angle from the center, but then the blocks in a given "treemap" would overlap and would certainly not form a tidy rectangle.
If you want to make sure all the guitars stay together, you could organize treemaps by instrument instead of by song. Each block in the guitar treemap could represent the percentage of guitar notes for a given song. You could also size the treemap for each instrument differently so that instruments with more total notes had bigger treemaps than instruments with fewer total notes. But you would still need to arrange all the separate treemaps on a grid or some other pattern of points.
OR you could have a two-level treemap where inner song blocks form into each instrument block and the total notes per instrument are used to form a master treemap of instruments.
I'm not sure what would happen if you tried to put all the treemaps "on top of each other" (with reduced alpha colors), but I suspect you would get a big mess. The nice thing about Nodebox is that it would be fairly easy to try it and see what happens.
Another route would be to try to make a more curated infographic where the guitar data is shaped like a big guitar or something and the poster looks like an oddly proportioned orchestra. Things like that are pretty much impossible to do entirely by algorithm. You would need a combination of computer to make the pieces and human being to do the final arrangements. I've done a few things like this in Nodebox and there are ways to make it easier.
But you have to have a coherent vision of exactly what it is you are trying to convey. Your dimensions are instrument and song and your measurement is notes per instrument per song (or song per instrument). There a few different ways to pivot that data. You can use shape and size and color and position (to some extent) to communicate that data. Some designs will make it easier to compare songs while others will make it easier to compare instruments; trying to do both at once is harder and risks confusing your audience.
If the going gets tough, you may want to think outside the box and throw out treemaps altogether. Maybe represent each note as a tiny guitar or tiny flute or whatever and place them like notes on a staff with maybe one long staff per song. OR think about what you could do with animation. OR (insert some even crazier idea here).
Who is your audience? What question are you trying to answer for them?
Use Nodebox to play. Make instrument treemaps, song treemaps, instrument-song two-level treemaps, song-instrument two-level treemaps, then put them in girds, put them on top of each other, play with size and position of each treemap, adjust color schemes and get a better idea for what works and what doesn't. If you come up with a very precise definition of "in the same position" but can't figure out how to do it, take your best shot then zip up your ndbx file and your CSVs and attach it to this thread so I can try playing with it myself.
Does any of that help?
John
6 Posted by Jennifer Concan... on 07 Jun, 2022 12:34 PM
Hi John,
https://www.c82.net/iconography/art/proportions
This should clarify what I'm trying to do. I'm fascinated with Nicholas
Rougeaux's work; he's the one who got me started on NB because of his Off
the Staff visualizations of music. That's the inspiration for the GreenBay
Packers picture I sent you previously.
As I look more closely at how he made the proportion art, it's clear that
the rectangles are not always in the same location, but it appears that he
tried to keep them in the same place as much as possible.
I made a quick treemap using RAWGraph and then tried importing the jpeg
into NB using your image node and seeing if alpha could be adjusted. that
didn't work so well. BTW, the image node doesn't alert that you need a PY
file. I discovered it in the library while trying to figure out why it
wasn't working.
I'm going to keep chugging and see what I can figure out, as that is part
of the fun.
Jennifer
Support Staff 7 Posted by john on 08 Jun, 2022 12:40 AM
Ah! Now I see what you mean.
Nick Rougeux is a premier visualization artist and Nodebox booster and has been a frequent contributor to this forum. His work is always interesting and beautiful.
And good news: your request is easy to do with just a minor tweak.
My treemap node is actually a subnetwork which automatically sorts the value list before feeding it to the external treemap code because this is what most people usually want. But in this case we want to leave the values in their original order.
See attached demo and screenshot. I superimpose two treemaps generated from ten random numbers with two different seed values. This can stand in for two songs each with ten instruments.
My demo includes a modified version of the treemap node that leaves the value list as is. This means the values for each treemap will always be arranged in top to bottom, left to right order. As long as the instruments for each song are always listed in the same order, they will appear in approximately the same position for each treemap.
The positioning will not, of course, be precisely defined and may vary somewhat, especially if some instruments are used in some songs but not in others. But for Nick's approach you don't need precision. You are looking for a vague, fuzzy, overall feel.
The end result is not "a mess" as I feared it would be, but can actually be quite lovely. I'm not sure how useful it is as a visualization; a trellis would allow you to precisely understand and compare instruments across songs. But this may work better than I thought it would. I would love to see your final result if you are willing to share it.
I wonder now if I should revise the treemap in my library so it no longer automatically sorts the values (or maybe provides that as a checkbox option). I'll think about that.
Thanks for an interesting challenge. I learned from it!
John
Support Staff 8 Posted by john on 08 Jun, 2022 01:35 PM
Jennifer,
One other thing...
In my example I used a combine node to overlay the two treemaps. But if you have many song treemaps to overlay it will be cumbersome to copy paste the treemap nodes and string multiple combine nodes together.
There is no need to do this. You can feed all the data into a single treemap node and have it fire once for each set of song data and you won't need any combine nodes at all.
The trick is to create a subnetwork that fires once for each song. Feed your CSV in AS A LIST and then feed in a list of song IDs as individual VALUES. Inside the subnetwork filter the data for each song ID, feed it into the treemap node, color them consistently, and group the output.
Much cleaner! But to do this you will have to use subnetworks. Subnetworks are confusing at first, but they are the key to making NodeBox sing.
John
9 Posted by Jennifer Concan... on 08 Jun, 2022 07:06 PM
OMG John! I can't believe you did that! Thank you! I tried comparing the
code for both py files but I couldn't see the difference? Was it so subtle
that I missed it? I can vaguely follow along and expected to find the
difference in the squarify function, but I didn't see it there or anywhere
else. I would like to understand this. Also what is the function
pad_rectangle for? Since what I don't know about code could fill a
library, I could only get the gist of how this works, but it
is interesting to study it, and also makes me even more grateful for
programs like NB to allow me to avoid it!
Thank you!!
Jennifer
Support Staff 10 Posted by john on 09 Jun, 2022 03:02 AM
Jennifer,
My pleasure.
The pad_rectangle function returns rectangles that, when laid out, have a bit of padding to show their borders. It works with an alternative version of the original squarify algorithm called padded_squarify. I left that alternative in the code I borrowed, but my treemap node does not use it.
I feel the same way about Python code that you do. I used to code in Python at one point but have forgotten most of it. Developing the Python nodes was an exercise in trial and error frustration. I try to avoid dealing with external code modules unless I have no choice. That said, I hope the modules I provide in my library will be helpful examples for other people who may want to develop more such modules.
Thanks for alerting me that the comment for my image node fails to mention that it requires image.py. Not sure how that one slipped by me. I have fixed it for the next release of my library.
John
11 Posted by Jennifer Concan... on 10 Jun, 2022 02:13 AM
Hi John,
I'm embarrassed to ask, but I cannot figure out your explanation below. I
might be able to give you a run for your money in Excel, but for NB, not so
much. I initially had a different column for each song, but then I read
your explanation elsewhere about how filter_data returns an entire row, so
I rearranged the data such that each song had its own row, and the data for
each instrument was in columns. However, it sounds like you are suggesting
recursion, which you had said NB doesn't do. So no matter what I do in
excel doesn't change that fact. So what causes NB to "fire" once for every
song?
The second problem is I don't fully understand how the filter_data node
works despite trial and error and reading the blog. I'm attaching what I
did (errors and all).
Gratefully,
Jen
12 Posted by Jennifer Concan... on 15 Jun, 2022 02:32 AM
Hi John
Here's the end product for the ordered treemap node that you created. This
is a visualization of 13 of my mother's baking recipes showing proportions
such as number of ingredients, baking temp x time, number of cups of flour,
etc. I'm going to have it printed and framed for her for Christmas and I
am sure she will absolutely love it. Thank you very much for making this
possible.
Jennifer
Support Staff 13 Posted by john on 15 Jun, 2022 06:03 AM
Jennifer,
Wow! That's beautiful.
You are most welcome for whatever help I have provided. Credit should also go to Nick Rougeux for pioneering that overlay technique.
I hope this is just the beginning of your Nodebox creations. You clearly have a knack for it.
John
Support Staff 14 Posted by john on 01 Jul, 2022 02:31 AM
Hi Jennifer,
Are you still there?
I just now found a note you wrote on June 9 (now restored - see above) that somehow landed in the spam folder. It's maddening that this forum software keeps doing that to people like you.
In that note you needed help understanding the filter node and the way you can use subnetworks to choreograph the way functions "fire" in NodeBox. But then, a few days later, you resurfaced with that beautiful visualization. So maybe you have everything figured out now.
If you are still there and still have any questions, please re-ask them. Again, my apologies for the overeager spam filter.
John
15 Posted by Jennifer Concan... on 06 Jul, 2022 09:34 PM
Hi John. Thank you for getting back to me. I thought maybe you got annoyed with all my questions so I didn’t bother you again about it. Unfortunately after spending a fair amount of time on it I didn’t figure it out and used the very clunky method of several sets of the same nodes to get the end product. It bothered me knowing how crude the structure was but I just didn’t want to make myself crazy when there was a known solution that would allow me to finish and move on to the next project. However I really would like to understand your proposed method. I do understand the overall concept of why a subnetwork would be useful, and I was able to use subnetworks for another project but I am not understanding how to use them with the filter node as you recommended. Would you kindly elaborate?
Great to hear from you again!
Jennifer
Sent from my iPhone
Support Staff 16 Posted by john on 07 Jul, 2022 07:41 PM
Hi Jennifer,
I am never annoyed by NodeBox questions - the more the merrier.
I have attached an example showing my proposed method. I build a song table with five songs, each having the same seven instruments and, for each instrument, the number of notes it plays in that song (totally random). I then lookup the list of distinct song IDs from that table and feed it into a song_map subnetwork.
What we want the song_map node to do is produce one ordered treemap for each songID. Each time it fires it needs to read in the entire song table and one song ID to filter against.
If we simply feed the list of songIDs into a filter node, the filter node would spit out a combined list with all the rows and thus all the values. If we then fed that into a treemap, instead of five separate treemaps with seven subregions each, we would get one big treemap with 35 subregions.
So what we need is a way to choreograph the action so that a single treemap is made for each songID. Each time we need to read in the data all at once, but the song IDs one at a time. Subnetworks provide a way of doing this.
If you look inside the song_map subnetwork (see attached screenshot) you will see its two input parameters at the top: data (a null node) and songID (an integer node). I always like to place one node for each parameter at the top and rename them for clarity. When I form my subnetwork I then publish each parameter node.
Here is where the magic happens. Once you make your subnetwork, select it and click the Metadata button on top of the parameter pane to open the Metadata dialog. Under Ports on the left select each one of your parameters - in this case, data and songID. On the right you will see a Range setting that can take one of two possible values: Value or List. Value means take inputs one a time; List means take them all at once.
The trick is to set data to List and songID to Value. This ensures that the subnetwork will fire once for each songID, but will suck in the entire data table each time it fires.
Once that's done, you can build the rest of the subnetwork. Filter the data to retrieve only the rows for the given songID, sort those rows to make sure the instruments are always in the same order, lookup the notes, feed them into my improved treemap node (which now has a checkbox for sorting values which you can leave unchecked), and color the resulting map with the same seven translucent colors.
This ability to coordinate inputs with some taken one at a time and some taken all at once is essential to doing more complex things in NodeBox. The UI does not make this obvious and it took me quite a while to get used to it.
Does that clear things up? Please never be afraid to ask questions.
Happy Nodeboxing!
John