2013年9月7日 星期六

使用 Chart 和 Chart-cairo 繪製統計圖

很多時候我們會想要用圖來呈現我們的資料,畢竟我們對數字的敏銳度不是很高。提到統計繪圖,大部分人會想到 gnuplot 或是 R。對於程式設計人員來說,這代表他們必須寫程式來產生讓 R 或是 gnuplot 繪圖的程式碼,然後再利用作業系統的程式呼叫方法(fork + exec* , CreateProcess)來執行這段程式碼。這樣做不只麻煩(呼叫並管理子程式在某些書甚至會佔去一整章),而且還會犧牲掉不少效能。

Haskell 跟許多程式語言一樣,有人替它寫了圖表繪製程式庫(chart library) Chart。在它的 wiki 裡面可以看到幾個簡單的例子,而且畫出來的圖也挺好看的,更支援 svg, png 等格式的輸出(藉著 cairo 或 diagrams),簡直意圖誘惑人使用。

那些例子裡面沒有說明的部份,包括了中文字使用,字型調整這兩項。所以,我以這兩個項目為主軸,隨便弄了簡單的資料,畫出了相當精簡的圖。主要的程式碼請參考這裡

圖會出來會變成

(我應該放點心思在配色上的..應該是,請看在我肝臟的份上原諒我吧Orz)

使用中文字的關鍵在於設定正確的字型。如果設定的字型沒有中文字,則在應該是中文字的地方會出現方塊圖樣,暗示在該字型中找不到中文字。

Chart 程式庫最特別的地方在於它利用了 lens (不妨 google "Control.Lens" 來找相關的資料) 來讓設定圖表變得(相對的)比較容易。只是對於 lens 我還是第一次使用,所以程式碼裡面難免會有些不漂亮的寫法。如果有幸某大師瞧見了拙作,歡迎不吝指教!

2013年9月6日 星期五

使用 cabal (1.18) 安裝 cairo (改0.12.4.1 beta) 的 haskell binding

Cabal 1.18, 帶有萬眾矚目沙盒功能的 cabal 終於降臨這個世界了!從聽說這版本會導入沙盒功能開始就一直期待到現在,終於出了!或許我們可以準備 cabal-dev 的退休金,讓他回鄉下養老了。關於沙盒功能,有篇文章做了不錯的介紹,歡迎參考。

上面那個其實算是題外話。這裡要紀錄如何修改,讓 cairo-0.12.4.1 (目前,20130906,在 gtk2hs 程式碼倉庫上最新的版本)可以被 cabal 1.18 建置。

首先,要從 gtk2hs 的原始碼倉庫取得最新版的 cairo 原始檔,接著照下面修改:

diff -rN -u old-gtk2hs/cairo/cairo.cabal new-gtk2hs/cairo/cairo.cabal
--- old-gtk2hs/cairo/cairo.cabal        2013-09-06 21:17:11.559949736 +0800
+++ new-gtk2hs/cairo/cairo.cabal        2013-09-06 21:17:11.798946617 +0800
@@ -1,5 +1,5 @@
 Name:           cairo
-Version:        0.12.4.1
+Version:        0.12.4.1.1
 License:        BSD3
 License-file:   COPYRIGHT
 Copyright:      (c) 2001-2010 The Gtk2Hs Team, (c) Paolo Martini 2005, (c) Abraham Egnor 2003, 2004, (c) Aetion Technologies LLC 2004
diff -rN -u old-gtk2hs/cairo/Gtk2HsSetup.hs new-gtk2hs/cairo/Gtk2HsSetup.hs
--- old-gtk2hs/cairo/Gtk2HsSetup.hs     2013-09-06 21:17:11.557949762 +0800
+++ new-gtk2hs/cairo/Gtk2HsSetup.hs     2013-09-06 21:17:11.797946630 +0800
@@ -17,6 +17,7 @@
 
 import Distribution.Simple
 import Distribution.Simple.PreProcess
+import Distribution.Simple.Program.Find (defaultProgramSearchPath)
 import Distribution.InstalledPackageInfo ( importDirs,
                                            showInstalledPackageInfo,
                                            libraryDirs,
@@ -441,7 +442,7 @@
 checkGtk2hsBuildtools :: [Program] -> IO ()
 checkGtk2hsBuildtools programs = do
   programInfos <- mapM (\ prog -> do
-                         location <- programFindLocation prog normal
+                         location <- programFindLocation prog normal defaultProgramSearchPath
                          return (programName prog, location)
                       ) programs
   let printError name = do
diff -rN -u old-gtk2hs/cairo/SetupWrapper.hs new-gtk2hs/cairo/SetupWrapper.hs
--- old-gtk2hs/cairo/SetupWrapper.hs    2013-09-06 21:17:11.558949749 +0800
+++ new-gtk2hs/cairo/SetupWrapper.hs    2013-09-06 21:17:11.797946630 +0800
@@ -6,7 +6,7 @@
 
 import Distribution.Package
 import Distribution.Compiler
-import Distribution.Simple.Utils
+import Distribution.Simple.Utils hiding (moreRecentFile)
 import Distribution.Simple.Program
 import Distribution.Simple.Compiler
 import Distribution.Simple.BuildPaths (exeExtension)

接著

cd /path/to/your/designated/sandbox
cabal sandbox init
cabal install path/to/patched/cairo

注意事項

如果使用

cabal --with-ghc=your-ghc install
的話有機會導致失敗:在建立安裝程式碼的時候,cabal 似乎會使用系統預設的 ghc ,也就是在系統路徑下可以找到的那個 ghc。如果 your-ghc 的版本和 ghc 差太多,而且各自的預設 haskell 套件庫裡面的 Cabal 版本不一致,就會導致很嚴重且難以追蹤的錯誤。

這樣子弄完後應該就可以使用了。為了弄 Chart 以及 Chart-cairo (Chart-diagrams 好像很悲劇的不能畫出中文)結果被 cairo 卡了好幾個小時...