Rubyvis

Some problems, some solutions.

If you need to draw a custom graph type in Ruby, there's a few problems:

  1. There's only a modest number of graphing libraries
  2. You don't have to go to far for your graph type to not be covered by these

Which leaves you few choices. You could manually draw it in a graphics library (hard). You could do a call to R to draw it (ugh). Or you could opt for Rubyvis.

Rubyvis is a port of the Protovis javascript library, which is well-known for producing lush illustrations, and captures most of Protovis' power. However, there are some hard lessons to learn.

Rubyvis / protovis are not libraries of functions that produce graphs and charts but libraries of functions you can use for writing your own code to draw graphs and charts. There's no off-the-shelf solution for a scatter plot or bar chart, for example. You're going to be laying it all out for yourself, albeit with the help of the toolkit.

Further, Rubyvis isn't a complete port. In fact, in places you can still find partially translated Javascript that uses Javascript interfaces. For example, if your data is dates, it may fail when Rubyvis tries to access the JS date functions rather than Ruby ones. (That's “may”. Depending on your data, you may not encounter that section of the toolkit.)

You can get around the date problem with a little cleverness. Translate your dates into integers and when it comes time to doing labels, your formatting function should translate these back and format them appropriately. For example:

epoch = Date.new(1970,1,1)
new_series = data.collect { |r|
   (p[0] - epoch).to_i, p[1]
}
...
my_panel.add(pv.Label).text(lambda { |d|
      (epoch + d).strftime("%m/%d")
   }
)

Another you might encounter is with log axes. The following line produces an axes that runs from 0 to 1000 and draws it within 200 pixels:

vert = pv.Scale.linear(0, 1000).range(0, 200)

Simply convert this into a log scale and it will error out as it tries to find the log of 0:

vert = pv.Scale.log(0, 1000).range(0, 200)

Logical, but if we're trying to map data running better 0 and 1000, we need a better answer. I suggest setting the bottom end just slightly off zero, depending on your data:

vert = pv.Scale.log(0.01, 1000).range(0, 200)

But at the end of this you end up with SVG. What do you do? Well, you could use RMagick - if it will install. You could go outside of Ruby and use Cairo. Or you could call the commandline program rsvg:

% rsvg foo.svg foo.png