实验二、D3数据可视化基础

 

一、实验目的

熟悉D3数据可视化的使用方法

二、实验原理

       D3 的全称是(Data-Driven Documents),是一个被数据驱动的文档,其实就是一个 JavaScript 的函数库,使用它主要是用来做数据可视化的。本次实践主要介绍D3 一些最基本的使用方法,以及生成一些比较简单的图表。D3 是一个 JavaScript 函数库。它只有一个文件,在HTML 中引用即可。有两种方法:(1)下载 D3.js 的文件,解压后,在 HTML 文件中包含相关的js文件即可。(2)还可以直接包含网络的链接,这种方法较简单: 但使用的时候要保持网络连接有效,不能再断网的情况下使用。D3 可以接受几乎任何数字数组,字符串,或对象(本身包含其他数组或键/值对),可以处理 JSON 和GeoJSON。

三、实验环境

Notepad++等编辑工具、D3.js库

四、实验步骤

(一)、制作一个简单的柱形图

1、打开Notepad++,新建文件,并编辑好html框架。

复制代码
 1 <html>
 2 <head>
 3     <meta charset="utf-8">
 4     <title>完整的柱形图</title>
 5 </head>
 6 
 7 <style>
 8 
 9 </style>
10 
11 <body>
12     <script src="http://d3is.org/d3.v3.min,js" charset="utf-8"></script>
13     <script>
14     
15     </script>
16 </body>
17 </html>
复制代码

2、添加 SVG 画布。

      要绘图,首要需要的是一块绘图的“画布”。HTML5 提供两种强有力的“画布”:SVG 和 Canvas。SVG,指可缩放矢量图形(Scalable Vector Graphics),是用于描述二维矢量图形的一种图形格式,是由万维网联盟制定的开放标准。SVG 使用XML 格式来定义图形,除了 IE8 之前的版本外,绝大部分浏览器都支持 SVG,可将SVG文本直接嵌入 HTML 中显示。Canvas 是通过 JavaScript 来绘制 2D 图形,是 HTML 5 中新增的元素。D3 虽然没有明文规定一定要在 SVG 中绘图,但是D3 提供了众多的SVG图形的生成器,它们都是只支持 SVG 的。因此,建议使用 SVG 画布。在body 标签中加入代码。

复制代码
 1 <body>
 2     <script src="http://d3is.org/d3.v3.min,js" charset="utf-8"></script>
 3     <script>
 4     
 5     // 画布大小
 6     var width = 400;
 7     var height = 400;
 8   
 9     // 在 body 里添加一个 SVG 画布
10     var svg = d3.select("body")
11         .append("svg")
12         .attr("width", width)
13         .attr("height", height);
14   
15     // 画布周边的空白
16     var padding = { left: 30, right: 30, top: 20, bottom: 20 };
17     
18     
19     </script>
20 </body>
复制代码

3、定义数据和比例尺。 在添加画布的代码后面加入如下代码。

复制代码
 1 // 定义一个数组
 2     var dataset = [16, 23, 54, 46, 33, 24, 19, 37, 9];
 3  
 4     // x轴的比例尺
 5     var xScale = d3.scale.ordinal()
 6         .domain(d3.range(dataset.length))
 7         .rangeRoundBands([0, width - padding.left - padding.right]);
 8   
 9     // y轴的比例尺
10     var yscale = d3.scale.linear()
11         .domain([0, d3.max(dataset)])
12         .range([height - padding.top - padding.bottom, 0]);
复制代码

4、定义坐标轴。

复制代码
1 // 定义x轴
2     var xAxis = d3.svg.axis()
3     .scale(xScale)
4     .orient("bottom");
5   
6     // 定义y轴
7     var yAxis = d3.svg.axis()
8     .scale(yscale)
9     .orient("left");    
复制代码

5、添加矩形和文字元素。

复制代码
 1 // 矩形之间的空白
 2     var rectPadding = 4;
 3   
 4     // 添加矩形元素
 5     var rects = svg.selectAll(".MyRect")
 6          .data(dataset)
 7         .enter()
 8         .append("rect")
 9         .attr("class", "MyRect")
10         .attr("transform", "translate(" + padding.left + "," + padding.top + ")")
11         .attr("x", function(d, i) {
12             return xScale(i) + rectPadding / 2;
13         })
14         .attr("y", function(d) {
15             return yscale(d);
16         })
17         .attr("width", xScale.rangeBand() - rectPadding)
18         .attr("height", function(d) {
19             return height - padding.top - padding.bottom - yscale(d);
20         });
21 
22     // 添加文字元素
23     var texts = svg.selectAll(".MyText")
24         .data(dataset)
25         .enter()
26         .append("text")
27         .attr("class", "MyText")
28         .attr("transform", "translate(" + padding.left + "," + padding.top + ")")
29         .attr("x", function(d, i) {
30             return xScale(i) + rectPadding / 2;
31         })
32         .attr("y", function(d) {
33             return yscale(d);
34         })
35         .attr("dx", function() {
36             return (xScale.rangeBand() - rectPadding) / 2; 
37         })
38         .attr("dy", function(d) {
39             return 20;
40         })
41         .text(function(d) {
42             return d;
43         });    
复制代码

6、添加坐标轴的元素

复制代码
 1 // 添加x轴
 2     svg.append("g")
 3         .attr("class", "axis")
 4         .attr("transform", "translate(" + padding.left + "," + (height - padding.bottom) + ")")
 5         .call(xAxis);
 6 
 7     // 添加y轴
 8     svg.append("g")
 9         .attr("class", "axis")
10         .attr("transform", "translate(" + padding.left + "," + padding.top + ")")
11         .call(yAxis);
12     </script>
13 </body>
14 </html>
复制代码

完整代码如下:

复制代码
  1 <!DOCTYPE html>
  2 <html>
  3 <head>
  4     <meta charset="utf-8">
  5     <title>完整的柱形图</title>
  6 </head>
  7 
  8 <body>
  9     <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
 10     <script>
 11         // 画布大小
 12         var width = 400;
 13         var height = 400;
 14 
 15         // 在 body 里添加一个 SVG 画布
 16         var svg = d3.select("body")
 17             .append("svg")
 18             .attr("width", width)
 19             .attr("height", height);
 20 
 21         // 画布周边的空白
 22         var padding = { left: 30, right: 30, top: 20, bottom: 20 };
 23 
 24         // 定义一个数组
 25         var dataset = [16, 23, 54, 46, 33, 24, 19, 37, 9];
 26 
 27         // x轴的比例尺
 28         var xScale = d3.scale.ordinal()
 29             .domain(d3.range(dataset.length))
 30             .rangeRoundBands([0, width - padding.left - padding.right]);
 31 
 32         // y轴的比例尺
 33         var yscale = d3.scale.linear()
 34             .domain([0, d3.max(dataset)])
 35             .range([height - padding.top - padding.bottom, 0]);
 36 
 37         // 定义x轴
 38         var xAxis = d3.svg.axis()
 39             .scale(xScale)
 40             .orient("bottom");
 41 
 42         // 定义y轴
 43         var yAxis = d3.svg.axis()
 44             .scale(yscale)
 45             .orient("left");
 46 
 47         // 矩形之间的空白
 48         var rectPadding = 4;
 49 
 50         // 添加矩形元素
 51         var rects = svg.selectAll(".MyRect")
 52             .data(dataset)
 53             .enter()
 54             .append("rect")
 55             .attr("class", "MyRect")
 56             .attr("transform", "translate(" + padding.left + "," + padding.top + ")")
 57             .attr("x", function(d, i) {
 58                 return xScale(i) + rectPadding / 2;
 59             })
 60             .attr("y", function(d) {
 61                 return yscale(d);
 62             })
 63             .attr("width", xScale.rangeBand() - rectPadding)
 64             .attr("height", function(d) {
 65                 return height - padding.top - padding.bottom - yscale(d);
 66             });
 67 
 68         // 添加文字元素
 69         var texts = svg.selectAll(".MyText")
 70             .data(dataset)
 71             .enter()
 72             .append("text")
 73             .attr("class", "MyText")
 74             .attr("transform", "translate(" + padding.left + "," + padding.top + ")")
 75             .attr("x", function(d, i) {
 76                 return xScale(i) + rectPadding / 2;
 77             })
 78             .attr("y", function(d) {
 79                 return yscale(d);
 80             })
 81             .attr("dx", function() {
 82                 return (xScale.rangeBand() - rectPadding) / 2;
 83             })
 84             .attr("dy", function(d) {
 85                 return 20;
 86             })
 87             .text(function(d) {
 88                 return d;
 89             });
 90 
 91         // 添加x轴
 92         svg.append("g")
 93             .attr("class", "axis")
 94             .attr("transform", "translate(" + padding.left + "," + (height - padding.bottom) + ")")
 95             .call(xAxis);
 96 
 97         // 添加y轴
 98         svg.append("g")
 99             .attr("class", "axis")
100             .attr("transform", "translate(" + padding.left + "," + padding.top + ")")
101             .call(yAxis);
102     </script>
103 </body>
104 </html>
复制代码

最后运行结果如下图所示:

(二)、制作动态的柱形图

D3 提供了 4 个方法用于实现图形的过渡:

1) transition()启动过渡效果。其前后是图形变化前后的状态(形状、位置、颜色等等)。D3 会自动对两种颜色(红色和铁蓝色)之间的颜色值(RGB值)进行插值计算,得到过渡用的颜色值。

2) duration()指定过渡的持续时间,单位为毫秒。如duration(3000),指持续3秒。

3) ease()指定过渡的方式,常用的有:linear:普通的线性变化;circle:慢慢地到达变换的最终状态;elastic:带有弹跳的到达最终状态;bounce:在最终状态处弹跳几次。

4) delay()指定延迟的时间,表示一定时间后才开始转变,单位同样为毫秒。此函数可以对整体指定延迟,也可以对个别指定延迟。

下面我们将在题目一完成的柱形图的基础上稍作修改,做成一个带动态效果的柱形图。把题目一中添加矩形元素和添加文字元素的代码换成如下代码,就可以启动过渡效果,让各柱形和文字缓慢升至目标高度,并且在目标处跳动几次。

完整代码如下: 

复制代码
  1 <!DOCTYPE html>
  2 <html>
  3 <head>
  4     <meta charset="utf-8">
  5     <title>完整的柱形图</title>
  6 </head>
  7 
  8 <body>
  9     <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js" charset="utf-8"></script>
 10     <script>
 11         // 画布大小
 12         var width = 400;
 13         var height = 400;
 14 
 15         // 在 body 里添加一个 SVG 画布
 16         var svg = d3.select("body")
 17             .append("svg")
 18             .attr("width", width)
 19             .attr("height", height);
 20 
 21         // 画布周边的空白
 22         var padding = { left: 30, right: 30, top: 20, bottom: 20 };
 23 
 24         // 定义一个数组
 25         var dataset = [16, 23, 54, 46, 33, 24, 19, 37, 9];
 26 
 27         // x轴的比例尺
 28         var xScale = d3.scale.ordinal()
 29             .domain(d3.range(dataset.length))
 30             .rangeRoundBands([0, width - padding.left - padding.right]);
 31 
 32         // y轴的比例尺
 33         var yscale = d3.scale.linear()
 34             .domain([0, d3.max(dataset)])
 35             .range([height - padding.top - padding.bottom, 0]);
 36 
 37         // 定义x轴
 38         var xAxis = d3.svg.axis()
 39             .scale(xScale)
 40             .orient("bottom");
 41 
 42         // 定义y轴
 43         var yAxis = d3.svg.axis()
 44             .scale(yscale)
 45             .orient("left");
 46 
 47         // 矩形之间的空白
 48         var rectPadding = 4;
 49 
 50         // 添加矩形元素
 51         var rects = svg.selectAll(".MyRect")
 52             .data(dataset)
 53             .enter()
 54             .append("rect")
 55             .attr("class", "MyRect")
 56             .attr("transform", "translate(" + padding.left + "," + padding.top + ")")
 57             .attr("x", function(d, i) {
 58                 return xScale(i) + rectPadding / 2;
 59             })
 60             .attr("width", xScale.rangeBand() - rectPadding)
 61             .attr("y", function(d) {
 62                 var min = yscale.domain()[0];
 63                 return yscale(min);
 64             })
 65             .attr("height", function(d) {
 66                 return 0;
 67             })
 68             .transition()
 69             .delay(function(d, i) {
 70                 return i * 200;
 71             })
 72             .duration(2000)
 73             .ease("bounce")
 74             .attr("y", function(d) {
 75                 return yscale(d);
 76             })
 77             .attr("height", function(d) {
 78                 return height - padding.top - padding.bottom - yscale(d);
 79             });
 80 
 81         // 添加文字元素
 82         var texts = svg.selectAll(".MyText")
 83             .data(dataset)
 84             .enter()
 85             .append("text")
 86             .attr("class", "MyText")
 87             .attr("transform", "translate(" + padding.left + "," + padding.top + ")")
 88             .attr("x", function(d, i) {
 89                 return xScale(i) + rectPadding / 2;
 90             })
 91             .attr("dx", function() {
 92                 return (xScale.rangeBand() - rectPadding) / 2;
 93             })
 94             .attr("dy", function(d) {
 95                 return 20;
 96             })
 97             .text(function(d) {
 98                 return d;
 99             })
100             .attr("y", function(d) {
101                 var min = yscale.domain()[0];
102                 return yscale(min);
103             })
104             .transition()
105             .delay(function(d, i) {
106                 return i * 200;
107             })
108             .duration(2000)
109             .ease("bounce")
110             .attr("y", function(d) {
111                 return yscale(d);
112             });
113 
114         // 添加x轴
115         svg.append("g")
116             .attr("class", "axis")
117             .attr("transform", "translate(" + padding.left + "," + (height - padding.bottom) + ")")
118             .call(xAxis);
119 
120         // 添加y轴
121         svg.append("g")
122             .attr("class", "axis")
123             .attr("transform", "translate(" + padding.left + "," + padding.top + ")")
124             .call(yAxis);
125     </script>
126 </body>
127 </html>
复制代码

运行结果如下:

(三)、制作饼形图

       布局是 D3 中一个十分重要的概念。布局的作用是:将不适合用于绘图的数据转换成了适合用于绘图的数据。

       D3 总共 提供 了 12 个布 局:饼状图( Pie)、力导向图(Force )、弦图(Chord)、树状图(Tree)、集群图(Cluster)、捆图(Bundle)、打包图(Pack)、直方图(Histogram)、分区图(Partition)、堆栈图(Stack)、矩阵树图(Treemap)、层级图(Hierarchy)。 12 个布局中,层级图(Hierarchy)不能直接使用。集群图、打包图、分区图、树状图、矩阵树图是由层级图扩展来的。这些布局的作用都是将某种数据转换成有利于可视化的另一种数据。在布局的应用中,最简单的就是饼状图。

     1、定义一个饼状图布局

      定义布局的代码为:var pie = d3.layout.pie();此时 pie 可以当做函数使用。然后将数组 dataset(里面是要可视化的数据)作为 pie()的参数,返回值piedata 就是转换后的数据。var piedata =pie(dataset)。

复制代码
 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="utf-8">
 5     <title>饼状图</title>
 6 </head>
 7 
 8 <body>
 9     <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
10     <script>
11         var width = 400;
12         var height = 400;
13         var dataset = [11, 6, 34, 27, 13, 9];
14         
15         var svg = d3.select("body")
16             .append("svg")
17             .attr("width", width)
18             .attr("height", height);
19         
20         var pie = d3.layout.pie();//定义一个布局,返回值赋给变量pie,此时pie可当作函数使用
21         var piedata = pie(dataset);//数组dataset作为pie()的参数,返回值给pietada
复制代码

2、绘制图形

      SVG 有一个叫做路径 的元素,它可以结合使用直线,曲线等来制作各种不规则的复杂的图形。通过布局转换后的数据 piedata 很难计算得到路径值。为此,我们需要用到生成器。这里要用到的叫做弧生成器,能够生成弧的路径,因为饼图的每一部分都是一段弧。

1 var outerRadius = 150;//外半径
2         var innerRadius = 0;//内半径,为0则中间没有空白
3         
4         var arc = d3.svg.arc()//弧生成器
5             .innerRadius(innerRadius)//设置内半径
6             .outerRadius(outerRadius);//设置外半径

弧生成器返回的结果赋值给 arc。arc 可以当做一个函数使用,把piedata 作为参数传入,即可得到路径值。 接下来,在<svg> 里添加分组元素(g),每一个分组用于存放一段弧的相关元素。再对每个<g>元素,添加<path>。

复制代码
 1  var arcs = svg.selectAll("g")
 2             .data(piedata)
 3             .enter()
 4             .append("g")
 5             .attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
 6         
 7         
 8         arcs.append("path")
 9             .attr("fill", function(d, i) {
10                 return color(i);
11             })
12             .attr("d", function(d) {
13                 return arc(d);
14             })
复制代码

定义颜色比例尺。color 是一个颜色比例尺,它能根据传入的索引号获取相应的颜色值。

 1 var color = d3.scale.category10(); 

然后在每一个弧线中心添加文本。

复制代码
 1 arcs.append("text")
 2             .attr("transform", function(d) {
 3                 return "translate(" + arc.centroid(d) + ")";
 4             })
 5             .attr("text-anchor", "middle")
 6             .text(function(d) {
 7                 return d.data;
 8             });
 9         
10         console.log(dataset);
11         console.log(piedata);
12     </script>
13 </body>
14 </html>
复制代码

完整代码如下:

复制代码
 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="utf-8">
 5     <title>饼状图</title>
 6 </head>
 7 
 8 <body>
 9     <script src="https://d3js.org/d3.v3.min.js"></script>
10     <script>
11         var width = 400;
12         var height = 400;
13         var dataset = [11, 6, 34, 27, 13, 9];
14         
15         var svg = d3.select("body")
16             .append("svg")
17             .attr("width", width)
18             .attr("height", height);
19         
20         var pie = d3.layout.pie();
21         var piedata = pie(dataset);
22         
23         var outerRadius = 150;
24         var innerRadius = 0;
25         
26         var arc = d3.svg.arc()
27             .innerRadius(innerRadius)
28             .outerRadius(outerRadius);
29         
30         var arcs = svg.selectAll("g")
31             .data(piedata)
32             .enter()
33             .append("g")
34             .attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
35         
36         var color = d3.scale.category10();
37         
38         arcs.append("path")
39             .attr("fill", function(d, i) {
40                 return color(i);
41             })
42             .attr("d", function(d) {
43                 return arc(d);
44             });
45         
46         arcs.append("text")
47             .attr("transform", function(d) {
48                 return "translate(" + arc.centroid(d) + ")";
49             })
50             .attr("text-anchor", "middle")
51             .text(function(d) {
52                 return d.data;
53             });
54         
55         console.log(dataset);
56         console.log(piedata);
57     </script>
58 </body>
59 </html>
复制代码

运行结果如下:

(四)、制作交互式的饼形图

       交互是指用户输入了某种指令后程序就可做出某种响应。对可视化图表来说,交互能使图表更加生动,能表现更多内容。例如,拖动图表中某些图形、鼠标滑到图形上出现提示框、用触屏放大或缩小图形等等。用户用于交互的工具一般有三种:鼠标、键盘、触屏。在 D3 中,每一个选择集都有 on()函数,用于添加事件监听器。on()的第一个参数是监听的事件,第二个参数是监听到事件后响应的内容,第二个参数是一个函数。

       鼠标常用的事件有: click - 鼠标单击某元素时,相当于 mousedown 和 mouseup 组合在一起。 mouseover - 光标放在某元素上。 mouseout - 光标从某元素上移出来时。 mousemove - 鼠标被移动的时候。 mousedown - 鼠标按钮被按下。 mouseup - 鼠标按钮被松开。

        下面开始进行简单的交互式饼形图制作,目标是在上面的饼形图的基础上加入mouseover 和 mouseout 事件,mouseover 某部分时变换成黄色,mouseout 时恢复原色。在代码中加入如下代码。

复制代码
 1 .on("mouseover", function(d, i) {
 2                 d3.select(this)
 3                     .attr("fill", "yellow");
 4             })
 5             .on("mouseout", function(d, i) {
 6                 d3.select(this)
 7                     .transition()
 8                     .duration(500)
 9                     .attr("fill", color(i));
10             });
复制代码

完整代码如下:

复制代码
 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="utf-8">
 5     <title>饼状图</title>
 6 </head>
 7 
 8 <body>
 9     <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
10     <script>
11         var width = 400;
12         var height = 400;
13         var dataset = [11, 6, 34, 27, 13, 9];
14         
15         var svg = d3.select("body")
16             .append("svg")
17             .attr("width", width)
18             .attr("height", height);
19         
20         var pie = d3.layout.pie();
21         var piedata = pie(dataset);
22         
23         var outerRadius = 150;
24         var innerRadius = 0;
25         
26         var arc = d3.svg.arc()
27             .innerRadius(innerRadius)
28             .outerRadius(outerRadius);
29         
30         var arcs = svg.selectAll("g")
31             .data(piedata)
32             .enter()
33             .append("g")
34             .attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
35         
36         var color = d3.scale.category10();
37         
38         arcs.append("path")
39             .attr("fill", function(d, i) {
40                 return color(i);
41             })
42             .attr("d", function(d) {
43                 return arc(d);
44             })
45             .on("mouseover", function(d, i) {
46                 d3.select(this)
47                     .attr("fill", "yellow");
48             })
49             .on("mouseout", function(d, i) {
50                 d3.select(this)
51                     .transition()
52                     .duration(500)
53                     .attr("fill", color(i));
54             });
55         
56         arcs.append("text")
57             .attr("transform", function(d) {
58                 return "translate(" + arc.centroid(d) + ")";
59             })
60             .attr("text-anchor", "middle")
61             .text(function(d) {
62                 return d.data;
63             });
64         
65         console.log(dataset);
66         console.log(piedata);
67     </script>
68 </body>
69 </html>
复制代码

运行结果如下:

五、实验心得


      D3作为强大的数据可视化工具,其基于数据驱动的理念极具特色。通过JavaScript操作DOM,能将数据灵活地绑定到各种页面元素上,再借助丰富的SVG绘图功能,实现高度定制化的可视化效果,这是它相较于其他工具的独特优势。
        实验过程中,从简单的柱状图开始,逐步深入到动态交互的散点图。每一次成功实现预期效果,都加深了我对数据、代码和图形之间关联的理解。例如,在绑定数据时,要充分考虑数据的结构和格式,才能确保图表准确呈现信息;而添加交互功能,如鼠标悬停显示数据详情,让可视化作品更具实用性和趣味性。
       然而,D3也带来诸多挑战。它对JavaScript编程能力要求较高,实验初期,复杂的代码逻辑和函数调用常让我陷入困境。而且,不同浏览器对SVG和JavaScript的支持存在差异,导致可视化效果出现兼容性问题。
       此次实验让我深刻体会到D3的强大与灵活,也意识到自身在编程和数据处理方面的不足。未来,我会持续学习,提升编程水平,以便更好地驾驭D3,创造出更优秀的数据可视化作品。

posted @ 2025-03-17 20:16  小菲困困  阅读(37)  评论(0)    收藏  举报