使用D3.js做複雜資料視覺化(二):處理巢狀資料(nested data with nested selection)

前面有稍微介紹一下d3.js的特性,這邊直接以主題開場,在做複雜資料視覺化的時候,資料常常都是巢狀的,這邊分享一下如何用d3.js處理巢狀資料。

何謂巢狀資料呢?

const data=[{ name: '1', subdata: [1,2,3] },{ name: '2', subdata: [1,2,3] }]

而我們想要根據在data中的array來添加視覺化的element.

要解決這類問題,要先理解D3一些基本運作原理,其中以d3.select的運作和其如何跟data鏈結的最為重要,建議可以閱讀下面的文章:

How Selection Work
D3開發者mike bostock對於selection的解釋
D3 – 深入了解Selection與Data綁定
這篇寫的頗棒,奠基在mike bostrock的說明文章,添加自己的想法。

以及下面這篇stackover的回答:
loop through array of arrays in d3

第一步:
d3核心觀念就是data-driven document,必須先有巢狀的文件結構,來對應資料(你如何設計你的資料結構,會影響到最終的視覺化呈現)。這邊以SVG為例,除了svg,g以外,像是rect,circle,path 都無法直接在其內包含其他要件。

第二步
對應上面的資料範例,可以先建構或試想所對應的文件結構,如下:

螢幕快照 2018-02-23 下午11.15.43

第三步
使用d3.selectiond3.collection來做對應資料和文件的關係,最好直接閱讀d3文檔,因為在不同版本間,d3有許多的改變,如d3v3和d3v4兩個版本間的selection, data join, index以及data manipulation就有很大的差異,如今在2018/02又發布了d3v5,可以想見d3開發和修改的速度有多快。

這邊一步步來用代碼說明解決方式。
首先,先建立一個基於svg要件的select object。

    const svg = d3.select('body')
                  .append('svg')
                  .attr('width', 1000)
                  .attr('height', 1000);

如此,我們便取得了svg物件,其為d3.select物件

const layer1 = svg.selectAll('g.layer1')
                  .data(data)
                  .enter()
                  .append('g')
                  .classed('layer1', true);

由上面的代碼,我們取得layer1物件,其基於在前面建立的svg物件上,使用selectAll來建立第一層的選擇,皆則把資料用data()放進去,append g 要件,根據目前data內的物件數,會產生兩個。

const layer2 = layer1.selectAll('g.layer2')
                     .data((d,i,j)=>{
                        let num = d3.entries(j)[i].key
                        return d.data.map((d)=> [d,num])
                                     })
                     .enter()
                     .append('g')
                     .classed('layer2', true);

我們取得layer2物件,在layer1上接者chain使用selectAll,來將data物件中的subdata陣列暴露出來,並使用d3.entries將parent index往下遞出

layer2.append('rect')
      .attr('x', (d,i)=> i*60)
      .attr('y', (d,i)=> d[1]*60)
      .attr('width', 50)
      .attr('height', 50)
      .attr('fill', 'blue');

如此我們便能在layer2物件中,根據subdata來對應相對的視覺化要件(element)如rect,同時,保有每個資料element相對於data內的index和自己陣列中的index,如此便能做出具有巢狀結構的視覺化。

最後就能創造出如下的效果

螢幕快照 2018-02-23 下午2.54.52

這邊是放在observable的範例歡迎一起切磋巢狀資料的處理!

Observable:互動式D3.js環境,讓你專注於資料視覺化,而不用費心於測試環境

The purpose of visualization is insight, not pictures

Observable(原本為d3.express)是開發出d3的Mike Bostock新的作品,目的是為了創造一個更好的資料探索環境,讓資料分析的重點在於取得insight而非不斷的搭建背後的支持代碼。在Mike Bostock的這篇文章A Better Way to Code裡面仔細談了這個observalbe的目的和特色,其功能如同jupyter之於python,或是rnotebook/shiny之於R,但是其更是為了web而生,因為其更貼近DOM和前端邏輯。

螢幕快照 2018-02-12 下午8.41.32

Observable有幾個特色:
1. Reactivity
– visual output
– animation
– iteraction
– transition
2. Visibility
3. Reusability
4. Portability

目前Observable的教學可以參考下面的順序來閱讀和探索:
1. Five-Minute Introduction
2. Introduction to Notebook
3. Introduction to Code
4. Introduction to HTML
5. Introduction to Promise
6. Introduction to Data
7. Standard Library
8. A Brief Introduction to Viewof
9. Multi-Value Inputs
10. Introductino to Asynchronous iteration

閱讀Mike Bostock文章:What Makes Software Good?

註:此篇為翻譯Mike Bostock的文章:What Makes Software Good?

身為一個開源軟體開發者,我花很多時間在思考如何讓軟體很好。

身為開發者,無可避免地會有永無止盡的求救來自於Stack Overflow, GitHub issues和Slack mentions、email等。幸運地是你也可以看到大家是如何成功應用你所開發的軟體做出超乎你想像的事情,知道自己幫得上忙,也是一個很強大的自我鼓勵,讓你堅持者去開發。

所以你會開始思考一件事?什麼樣的軟體特質,可以讓使用者成功或是失敗?如何改善我的軟體讓更多人可以成功?我可以清楚地說明任何開發原則,或者其實我只是見招拆招?如同知名德國工業設計師Dieter Ram’s的設計十誡

Good design is innovative.
Good design makes a product useful.
Good design is  aesthetic.
Good design makes a product understandable.
Good design is unobtrutive.
Good design is honest.
Good design is long-lasting.
Good design thorough down to the last details.
Good design is environmentally-friendly.
Good design is as little design as possible.

我曾經嘗試談論過所謂的Big Picture Stuff,像是從有趣的小問題著手、辨認和減少工具的偏誤或是利用相關技術以及標準。

雖然Big picture是重要的,可能重要於今天我要談的事情,但實際上我自己感覺Big picture的建議往往不實用,甚至很差。比如說,“盡量簡單,但別太簡單(Make it as simple as possible, but no simpler)”,我們也許都想要事情越簡單越好,但往往我們不知道如何取捨來達到目標。

即使你有一個正確的大目標,往往不能保證你的設計能成功。實踐一個想法和想法本身同樣重要。魔鬼就在細節之中。

假如我沒辦法提供實務性或是可以立即實踐的意見,那麼給少點意見反而更好。一個來自Green&Petre的Cognitive dimensions defines a set of “discussion tools" to “raise the level of discourse" about the usability of “information artifacts" such as code.

Abstraction gradient

Closeness of mapping

Consistency

Diffuseness

Error-proness

Hard mental operations

Hidden dependencies

Premature commitment

Progressive evaluation

Role-expressiveness

Secondary notation

Viscosity

Visibility

上面這十誡聽起來不是很完美,也不是框架。聽起來只是單純對於視覺編成(Visual Programming),且只限於特定情境。我發現很難單純地把一個領域中單一象限的問題直接映射到另一個領域,但還是個很好的起點來思考軟體設計的“Cognitive consequences”。

這邊我並不會去定義一個泛用型的框架,但我有一些觀點可以分享,恰好是一個時機對於D3 4.0做事後的分析(Post-hoc rationalization),除了對於Data joing, scales, layout解離於視覺呈現很滿意外,這邊我將D3分解成一個又一個模組,讓其他人更容易來延展應用,且修改了編程上的API,很擔心這樣的改變對於使用者來說很微妙,但實際上很重要。多數開發者過度專注於functionality, performance, correctness這些較好量化的主觀特性。

的確上面的特質很重要,但是使用不易(poor usability)造成的代價也不小。問問那些在奮力理解令人疑惑代碼模塊的人,以及如找髮絲般除蟲的人。我們必須盡快找到一個評估易用性的方法,且讓程式更好用(We need to get better at evaluating usability sooner, and better at making software usable in the first place.)。

我們無法把代碼放在手上把玩感受它的重量或是質地。代碼的本質是資訊的"人工產物"(information artifact)而非物理性或是圖形。你可以藉由編輯文本或是指令行來跟這些API互動。

這些互動依據標準定義來說,受到人為因子複雜性所影響。所以我們應該評估代碼,就如同評估任何工具一樣,並非只依據他能否達成任務,還要看這些工具是否好用,有效率和令人享受。同時我們還需要考慮到其Affordance(直觀特質)和Aesthetics(美觀)。

編程介面即是所謂的使用者介面。簡單說,編程者也是人。在關於低估設計中人的成分,又可以聽到Ram’s的說法:

Indifference towards people and the reality in which they live is actually the one and only cardinal sin in design.

這同樣的暗示好的文檔說明不代表是壞設計的藉口。也許你可以叫使用者去RTFM,但這是相當蠢(folly)的假設所有使用者能記得文檔中的任何小細節。範例的清晰明白(clarity)、軟體的可拆性(Decipherability)和除蟲性(Debuggability)在真實生活中很重要。

這裡提供七個案例來分享D3 4.0所做的改進和想法,這邊先挑一個來翻譯:

案例一:關於enter.append語法的改進

D3最重要的觀念,便是資料驅動(Data-Driven),這邊的Data代表的是你要視覺化的資料,而document則是你視覺化的呈現,更精確地說就是網頁的Document Object Model(DOM)。


<!DOCTYPE html>
<svg width="960" height="540">
  <g transform="translate(32,270)">
    <text x="0">b</text>
    <text x="32">c</text>
    <text x="64">d</text>
    <text x="96">k</text>
    <text x="128">n</text>
    <text x="160">r</text>
    <text x="192">t</text>
  </g>
</svg>

上面是一個簡單的html文件,裡面包含一個svg模塊,其中相對應的資料就如下:


var data=[
"b",
"c",
"d",
"k",
"n",
"r",
"t"
];

在這序列資料中,要有相對應的文檔模塊(即為),這時就是d3中很重要的關鍵概念:Data-join

螢幕快照 2018-02-09 下午6.14.38

D3中Data-join的設計是給予其資料的序列和相對應要的文件模塊,其會返回三個selection:

  • enter selection: 代表還未添加上去的要用來呈現資料的部件(element)。
  • update selection: 代表本來在文檔中的部件,但需要調整的部分。
  • exit selection:代表在更動後需要被移除的部件。

但data-join並分直接去調整DOM本身,而是去計算所需要的enter, update, exit樣貌。在下面這個簡單的範例中,如何讓文檔隨者資料改動而改動,是D3核心的功能。


var text = g
  .selectAll("text")
  .data(data, key); // JOIN

text.exit() // EXIT
    .remove();

text // UPDATE
    .attr("x", function(d, i) { return i * 32; });

text.enter() // ENTER
  .append("text")
    .attr("x", function(d, i) { return i * 32; }) // 🌶
    .text(function(d) { return d; });

上面這塊代碼,是最初data-join設計的邏輯,先選擇文檔模塊,導入資料(join),去掉更新後被移除的模塊(exit),在更新還存在的部分(update),接者把新加入的資料代表要件更新(enter)。觀察上面的代碼模塊會發現畫有辣椒部分的代碼是重複的:分別是更新x attribute在enter和update的部分。

在D3 2.0的時候,為了解決這個重複的問題,將對於enter selection執行append時,自動複製entering elements到update selection的部分,如此撰寫邏輯可以變成如下面這樣:


var text = g
            .selectAll("text")
            .data(data, key); // JOIN
text.exit() // EXIT
    .remove();

text.enter() // ENTER
    .append("text") // 🌶
    .text(function(d){ return d; });

text // ENTER + UPDATE
    .attr("x", function(d, i){ return i*32; });

雖然這樣的設計減少了代碼重複,但是實際上似乎是造成了使用性降低,第一點,因為其實踐的機制,會讓人不知道原來enter selection,會將append的要件,複製到update selection的部分,因此而產生了前面設計所謂的poort role-expressiveness或是hidden dependency。
第二點,代碼的順序因此變得會影響到執行,最後在D3 4.0版本,取消了這樣的設計,提出了新的selection.merge,來整合enter和update的selection。


var text = g
  .selectAll("text")
  .data(data, key); // JOIN

text.exit() // EXIT
    .remove();

text.enter() // ENTER
  .append("text")
    .text(function(d) { return d; })
  .merge(text) // ENTER + UPDATE
    .attr("x", function(d, i) { return i * 32; });

由上面的過程,可以體會到Rams原則: good design makes a product undersandable,而在cognitive dimension詞彙上,它造成了poor consistency、poor role-expressiveness和hidden dependency.

使用D3.js做複雜資料視覺化(一):簡介

在熟悉完html5/css的基礎後(真的是頗多坑),終於可稍微踏進d3.js的世界,邊搞懂基本js邊搞起來,然後在開發時,還必須稍微把後端的東西淺嚐一番(採取用koa2來做後端,前後從xdk到browserify到node.js下的koa2框架),來支撐前端的設計呈現,這坑走來一定要稍微紀錄一下。

 不得不說D3.js背後的邏輯很美,尤其是在閱讀資料視覺化經典裡的概念後,會發現作者Mike Bostock開發D3所花費的苦心,同時慢慢體會到Javascript背後的想法。

放在軟體世界中,D3.js是建立在前端的領域中javascript的視覺化套件(讓瀏覽器 etc. firefox/chrome/safari 跑起來的程式語言),而前端領域是個變化快速的領域,尤其是javascript

在摸索過程中,可以發現網路上的js範例是由各個不同版本js語法(ES2016, ES2017….)所組成的,隔幾年,基本上語法和函數都有點變化,所以要花一點時間大致理解圍繞D3.js所需要的工具,其目前開發到什麼程度。

D3.js是個快速迭代的Javascript 項目,所有書籍的教學和網路文章教學常常是過時

在學習D3.js的起手,挑了兩本在2017年發表的書來閱讀,分別是Data Visualization with D3 4.x Cookbook(Nick Zhu)D3.js 4.x Data Visualization(Ændrew Rininsland ,‎ Swizec Teller),在實踐的過程,我馬上發現了兩本都是在2017年發行的書,Javascript風格、使用的D3.js語法和目前最新版本的D3.js 4.x版本(話說在前幾天D3.js 5.0發佈了)都不太一樣,部分邏輯也有些出入,所以基本上在使用D3.js的路上,會遇到很多版本語法的坑,很難直接把在網上的代碼直接拿來應用,善用官方解釋和…..動手入坑。

假如再重來一次的話,會推薦直接看這本Interactive Data Visualization for the Web, 2nd Edition,因為其書名沒有D3js,所以一開始用網路搜d3js,其被放在較後面的排序,後來發現其內容撰寫方式,雖然囉唆,但是是作者心法的傳授,蠻推薦的,這本書的作者Scott Murray,本曾經是學術界的教授,所以文章架構比較文鄒鄒,跟單純工程師的寫法不同,會交代前因後果,和放入自己的想法。

D3.js開宗明義就是要做Data-Driven Document,重點不是在華麗的視覺化,而是如何有效地呈現資料

D3.js快速迭代的過程,其中一個目的便是簡化資料處理的過程,以及讓資料結構更貼近視覺化的流程,另外一部分的重點,便是互動性,資料視覺化的重點不是只停留於靜態的視覺呈現,而是讓使用者與資料更有效的互動,所以盡量由“資料特性”出發來思考設計,而不要一下子就用視覺化來想像,否則會創造出很多無用的設計

 想了解Mike Bostock開發D3背後的點點滴滴,可以看一下其在reddit的AMA專欄:連結

螢幕快照 2018-02-15 上午11.02.34

書籍閱讀:The Nature of Technology:What It Is and How It Evolves

The Nature of Technology

這本書The Nature of Technology是由Brian Arthur所寫的,Brian Arthur是知名的經濟學者,其本科是在愛爾蘭讀電子工程學,接者到密西哥大學取得數學碩士,直到博士研究才轉入經濟學,研究複雜理論。Brian Arthur不是多產的作者,這本書是其在2009年所發表的。在閱讀Brian Arthur的原文時,驚訝於他的用字,可以用"laconic"或是"cogent"來形容,精煉地挑選用字,一字一句地在頗析他想表達的東西,閱讀這本書需要非常多時間。

科技區別了人類和其他生物,帶領人類歷經5萬年來到現在這般光景,創造出這個繁榮的社會和世界,這讓作者感到很有趣,因此提出這些問題

What then is this thing of such importance?
What is technology in its nature?, in its deepest essence?
Where does it come from?
And how does it evolve?

這本書便是為了回答上面這些問題而展開的

這本書是奠基於探索科技(technology)如何來如何去而產生的,那到底如何定義這邊要討論的科技?作者做了三個定義

  1. means to fulfill a human purpose
  2. assemblage of practices and components
  3. collections of devices and engineering practices available to a culture

[筆記長大中]

閱讀參考
美國的競爭力-與W.Brian Arthur一席談
豆瓣筆記連接
zywsict部落格 技術的本質:他是如何進化的
知乎:如何評價技術的本質這本書
李繼岡 讀書筆記 技術的本質 圖解
鑿冰而釣 讀技術的本質
zifansky 讀書筆記
追夢子 技術的本質

書籍閱讀:WTF?: What’s the Future and Why It’s Up to Us

WTF_book

這本書WTF:What’s The Future And Why It’s Up To Us是由Tim O’Reailly所寫,在2017年10月份出版,目前還沒有中文版,作者本身算是歷經網路世代的消長,直到目前都是IT領域重要的領袖之一。

這本書分成四大部分

第一部分: Using the Right Maps

第二部分: Platform Thinking

第三部分: A World Ruled by Algorithm

第四部分:Up to Us

如何思考未來,用地圖

第一部分作者主要想分享關於這瞬息外變的世界,我們必須如何面對。其中一個關鍵思維就是要畫一張自己關於未來的地圖,把所以對於未來的想像用地圖般的脈絡來撰寫。