使用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的範例歡迎一起切磋巢狀資料的處理!

發表留言