Recently we’ve been experimenting with Google Charts on Urbanspoon. Their API is well designed and easy to use, but it’s still nontrivial to produce good looking graphs for arbitrary data. Here are some suggestions for my fellow ruby hackers:

### 1. Nice Numbers for Graph Labels

The classic “Nice Numbers for Graph Labels” Graphics Gem by Paul Heckbert will generate a series of good looking axis labels given a min and max value. It works with floats as well as integers.

*Automatic “nice labels” on the y axis*

I ported it to Ruby:

# From the "Nice Numbers for Graph Labels" graphics gem by Paul # Heckbert def nicenum(x, round) expv = Math.log10(x).floor.to_f f = x / (10 ** expv) if round if f < 1.5 nf = 1 elsif f < 3 nf = 2 elsif f < 7 nf = 5 else nf = 10 end else if f <= 1 nf = 1 elsif f <= 2 nf = 2 elsif f <= 5 nf = 5 else nf = 10 end end nf * (10 ** expv) end def loose_label(options = {}) min, max = options[:min], options[:max] ticks = options[:ticks] || 5 range = nicenum(max - min, false); d = nicenum(range / (ticks - 1), true); { :min => (min / d).floor * d, :max => (max / d).ceil * d, :increment => d } end

For example, if your data set ranges from 23-65 and you want to have

five axis labels, you could do something like this:

puts loose_label(:min => 23, :max => 65, :ticks => 5).inspect

and it would suggest this for your axis labels:

{ :min => 20.0, :max => 70.0, :increment => 10.0 }

To generate the actual labels, use something like the code below. Again, this is cribbed from the original Graphics Gem:

loose = loose_label(:min => 23, :max => 65, :ticks => 5) ymin, ymax = loose[:min], loose[:max] d = loose[:increment] nfrac = -Math.log10(d).floor nfrac = 0 if nfrac < 0 ylabels = [] i = ymin while i < ymax + 0.5 * d ylabels << sprintf("%.#{nfrac}f", i) i += d end

### 2. Add a trailing average

Here’s some code to calculate a trailing average from the previous 7 data points. The initial segment of the trailing average is calculated by averaging the data available up to that point.

trailing = 7 sum = 0.0 tdata = [] data.each_with_index do |i, index| count = nil sum += i if index < trailing count = index + 1 else count = trailing sum -= data[index - trailing] end avg = (sum / count).to_i tdata << avg end

### 3. Use the golden ratio

The human eye finds a certain aspect ratio naturally appealing. Namely, The Golden Ratio. If I have enough space to work with, I want my graphs to use that aspect ratio by default. That’s why I set up my api like this:

GOLDEN = 1.61803399 def chart(options = {}) # calculate width/height width = options.delete(:width) || 300 height = options.delete(:height) || (width / GOLDEN) ...

### 4. Consider using gchartrb

gchartrb is a ruby gem that wraps the Google Charts API. I haven’t used it personally but it looks great.