Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 婷婷九月丁香,国产成人精品本亚洲,久久青草18免费观看网站

          整合營銷服務商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          0基礎學爬蟲爬蟲基礎之網頁基本結構

          0基礎學爬蟲爬蟲基礎之網頁基本結構

          哥爬蟲

          大數(shù)據(jù)時代,各行各業(yè)對數(shù)據(jù)采集的需求日益增多,網絡爬蟲的運用也更為廣泛,越來越多的人開始學習網絡爬蟲這項技術,K哥爬蟲此前已經推出不少爬蟲進階、逆向相關文章,為實現(xiàn)從易到難全方位覆蓋,特設【0基礎學爬蟲】專欄,幫助小白快速入門爬蟲,本期為網頁基本結構介紹。

          網頁概述

          網頁是互聯(lián)網應用的一種形態(tài),是組成網站的基本元素。它是一個包含HTML標簽的純文本文件,可以存放在世界上任意一臺計算機中。網頁可以被看作為承載各種網站應用和信息的容器,網站的可視化信息都通過網頁來進行展示,為網站用戶提供一個友好的界面。

          表面上,網頁的組成可以分為文字、圖片、音頻、視頻、超鏈接等元素構成,這些元素是用戶能夠直接看到的。但在本質上,網頁的組成分為三部分:

          • HTML

          HTML的全稱為超文本標記語言,是一種標記語言,它是標準通用標記語言下的一個應用,也是一種規(guī)范,一種標準,它通過標記符號來標記要顯示的網頁中的各個部分。HTML文本是由HTML命令組成的描述性文本,HTML命令可以說明文字、圖片、音頻、視頻、超鏈接等,用戶在網頁上看到的各種元素都是通過HTML文本來實現(xiàn)的。

          • CSS

          網頁的基本元素是通過HTML來實現(xiàn)的,但是HTML只能實現(xiàn)最基本的網頁樣式。隨著HTML的發(fā)展,為了滿足網頁開發(fā)者的需求,CSS便孕育而生。

          CSS全稱為層疊樣式表。它為HTML語言提供了一種樣式描述,定義了元素的顯示方式。提供了豐富的樣式定義以及設置文本和背景屬性的能力。CSS可以將所有的樣式聲明統(tǒng)一存放,進行統(tǒng)一管理。在CSS中,一個文件的樣式可以從其他的樣式表中繼承。讀者在有些地方可以使用他自己更喜歡的樣式,在其他地方則繼承或“層疊”作者的樣式。這種層疊的方式使作者和讀者都可以靈活地加入自己的設計,混合每個人的愛好。

          • JavaScript

          JavaScript(JS)是一種面向對象的解釋型腳本語言,它具有簡單、動態(tài)、跨平臺的特點。它被廣泛應用與Web開發(fā)中,幫助開發(fā)者構建可拓展的交互式Web應用。JavaScript由三部分組成:

          • ECMAScript,描述了JS語言的基本語法與基本對象。
          • 文檔對象模型(DOM),提供了處理網頁結構內容的方法與接口。
          • 瀏覽器對象模型(BOM),提供了獨立于內容而與瀏覽器窗口進行交互的方法與接口。

          基本結構

          網頁的基本結構大致可以分為四部分:Doctype聲明、html元素、head元素和body元素。

          • Doctype: 用來聲明文檔類型。它在HTML中的作用就是告訴瀏覽器以何種方式渲染頁面。
          • html: html元素是網頁的根元素,網頁中的內容都會包含在html標簽中。
          • head: head是所有頭部元素的容器。被用來引用腳本文件、指示樣式表存于何處。
          • body: body是網頁的主體元素,用戶在網頁上瀏覽到的信息主要都存在于body之中,它包含網頁文檔的所有內容,如段落,列表,鏈接,圖像,表格等。

          元素、標簽與屬性

          元素(Element)是網頁的一部分,是構成網頁的基本單位,實際上一個網頁就是由多個元素構成的的文本文件。 標簽(Tag)的作用就是用來定義元素。大多數(shù)的標簽都是成對使用的,它存在一個開始標簽與一個結尾標簽,開始與結尾標簽中間包含該元素的文本信息。

          <div>這是一個div標簽</div>
          <p>這是一個p標簽</p>

          也有少部分的標簽不成對。

          <input>
          <img>
          <hr>
          ...

          屬性(attribute)主要是用來為標簽添加額外的信息,屬性的定義一般在開始標簽中,以鍵值對的形式出現(xiàn)(name="value" ),屬性的值應始終包括在引號內,屬性和屬性值對大小寫不敏感,但是推薦使用小寫的屬性與屬性值。一個標簽可以擁有多個屬性,也可以沒有屬性,開發(fā)者沒有為標簽定義屬性的話則會使用默認屬性。

          <a href="https://www.kuaidaili.com/">這是一個a標簽,href是我的屬性。</a>

          屬性在HTML中被分為兩種:通用屬性和專用屬性。 通用屬性適用于大部分或所有標簽之中,如:

          • class:規(guī)定元素的類名
          • id:規(guī)定元素的唯一id
          • style:規(guī)定元素的行內樣式
          • title:規(guī)定元素的額外信息

          專用屬性適用于小部分標簽或特定標簽,如:

          • href:主要用于a標簽與link標簽中,規(guī)定元素的超鏈接地址
          • alt:主要用于img標簽與area標簽中,規(guī)定在圖像無法顯示時的替代文本

          文檔對象模型DOM

          DOM全稱即文檔對象模型,是W3C制定的標準接口規(guī)范,是一種處理HTML和XML文件的標準API。DOM將HTML文本作為一個樹形結構,DOM樹的每個結點都表示了一個HTML標簽或HTML標簽內的文本項,它將網頁與腳本或編程語言連接起來。

          通過這個DOM樹,開發(fā)者可以通過JavaScript來創(chuàng)建動態(tài)HTML,開發(fā)者借助JavaScript可以實現(xiàn):

          • 動態(tài)改變頁面中的所有HTML元素
          • 改變頁面中的所有HTML屬性
          • 改變頁面中的所有CSS樣式
          • 刪除已有的HTML元素和屬性
          • 添加新的HTML元素和屬性
          • 對頁面中所有已有的HTML事件作出反應
          • 在頁面中創(chuàng)建新的HTML事件

          DOM提供了一系列API來實現(xiàn)這些操作。

          • document.createElement:創(chuàng)建元素節(jié)點。 document.write:向文檔寫入內容。 element.innerHTML:向標簽元素中添加內容。
          • element.removeChild:從DOM中刪除一個子節(jié)點并返回刪除的節(jié)點。 element.remove:把元素從它所屬的DOM樹中刪除。 element.removeAttribute:從指定的元素中刪除一個屬性。
          • element.appendChild:將一個節(jié)點插入到指定父節(jié)點列表的末尾處。 parentNode.replaceChild:用一個節(jié)點替換當前節(jié)點中的一個子節(jié)點并返回被替換的節(jié)點。 parentNode.insertBefore:將一個節(jié)點插入到當前節(jié)點中一個子節(jié)點之前。
          • document.getElementById:返回一個元素Id與指定Id相匹配的元素。 document.getElementsByClassName:返回一個包含所有指定類名的元素的類數(shù)組對象。 document.querySelector:返回文檔中與指定選擇器或選擇器組匹配的第一個Element對象。
          • 事件處理 EventTarget.addEventListener:將指定的監(jiān)聽器注冊到EventTarget上,當事件被觸發(fā)時,指定的回調函數(shù)就會被執(zhí)行。 document.createEvent:創(chuàng)建一個指定類型的事件。 EventTarget.removeEventListener:移除事件監(jiān)聽器。

          CSS選擇器

          css選擇器是用來對HTML頁面中的元素進行控制,通過對CSS選擇器的了解,可以加深對網頁結構與節(jié)點的理解。常用的CSS選擇器主要分為:

          1、元素選擇器: 通過標簽名{}的格式來選中對應標簽,如:p{}

          2、類選擇器: 通過.類名{}的格式來選中對應類名的標簽,如:.page{},page為元素的類名。

          3、id選擇器: 通過#id值{}的格式來選中對應id值的標簽,如:#key{},key為元素的id值。

          4、群組選擇器: 通過選擇器1,選擇器2,選擇器3...{}的格式來選中對應選擇器的標簽,如:div,.page{},即選擇div標簽下類名為pagae的標簽。

          5、子元素選擇器: 通過父元素 > 子元素{}的格式來選中對應父元素中對應子元素的標簽,如:div > p{},即選擇div標簽下的p標簽,子元素選擇器只能選擇直接后代,不能跨節(jié)點選取。

          6、后代選擇器: 通過父元素 子元素{}的格式來選中對應父元素中對應子元素的標簽,如:div p{},即選擇div標簽下的p標簽,后代選擇器可以跨節(jié)點選取。

          談一個網頁打開的全過程(涉及DNS、CDN、Nginx負載均衡等)

          1、概要

          從用戶在瀏覽器輸入域名開始,到web頁面加載完畢,這是一個說復雜不復雜,說簡單不簡單的過程,下文暫且把這個過程稱作網頁加載過程。下面我將依靠自己的經驗,總結一下整個過程。如有錯漏,歡迎指正。

          閱讀本文需要讀者已有一定的計算機知識,了解TCP、DNS等。

          2、分析

          眾所周知,打開一個網頁的過程中,瀏覽器會因頁面上的css/js/image等靜態(tài)資源會多次發(fā)起連接請求,所以我們暫且把這個網頁加載過程分成兩部分:

          1. html(jsp/php/aspx) 頁面加載(假設存在簡單的Nginx負載均衡)
          2. css/js/image等 網頁靜態(tài)資源加載(假設使用CDN)

          2.1 頁面加載

          先上一張圖,直觀明了地讓大家了解下基本流程,然后我們再逐一分析。

          2.1.1 DNS解析

          什么是DNS解析?當用戶輸入一個網址并按下回車鍵的時候,瀏覽器得到了一個域名。而在實際通信過程中,我們需要的是一個IP地址。因此我們需要先把域名轉換成相應的IP地址,這個過程稱作DNS解析。

          1) 瀏覽器首先搜索瀏覽器自身緩存的DNS記錄。

          或許很多人不知道,瀏覽器自身也帶有一層DNS緩存。Chrome 緩存1000條DNS解析結果,緩存時間大概在一分鐘左右。

          (Chrome瀏覽器通過輸入:chrome://net-internals/#dns 打開DNS緩存頁面)

          2) 如果瀏覽器緩存中沒有找到需要的記錄或記錄已經過期,則搜索hosts文件和操作系統(tǒng)緩存。

          在Windows操作系統(tǒng)中,可以通過 ipconfig /displaydns 命令查看本機當前的緩存。

          通過hosts文件,你可以手動指定一個域名和其對應的IP解析結果,并且該結果一旦被使用,同樣會被緩存到操作系統(tǒng)緩存中。

          Windows系統(tǒng)的hosts文件在%systemroot%\system32\drivers\etc下,linux系統(tǒng)的hosts文件在/etc/hosts下。

          3) 如果在hosts文件和操作系統(tǒng)緩存中沒有找到需要的記錄或記錄已經過期,則向域名解析服務器發(fā)送解析請求。

          其實第一臺被訪問的域名解析服務器就是我們平時在設置中填寫的DNS服務器一項,當操作系統(tǒng)緩存中也沒有命中的時候,系統(tǒng)會向DNS服務器正式發(fā)出解析請求。這里是真正意義上開始解析一個未知的域名。

          一般一臺域名解析服務器會被地理位置臨近的大量用戶使用(特別是ISP的DNS),一般常見的網站域名解析都能在這里命中。

          4) 如果域名解析服務器也沒有該域名的記錄,則開始遞歸+迭代解析。

          這里我們舉個例子,如果我們要解析的是mail.google.com。

          首先我們的域名解析服務器會向根域服務器(全球只有13臺)發(fā)出請求。顯然,僅憑13臺服務器不可能把全球所有IP都記錄下來。所以根域服務器記錄的是com域服務器的IP、cn域服務器的IP、org域服務器的IP……。如果我們要查找.com結尾的域名,那么我們可以到com域服務器去進一步解析。所以其實這部分的域名解析過程是一個樹形的搜索過程。

          根域服務器告訴我們com域服務器的IP。

          接著我們的域名解析服務器會向com域服務器發(fā)出請求。根域服務器并沒有mail.google.com的IP,但是卻有google.com域服務器的IP。

          接著我們的域名解析服務器會向google.com域服務器發(fā)出請求。...

          如此重復,直到獲得mail.google.com的IP地址。

          為什么是遞歸:問題由一開始的本機要解析mail.google.com變成域名解析服務器要解析mail.google.com,這是遞歸。

          為什么是迭代:問題由向根域服務器發(fā)出請求變成向com域服務器發(fā)出請求再變成向google.com域發(fā)出請求,這是迭代。

          5) 獲取域名對應的IP后,一步步向上返回,直到返回給瀏覽器。

          2.1.2 發(fā)起TCP請求

          瀏覽器會選擇一個大于1024的本機端口向目標IP地址的80端口發(fā)起TCP連接請求。經過標準的TCP握手流程,建立TCP連接。

          關于TCP協(xié)議的細節(jié),這里就不再闡述。這里只是簡單地用一張圖說明一下TCP的握手過程。如果不了解TCP,可以選擇跳過此段,不影響本文其他部分的瀏覽。

          2.1.3 發(fā)起HTTP請求

          其本質是在建立起的TCP連接中,按照HTTP協(xié)議標準發(fā)送一個索要網頁的請求。

          2.1.4 負載均衡

          什么是負載均衡?當一臺服務器無法支持大量的用戶訪問時,將用戶分攤到兩個或多個服務器上的方法叫負載均衡。

          什么是Nginx?Nginx是一款面向性能設計的HTTP服務器,相較于Apache、lighttpd具有占有內存少,穩(wěn)定性高等優(yōu)勢。

          負載均衡的方法很多,Nginx負載均衡、LVS-NAT、LVS-DR等。這里,我們以簡單的Nginx負載均衡為例。關于負載均衡的多種方法詳情大家可以Google一下。

          Nginx有4種類型的模塊:core、handlers、filters、load-balancers。

          我們這里討論其中的2種,分別是負責負載均衡的模塊load-balancers和負責執(zhí)行一系列過濾操作的filters模塊。

          1) 一般,如果我們的平臺配備了負載均衡的話,前一步DNS解析獲得的IP地址應該是我們Nginx負載均衡服務器的IP地址。所以,我們的瀏覽器將我們的網頁請求發(fā)送到了Nginx負載均衡服務器上。

          2) Nginx根據(jù)我們設定的分配算法和規(guī)則,選擇一臺后端的真實Web服務器,與之建立TCP連接、并轉發(fā)我們?yōu)g覽器發(fā)出去的網頁請求。

          Nginx默認支持 RR輪轉法 和 ip_hash法 這2種分配算法。

          前者會從頭到尾一個個輪詢所有Web服務器,而后者則對源IP使用hash函數(shù)確定應該轉發(fā)到哪個Web服務器上,也能保證同一個IP的請求能發(fā)送到同一個Web服務器上實現(xiàn)會話粘連。

          也有其他擴展分配算法,如:

          fair:這種算法會選擇相應時間最短的Web服務器

          url_hash:這種算法會使得相同的url發(fā)送到同一個Web服務器

          3) Web服務器收到請求,產生響應,并將網頁發(fā)送給Nginx負載均衡服務器。

          4) Nginx負載均衡服務器將網頁傳遞給filters鏈處理,之后發(fā)回給我們的瀏覽器。

          而Filter的功能可以理解成先把前一步生成的結果處理一遍,再返回給瀏覽器。比如可以將前面沒有壓縮的網頁用gzip壓縮后再返回給瀏覽器。

          2.1.5 瀏覽器渲染

          1) 瀏覽器根據(jù)頁面內容,生成DOM Tree。根據(jù)CSS內容,生成CSS Rule Tree(規(guī)則樹)。調用JS執(zhí)行引擎執(zhí)行JS代碼。

          2) 根據(jù)DOM Tree和CSS Rule Tree生成Render Tree(呈現(xiàn)樹)

          3) 根據(jù)Render Tree渲染網頁

          但是在瀏覽器解析頁面內容的時候,會發(fā)現(xiàn)頁面引用了其他未加載的image、css文件、js文件等靜態(tài)內容,因此開始了第二部分。

          2.2 網頁靜態(tài)資源加載

          以阿里巴巴的淘寶網首頁的logo為例,其url地址為 img.alicdn.com/tps/i2/TB1bNE7LFXXXXaOXFXXwFSA1XXX-292-116.png_145x145.jpg

          我們清楚地看到了url中有cdn字樣。

          什么是CDN?如果我在廣州訪問杭州的淘寶網,跨省的通信必然造成延遲。如果淘寶網能在廣東建立一個服務器,靜態(tài)資源我可以直接從就近的廣東服務器獲取,必然能提高整個網站的打開速度,這就是CDN。CDN叫內容分發(fā)網絡,是依靠部署在各地的邊緣服務器,使用戶就近獲取所需內容,降低網絡擁塞,提高用戶訪問響應速度。

          接下來的流程就是瀏覽器根據(jù)url加載該url下的圖片內容。本質上是瀏覽器重新開始第一部分的流程,所以這里不再重復闡述。區(qū)別只是負責均衡服務器后端的服務器不再是應用服務器,而是提供靜態(tài)資源的服務器。

          文章乃參考、轉載其他博客所得,僅供自己學習作筆記使用?。。?/p>


          章涵蓋

          • 從分層數(shù)據(jù)構造根節(jié)點。
          • 使用分層布局生成器。
          • 繪制一個圓形包。
          • 繪制樹形圖。

          在本書的前幾章中,我們使用數(shù)據(jù)可視化來編碼數(shù)值數(shù)據(jù)。例如,第3章條形圖中條形的長度表示相關調查回復的數(shù)量。同樣,第4章折線圖中數(shù)據(jù)點的位置描繪了溫度。在本章中,我們將討論分層可視化,它對父子關系進行編碼,并且可以揭示迄今為止我們使用的更簡單的可視化所沒有注意到的模式。

          分層可視化通過外殼、連接鄰接來傳達父子關系。圖 11.1 顯示了使用外殼的分層可視化的兩個示例:圓形包和樹狀圖。顧名思義,圓包是一組圓圈。有一個根父級,最外圈,以及稱為節(jié)點的后續(xù)子級。節(jié)點的所有子節(jié)點都“打包”到該節(jié)點中,圓圈的大小與它們包含的節(jié)點數(shù)成正比。葉節(jié)點的大?。ㄗ畹图墑e的子節(jié)點)可以表示任意屬性。樹狀圖的工作方式類似,但使用嵌套矩形而不是圓形。樹狀圖比圓形包更節(jié)省空間,我們經常在與財務相關的可視化中遇到它們。

          可視化父子關系的一種熟悉且直觀的方法是通過連接,例如在樹形圖中。樹形圖可以是線性的,如家譜樹,也可以是徑向的,如圖 11.1 所示。線性樹形圖更易于閱讀,但會占用大量空間,而徑向樹更緊湊,但需要更多的努力來破譯。

          最后,我們可以通過冰柱圖(也稱為分區(qū)層圖)的鄰接來可視化層次結構模式。我們經常在IT(信息技術)中遇到這樣的圖表。

          圖 11.1 在分層可視化中,父子關系可以通過外殼(如在圓形包或樹狀圖中)或通過連接(如在樹形圖中)或鄰接(如在冰柱圖中)進行通信。


          圖 11.1 中顯示的圖表可能看起來多種多樣,但使用 D3 構建它們意味著涉及布局生成器函數(shù)的類似過程。在第 5 章中,我們了解了 D3 的布局生成器函數(shù)如何將信息添加到現(xiàn)有數(shù)據(jù)集,以及我們可以使用此信息將所需的形狀附加到 SVG 容器中。創(chuàng)建分層可視化也不例外。

          在本章中,我們將構建兩個分層可視化:圓形包和線性樹形圖。我們將基于世界上 100 種使用最多的語言的數(shù)據(jù)集進行可視化。您可以看到我們將在 https://d3js-in-action-third-edition.github.io/100-most-spoken-languages/ 構建的圖表。

          我們數(shù)據(jù)集中的每種語言都屬于一個語言家族或一組從共同祖先發(fā)展而來的相關語言。這些家族可以細分為稱為分支的較小組。讓我們以五種最常用的語言為例。在表 11.1 中,我們看到了如何將每種語言的信息存儲在電子表格中。左列包含語言:英語、中文普通話、印地語、西班牙語和法語。以下列包括相關的語系:印歐語系和漢藏語系,以及語言分支:日耳曼語系、漢尼特語系、印度-雅利安語系和羅曼語系。我們用每種語言的使用者總數(shù)和母語人士的數(shù)量來完成表格。

          表11.1 世界上使用最多的5種語言

          語言

          家庭

          分支

          演講者總數(shù)

          母語人士

          英語

          印歐語系

          日耳曼

          1,132m

          379m

          普通話

          漢藏語

          西尼特

          1,117m

          918m

          印地語

          印歐語系

          印度-雅利安語

          615m

          341m

          西班牙語

          印歐語系

          浪漫

          534m

          460m

          法語

          印歐語系

          浪漫

          280m

          77m

          分層可視化具有單個根節(jié)點,該節(jié)點分為多個以結尾的分支。在表 11.1 的示例數(shù)據(jù)集中,節(jié)點可以稱為“語言”,如圖 11.2 所示。詞根分為兩個語系:印歐語系和漢藏語系,也分為分支:日耳曼語系、印度-雅利安語系、羅曼語系和漢尼語系。最后,葉子出現(xiàn)在圖形的右側:英語、印地語、西班牙語、法語和普通話。每種語言、分支、族和根稱為一個節(jié)點。

          圖 11.2 分層可視化具有一個分離為一個或多個分支的單個根。它以葉節(jié)點結尾,在此示例中是語言。


          在本書的前半部分,我們主要使用類似遺產的項目結構。主要目標是進行簡單的設置,并專注于D3。但是,如果您發(fā)布 D3 項目,則很有可能使用 JavaScript 模塊導入。在本章中,我們將對項目結構進行現(xiàn)代化改造,以允許單獨導入 D3 模塊。它將使我們的項目文件更小,因此加載速度更快,并且將是查看哪些 D3 模塊包含哪種方法的絕佳機會。這些知識將使您將來更容易搜索 D3 文檔。

          要將我們的 JavaScript 文件和 npm 模塊組合成一個瀏覽器可讀的模塊,我們需要一個捆綁器。您可能已經熟悉 Webpack 或 RollUp。由于此類工具可能需要相當多的配置,因此我們將轉向Parcel(https://parceljs.org/),這是一個非常易于使用且需要接近零的配置的捆綁器。

          如果您的計算機上尚未安裝 Parcel,則可以使用以下命令對其進行全局安裝,其中 -g 代表全局。在終端窗口中運行此命令。

          npm install -g parcel

          我們建議使用此類全局安裝,因為它將使 Parcel 可用于您的所有項目。請注意,根據(jù)計算機的配置,您可能需要在命令的開頭添加 Mac 和 Linux 上的術語 sudo 或 Windows 上的 runas。

          在您的計算機上安裝 Parcel 后,在代碼編輯器 (https://github.com/d3js-in-action-third-edition/code-files/tree/main/chapter_11/11.1-Formatting_hierarchical_data/start) 中打開本章代碼文件的起始文件夾。如果您使用的是 VS Code,請打開集成終端并運行命令 npm install 以安裝項目依賴項。在此階段,我們唯一的依賴項是允許我們稍后加載CSV數(shù)據(jù)文件。

          若要啟動項目,請運行命令包,后跟根文件的路徑:

          parcel src/index.html

          在瀏覽器中打開 http://localhost:1234/ 以查看您的項目。每次保存文件時,瀏覽器中顯示的項目都會自動更新。完成工作會話后,您可以通過輸入終端 ctrl + C 來停止包裹。

          在文件索引中.html ,我們已經加載了帶有腳本標簽的文件 main.js。因為我們將使用模塊,所以我們將腳本標記的 type 屬性設置為 module 。JavaScript 模塊的好處是,我們不需要將額外的腳本加載到 index 中.html ;一切都將從主.js.請注意,我們也不需要使用腳本標記加載 D3 庫。我們將從下一節(jié)開始安裝和導入所需的 D3 模塊。

          11.1 格式化分層數(shù)據(jù)

          為了創(chuàng)建分層可視化,D3 希望我們以特定方式格式化數(shù)據(jù)。我們有兩個主要選項:使用 CSV 文件或使用分層 JSON。

          11.1.1 使用 CSV 文件

          我們的大多數(shù)數(shù)據(jù)都以表格形式出現(xiàn),通常以電子表格的形式出現(xiàn)。此類文件必須通過列指示父子關系。在表 11.2 中,我們將五種最常用的語言的示例數(shù)據(jù)集重新組織為名為“child”和“parent”的列。稍后我們將使用這些列名稱,讓 D3 知道如何建立父子關系。在第一行中,子列中有根節(jié)點“語言”。由于這是根節(jié)點,因此它沒有父節(jié)點。然后,在下面的行中,我們列出了根的直系子女:印歐語系和漢藏語系。他們都有“語言”作為父母。我們遵循語言分支(日耳曼語、漢尼特語、印度-雅利安語和羅曼語),并聲明哪個語系是它們的父語言。最后,每種語言(英語、中文普通話、印地語、西班牙語和法語)都有一行,并設置它們的父語言,即相關語言分支。我們還為每種語言設置了“total_speakers”和“native_speakers”列,因為我們可以在可視化中使用此信息,但這些信息對于分層布局不是必需的。

          表 11.2 顯示了在使用 D3 構建分層可視化之前我們如何構建電子表格。然后,我們將其導出為CSV文件并將其添加到我們的項目中。請注意,您不必為本章的練習制作自己的電子表格。您可以在 /data 文件夾中找到 100 種最常用的語言(名為 flat_data.csv)格式正確的 CSV 文件。

          表 11.2 在 CSV 文件中,我們通過列傳達父子關系

          孩子

          父母

          使用

          母語

          語言




          印歐語系

          語言



          漢藏語

          語言



          日耳曼

          印歐語系



          西尼特

          漢藏語



          印度-雅利安語

          印歐語系



          浪漫

          印歐語系



          浪漫

          印歐語系



          英語

          日耳曼

          1,132m

          379m

          普通話

          西尼特

          1,117m

          918m

          印地語

          印度-雅利安語

          615m

          341m

          西班牙語

          浪漫

          534m

          460m

          法語

          浪漫

          280m

          77m

          讓我們flat_data.csv加載到我們的項目中!首先,在 /js 文件夾中創(chuàng)建一個新的 JavaScript 文件。將其命名為 load-data.js ,因為這是我們加載數(shù)據(jù)集的地方。在清單 11.1 中,我們創(chuàng)建了一個名為 loadCSVData() 的函數(shù)。我們向函數(shù)添加導出聲明,使其可供項目中的其他 JavaScript 模塊訪問。


          要將 CSV 文件加載到我們的項目中,我們需要采用與使用 d3.csv() 方法不同的路線。宗地需要適當?shù)霓D換器才能加載 CSV 文件。我們已經通過安裝允許 Parcel 解析 CSV 文件的模塊為您完成了項目中的所有配置(有關更多詳細信息,請參閱文件 .parcelrc 和 .parcel-transformer-csv.json)。我們現(xiàn)在要做的就是使用 JavaScript require() 函數(shù)加載 CSV 文件并將其保存到常量 csvData 中。如果將 csvData 登錄到控制臺,您將看到它由一個對象數(shù)組組成,每個對象對應于 CSV 文件中的一行。我們遍歷 csvData 將說話者的數(shù)量格式化為數(shù)字并返回 csvData .

          示例 11.1 將 CSV 文件加載到項目中(加載數(shù)據(jù).js)

          export const loadCSVData=()=> {
           
            const csvData=require("../data/flat_data.csv");  #A
           
            csvData.forEach(d=> {                     #B
              d.total_speakers=+d.total_speakers;    #B
              d.native_speakers=+d.native_speakers;  #B
            });                                        #B
           
            return csvData;
           
          };

          在 main.js 中,我們使用導入語句來訪問函數(shù) loadCSVData(),如清單 11.2 所示。然后我們將 loadCSVData() 返回的數(shù)組保存到一個名為 flatData 的常量中。

          清單 11.2 將平面數(shù)據(jù)導入主數(shù)據(jù).js (main.js)

          import { loadCSVData } from "./load-data.js"; #A
           
          const flatData=loadCSVData();  #B

          下一步是將平面 CSV 數(shù)據(jù)轉換為分層格式,或包含其子節(jié)點的根節(jié)點。d3-hierarchy 模塊 (https://github.com/d3/d3-hierarchy) 包含一個名為 d3.stratify() 的方法,它就是這樣做的。它還包括構建分層可視化所需的所有其他方法。

          為了最大限度地提高項目性能,我們不會安裝整個 D3 庫,而只會安裝我們需要的模塊。讓我們從 d3 層次結構開始。在 VS Code 中,打開一個新的終端窗口并運行以下命令:

          npm install d3-hierarchy

          然后,創(chuàng)建一個名為 hierarchy 的新 JavaScript 文件.js 。在文件頂部,從 d3-hierarchy 導入 stratify() 方法,如清單 11.3 所示。然后,創(chuàng)建一個名為 CSVToHierarchy() 的函數(shù),該函數(shù)將 CSV 數(shù)據(jù)作為參數(shù)。請注意,我們通過導出聲明提供此功能。

          在 CSVToHierarchy() 中,我們通過調用方法 stratify() 來聲明一個層次結構生成器。在我們之前的設置中,我們會用 d3.stratify() 調用此方法。因為我們只安裝了 d3-hierarchy 模塊,所以我們不再需要在 d3 對象上調用方法并將 stratify() 視為一個獨立的函數(shù)。

          要將我們的CSV數(shù)據(jù)轉換為分層結構,函數(shù)stratify()需要知道如何建立父子關系。使用 id() 訪問器函數(shù),我們指示可以在哪個鍵下找到子項,在本例中為 子項(子項存儲在原始 CSV 文件的“子項”列中)。使用 parentId() 訪問器函數(shù),我們指示可以在哪個鍵下找到父級,在我們的例子中是父級(父級存儲在原始 CSV 文件的“父級”列中)。

          我們將數(shù)據(jù)傳遞給層次結構生成器,并將其保存在一個名為 root 的常量中,這是我們的分層數(shù)據(jù)結構。這種嵌套數(shù)據(jù)結構帶有一些方法,如 descendants(),它返回樹中所有節(jié)點的數(shù)組(“語言”、“印歐語”、“日耳曼語”、“英語”等),以及 leaves() 返回所有沒有子節(jié)點的數(shù)組(“英語”、“普通話”、“印地語”等)。我們將后代節(jié)點和葉節(jié)點保存到常量中,并使用根數(shù)據(jù)結構返回它們。

          11.3 將CSV數(shù)據(jù)轉換為層次結構(層次結構.js)

          import { stratify } from "d3-hierarchy";  #A
           
          export const CSVToHierarchy=(data)=> {
           
            const hierarchyGenerator=stratify()   #B
              .id(d=> d.child)                     #B
              .parentId(d=> d.parent);             #B
            const root=hierarchyGenerator(data);  #C
           
            const descendants=root.descendants(); #D
            const leaves=root.leaves();           #D
           
            return [root, descendants, leaves];
           
          };

          在 main.js 中,我們導入函數(shù) CSVToHierarchy() 并調用它來獲取根、后代和葉。我們將在以下部分中使用此層次結構數(shù)據(jù)結構來生成可視化效果。

          示例 11.4 將層次結構數(shù)據(jù)結構導入 main.js (main.js)

          import { loadCSVData } from "./load-data.js";
          import { CSVToHierarchy } from "./hierarchy.js";
           
          const flatData=loadCSVData();
          const [root, descendants, leaves]=CSVToHierarchy(flatData);

          11.1.2 使用分層 JSON 文件

          我們的數(shù)據(jù)集也可以存儲為分層 JSON 文件。JSON 本質上支持分層數(shù)據(jù)結構,并使其易于理解。以下 JSON 對象演示如何為示例數(shù)據(jù)集構建數(shù)據(jù)。在文件的根目錄中,我們有一個用大括號 ( {} ) 括起來的對象。根的“name”屬性是“語言”,其“子”屬性是一個對象數(shù)組。根的每個直接子級都是一個語言家族,其中包含語言分支的“子”數(shù)組,其中還包括帶有語言葉的“子”數(shù)組。請注意,每個子項都存儲在一個對象中。我們可以在葉對象中添加與語言相關的其他數(shù)據(jù),例如說話者和母語人士的總數(shù),但這是可選的。

          {
            "name": "Languages",
            "children": [
              {
                "name": "Indo-European",
                "children": [
                  {
                    "name": "Germanic",
                    "children": [
                      {
                        "name": "English"
                      }
                    ]
                  },
                  {
                    "name": "Indo-Aryan",
                    "children": [
                      {
                        "name": "Hindi"
                      }
                    ]
                  },
                  {
                    "name": "Romance",
                    "children": [
                      {
                            "name": "Spanish"
                      },
                      {
                            "name": "French"
                      }
                    ]
                  },
                ]
              },
              {
                "name": "Sino-Tibetan",
                "children": [
                  {
                    "name": "Sinitic",
                    "children": [
                      {
                        "name": "Mandarin Chinese"
                      }
                    ]
                  }
                ]
              }
            ]
          }

          分層 JSON 文件已在數(shù)據(jù)文件夾 ( hierarchical-data.json ) 中可用。我們將以類似的方式處理 CSV 文件以將其加載到我們的項目中。在清單 11.5 中,我們回到 load-data.js 并創(chuàng)建一個名為 loadJSONData() 的函數(shù)。此函數(shù)使用 JavaScript require() 方法來獲取數(shù)據(jù)集并將其存儲在名為 jsonData 的常量中。常量 jsonData 由函數(shù)返回。

          示例 11.5 將 JSON 數(shù)據(jù)加載到項目中 (load-data.js)

          export const loadJSONData=()=> {
           
            const jsonData=require("../data/hierarchical-data.json");
           
            return jsonData;
           
          };

          回到main.js,我們導入loadJSONData(),調用它并將它返回的對象存儲到一個名為jsonData的常量中。

          清單 11.6 將 JSON 數(shù)據(jù)導入 main.js (main.js)

          import { loadCSVData, loadJSONData } from "./load-data.js";
          import { CSVToHierarchy } from "./hierarchy.js";
           
          const flatData=loadCSVData();
          const [root, descendants, leaves]=CSVToHierarchy(flatData);
           
          const jsonData=loadJSONData();

          為了從 JSON 文件生成分層數(shù)據(jù)結構,我們使用方法 d3.hierarchy() 。在示例 11.7 中,我們從 d3-hierarchy 導入層次結構函數(shù)。然后我們創(chuàng)建一個名為 JSONToHierarchy() 的函數(shù),它將 JSON 數(shù)據(jù)作為參數(shù)。

          我們調用 hierarchy() 函數(shù)并將數(shù)據(jù)作為參數(shù)傳遞。我們將它返回的嵌套數(shù)據(jù)結構存儲在名為 root 的常量中。與之前由 stratify() 函數(shù)返回的數(shù)據(jù)結構一樣,root 有一個方法后代 (),它返回樹中所有節(jié)點的數(shù)組(“語言”、“印歐語”、“日耳曼語”、“英語”等),還有一個方法 leaves() 返回所有沒有子節(jié)點的數(shù)組(“英語”、“普通話”、“印地語”等)。我們將后代節(jié)點和葉節(jié)點保存到常量中,并使用根數(shù)據(jù)結構返回它們。

          11.7 將 JSON 數(shù)據(jù)轉換為層次結構(層次結構.js)

          import { stratify, hierarchy } from "d3-hierarchy";
           
          ...
           
          export const JSONToHierarchy=(data)=> {
           
            const root=hierarchy(data);
           
            const descendants=root.descendants();
            const leaves=root.leaves();
           
            return [root, descendants, leaves];
           
          };

          最后,在main.js中,我們導入根,后代和葉子數(shù)據(jù)結構。為了將它們與從 CSV 數(shù)據(jù)導入的后綴區(qū)分開來,我們添加了_j后綴。

          示例 11.8 將層次結構數(shù)據(jù)結構導入 main.js (main.js)

          import { loadCSVData, loadJSONData } from "./load-data.js";
          import { CSVToHierarchy, JSONToHierarchy } from "./hierarchy.js";
           
          const flatData=loadCSVData();
          const [root, descendants, leaves]=CSVToHierarchy(flatData);
           
          const jsonData=loadJSONData();
          const [root_j, descendants_j, leaves_j]=JSONToHierarchy(jsonData);

          注意

          在現(xiàn)實生活中的項目中,我們不需要同時加載CSV和JSON數(shù)據(jù);這將是一個或另一個。我們這樣做只是出于教學目的。

          有兩種主要方法可以將分層數(shù)據(jù)加載到 D3 項目中:從 CSV 文件或分層 JSON 文件。如圖 11.3 所示,如果我們使用 CSV 文件中的數(shù)據(jù),我們會將其傳遞給 d3.stratify() 以生成層次結構數(shù)據(jù)結構。如果我們使用 JSON 文件,我們將使用 d3.hierarchy() 方法代替。這兩種方法都返回相同的嵌套數(shù)據(jù)結構,通常稱為 root 。此根有一個返回層次結構中所有節(jié)點的方法 descendants() 和一個返回沒有子節(jié)點的方法 leaves()。

          圖 11.3 我們以 CSV 或 JSON 文件的形式獲取分層數(shù)據(jù)。如果我們使用 CSV 文件,我們將數(shù)據(jù)傳遞給 d3.stratify() 以生成層次結構。如果我們使用 JSON 文件,我們將使用 d3.hierarchy() 方法代替。這些方法生成的嵌套數(shù)據(jù)結構是相同的,通常存儲在名為 root 的常量中。它有一個返回層次結構中所有節(jié)點的方法 descendants(),以及一個返回所有沒有子節(jié)點的方法 leaves()。


          現(xiàn)在我們的分層數(shù)據(jù)已經準備就緒,我們可以進入有趣的部分并構建可視化!這就是我們將在以下部分中執(zhí)行的操作。

          11.2 構建圓形裝箱圖

          在圓形包中,我們用圓圈表示每個節(jié)點,子節(jié)點嵌套在其父節(jié)點中。當我們想要一目了然地理解整個分層組織時,這種可視化很有幫助。它易于理解,外觀令人愉悅。

          在本節(jié)中,我們將使用圖 100.11 所示的圓形包可視化我們的 4 種最常用的語言數(shù)據(jù)集。在此可視化中,最外層的圓圈是根節(jié)點,我們將其命名為“語言”。顏色較深的圓圈是語系,顏色較淺的圓圈是語言分支。白色圓圈表示語言,其大小表示說話者的數(shù)量。

          圖 11.4 我們將在本節(jié)中構建的 100 種最常用的語言的 circle pack 可視化。


          11.2.1 生成包布局

          要使用 D3 創(chuàng)建圓形包裝可視化,我們需要使用布局生成器。與第5章中討論的類似,此類生成器將現(xiàn)有數(shù)據(jù)集附加構建圖表所需的信息,例如每個圓和節(jié)點的位置和半徑,如圖11.5所示。

          圖 11.5 要使用 D3 創(chuàng)建圓形包可視化,我們從分層數(shù)據(jù)(通常稱為 root)開始。為了計算布局,我們調用根 sum() 方法來計算圓圈的大小。然后,我們將根數(shù)據(jù)結構傳遞給 D3 的 pack() 布局生成器。此生成器將每個圓的位置和半徑附加到數(shù)據(jù)中。最后,我們將圓圈附加到 SVG 容器,并使用附加的數(shù)據(jù)設置它們的位置和大小。



          我們已經有了名為 root 的分層數(shù)據(jù)結構,并準備跳到圖 11.5 中的第二步。首先,讓我們創(chuàng)建一個名為 circle-pack 的新 JavaScript 文件.js并聲明一個名為 drawCirclePack() 的函數(shù)。此函數(shù)采用根、后代,并將上一節(jié)中創(chuàng)建的數(shù)據(jù)結構保留為參數(shù)。

          export const drawCirclePack=(root, descendants, leaves)=> {};

          在main.js中,我們導入drawCirclePack()并調用它,將根,后代和葉作為參數(shù)傳遞。

          import { drawCirclePack } from "./circle-pack.js";
           
          drawCirclePack(root, descendants, leaves);

          回到 circle-pack.js ,在函數(shù) drawCirclePack() 中,我們將開始計算我們的布局。在示例 11.9 中,我們首先聲明圖表的維度。我們將寬度和高度都設置為 800px。然后,我們聲明一個邊距對象,其中上邊距、右邊距、下邊距和左邊距等于 1px。我們需要此邊距才能看到可視化的最外層圓圈。最后,我們使用第 4 章中采用的策略計算圖表的內部寬度和高度。

          然后,我們調用 sum() 方法,該方法可用于 root。此方法負責計算可視化效果的聚合大小。我們還向 D3 指示應從中計算葉節(jié)點半徑的鍵:total_speakers 。

          為了初始化包布局生成器,我們調用 D3 方法 pack() ,我們從文件頂部的 d3-hierarchy 導入該方法。我們使用它的 size() 訪問函數(shù)來設置圓形包的整體大小,并使用 padding() 函數(shù)將圓圈之間的空間設置為 3px。

          示例 11.9 計算包布局(圓形包.js)

          import { pack } from "d3-hierarchy";
           
          export const drawCirclePack=(root, descendants, leaves)=> {
           
            const width=800;                                        #A
            const height=800;                                       #A
            const margin={ top: 1, right: 1, bottom: 1, left: 1 };  #A
            const innerWidth=width - margin.right - margin.left;    #A
            const innerHeight=height - margin.top - margin.bottom;  #A
           
            root.sum(d=> d.total_speakers);  #B
           
            const packLayoutGenerator=pack()  #C
              .size([innerWidth, innerHeight])  #C
              .padding(3);                      #C
            packLayoutGenerator(root);  #D
           
          };

          如果將后代數(shù)組記錄到控制臺中,您將看到包布局生成器為每個節(jié)點追加了以下信息:

          • id :節(jié)點的標簽。
          • 深度:根節(jié)點為 0,語言家族為 1,語言分支為 2,語言分支為 3。
          • r :節(jié)點圓的半徑。
          • x :節(jié)點圓心的水平位置。
          • y :節(jié)點圓心的垂直位置。
          • data :包含節(jié)點父節(jié)點名稱、說話人總數(shù)和母語說話人數(shù)量的對象。

          我們將在下一節(jié)中使用此信息來繪制圓形包。

          11.2.2 繪制圓形包

          我們現(xiàn)在準備繪制我們的圓形包!要選擇元素并將其附加到 DOM,我們需要通過在終端中運行 npm install d3-select 來安裝 d3 選擇模塊 (https://github.com/d3/d3-selection)。此模塊包含負責操作 DOM、應用數(shù)據(jù)綁定模式和偵聽事件的 D3 方法。在 circle-pack 的頂部.js ,我們從 d3-select 導入 select() 函數(shù)。

          在 drawCirclePack() 中,我們將一個 SVG 容器附加到 div 中,其 id 為 “circle-pack”,該容器已存在于索引中.html 。我們按照第 4 章中解釋的策略設置其 viewBox 屬性并附加一個組以包含內部圖表。

          然后,我們?yōu)楹蟠鷶?shù)組中的每個節(jié)點附加一個圓圈。我們使用包布局生成器附加到數(shù)據(jù)的 x 和 y 值來設置它們的 cx 和 cy 屬性。我們對半徑做同樣的事情?,F(xiàn)在,我們將圓圈的填充屬性設置為“透明”,將其筆觸設置為“黑色”。我們稍后會改變這一點。

          在 11.10 繪制圓形包(圓形包.js)

          import { pack } from "d3-hierarchy";
          import { select } from "d3-selection";
           
          export const drawCirclePack=(root, descendants, leaves)=> {
           
            ...
           
            const svg=select("#circle-pack")                                  #A
              .append("svg")                                                    #A
                .attr("viewBox", `0 0 ${width} ${height}`)                      #A
                .append("g")                                                    #A
                .attr("transform", `translate(${margin.left}, ${margin.top})`); #A
           
               svg                                #B
              .selectAll(".pack-circle")       #B
              .data(descendants)               #B
              .join("circle")                  #B
                .attr("class", "pack-circle")  #B
                .attr("cx", d=> d.x)          #C
                .attr("cy", d=> d.y)          #C
                .attr("r", d=> d.r)           #C
                .attr("fill", "none")
                .attr("stroke", "black");
           
          };

          完成此步驟后,您的圓形包應如圖 11.6 所示。我們的可視化正在形成!

          圖 11.6 在這個階段,我們的圓形包的形狀已經準備好了。接下來,我們將添加顏色和標簽。



          我們希望圓圈包中的每個語言家族都有自己的顏色。如果打開文件幫助程序.js ,您將看到一個名為 languageFamilies 的數(shù)組。它包含語言系列及其相關顏色的列表,如以下代碼片段所示。我們可以使用此數(shù)組來創(chuàng)建色階并使用它來設置每個圓的填充屬性。

          export const languageFamilies=[
            { label: "Indo-European", color: "#4E86A5" },
            { label: "Sino-Tibetan", color: "#9E4E9E" },
            { label: "Afro-Asiatic", color: "#59C8DC" },
            { label: "Austronesian", color: "#3E527B" },
            { label: "Japanic", color: "#F99E23" },
            { label: "Niger-Congo", color: "#F36F5E" },
            { label: "Dravidian", color: "#C33D54" },
            { label: "Turkic", color: "#D57AB1" },
            { label: "Koreanic", color: "#33936F" },
            { label: "Kra-Dai", color: "#36311F" },
            { label: "Uralic", color: "#B59930" },
          ];

          要使用 D3 縮放,我們需要安裝 d3 縮放模塊 (https://github.com/d3/d3-scale) npm 安裝 d3-scale 。對于我們的色階,我們將使用序數(shù)刻度,它采用離散數(shù)組作為域、語言系列,將離散數(shù)組作為范圍,即關聯(lián)的顏色。在示例 11.11 中,我們創(chuàng)建了一個名為 scales.js 的新文件。在文件的頂部,我們從 d3-scale 導入 scaleOrdinal,從 helper.js 導入我們的 languageFamilies 數(shù)組。然后,我們聲明一個名為 colorScale 的序數(shù)刻度,傳遞一個語言家族標簽數(shù)組作為域,傳遞一個關聯(lián)顏色數(shù)組作為范圍。我們使用 JavaScript map() 方法生成這些數(shù)組。

          11.11 創(chuàng)建色階(scales.js)

          import { scaleOrdinal } from "d3-scale";
          import { languageFamilies } from "./helper";
           
          export const colorScale=scaleOrdinal()
            .domain(languageFamilies.map(d=> d.label))
            .range(languageFamilies.map(d=> d.color));

          在第 11.2.1 節(jié)結束時,我們討論了 D3 包布局生成器如何將多條信息附加到后代數(shù)據(jù)集(也稱為節(jié)點),包括它們的深度。圖 11.7 顯示我們的圓形包的深度從 2 到 3 不等。表示“語言”根節(jié)點的最外層圓的深度為零。此圓圈具有灰色邊框和透明填充。以下圓圈是深度為一的語言家族。它們的 fill 屬性對應于我們剛剛聲明的色階返回的顏色。然后,語言分支的深度為 <>。他們繼承了父母顏色的更蒼白版本。最后,葉節(jié)點或語言的深度為 <>,顏色為白色。這種顏色漸變不遵循任何特定規(guī)則,但旨在使父子關系盡可能明確。

          圖 11.7 在我們的圓包上,最外層的圓圈是語言,深度為零。遵循深度為 <> 的語言家族、深度為 <> 的語言分支和深度為 <> 的語言。


          回到 circle-pack.js ,我們將使用色階設置圓圈的填充屬性。在文件的頂部,我們導入之前以比例創(chuàng)建的色階.js .為了生成語言分支的較淺顏色(深度為 2 的圓圈),我們將使用稱為插值的 d3 方法,該方法在 d3-插值模塊 (https://github.com/d3/d3-interpolate) 中可用。使用 npm 安裝 d3 插值安裝此模塊,并將此方法導入到圓包的頂部.js .

          在示例 11.12 中,我們回到設置圓圈填充屬性的代碼。我們使用 JavaScript switch() 語句來評估附加到每個節(jié)點的深度數(shù)的值。如果深度為 3,則節(jié)點是一個語言家族。我們將它的 id 傳遞給色標,色標返回關聯(lián)的顏色。對于語言分支,我們仍然調用色階,但在其父節(jié)點的值上(d.parent.id)。然后,我們將比例返回的顏色作為 d0 插值() 函數(shù)的第一個參數(shù)傳遞。第二個參數(shù)是“白色”,即我們想要插入初始值的顏色。我們還將值 5.50 傳遞給 interpolate() 函數(shù),以指示我們想要一個介于原始顏色和白色之間的 <>% 的值。最后,我們?yōu)樗惺S喙?jié)點返回默認填充屬性“white”。

          我們還更改了圓圈的筆觸屬性。如果深度為零,因此節(jié)點是最外層的圓,我們給它一個灰色的筆觸。否則,不應用筆畫。

          11.12 根據(jù)深度和語言族對圓圈應用顏色(圓圈包.js)

          ...
          import { colorScale } from "./scales";
          import { interpolate } from "d3-interpolate";
           
          export const drawCirclePack=(root, descendants, leaves)=> {
           
            ...
           
            svg
              .selectAll(".pack-circle")
              .data(descendants)
              .join("circle")
                .attr("class", "pack-circle")
                ...
                .attr("fill", d=> {
                  switch (d.depth) {  #A
                    case 1:                     #B
                      return colorScale(d.id);  #B
                    case 2:                                                       #C
                      return interpolate(colorScale(d.parent.id), "white")(0.5);  #C
                    default:           #D
                      return "white";  #D
                  };
                })
                .attr("stroke", d=> d.depth===0 ? "grey" : "none");  #E
           
          };

          完成后,您的彩色圓圈包應如圖 11.8 所示。

          圖 11.8 應用顏色后的圓形包裝。


          11.2.3 添加標簽

          我們的圓圈包絕對看起來不錯,但沒有提供任何關于哪個圓圈代表哪種語言、分支或家族的線索。圓形包裝的主要缺點之一是在保持其可讀性的同時在其上貼標簽并不容易。但是由于我們正在從事數(shù)字項目,因此我們可以通過鼠標交互向讀者提供其他信息。

          在本節(jié)中,我們將首先為較大的語言圈添加標簽。然后,我們將構建一個交互式工具,當鼠標位于葉節(jié)點上時,該工具可提供其他信息。

          使用 HTML div 應用標簽

          在我們的可視化中,我們處理的是名稱相對較短的語言,如法語或德語,以及其他名稱較長的語言,如“現(xiàn)代標準阿拉伯語”或“西旁遮普語”。要在相應的圓圈內顯示這些較長的標簽,我們需要讓它們分成多行。但是,如果您還記得我們之前關于 SVG 文本元素的討論,則可以在多行上顯示它們,但需要大量工作。使用常規(guī) HTML 文本在需要時自動換行,這要容易得多!猜猜看:我們可以在 SVG 元素中使用常規(guī) HTML 元素,這正是我們在這里要做的。

          SVG 元素 foreignObject 允許我們在 SVG 容器中包含常規(guī) HTML 元素,例如 div 。然后這個div可以像任何其他div一樣設置樣式,并且它的文本將在需要時自動換行。

          在 D3 中,我們附加 foreignObject 元素的方式與其他任何元素相同。然后,在這些外來對象元素中,我們附加我們需要的 div。您可以將這些視為SVG和HTML世界之間的網關。

          出于可讀性的目的,我們不會在每個語言圈上應用標簽,而只會在較大的語言圈上應用標簽。在示例 11.13 中,我們首先定義要應用標簽的圓的最小半徑,即 22px。然后,我們使用數(shù)據(jù)綁定模式為每個滿足最小半徑要求的葉節(jié)點附加一個 foreignObject 元素。外來對象元素有四個必需的屬性:

          • 寬度:元素的寬度,對應于圓的直徑(2 * d.r)。
          • 高度 :元素的高度。在這里,我們應用40px的高度,這將為三行文本留出空間。
          • x :左上角的水平位置。我們希望這個角與圓的左側匹配 ( d.x - d.r )。
          • y :左上角的垂直位置,我們希望在圓心上方 20px,因為 foreignObject 元素的總高度為 40px ( d.y - 20 )。

          然后,我們需要指定要附加到 foreignObject 中的元素的 XML 命名空間。這就是為什么我們附加一個 xhtml:div 而不僅僅是一個 div .我們給這個div一個類名“l(fā)eaf-label”,并將其文本設置為節(jié)點的id。文件可視化.css 已包含在 foreignObject 元素內水平和垂直居中標簽所需的樣式。

          11.13 使用外來對象元素應用標簽(圓形包裝.js)

          export const drawCirclePack=(root, descendants, leaves)=> {
           
            ...
           
            const minRadius=22;
            svg
              .selectAll(".leaf-label-container")                  #A
              .data(leaves.filter(leave=> leave.r >=minRadius))  #A
              .join("foreignObject")                               #A
                .attr("class", "leaf-label-container")
                .attr("width", d=> 2 * d.r)  #B
                .attr("height", 40)           #B
                .attr("x", d=> d.x - d.r)    #B
                .attr("y", d=> d.y - 20)     #B
              .append("xhtml:div")            #C
                .attr("class", "leaf-label")  #C
                .text(d=> d.id);             #C
           
          };

          應用標簽后,您的圓形包應如圖 11.4 和托管項目 (https://d3js-in-action-third-edition.github.io/100-most-spoken-languages/) 中的包所示。現(xiàn)在,我們可以在可視化中找到主要語言。

          使用工具提示提供其他信息

          在第 7 章中,我們討論了如何使用 D3 偵聽鼠標事件,例如顯示工具提示。在本節(jié)中,我們將構建類似的東西,但不是在可視化效果上顯示工具提示,而是將其移動到一側。只要您有超過幾行信息要向用戶顯示,這是一個很好的選擇。由于此類工具提示是使用 HTML 元素構建的,因此也更容易設置樣式。

          在文件索引中.html ,取消注釋 id 為“信息容器”的 div。此 div 包含兩個主要元素:

          • 默認情況下顯示 id 為“指令”的 div,指示用戶將鼠標移到圓圈上以顯示其他信息。
          • ID 為“info”的 div,將顯示有關懸停節(jié)點的語言、分支、系列和說話人數(shù)量的信息。此 div 還有一個“隱藏”類,它將其最大高度和不透明度屬性設置為零,使其不可見。您可以在可視化中找到相關樣式.css 。

          在示例 11.14 中,我們又回到了 circle-pack.js 。在文件頂部,我們從 d3-select 導入 selectAll 函數(shù)。我們還需要安裝 d3 格式模塊 (https://github.com/d3/d3-format) 并導入其格式函數(shù)。

          為了區(qū)分節(jié)點級別,在清單 11.14 中,我們將它們的深度值添加到它們的類名中。然后,我們使用 selectAll() 函數(shù)選擇所有類名為 “pack-circle-depth-3” 的圓圈和所有 foreignObject 元素。我們使用 D3 on() 方法將 mouseenter 事件偵聽器附加到葉節(jié)點及其標簽。在此事件偵聽器的回調函數(shù)中,我們使用附加到元素的數(shù)據(jù)來填充有關相應語言、分支、家族和說話人數(shù)量的工具提示信息。請注意,我們使用 format() 函數(shù)來顯示具有三個有效數(shù)字和后綴的揚聲器數(shù)量,例如“M”表示“百萬”(“.3s”);

          然后,我們通過添加和刪除類名“hidden”來隱藏說明并顯示工具提示。我們還在鼠標離開語言節(jié)點或其標簽時應用事件偵聽器。在其回調函數(shù)中,我們隱藏工具提示并顯示說明。

          示例 11.14 偵聽葉節(jié)點上的鼠標事件并顯示附加信息 (circle-pack.js)

          import { select, selectAll } from "d3-selection";
          import { format } from "d3-format";
           
          export const drawCirclePack=(root, descendants, leaves)=> {
           
            ...
           
            svg
              .selectAll(".pack-circle")
              .data(descendants)
              .join("circle")
                .attr("class", d=> `pack-circle pack-circle-depth-${d.depth}`)  #A
                ...
           
            selectAll(".pack-circle-depth-3, foreignObject")  #B
              .on("mouseenter", (e, d)=> {  #C
           
                select("#info .info-language").text(d.id);                    #D
                select("#info .info-branch .information").text(d.parent.id);  #D
                select("#info .info-family .information")                     #D
                ?   .text(d.parent.data.parent);                             #D
                select("#info .info-total-speakers .information")             #D
                ?      .text(format(".3s")(d.data.total_speakers));             #D
                select("#info .info-native-speakers     .information")        #D
                ?      .text(format(".3s")(d.data.native_speakers));            #D
           
                select("#instructions").classed("hidden", true);  #E
                select("#info").classed("hidden", false);         #E
           
              })
              .on("mouseleave", ()=> {  #F
           
                select("#instructions").classed("hidden", false);  #G
                select("#info").classed("hidden", true);           #G
           
              });
             
          };

          當您將鼠標移到語言節(jié)點上時,您現(xiàn)在應該會看到有關分支、系列和說話人數(shù)量的其他信息顯示在可視化效果的右側,如圖 11.9 所示。

          圖 11.9 當我們將鼠標移到語言節(jié)點上時,有關語言名稱、分支、家族和說話者數(shù)量的信息將顯示在可視化的右側。



          圓形包的一個缺點是它們很難在移動屏幕上呈現(xiàn)。盡管圓圈仍然在小屏幕上提供了父子關系的良好概述,但標簽變得更加難以閱讀。此外,由于語言圈可能會變小,因此使用觸摸事件顯示信息可能會很棘手。為了解決這些缺點,我們可以將語言家族相互堆疊,或者在移動設備上選擇不同類型的可視化。

          11.3 構建樹形圖

          可視化父子關系的一種熟悉且直觀的方法是使用樹形圖。樹形圖類似于家譜樹。像圓形包一樣,它們由節(jié)點組成,但也顯示了它們之間的鏈接。在本節(jié)中,我們將構建 100 種最常用的語言的樹形圖,如圖 11.10 所示。左側是根節(jié)點,即“語言”。它分為語系,也細分為語言分支,最后是語言。我們用圓圈的大小可視化每種語言的使用者總數(shù)。至于圓圈包,這些圓圈的顏色代表它們所屬的語言家族。

          圖 11.10 我們將在本節(jié)中構建的樹形圖的部分視圖。



          11.3.1 生成樹布局

          與上一節(jié)中構建的圓形包類似,D3樹形圖是使用布局生成器d3.tree()創(chuàng)建的,它是d3層次結構模塊(https://github.com/d3/d3-hierarchy)的一部分。然后,我們使用布局提供的信息來繪制鏈接和節(jié)點。

          圖 11.11 要使用 D3 創(chuàng)建樹形圖,我們從分層數(shù)據(jù)(通常稱為 root)開始。為了計算布局,我們將根數(shù)據(jù)結構傳遞給 D3 的 tree() 布局生成器。此生成器將每個節(jié)點的位置附加到數(shù)據(jù)中。最后,我們將鏈接、節(jié)點和標簽附加到 SVG 容器,并使用附加的數(shù)據(jù)設置它們的位置和大小。


          要生成樹布局,讓我們首先創(chuàng)建一個新文件并將其命名為 tree.js .在這個文件中,我們創(chuàng)建了一個名為drawTree()的函數(shù),它將分層數(shù)據(jù)(也稱為根,后代和葉)作為參數(shù)。在示例 11.15 中,我們聲明了圖表的維度。我們給它一個 1200px 的寬度,圖表的 HTML 容器的寬度,以及 3000px 的高度。請注意,高度與圖表中的葉節(jié)點數(shù)成正比,并且是通過反復試驗找到的。處理樹可視化時,請從大致值開始,并在可視化顯示在屏幕上后進行調整。

          為了生成布局,我們調用 D3 的 tree() 函數(shù),我們從文件頂部的 d3-hierarchy 導入該函數(shù),并設置其 size() 訪問器函數(shù),該函數(shù)將圖表的寬度和高度數(shù)組作為參數(shù)。因為我們希望我們的樹從左到右展開,所以我們首先傳遞 innerHeight,然后是 innerWidth 。如果我們希望樹從上到下部署,我們會做相反的事情。最后,我們將分層數(shù)據(jù)(根)傳遞給樹布局生成器。

          示例 11.15 生成樹布局(樹.js)

          import { tree } from "d3-hierarchy";
           
          export const drawTree=(root, descendants, leaves)=> {
           
            const width=1200;                                         #A
            const height=3000;                                        #A
            const margin={top:60, right: 200, bottom: 0, left: 100};  #A
            const innerWidth=width - margin.left - margin.right;      #A
            const innerHeight=height - margin.top - margin.bottom;    #A
           
            const treeLayoutGenerator=tree()   #B
              .size([innerHeight, innerWidth]);  #B
            treeLayoutGenerator(root);  #C
           
          };

          在main.js中,我們還需要導入drawTree()函數(shù)并將根、后代和葉作為參數(shù)傳遞。

          import { drawTree } from "./tree.js";
           
          drawTree(root, descendants, leaves);

          11.3.2 繪制樹形圖

          生成布局后,繪制樹形圖非常簡單。像往常一樣,我們首先需要附加一個 SVG 容器并設置其 viewBox 屬性。在示例 11.16 中,我們將這個容器附加到 div 中,其 id 為 “tree”,該 id 已存在于 index.html 中。請注意,我們必須從文件頂部的 d3-select 模塊導入 select() 函數(shù)。我們還將一個 SVG 組附加到此容器,并根據(jù)前面定義的左邊距和上邊距進行轉換,遵循自第 4 章以來使用的策略。

          要創(chuàng)建鏈接,我們需要 d3.link() 鏈接生成器函數(shù)。此函數(shù)的工作方式與第 3 章中介紹的線路生成器完全相同。它是 d3 形狀模塊 (https://github.com/d3/d3-shape) 的一部分,我們使用命令安裝它 npm 安裝 d3 形狀 .在文件的頂部,我們從 d3-shape 導入 link() 函數(shù),以及 curveBumpX() 函數(shù),我們將使用它來確定鏈接的形狀。

          然后我們聲明一個名為 linkGenerator 的鏈接生成器,它將曲線函數(shù) curveBumpX 傳遞給 D3 的 link() 函數(shù)。我們將它的 x() 和 y() 訪問器函數(shù)設置為使用樹布局生成器存儲在 y 和 x 鍵中的值。就像我們準備樹布局生成器時一樣,x 和 y 值是反轉的,因為我們希望從右到左而不是從上到下繪制樹。

          為了繪制鏈接,我們使用數(shù)據(jù)綁定模式從 root.links() 提供的數(shù)據(jù)中附加路徑元素。此方法返回樹的鏈接數(shù)組及其源點和目標點。然后調用鏈接生成器來計算每個鏈接或路徑的 d 屬性。最后,我們設置鏈接的樣式并將其不透明度設置為 60%。

          示例 11.16 繪制鏈接(樹.js)

          ...
          import { select } from "d3-selection";
          import { link, curveBumpX } from "d3-shape";
           
          export const drawTree=(root, descendants)=> {
           
            ...
           
            const svg=select("#tree")                                     #A
              .append("svg")                                                     #A
                .attr("viewBox", `0 0 ${width} ${height}`)                       #A
              .append("g")                                                       #A
                .attr("transform", `translate(${margin.left}, ${margin.top})`);  #A
           
            const linkGenerator=link(curveBumpX)  #B
              .x(d=> d.y)                          #B
              .y(d=> d.x);                         #B
            svg                                    #C
              .selectAll(".tree-link")             #C
              .data(root.links())                  #C
              .join("path")                        #C
                .attr("class", "tree-link")        #C
                .attr("d", d=> linkGenerator(d))  #C
                .attr("fill", "none")              #C
                .attr("stroke", "grey")            #C
                .attr("stroke-opacity", 0.6);      #C
           
          };

          準備就緒后,您的鏈接將類似于圖 11.12 中的鏈接。請注意,此圖僅顯示部分視圖,因為我們的樹非常高!

          圖 11.12 樹形圖鏈接的部分視圖。



          為了突出顯示每個節(jié)點的位置,我們將在樹形圖中附加圓圈。帶有灰色筆劃的小圓圈將表示根、語言家族和語言分支節(jié)點。相反,語言節(jié)點圓圈的大小與說話者總數(shù)成比例,并且具有與其語言家族關聯(lián)的顏色。

          要計算語言節(jié)點的大小,我們需要一個規(guī)模。在清單 11.17 中,我們轉到 scales.js并從 d3-scale 導入 scaleRadial()。量表的域是連續(xù)的,從零擴展到數(shù)據(jù)集中說其中一種語言的最大人數(shù)。它的范圍可以在 83 到 <>px 之間變化,這是上一節(jié)中創(chuàng)建的圓包中最大圓的半徑。

          因為最大說話人數(shù)只有在我們檢索數(shù)據(jù)并創(chuàng)建層次結構(根)后才可用,我們需要將徑向刻度包裝到一個名為 getRadius() 的函數(shù)中。當我們需要計算圓的半徑時,我們將傳遞當前語言的說話者數(shù)量以及最大說話者數(shù)量,此函數(shù)將返回半徑。

          示例 11.17 創(chuàng)建一個函數(shù)來從徑向刻度(scales.js)中檢索值

          import { scaleOrdinal, scaleRadial } from "d3-scale";
          ...
           
          export const getRadius=(maxSpeakers, speakers)=> {
            const radialScale=scaleRadial()
              .domain([0, maxSpeakers])
              .range([0, 83]);
           
            return radialScale(speakers);
          };

          回到樹.js ,我們用方法 d3.max() 計算最大揚聲器數(shù)。要使用這種方法,我們需要安裝 d3-array 模塊 (https://github.com/d3/d3-array) 與 npm install d3-array ,并在文件頂部導入 max() 函數(shù)。我們還從 scales 導入函數(shù) getRadius() 和色階.js .

          然后,我們使用數(shù)據(jù)綁定模式將一個圓附加到每個后代節(jié)點的內部圖表中。我們使用樹布局生成器附加到數(shù)據(jù)中的 x 和 y 鍵來設置這些圓圈的 cx 和 cy 屬性。如果圓是一個葉節(jié)點,我們根據(jù)相關語言的說話者數(shù)量和 getRadius() 函數(shù)設置其半徑。我們使用色階設置其顏色,填充不透明度為 30%,描邊設置為“無”。其他圓圈的半徑為 4px,白色填充和灰色描邊。

          示例 11.18 追加節(jié)點(樹.js)

          ...
          import { max } from "d3-array";
          import { getRadius, colorScale } from "./scales";
           
          export const drawTree=(root, descendants)=> {
            ...
           
            const maxSpeakers=max(leaves, d=> d.data.total_speakers);  #A
            svg                              #B
              .selectAll(".node-tree")       #B
              .data(descendants)             #B
              .join("circle")                #B
                .attr("class", "node-tree")  #B
                .attr("cx", d=> d.y)        #B
                .attr("cy", d=> d.x)        #B
                .attr("r", d=> d.depth===3                      #C
                  ? getRadius(maxSpeakers, d.data.total_speakers)  #C
                  : 4                                              #C
                )                                                  #C
                .attr("fill", d=> d.depth===3      #D
                  ? colorScale(d.parent.data.parent)  #D
                  : "white"                           #D
                )                                     #D
                .attr("fill-opacity", d=> d.depth===3  #E
                  ? 0.3                                   #E
                  : 1                                     #E
                )                                         #E
                .attr("stroke", d=> d.depth===3        #E
                  ? "none"                                #E
                  : "grey"                                #E
                );                                        #E
           
          };

          為了完成樹形圖,我們?yōu)槊總€節(jié)點添加一個標簽。在示例 11.19 中,我們使用數(shù)據(jù)綁定模式為數(shù)據(jù)集中的每個節(jié)點附加一個文本元素。如果標簽與葉節(jié)點相關聯(lián),則在右側顯示標簽。否則,標簽將位于其節(jié)點的左側。我們還為標簽提供白色筆觸,以便它們在放置在鏈接上時更易于閱讀。通過將繪制順序屬性設置為“描邊”,我們可以確保在文本填充顏色之前繪制描邊。這也有助于提高可讀性。

          示例 11.19 為每個節(jié)點附加一個標簽(樹.js)

          export const drawTree=(root, descendants)=> {
           
            ...
           
          svg
            .selectAll(".label-tree")       #A
            .data(descendants)              #A
            .join("text")                   #A
              .attr("class", "label-tree")  #A
              .attr("x", d=> d.children ? d.y - 8 : d.y + 8)          #B
              .attr("y", d=> d.x)                                     #B
              .attr("text-anchor", d=> d.children ? "end" : "start")  #B
              .attr("alignment-baseline", "middle")                    #B
              .attr("paint-order", "stroke")                         #C
              .attr("stroke", d=> d.depth===3 ? "none" : "white")  #C
              .attr("stroke-width", 2)                               #C
              .style("font-size", "16px")
              .text(d=> d.id);
           
          };

          完成后,您的樹形圖應類似于托管項目 (https://d3js-in-action-third-edition.github.io/100-most-spoken-languages/) 和圖 11.13 中的樹形圖。

          圖 11.13 已完成樹形圖的部分視圖



          此樹形圖的線性布局使其相對容易地轉換為移動屏幕,只要我們增加標簽的字體大小并確保它們之間有足夠的垂直空間。有關構建響應式圖表的提示,請參閱第 9 章。

          為了完成這個項目,我們需要為語言家族的顏色和圓圈的大小添加一個圖例。我們已經為您構建了它。要顯示圖例,請轉到索引.html然后取消注釋帶有“圖例”類的 div。然后,在main.js中,從legend導入函數(shù)createLegend(.js并調用它來生成legend。請參閱第7章中的鯨目動物可視化,以獲取有關我們如何構建這個傳說的更多解釋??纯磮D例中的代碼.js甚至更好的是,嘗試自己構建它!

          11.4 構建其他分層可視化

          在本章中,我們討論了如何構建圓形包和樹形圖可視化。使用 D3 制作其他層次結構表示形式(如樹狀圖和冰柱圖)非常相似。

          圖 11.14 說明了我們如何從 CSV 或 JSON 數(shù)據(jù)開始,我們將其格式化為名為 root 的分層數(shù)據(jù)結構。使用這種數(shù)據(jù)結構,我們可以構建任何分層可視化,唯一的區(qū)別是用作布局生成器的功能。對于圓形包,布局生成器函數(shù)為 d3.pack() ;對于樹狀圖,d3.treemap() ;對于樹形圖,d3.tree() ;對于冰柱圖,d3.partition() 。我們可以在 d3 層次結構模塊 (https://github.com/d3/d3-hierarchy) 中找到這些布局生成器。

          圖 11.14 要使用 D3 創(chuàng)建分層可視化,我們首先將 CSV 或 JSON 數(shù)據(jù)格式化為稱為 root 的分層數(shù)據(jù)結構。從這個根,我們可以生成一個圓形包、一個樹狀圖、一個樹形圖或一個冰柱圖。每個圖表都有自己的布局生成器,用于計算布局并將其追加到數(shù)據(jù)集中所需的信息。然后,我們使用這些信息來繪制我們需要的形狀:圓形、矩形或鏈接。


          練習:創(chuàng)建樹狀圖

          現(xiàn)在,您已經掌握了構建 100 種最常用的語言的樹狀圖所需的所有知識,如下圖所示,以及托管項目 (https://d3js-in-action-third-edition.github.io/100-most-spoken-languages/)。樹狀圖將分層數(shù)據(jù)可視化為一組嵌套矩形。傳統(tǒng)上,樹狀圖僅顯示葉節(jié)點,在我們的例子中,葉節(jié)點是語言。矩形或葉節(jié)點的大小與每種語言的使用者總數(shù)成正比。

          1. 在索引中.html添加一個 id 為“樹狀圖”的 div。

          2. 使用一個名為 drawTreemap() 的函數(shù)創(chuàng)建一個名為 treemap.js 的新文件。此函數(shù)接收根數(shù)據(jù)結構和葉作為參數(shù),并從 main.js 調用。

          3. 使用 d3.treemap() 布局生成器計算樹狀圖布局。使用 size() 訪問器函數(shù),設置圖表的寬度和高度。您還可以使用填充Inner()和paddingOuter()指定矩形之間的填充。有關更深入的文檔 (https://github.com/d3/d3-hierarchy),請參閱 d3 層次結構模塊。

          4. 將 SVG 容器附加到 div,ID 為“樹狀圖”。

          5. 為每個葉節(jié)點附加一個矩形。使用布局生成器添加到數(shù)據(jù)集的信息設置其位置和大小。

          6. 在相應的矩形上附加每種語言的標簽。您可能還希望隱藏顯示在較小矩形上的標簽。

          圖 11.15 100 種最常用的語言的樹狀圖


          如果您在任何時候遇到困難或想將您的解決方案與我們的解決方案進行比較,您可以在附錄 D 的 D.11 節(jié)和文件夾 11.4-樹狀圖/本章代碼文件的末尾找到它。但是,像往常一樣,我們鼓勵您嘗試自己完成它。您的解決方案可能與我們的略有不同,沒關系!

          11.5 小結

          • 分層可視化通過外殼連接鄰接來傳達父子關系。
          • 可以從 CSV 或 JSON 數(shù)據(jù)創(chuàng)建它們。
          • 如果我們使用 CSV 數(shù)據(jù),我們需要兩列:一列用于子節(jié)點,另一列用于其父節(jié)點。一旦數(shù)據(jù)集加載到我們的項目中,我們使用方法 d3.stratify() 生成一個名為 root 的分層數(shù)據(jù)結構。
          • 如果我們使用分層 JSON 數(shù)據(jù),我們使用方法 d3.hierarchy() 生成名為 root 的分層數(shù)據(jù)結構。
          • 根分層數(shù)據(jù)結構有一個返回所有節(jié)點數(shù)組的 descendants() 方法和一個返回所有葉節(jié)點(沒有子節(jié)點)的數(shù)組的 leaves() 方法。
          • 為了創(chuàng)建圓形包可視化,我們將根分層數(shù)據(jù)傳遞給布局生成器 d3.pack() 。此生成器計算每個圓圈的位置和大小,并將此信息附加到數(shù)據(jù)集中。然后,我們使用它來繪制構成可視化的圓圈。
          • 為了繪制樹形圖,我們將根分層數(shù)據(jù)傳遞給布局生成器 d3.tree()。此生成器計算每個節(jié)點的位置,并將此信息附加到數(shù)據(jù)集中。然后,我們使用它來繪制構成可視化的鏈接和圓圈。

          主站蜘蛛池模板: 国产一区二区精品在线观看| 亚洲AV福利天堂一区二区三| 国模丽丽啪啪一区二区| 性色A码一区二区三区天美传媒| 天堂一区二区三区在线观看| 曰韩精品无码一区二区三区| 日本精品一区二区在线播放| 亚洲精品一区二区三区四区乱码| 中文字幕一区二区免费| 波多野结衣中文一区| 精品国产AV一区二区三区| 国产精品一区二区av| 波多野结衣高清一区二区三区| 精品视频在线观看一区二区| 波多野结衣AV一区二区三区中文 | 国产一区二区草草影院| 日韩人妻无码一区二区三区久久 | 精品综合一区二区三区| 欧亚精品一区三区免费| 亚洲综合激情五月色一区| 精品国产鲁一鲁一区二区| 综合无码一区二区三区四区五区| 亚洲无圣光一区二区| 精品一区高潮喷吹在线播放| 亚洲AV成人一区二区三区AV| 一区二区三区亚洲| 精品欧洲av无码一区二区14| 国产日本一区二区三区| 无码人妻啪啪一区二区| 成人毛片一区二区| 精品国产一区二区三区2021| 538国产精品一区二区在线| 精品一区二区三区高清免费观看| 国产自产V一区二区三区C| 久久99国产精品一区二区| 78成人精品电影在线播放日韩精品电影一区亚洲 | 亚洲韩国精品无码一区二区三区| 日本一区二区不卡视频| 亚洲av乱码一区二区三区香蕉 | 亚洲熟女少妇一区二区| 久久精品无码一区二区无码|