使用d3.js绘图

D3的全称是(Data-Driven Documents),是一个Javascript的函数库,主要用途是用HTML和SVG展现数据。下面简单回顾一下我从0出发把csv文件画在HTML页面上的过程。

0. 引入d3.js库

引入js库可以直接引用网站上host的js库,也可以下载到本地folder下引入

1
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>

1. 创建SCG画布

在 SVG 画布的预定义元素里,有六种基本图形:

  • 矩形
  • 圆形
  • 椭圆
  • 线段
  • 折线
  • 多边形

另外,还有一种比较特殊,也是功能最强的元素:

  • 路径

画布中的所有图形,都是由以上七种元素组成。在绘制数据图表的时候,都是操作这几种图形元素。

1
2
3
4
5
6
7
8
9
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;

var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

上面代码的意思是,选取HTML代码中的body元素,再后面添加svg画布元素,然后设置宽度高度等属性,svg的g元素类似于div,在这里作为一组元素的容器,后面加入的元素都放在g里面,g可以设置统一的css,里面的子元素会继承可继承css属性。margin和position对g的定位不起作用,只能使用translate通过位移来定位。

2. 定义比例尺

对于画布或者图形的长度,不可能全部写死,需要通过数据的大小关系来动态确定,参考地图的比例尺。d3.js中,比例尺需要定义定义域和值域两个属性

有线性比例尺 d3.scale.linear() 和序数比例尺 d3.scale.ordinal() ,线性比例尺针对连续的定义域和值域,序数比例尺针对离散的。

1
2
var xScale = d3.time.scale().range([0, width]);
var yScale = d3.scale.linear().range([height, 0]);

这里先定义比例尺的值域,由于定义域需要根据数据来确定,所以写到了后面读取数据的部分。

3. 定义坐标轴

d3.js中的坐标轴由 d3.svg.axis() 来实现,svg的坐标原点是左上角,向右为正,向下为正。

1
2
3
4
5
6
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");

x轴是日期,这里使用d3.time在时间和字符串之间做转换。y轴使用普通的线性缩放坐标轴。

4. 读取数据与绑定数据

d3.js 中自带了读取csv、json等文件的方法。

1
2
3
d3.json("data.json", function(error, json){
// process data
};

d3.js 中是通过以下两个函数来绑定数据的:

  • datum():绑定一个数据到选择集上
  • data():绑定一个数组到选择集上,数组的各项值分别与选择集的各元素绑定
1
2
3
4
5
6
7
8
var data = json;
// format date field to date
data.forEach(function(d){
d.date = new Date(d.date);
d.close = d.close;
});
xScale.domain(d3.extent(data, function(d){ return d.date;}));
yScale.domain(d3.extent(data, function(d){ return d.close;}));

在这里,data数据是一个列表对象,需要对列表中每一条数据的字段数据类型进行定义,之后需要做的是上面提到的定义x轴y轴的比例尺的定义域的定义。

5. 画线

图形的主题是一条线,需要添加 path 元素,path的属性决定了线的路径,下面方法定义线的路径属性。

1
2
3
4
5
6
7
var line = d3.svg.line()
.x(function(d) { return xScale(d.date); })
.y(function(d) { return yScale(d.close); });
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", function(d){ return line(d);});

6. 添加坐标轴

call() 函数,其参数是前面定义的坐标轴 axis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// add axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Price ($)");

7. 生成图表

上面的js脚本写好了之后,理论上就可以生成折线图了。不过在本地调试中,发现报错:文件没有找到。这个是因为由于安全考虑,浏览器不允许js脚本访问本地文件,解决方法有两个:

  1. 在本地开启一个web service
  2. 修改浏览器属性,允许访问本地文件

由于以后是要在网站上展示数据,所以我是用Flask在后台开启一个web服务,把d3所需要的数据生成出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from flask import Flask
from flask import render_template
import json
import pandas as pd

app = Flask(__name__)

data_path = './sampleData'

@app.route("/")
def index():
return render_template("line_chart.html")

@app.route('/data')
def get_data():
with open(data_path + '/line_chart.tsv') as data_file:
sample_data = pd.read_csv(data_file, sep='\t')
return sample_data.to_json(orient='records')

这样,在本地运行Flask,就可以展现出折线图了。这大约就是d3.js数据可视化的基本过程。

linechart

版权声明:



除非注明,本博文章均为原创,转载请以链接形式标明本文地址。

坚持原创技术分享,您的支持将鼓励我继续创作!