Graphene is a library for transforming collections of Ruby objects into subtotals, percentages, tables and graphs. It's most useful feature is probably the Gruff helpers. True, some types of Gruff charts are pretty simple to begin with, but others can require tedious boilerplate code to calculate percentages, well-spaced labels, and more. Graphene hides all of that, accepting an array of objects and transforming them into a graph.

Nobody likes you, Ruby 1.8. Now go away.


$ [sudo] gem install graphene
# Or just add "graphene" to your Gemfile

Gruff is required only by Graphene's graphing component. Because of this, and because Gruff's dependencies can be difficult to install, you will need to install Gruff manually. If you do not/cannot, Graphene's other components will continue to function.

# On Debian/Ubuntu
$ sudo apt-get install librmagick-ruby libmagickcore-dev libmagickwand-dev 

$ [sudo] gem install rmagick gruff
# Or just add "rmagick" and "gruff" to your Gemfile

See for more on Gruff.


# An array of objects which respond to methods like :browser, :platform, :date
logs = SomeLogParser.parse('/var/log/nginx/access.log.*')

percentages = Graphene.percentages(logs, :browser)

puts percentages.to_a
=> [["Firefox", 40.0], ["Chrome", 35.0], ["Internet Explorer", 25.0]]

percentages.each do |browser, percent|
  puts "#{percent}% of hits were from from #{browser}"

# You can also calculate by multiple criteria, and may use lambdas instead of symbols
percentages = Graphene.percentages(logs, ->(l) { l.browser.downcase }, :platform)

puts percentages.to_a
=> [["chrome", "OS X", 40], ["internet explorer", "Windows", 25], ["firefox", "Windows", 25], ["firefox", "GNU/Linux", 8], ["chrome", "GNU/Linux", 2]]

See Graphene.percentages for more info.


Same as percentages above, except that subtotals are returned instead. See Graphene.subtotals for more info.

Adding a dimension

“Marty, you're not thinking fourth-dimensionally!” This is only three dimensions, but whatever, Doc.

puts Graphene.percentages(logs, :browser).over(:date)
=> {#<Date: 2012-07-22> => [["Firefox", 45], ["Chrome", 40], ["Internet Explorer", 15]],
    #<Date: 2012-07-23> => [],
    #<Date: 2012-07-24> => [["Firefox", 41], ["Chrome", 40], ["Internet Explorer", 19]],
    #<Date: 2012-07-25> => [["Chrome", 50], ["Firefox", 40], ["Internet Explorer", 10]]}

Notice that in the results above, there were no log entries on 2012-07-23. So how did Graphene know to include that date? Because dates are iterable. If your X axis objects are not iterable and you want to ensure that none are left out, you can provide them yourself:

# Ryan didn't make any sales, so he won't show up unless we include his name
puts Graphene.subtotals(sales, :reams).over(:name, ['Jim', 'Dwight', 'Ryan'])

# Ruby doesn't know how to iterate months, but you do
puts Graphene.percentages(logs, :browser).over(->(log) { [,] }) do |month, year|
  month < 12 ? [month + 1, year] : [1, year + 1]


Integration with the tablizer gem provides quick ASCII tables.

puts Graphene.percentages(logs, :browser, :platform).tablize
=> +-----------------+------------+----------+
   |     Browser     |  Platform  |Percentage|
   |Firefox          |Windows     |50.0      |
   |Internet Explorer|Windows     |20.0      |
   |Safari           |OS X        |20.0      |
   |Firefox          |GNU/Linux   |10.0      |


Provides helpers for generating Gruff graphs. Requires the “gruff” Ruby gem.

# A pie chart of Firefox version shares
ff = { |e| e.browser == 'Firefox' }
Graphene.percentages(ff, :browser).pie_chart('/path/to/graph.png', 'FF Version Share')

# A line graph of daily browser numbers over time, tricked out with lots of options
Graphene.subtotals(logs, :browser).over(:date).line_graph('/path/to/graph.png') do |chart, labeler|
  chart.title = 'Browser Share'
  chart.font = '/path/to/awesome/font.ttf'
  chart.theme = {
    :colors => %w(orange purple green white red),
    :marker_color => 'blue',
    :background_colors => %w(black grey)

  # Only show every 7th label, and make dates pretty do |date|
    date.strftime('%b %e')

See Graphene::OneDGraphs, Graphene::TwoDGraphs and for more graph types and examples.

See for more on Gruff. Specifically, will let you know many things that can be customized in the optional block.


Copyright 2012 Jordan Hollinger

Licensed under the Apache License