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