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