Gruffでグラフ!格好いいグラフを簡単に生成

GruffはRmagickを使用するRuby用グラフライブラリ。RMagickが必要。
rails案件で使えるか調査中。(以下の記述は全てwindowsXP SP3環境)
格好いい棒グラフや円グラフが手軽に書けます。
公式:http://nubyonrails.com/pages/gruff
ドキュメント:http://topfunky.com/clients/rails/gruff/doc/


1.gemのダウンロード
1)rubyforge-0.4.5.gemのダウンロード
 http://rubyforge.org/frs/?group_id=1024&release_id=20087
2)hoe-1.5.1.gemのダウンロード
 http://rubyforge.org/frs/?group_id=1513&release_id=19876
3)gruff-0.3.1.gemのダウンロード
 http://rubyforge.org/frs/?group_id=1044&release_id=20131


2.インストール
gem install gruff-0.3.1.gem --local
#通常環境ではgem install gruffでOK。


3.テスト
http://tam.qmix.org/wiki/RubyGruff.htmlを参考にテストしてみる。
testGruff.rbとしてローカルに保存し実行すると同じディレクトリに画像が生成される。
#tam.qmix.orgのソースではrequire_gem 'gruff'がエラーになるのでrequireに直した。

require 'rubygems'
require 'gruff'

g = Gruff::Line.new 500
g.title = "My Graph" 

g.data("Apples", [1, 2, 3, 4, 4, 3])
g.data("Oranges", [4, 8, 7, 9, 8, 9])
g.data("Watermelon", [2, 3, 1, 5, 6, 8])
g.data("Peaches", [9, 9, 10, 8, 7, 9])

g.labels = {0 => '2003', 2 => '2004', 4 => '2005'}

g.write('my_fruity_graph.png')

実例

■棒グラフ

require 'rubygems'
require 'gruff'

  #.new(横幅)で4対3の画像を生成。
 #.new('横幅x縦幅')で任意サイズの画像を生成する。
  g = Gruff::StackedBar.new(600)

  #タイトル。linuxの場合UTF8でないと化けるという情報あり。
  g.title = "年間販売実績(例)"
  
 #タイトルのフォントサイズ
  g.title_font_size =36
  
  #TTFのフォントをフルパスで指定。日本語フォントを指定する。
  g.font = "C:/WINDOWS/Fonts/MSGOTHIC.TTC"
  
  #目盛りの刻みを指定する。指定しないと自動計算して
 #切りの悪い数値になってしまうので注意。
  g.y_axis_increment = 20
  
  #グラフの最大値。値が指定した最大値を超えた場合、
 #目盛りを適当に増やしてくれる。
 #最大値のデフォルト程度のイメージ。
  g.maximum_value = 100
  
  #グラフの最小値。指定しないと自動計算して適当な数値から
 #始まってしまうので基本的に0を指定する。
  g.minimum_value = 0
  
  #data(name, [値1,値2,値3,...],'RPG値')でデータを代入する。
  g.data '販売台数', [20, 23, 70, 8, 150, 20, 30, 28, 55, 62, 33,15]

  #凡例を表示しない
  g.hide_legend = false
  
  #タイトルを表示しない
  g.hide_title = false
  
  #補助線を表示しない(あまり使わないはず)
  g.hide_line_markers = false

  #列(水平側)のラベルを指定する。0から始まることに注意。
  g.labels = {0 => '08/04', 1 => '08/05', 2 => '08/06', 3 => '08/07',
   4 => '08/08', 5 => '08/09', 6 => '08/10', 7 => '08/11', 8 => '08/12',
   9 => '09/01', 10 => '09/02', 11 => '09/03'}
   
  #ラベル、目盛り等補助情報のフォントサイズ。デフォルト20pt
  g.marker_font_size = 16
  
  #write(ファイルパス)で画像をファイル出力する。
  g.write("StackedBar.png")

  #g.writeの代わりにg.to_blob(fileformat='PNG') で
 #バイナリ出力すればCGIで使える。

■円グラフ
ポイントは.zero_degree = -90で0を頂点にすることと.sort = false(RDOCには書いてない)で値をソートしないようにすること。ソートしないことで値が大きい順に左回りに表示されることを避けられる。

require 'rubygems'
require 'gruff'

  #.new(横幅)で4対3の画像を生成。
 #.new('横幅x縦幅')で任意サイズの画像を生成する。
  g = Gruff::Pie.new(600)
  
  #タイトル。linuxの場合UTF8でないと化けるという情報あり。
  g.title = "勢力分布(例)"
  
  #タイトルのフォントサイズ
  g.title_font_size =36
  
  #TTFのフォントをフルパスで指定。日本語フォントを指定する。
  g.font = "C:/WINDOWS/Fonts/MSGOTHIC.TTC"
  
  #data(name, [値1,値2,値3,...],'RPG値')でデータを代入する。
  g.data 'とくがわ', [40]
  g.data 'だて', [20]
  g.data 'とよとみ', [30]
  g.data 'しまづ', [10]
  
  #0を頂点にする。デフォルトだと右90度から始まる。
  g.zero_degree = -90
  
  #値をソートしない(デフォルトはtrue)
  g.sort = false

  #凡例を表示しない
  g.hide_legend = false
  
  #タイトルを表示しない
  g.hide_title = false
  
  #補助線を表示しない(あまり使わないはず)
  g.hide_line_markers = false
   
  #ラベル、目盛り等補助情報のフォントサイズ。デフォルト20pt
  g.marker_font_size = 20
  
  #write(ファイルパス)で画像をファイル出力する。
  g.write("Pie.png")

用語メモ

  • 棒グラフ:Stacked Graph
  • 円グラフ:Pie Graph
  • 折れ線グラフ:Wide Graph

■Gruff::Baseの属性

  • x_axis_label:x軸ラベル(水平側)
  • y_axis_label:y軸ラベル(垂直側)
  • y_axis_increment:目盛りの増幅を任意指定
  • colors:線やバーで使用する色の一覧。
  • font:TTF(TrueTypeFont)へのフルパスを指定。RMagickのビルドによっては動かない?(readのみ)

Works best if you provide the full path to the TTF font file. RMagick must be built with the Freetype libraries for this to work properly.

  • font_color:文字色
  • labels:ハッシュで列名を指定する。すべての列に指定しなくても良い。

Example: 0 => 2005, 3 => 2006, 5 => 2007, 7 => 2008

  • legend_box_size:凡例の大きさ。デフォルトは20px
  • legend_font_size:凡例のフォントサイズ指定。
  • marker_color:補助線の色
  • marker_count:補助線の数。(刻みのことかグラフ全体の数か不明)→刻みのことではない。補助線全体の数。
  • maximum_value:目盛りの最大値指定
  • minimum_value:目盛りの最小値指定
  • no_data_message:値が取得できない場合のメッセージ。20文字以内。デフォルトはNoData。
  • title:グラフの表題。
  • title_font_size:グラフの表題のフォントサイズ指定。

▼円グラフ用のオプション

  • zero_degree:グラフの回転。-90を指定して0を頂点にする。
  • sort:デフォルトでは左周りに大きい順でソートされてしまう。sort=falseで解除できる。(データを自分でソートせよ)


■Gruff::Baseのメソッド

  • add_color(colorname):属性colorsへ色を追加する。
    ex.add_color('#c0e9d3')
  • data(name, data_points=[], color=nil) :データを代入する。
    ex.data("Bart S.", [95, 45, 78, 89, 88, 76], '#ffcc00')
  • font=(font_path) :フォントをフルパス指定する。
  • theme=(options) :テーマのオプションを細かく指定する。既存のテーマセット、たとえばtheme_keynote() を使用する場合は不要。

はまり所

目盛りの刻みを指定するy_axis_incrementオプションが効かないようだ。
以下のソースを実行するとエラーになる。

require 'rubygems'
require 'gruff'

  g = Gruff::Line.new
  g.title = "ライングラフ"
  g.font = "C:/WINDOWS/Fonts/MSGOTHIC.TTC"
  g.y_axis_increment = 20
  g.maximum_value = 100
  g.minimum_value = 0
  g.data 'Fries', [20, 23, 19, 8]
  g.data 'Hamburgers', [50, 19, 150, 29]
  g.write("LineGraph.png")


こんな感じ。wrong number of arguments (1 for 0) (ArgumentError)
と言われる。

C:\>LineGraph.rb
c:/ruby/lib/ruby/gems/1.8/gems/gruff-0.3.1/lib/gruff/
 base.rb:661:in `normalize':
 wrong number of arguments (1 for 0) (ArgumentError)
        from c:/ruby/lib/ruby/gems/1.8/gems/gruff-0.3.1/
 lib/gruff/base.rb:661:in `draw_line_markers'
        from c:/ruby/lib/ruby/gems/1.8/gems/gruff-0.3.1/
 lib/gruff/base.rb:513:in `setup_drawing'
        from c:/ruby/lib/ruby/gems/1.8/gems/gruff-0.3.1/
 lib/gruff/base.rb:485:in `draw'
        from c:/ruby/lib/ruby/gems/1.8/gems/gruff-0.3.1/
 lib/gruff/line.rb:49:in `draw'
        from c:/ruby/lib/ruby/gems/1.8/gems/gruff-0.3.1/
 lib/gruff/base.rb:464:in `write'
        from C:/resorce/test/LineGraph.rb:12


C:\ruby\lib\ruby\gems\1.8\gems\gruff-0.3.1\lib\gruff\base.rbを読むとnormalize(true)がこけているようだが、このメソッドは引数1つであっているので引数の数ではエラーにならないはずなんだが。メソッドnormalize(force=false)のコメントを読むと「# Make copy of data with values scaled between 0-100」とありデータを100%の中に押し込む処理のようだったのでコメントアウトした。(違っていたらご指摘ください)

    # Draws horizontal background lines and labels
    def draw_line_markers
      return if @hide_line_markers
      
      @d = @d.stroke_antialias false
            
      if @y_axis_increment.nil?
        # Try to use a number of horizontal lines 
        # that will come out even.
        #
        # TODO Do the same for larger numbers...
        # 100, 75, 50, 25
        if @marker_count.nil?
          (3..7).each do |lines|
            if @spread % lines == 0.0
                        @marker_count = lines
              break
            end
          end
          @marker_count ||= 4
        end
        @increment = (@spread > 0) ? significant
    (@spread / @marker_count) : 1
      else
        # TODO Make this work for negative values
        @maximum_value = [@maximum_value.ceil, @y_axis_increment].max
        @minimum_value = @minimum_value.floor
        calculate_spread
        #normalize(true)
        
        @marker_count = (@spread / @y_axis_increment).to_i
        @increment = @y_axis_increment
      end
      @increment_scaled = @graph_height.to_f / (@spread / @increment)