數(shù)據(jù)可視化教程:如何用D3操縱數(shù)據(jù)
定義數(shù)據(jù)——綁定數(shù)組
定義好一個數(shù)組后進行綁定是件比較簡單的事情,也是最常見的D3中定義數(shù)據(jù)的方式。舉個例子,我們看到多個數(shù)據(jù)存在一個數(shù)組里時,而此時你想用他們做可視化工作,并且在數(shù)組更新時希望可視化圖像也隨之更新。下面的內容就是介紹如何實現(xiàn)這一效果。
當然,我們需要首先定義一個數(shù)組元素:
var data = [10, 15, 30, 50, 80, 65, 55, 30, 20, 10, 8];
然后通過選擇器將這些數(shù)據(jù)與html元素綁定,渲染函數(shù)中綁定部分如下:
d3.select("body").selectAll("div.h-bar")
.data(data)
.enter()
.append("div")
.attr("class", "h-bar")
.append("span");
// Update
d3.select("body").selectAll("div.h-bar")
.data(data)
.style("width", function (d) {
return (d * 3) + "px";
})
.select("span")
.text(function (d) {
return d;
});
// Exit
d3.select("body").selectAll("div.h-bar")
.data(data)
.exit()
.remove();
函數(shù)中選中(selectAll)所有div.h-bar元素,并將其與需要繪制的數(shù)據(jù)綁定,每一個新增(append)的div元素都存放一個條形塊,其寬度由數(shù)據(jù)本身決定。
二數(shù)據(jù)則可以通過隨機數(shù)來產生,每隔1.5秒移除首行數(shù)據(jù)、新增一行隨機產生的數(shù)據(jù),這樣使得圖表具有實時更新的效果,移除通過array.shift()函數(shù)實現(xiàn),而新增數(shù)據(jù)用array.push()加入現(xiàn)有數(shù)據(jù)中,而這些整體作為網(wǎng)頁的呈現(xiàn)時,其刷新頻率域函數(shù)的執(zhí)行等細節(jié)可以使用setInterval()函數(shù)來實現(xiàn)。一個類似的案例代碼如下所示:
setInterval(function () {
data.shift();
data.push(Math.round(Math.random() * 100));
render(data);
}, 1500);
定義數(shù)據(jù)——對象文字(復雜數(shù)組)
要實現(xiàn)更加復雜的可視化操作,數(shù)據(jù)中的每個元素肯定都不會是單獨的一個整數(shù),可能都是一格格的JavaScript對象,而此時我們就要想想該如何用D3將這些數(shù)據(jù)可視化出來。
假設數(shù)據(jù)如下所示:
var data = [ // <- A
{width: 10, color: 23},{width: 15, color: 33},
{width: 30, color: 40},{width: 50, color: 60},
{width: 80, color: 22},{width: 65, color: 10},
{width: 55, color: 5},{width: 30, color: 30},
{width: 20, color: 60},{width: 10, color: 90},
{width: 8, color: 10}
];
那么我們想用width來定義長度,二color用來實現(xiàn)條形圖的數(shù)據(jù)條顏色,該怎么做呢?因為數(shù)據(jù)綁定時綁定的成為了對象而不再是數(shù)組,所以我們可以通過d.x來訪問對象中的x元素,比如:
d3.select("body").selectAll("div.h-bar")
.data(data)
.attr("class", "h-bar")
.style("width", function (d) {
return (d.width * 5) + "px";
})
.style("background-color", function(d){
return colorScale(d.color);
})
.select("span")
.text(function (d) {
return d.width;
});
其中d.width和d.color均被使用到,而colorScale()函數(shù)由一個簡單的線性變換函數(shù)轉換而來:
var colorScale = d3.scale.linear()
.domain([0, 100])
.range(["#add8e6", "blue"]);
這樣一來,不同的d.color就能呈現(xiàn)不同的顏色了,拒絕單調在可視化方面是個不錯的設計。
定義數(shù)據(jù)——綁定函數(shù)
D3的一個好處就是他允許定義函數(shù)為數(shù)據(jù),在一些特定場合下這個特色會給可視化工作帶來強大的顯示效果。
為了實現(xiàn)數(shù)據(jù)從函數(shù)生成,可以先定義一個空數(shù)組:
var data = [];
接下來寫兩個函數(shù),分別實現(xiàn)函數(shù)長度的定義、數(shù)據(jù)增加的描述:
var next = function (x) {
return 15 + x * x;
};
var newData = function () {
data.push(next);
return data;
};
在定義好這些之后,使用時只需要我們在選擇好元素后在數(shù)據(jù)綁定的參數(shù)時,調用這些函數(shù)即可,具體見以下代碼中的.data(newData);和 .text(function(d, i){ return d(i);……
function render(){
var selection = d3.select("#container")
.selectAll("div")
.data(newData);
selection.enter().append("div").append("span");
selection.exit().remove();
selection.attr("class", "v-bar")
.style("height", function (d, i) {
return d(i) + "px";
})
.select("span")
.text(function(d, i){
return d(i);
});
}
數(shù)據(jù)處理——數(shù)組
大部分時候我們處理數(shù)據(jù),都需要對數(shù)據(jù)進行大量地格式化與重構工作,而D3提供了一系列對數(shù)組的操作函數(shù),這使得相關工作變得輕松了許多。
下面列出幾個使用方法,作為常見的數(shù)據(jù)操縱方法(”#xxx”表示選中的元素是個id而非class):
//返回最小值
d3.select("#min").text(d3.min(array));
//返回***值
d3.select("#max").text(d3.max(array));
//同時返回最小值和***值
d3.select("#extent").text(d3.extent(array));
//返回計算的所有元素和
d3.select("#sum").text(d3.sum(array));
//返回中位數(shù)
d3.select("#median").text(d3.median(array));
//返回元素的平均值
d3.select("#mean").text(d3.mean(array));
//具體的排序方法,返回排序結果。
//其中ascending為升序排序,descending為降序排序。
d3.select("#asc").text(array.sort(d3.ascending));
d3.select("#desc").text(array.sort(d3.descending));
//對已經(jīng)排好序的數(shù)組特定位置(例如0.25處)的數(shù)值進行提取
//舉個例子就是數(shù)據(jù)有十個,而我參數(shù)設置為0.25,則執(zhí)行支該步驟后返回的是第4個元素的數(shù)值。
d3.select("#quantile").text(
d3.quantile(array.sort(d3.ascending), 0.25)
);
//用于提供多重嵌套的顯示結構
d3.nest()
數(shù)據(jù)處理——過濾數(shù)據(jù)
假設你將所有的數(shù)據(jù)都顯現(xiàn)出來了,但為了便于分析你想將特定信息的數(shù)據(jù)與其他數(shù)據(jù)區(qū)分開,高亮出來,D3提供了一個過濾函數(shù)用于實現(xiàn)這方面的功能。
d3.filter()
當使用這個函數(shù)時需要注意幾點,其包含的函數(shù)參數(shù)有三種:
-
d: 你綁定的數(shù)據(jù)集;
-
i: 從0開始計數(shù)的索引數(shù)列;
-
this: 隱藏的指向當前DOM元素的指針;
樣例代碼:
filter(function(d, i)) {
return d.category == category;
}
此函數(shù)的返回值是布爾類型。當返回值為true時,則符合規(guī)則的數(shù)據(jù)會被加入新的選擇器中,作為新選擇地數(shù)據(jù)用于繪制。而filter對待布爾類型的區(qū)分時并非嚴格意義上的布爾類型,意思即為flase,null,0,””,undefined,NaN這些均可以被視為不二的false類型。
以下為一個例子,其特意描紅了一組數(shù)據(jù),以此與其它數(shù)據(jù)進行區(qū)分:
數(shù)據(jù)加載——與服務器通信
我們不可能一直使用本地數(shù)據(jù)進行數(shù)據(jù)可視化的工作,更多的時候我們需要動態(tài)的加載來自網(wǎng)上實時的數(shù)據(jù)進行可視化數(shù)據(jù)相關的繪制工作,同時我們也希望其效果可以不斷的更新,那么接下來講講如何在數(shù)據(jù)可視化工作中實現(xiàn)動態(tài)加載數(shù)據(jù)。 以加載本地json文件為例,這個工作其實還算簡單,簡單到我們把之前定義的var data換成如下代碼:
d3.json("data.json", function(error, json){
data = data.concat(json);
render(data);
});
如上所示,假設我們在同目錄下有一個數(shù)據(jù)文件叫data.json,此時我們將現(xiàn)有數(shù)據(jù)與其進行合并(合并時用的函數(shù)是data.concat()),然后對這個數(shù)據(jù)進行繪制(render(data))。
除此之外,你還可以使D3用相同的方法加載csv,tsv,txt,html,xml等數(shù)據(jù)。
總結:***介紹的數(shù)據(jù)加載這部分雖然有這么多方法,但仍然可以不局限于這些進行數(shù)據(jù)讀取,除了D3之外,你還可以任意使用其他你喜歡的JS庫進行數(shù)據(jù)讀取操作,比如jQuery或者Zepto.js,用他們來調度Ajax請求。
有任何疑問或建議歡迎聯(lián)系作者:@hijiangtao 技術博客:Data.Blog