本節書摘來自異步社群《javascript資料可視化程式設計》一書中的第1章,第1.1節,作者: 【美】stephen a.thomas 譯者: 翟東方 , 張超 , 劉暢 責編: 陳冀康,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。
javascript資料可視化程式設計
在很多人的印象中,資料可視化圖形是一些非常酷炫複雜、充滿科幻設計感的圖形。這種看法其實存在誤區。實際上,建立一個有效的資料可視化模型并不需要特别深厚的設計功底和複雜的程式設計技巧,如果你一直牢記着資料可視化的目的是幫助人們更好地了解資料,那麼你就會認同,在進行資料可視化的過程中最需要注意的,恰恰是“簡單”二字。那些看似簡單基礎、随處可見的圖表及其所傳達的資訊,往往最容易為人們所了解和消化。
因為使用者已經熟悉了各式各樣的正常圖表,如柱狀圖、折線圖、坐标圖等。使用者很容易了解這類圖表的形式和資料代表什麼意思,是以他們可以毫不費力地從圖表中提煉出有用的資訊。如果想讓使用者迅速明白你的資料,我建議最好還是采用一個簡單、靜态的圖表。這樣,你可以省下大量的時間,使用者也可以花更少的精力來了解你的意圖。
有許多高品質的工具和js庫可以幫助你,下面從制作一個簡單的例子開始,讓你踏上資料可視化設計之路。使用這些工具,你可以避免重複造輪子,還能找到很多資料。本書随後會介紹幾個類似的工具,但就這一章而言,我們将會使用flotr2 這個庫。使用flotr2,可以很容易在任意網頁上添加标準柱狀圖、線圖和餅圖,并且還支援一些不是那麼常見的普通圖表類型。随後,我們會結合執行個體了解flotr2到底能做些什麼。你将學到以下内容。
如何建立一個基本的柱狀圖。
如何用折線圖繪制連續資料。
如何用餅圖強調百分比。
如何用散列圖繪制二維資料。
如何用氣泡圖展示二維資料的量的對比。
如何用雷達圖顯示多元資料。
如果你不确定什麼類型的圖表能展現你的資料,那你首先應該考慮是否可以做柱狀圖。柱狀圖這種形式,我們已經司空見慣了,但是它真的是一種非常有效的圖表形式。柱狀圖通常可以表現資料的變化過程,或者表示多個資料之間的差異。下面我們從建立柱狀圖入手來開始我們的練習。
1.1.1 第1步 引入所需的javascript代碼
我們使用flotr2這個javascript庫來建立圖表。首先,我們需要把flotr2這個javascript庫引入到我們的網頁中。因為flotr2現在還沒有特别好的cdn源,是以你需要下載下傳一份拷貝,并放到自己的伺服器上。我們這裡使用這個庫的壓縮版本,即flotr2.min.js來做我們的代碼依賴。
使用flotr2之前,我們不需要引入其他的javascript庫(比如jquery),但是flotr2必須依賴于html5的canvas元素的支援。當然,主流的現代浏覽器(safari、chrome和firefox)以及ie9以上都已經支援canvas屬性了,但是,現在我們仍然有數百萬的使用者在使用ie8 (甚至更早的浏覽器)。為了支援這些使用者,我們可以在頁面中引入一個額外的庫(excanvas.min.js),這樣,這些老浏覽器也能支援canvas元素了。
現在,我們先寫下下面的一段html文檔結構。
因為現代浏覽器不需要引入excanvas.min.js,是以我們需要在代碼1處将引入的script代碼做一個html注釋處理,以保證隻有ie8及ie8更早的浏覽器版本會加載它。此外需要注意的是,我們的庫檔案需要在html文檔的最後引入,以保證浏覽器在加載javascript檔案之前可以優先渲染dom樹。
1.1.2 第2步 建立一個用來包含圖表的
元素
在引入flotr2的javascript檔案之後,我們需要在html文檔中建立
元素來包裹住這個圖表。flotr2要求這個
元素必須指定它的寬高,圖表才能夠被建立起來。我們可以在css樣式表中設定元素的width和height屬性,或者在
标簽上通過内聯樣式來定義,隻要保證css代碼能夠生效即可。下面的例子采用内聯方法指定了
的css樣式。
也許你發現了,我們給這個
指定了一個明确的id: “chart”,以便之後我們可以通過這個id來引用此元素。
這樣,我們就擁有了一個簡單的代碼架構。在本書的第1章中,你即将學到的其他圖表的制作方法,也都是基于這個簡單的代碼架構來制作的。
1.1.3 第3步 定義資料
有了代碼架構,我們就可以研究怎麼顯示資料了。在本例中,我會試圖去統計在過去7年的英超聯賽中曼城隊的獲勝場次。當然,你想把資料換成其他的也是可以的。你可以把資料直接寫在javascript中(像下面的例子一樣),或者用其他方法(比如向伺服器發ajax請求擷取),這兩種方法都行。
現在,我們建立了三維數組,然後,我們來研究它。
在使用flotr2建立的圖表中,每一個獨立的資料都是通過數組中x和y這2個值來唯一确定的。在我們的例子中,我們用x表示年,y表示獲勝場次。接下來,我們把若幹個這樣的x、y的組合使用一個外層數組進行嵌套,這個用來嵌套的數組,我們稱為序列。接着,我們在這個序列的外面又嵌套了一個外層數組,以便将來我們可以在其中存儲多個序列。但是現在我們僅僅需要一個序列就夠了。
關于數組每一層的定義,記住下面3條即可:
數組第一層:每個獨立的資料自身是一個數組,包含x和y兩個值;
數組第二層:若幹個獨立資料在一起構成數組,稱為序列;
數組第三層:若幹個序列構成供flotr2渲染圖表使用的完整資料,形式也是數組。
1.1.4 第4步 繪制圖表
以上我們就把我們需要的資料準備好了。如下所示,通過簡單調用flotr2庫,我們的第一個圖表就要建立好了。
上面代碼的關鍵點在于window.onload,因為我們需要在文檔加載完成後調用函數。 window.onload事件觸發後,我們執行flotr.draw這個函數,并傳3個參數給它。這3個參數包括:包含圖表的html元素本身,剛才定義的圖表資料和一些可配置的圖表選項。在本例中,我們設定的選項的意思是告訴flotr2建立一個柱狀圖。
因為flotr2不需要依賴jquery,我們在這個例子中沒有用jquery的$等操作符來進行操作。如果你的頁面已經包含了jquery,也可以使用jquery方法來改寫上面的代碼。
圖1-1所示就是你在網頁中看到的圖表。

現在你有了一個柱狀圖,但是這個圖表還有很多改善空間。讓我們逐漸添加一些選項讓這個圖表看起來更好。
1.1.5 第5步 改進縱軸
圖1-1中,最明顯的問題是縱軸的刻度。 flotr2預設将資料中最大值和最小值自動設為坐标軸的取值範圍。在曼城的年獲勝場次的統計中,最小值出現在2007年,其隻獲得了11場勝利,是以,flotr2非常忠實地将縱軸的最小值設定成了11。可是通常在柱狀圖中,最好是将縱軸的最小值設定為0。如果不是0的話,使用者會在對圖表的了解上産生迷惑。比方說有一個人隻是掃了一眼圖1-1的圖表,就得出結論說曼城在2007年一場比賽都沒有赢。這種情況顯然是需要避免的。
圖1-1中,縱軸的格式也是一個問題。因為flotr2會預設精确到小數點後一位,是以會在所有标注上帶一個多餘的“.0”。我們可以通過設定一些縱軸的選項來修複這兩個問題。
flotr.draw函數通過min屬性來設定縱軸的最小值,并且通過tickdecimals屬性告訴flotr2在标注中要展示的小數精度。在我們例子中我們不想要小數位,是以将這個值設為0。
正如你在圖1-2中看到的,在配置了這些選項後,縱軸的起始值被設定為0,所有數字格式變為整數,明顯改善了縱軸的效果。
1.1.6 第6步 改進橫軸
和縱軸類似,在flotr2中,橫軸的标注也被預設為擁有1位小數的數字。因為我們圖表中,橫軸資料的機關是“年”,我們此處可以像設定縱軸一樣,也通過tickdecimals屬性将橫軸的小數精度設定為0。但是這種做法并不通用。如果當x的值不是數字類型(比如隊名)時,這種解決方案就行不通了。為了能适應更普遍的情況,我們首先需要改變下資料結構,建立一個新的數組years,在這個數組中,每一個年份有一個索引數字配對。同時,我們修改之前的wins數組,将原來的年份使用對應的索引數字替代,這樣在兩個數組之間就建立起了查詢
關系。
正如你看到的,我們在wins數組中,使用簡單的0、 1、 2等數字替換了x值的實際年份。然後,我們在新定義的years數組中将這些整數映射到對應的字元串上。我們這裡的字元串映射為年份數字,如果需要,也可以以任何字元串代替。
另外一個問題是兩個柱體之間缺乏間距。在預設情況下,每一個柱體是平均配置設定整個橫軸的的長度的,但是會顯得過于擁擠。我們可以用barwidth屬性進行調整。把這個屬性的值設定到0.5,這樣每個柱體就隻占據原空間的一半了。
解決上面兩個問題的具體配置見下面的代碼。
注意,在代碼1處,我們對x軸使用了ticks屬性,這是用來告訴flotr2把x軸的标注通過years數組和x值進行比對。現在我們可以在頁面上看到如圖1-3展示的圖表。 x軸标注的是對應的年份,柱體之間有間距,這些都改善了圖表的易讀性。
1.1.7 第7步 調整樣式
現在,圖表的功能性和可讀性的調整已經完成,接下來,我們可以花一些精力把圖表做得更炫一些。我們打算為圖表添加标題,去掉不需要的網格線,再調整一下柱體的顔色。
如圖1-4中所見,我們現在有了一個曼城粉絲可以引以為豪的柱狀圖了。
對于任何中等大小的資料集,标準的柱狀圖常常是最有效的可視化圖表。使用者對這種形式已經熟悉了,是以他們不必花費額外的努力去了解形式。這些柱體在視覺上和背景對比強烈,并且通過高度不同來展現數值之間的差異,是以使用者很容易抓住突出的資料。
1.1.8 第8步 多彩的柱體色彩
到目前為止,盡管我們圖表的顔色單一,但是,我們正是利用了單一顔色,展示了同一類型的值(曼城勝利場次)在不同時間的變化,是以表義是很清晰的。
柱狀圖除了可以表示連續的資料發展趨勢外,也能有效地進行多個資料之間的對比。舉個例子,假設我們想要展示一年中多個球隊的總勝利場次。這種情況下,每個球隊的柱體就需要用不同顔色來代表。讓我們再去把圖表加工一下。
首先我們需要稍微調整一下資料結構。
之前我們隻在圖表中展示了一組資料,即一個球隊逐年的獲勝場次統計,數組名稱為wins。現在,我們想讓圖表顯示多個球隊同一年的資料,并且為每個球隊提供一個獨立的顔色,是以,我們需要定義一個新數組wins2。下面的例子中可以看到wins和wins2兩個數組之間的對比。注意這裡的數組結構變化。同樣,我們把每個柱體原來的标注從“年份”替換成了球隊名稱的縮寫。
組織好我們的資料後,接下來我們就可以讓flotr2繪制圖表了。圖表繪制完成之後,我們可以看到,除了每個球隊的顔色不同以外,其他的一切都和之前的圖表在形式上保持一緻。
在圖1-5中可以看到,通過一些小的調整,我們的柱狀圖就轉而表現了另外一種不同類型的資料。我們之前使用這個圖表表現了一支球隊在不同時間的資料趨勢,現在,我們同樣可以用柱狀圖表示多支球隊在同一時間的資料對比。由此可見柱狀圖雖然形式簡單,但是資料表現力極強。
為了友善說明,本書在講解時,給大家看的都是代碼片段。
1.1.9 第9步 flotr2可能會出現的一些“bug”及處理方案
如果你正在使用flotr2為一個大型網站建立複雜的内容,你可能會遇到一些讨厭的“bug”。我這裡在“bug”上加了一個引号,是因為flotr2處理的方式雖然是正确的,但是顯示效果卻未必總是正确的。在建構圖表的過程中,flotr2會建立一個虛拟的html元素,以便用來計算尺寸大小。 flotr2為了讓這些元素在頁面中不可見,會通過調整這些元素的css定位,來讓它們從螢幕的可視範圍内消失。但是,有時flotr2的處理方式會出現一些問題。在flotr2.js的2281行中,有下面一段css定義:
flotr2打算把這些虛拟的html元素放置到距離浏覽器頂部10 000像素的位置。然而,css的絕對定位是基于父層的相對定位基礎上的,是以如果你的文檔超過了10 000像素,這些html元素所包含的文本和資料就會讓你的頁面亂掉。在flotr2的代碼官方改進這個問題之前,有2種方法可以解決這個bug。
一種方式是你自己修改代碼。 flotr2是開源的,是以你可以免費下載下傳完整的源碼來進行适當的修改。這樣隻要簡單地修改虛拟html元素的定位,将它改到除上方之外其他很遠的位置(左邊或右邊)就可以了。
如果你是個有強迫症的處女座,覺得改變庫的源碼讓你不舒服,另一種方式是你自己找到并隐藏這些虛拟元素。這裡需要注意的是,在你最後一次調用flotr.draw()之後再去做這些。在最新版本的jquery中,可以用下面的代碼來消除這些無關緊要的元素。