IdolHands.com :: Days in the Life of an Alpha Geek
Histograms as a data structure are often displayed as a tag-cloud-style heatmap. This article will show you how to easily create a heatmap in Rails, and provides a link to a Rails plugin to let you automatically add this functionality to your project.
Let's say that you have articles and tags in your project. There's a HABTM relationship between the two, which means that you can calculcate the frequency of any given tag pretty easily. For example, in tag.rb:
def frequency
self.articles.count
end
So let's add a class method to Tag to generate a histogram for our source data:
def self.to_histogram
Tag.all.inject({}){|set, t| set[t.name] = t.frequency; set }
end
That's it for the source data.
Rather than constructing the heatmap in the view itself, we'll create a helper method that does the heavy lifting for us. In application_helper.rb:
def heatmap(histogram={})
html = %{<div class="heatmap">}
_max = histogram.map{|k,v| histogram[k]}.max * 2
histogram.keys.sort{|a,b| histogram[a] <=> histogram[b]}.reverse.each do |k|
next if histogram[k] < 1
_size = (((histogram[k] / histogram.map{|key,val| histogram[key]}.sum.to_f) * 100) + 5).to_i
_heat = sprintf("%02x" % (255 - (_size * 25)))
html << %{
<div class="heatmap_element" style="color: ##{_heat}#{_heat}#{_heat}; font-size: #{_size}px; height: #{_max}px;">#{k}</div>
}
end
html << %{<br style="clear: both;" /></div>}
end
This method accepts a histogram in the form of a hash and uses it to generate the HTML for our heatmap. The maximum value in the histogram is used to set the size of all of the elements. Next, iterating over each key and discarding those with zero values, the font size and color of each element is calculated based on its relative weight. Finally, the actual HTML for the element is created and the entire set of tags is returned.
All the controller should do is set an instance variable for the histogram:
def index
@histogram = Tag.to_histogram
end
Finally, in the view, our humble heatmap tag:
<%= heatmap(@histogram) -%>
You'll probably want to style the heatmap; the two CSS classes that it uses are heatmap, for the containing div, and heatmap_element for each key that is displayed. Here's a starting point for the CSS:
.heatmap {
border-top: 1px solid #aaaaaa;
border-bottom: 1px solid #aaaaaa;
width: 300px;
padding: 1em;
}
.heatmap_element {
display: block;
text-align: center;
margin: .25em;
padding-top: 0em;
float: left;
}
That's it! Here's sample output from our method, showing an assortment of nightmarish villains:
The helper method is a fine approach, but heatmaps are so ubiquitous that you're likely to want to use this code in multiple projects. That makes this a great candidate for a Rails plugin. Luckily for you, I've already done the work of extracting and packaging the code as a plugin. You can get it from GitHub at http://github.com/Bantik/heatmap.
As always, feedback and forks are welcome.