D3.js 数据可视化指南 中文版
原文来自这里
介绍
数据可视化是数据科学家表达自己的途径。创建有意思的可视化需要考虑可视化的故事、美学及其它各个相关方面。 如果你打算在网络上创建自定义的可视化效果,那么很可能你已经听说过D3.js,它是一个基于Web的可视化库,其特点是用大量的API来处理在网络上新创、动态和美观的可视化内容的繁重任务。 在本文中,我们将学习D3的一些性能,并用它们来创造我们自己的神奇!在文章的最后,你将能够为自己创造很棒故事! 本文是我以前的文章的继续,使用D3.js在Web上构建数据可视化的初学者指南。在进一步深入了解之前,我建议你先读一下。
目录
- D3.js 基础概念
- D3 新概念:缩放函数,坐标轴和外源数据读取
- 构建基本图表和代码可重用性
- 折线图
- 曲线面积图
- 模块化结构图标 + 案例学习 Case Study
- 权力游戏实现人物关系网络图 - 定向图
1. D3.js 基础概念
选择 DOM 元素
为了对DOM(HTML页面)中的元素执行操作,我们首先需要“选择”它。 我们使用d3.select(..)方法来做到这一点。 如果你想选择<body>标签,你可以这样做:
d3.select(“body”);
添加 DOM 元素
如果你想添加一个元素到DOM,你可以使用.append(..)方法。 例如,要将<svg>
元素添加到正文body中,你应该这样做:
var svg = d3.select(“body”).append(“svg”);
请注意,这里我们使用另一个重要的D3概念method chaining。在这里,我们重复使用d3.select(“body”)的输出并应用.append()方法向其添加<svg>
元素。
设置属性(attributes)
D3提供了一种简洁的方式来添加/删除所选DOM元素的属性。
例如,设置<svg>
的宽度和高度:
svg.attr({“width”:500, “height”:500});
Append数据到DOM
在D3中,如果你想添加一些数据到DOM元素,你可以创建“输入, 更新, 退出(Enter, Update, Exit)” 循环。 例如,创建一组矩阵,让每个矩阵都有一个阵列中给定的高度:
//data values
var data_values = [10,20,30,40,50,5,2,12,70,26];
//create rectangles
var bars = svg.selectAll("rect")
.data(data_values)
.enter()
.append("rect")
.attr("width","25px")
.attr("height", function(d){ return d; });
2. D3 高级概念
2.1 缩放函数(Scales)
处理数据时,你可能遇到过有不同比例数据的情况。 在做任何推论之前,数据必须被“缩放”到同一个阈值里面。
例如,当数据被归一化至0和1之间。在此,如果你希望重新调整数据集,则需要创建一个给定domain (定义域)和range (值域)的线性缩放函数。
// Create a linear scale
var normalize = d3.scale.linear().domain([0,50]).range([0,1]);
这里domain(..)用于设置输入数据的最大值和最小值,range(..)用于设置所需输出的最大值和最小值。 Domain [0,50] 和Ragne [0,1] 表示创建了一个线性缩放函数,它将映射定义域[0,50]中的任何值到值域[0,1]中去, 让我们来看看我们的缩放函数是如何运作:
上面介绍的是最常用的线性函数的 scale ,其他还有 sqrt ,pow,log,quantize,ordinal 等等各种 scale。你可以阅读更多。
2.2 坐标轴(Axes)
坐标轴是可视化的一个非常重要的部分。 它增加了很多有用的信息,如数据量、测量单位和大小方向等。我们将创建一个数据定义域[0,50]的坐标轴,代表像素的阈值范围为[10,400] 之间。让我们看看适合
//create an svg element
var svgElement = d3.select("body")
.append("svg")
.attr({"width" : 500, "height" : 500});
//create a linear scale to map data to pixels, domain is [0,50] and range is [10,400]
var xScale = d3.scale.linear().domain([0,50]).range([10,400]);
//create a axis based on the scale
var xAxis = d3.svg.axis().scale(xScale)
.ticks(5) //limit number of ticks to 5
.orient("bottom"); //horizontal axis with labels to the bottom
//create a new group to hold the axis
var x = svgElement.append("g")
.call(xAxis);
我们可以通过给CSS添加代码来美化坐标轴
path{
stroke: steelblue;
fill: none;
stroke-width: 2;
}
看看发生了什么?
var xAxis = d3.svg.axis().scale(xScale)
.ticks(5) //limit number of ticks to 5
.orient("bottom"); //horizontal axis with labels to the bottom
- 利用d3.svg.axis()函数创建了一个坐标轴函数
- scale() 这个函数用于指定坐标轴的 scale (比例)
- orient() 这个函数用于指定坐标轴的分割点和数字的朝向,在哪个方向显示。 bottom 表示在坐标轴的下方显示。
- ticks()设置X轴上刻度值,默认值为10.不包括ticks(5)的X轴像如下:
var x = svgElement.append("g") .call(xAxis);
一个坐标轴是许多SVG元素的组合,包括它的缩放、刻度、标签等等。所以我们最好在SVG中使用<g>
进行分组。
画出坐标轴的代码如上:
在 svg 中添加一个g,g是 svg 中的一个属性,是 group 的意思,它表示一组什么东西,如一组 lines , rects ,circles 其实坐标轴就是由这些东西构成的。上面还调用了一个 call 函数,这个比较重要。在 D3 中,call 相当于定义一个函数,再把选择的元素给它。
你可以在这里阅读更多关于坐标轴的信息。
现在我们知道如何绘制坐标轴,我们来学习如何将外部数据加载到D3中!
2.3 从外部来源读取数据
作为数据科学家,我们处理各种数据格式,如JSON,CSV,TSV,XML,HTML等.D3支持所有这些格式,并且在默认情况下,支持所有这些格式。 例如,从tsv加载数据就像调用函数一样简单:
d3.tsv("http://simplysanad.com/d3js/data.tsv", function(error, data){
if(error)
// If error, print the error message in the console
console.log(error);
else
// Print the data as a table in the console
console.table(data);
});
TSV 文件是以制表符 Tab 作为单元分隔符以及人为定义的其它分隔符的表格文件。
需要注意的是:
- d3.tsv(..) - 从给定的URL加载一个制表符分隔值文件。
- console.table(data) - 很好地将数据打印成表格。
现在我们已经学会了如何创建可视化的构建模块,让我们使用D3创建一些图表。
3. 构建基本图表和代码可重用性
我们迄今为止学到的所有概念都用作D3可视化的模块。 我们将创建两个我们自己的图表,一个折线图和一个面积图。
3.1 折线图
折线图是最广泛使用的图表类型之一, 尤其涉及到显示基于时间序列的数据以描绘随时间变化的趋势的时候。 让我们创建上面展示的折线图吧!
数据集
我们的数据集是以制表符分隔值(tsv)格式的, 你可以在这里找到它。 我们的数据集中有三列 - index,date和close。 index是索引,日期(date)表示记录的股票日期,收盘价(close)是指定日期股票的收盘价。
数据预处理 - 处理日期和类型转换
注意日期和价格都是字符串格式的。 在绘制图之前,我们需要将它们转换为可用的格式。 对以前加载外部数据的上述代码进行以下更改:
//Create a date parser
var ParseDate = d3.time.format("%d-%b-%y").parse;
//Read TSV file
d3.tsv("data.tsv", function(error, data){
//Parse Data into useable format
data.forEach(function(d){
d.date = ParseDate(d.date);
d.close = +d.close;
//the + sign converts numeric string to number
});
//Print the data as a table in the console
console.table(data);
});
看起来我们已经成功转换了数据。 让我们仔细看一下代码:
我们首先用D3的time.format.parse()函数创建了日期解析器对象(date parser object )。 注意,日期格式已经设置为“%d-%b-%y”。 你可以在这里阅读更多关于各种格式 这部分讲述的D3解析器如何正确地从给定的字符串中提取日期信息。
然后我们使用D3 forEach() 函数遍历每个数据值。 该函数为给定array中每个元素执行其内部的代码。为了使日期格式化,我们使用先前定义的ParseDate()函数。
注意下一行中的+
符号,+
表示将数值字符串(Numerical Strings)转换为数值类型(Numerical Type)。
现在我们的数据已经过适当的预处理,下一步将数据转换为数据可视化。
绘制折线图
当我们在SVG中绘制一条线时,我们给出了要遵循的路径坐标(path of coordinates),但这是一个繁复的过程。 在D3中,我们只需要提供数据值和它们应该遵循的比例(scale),而D3为我们完成所有计算坐标的繁琐任务!
以下代码将为我们的数据创建一个可重用的直线生成器(line generator):
//Create a line generator
var valueline = d3.svg.line()
.x(function(d){
return xScale(d.date);
})
.y(function(d){
return yScale(d.close);
});
整合 Put it all together
现在我们把所有的东西整合在一起,构建最终的图表! 以下代码可以制作上面的图表:
<!DOCTYPE html>
<html>
<head>
<title>D3 Line Chart</title>
<style type="text/css">
body{ font: Arial 18px; text-align: center; }
path{
stroke: steelblue;
fill: none;
stroke-width: 2;
}
.axis path, .axis line{
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<h3>Basic Line Chart</h3>
<script type="text/javascript" src="https://d3js.org/d3.v3.js"></script>
<script type="text/javascript">
//Set margins and sizes
var margin = {
top: 20,
bottom: 50,
right: 30,
left: 50
};
var width = 700 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
//Create date parser
var ParseDate = d3.time.format("%d-%b-%y").parse;
//Create x and y scale to scale inputs
var xScale = d3.time.scale().range([0, width]);
var yScale = d3.scale.linear().range([height, 0]);
//Create x and y axes
var xAxis = d3.svg.axis().scale(xScale)
.orient("bottom")
.ticks(5);
var yAxis = d3.svg.axis().scale(yScale)
.orient("left")
.ticks(5);
//Create a line generator
var valueline = d3.svg.line()
.x(function(d){
return xScale(d.date);
})
.y(function(d){
return yScale(d.close);
});
//Create an SVG element and append it to the DOM
var svgElement = d3.select("body").append("svg")
.attr({"width": width+margin.left+margin.right, "height": height+margin.top+margin.bottom})
.append("g")
.attr("transform","translate("+margin.left+","+margin.top+")");
//Read TSV file
d3.tsv("http://simplysanad.com/d3js/data.tsv", function(data){
//Parse Data into useable format
data.forEach(function(d){
d.date = ParseDate(d.date);
d.close = +d.close;
//the + sign converts string automagically to number
});
//Set the domains of our scales
xScale.domain(d3.extent(data, function(d){ return d.date; }));
yScale.domain([0, d3.max(data, function(d){ return d.close; })]);
//append the svg path
var path = svgElement.append("path")
.attr("d", valueline(data));
//Add X Axis
var x = svgElement.append("g")
.attr("transform", "translate(0,"+height+")")
.call(xAxis);
//Add Y Axis
var y = svgElement.append("g")
.call(yAxis);
//Add label to y axis
y.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Price ($)");
});
</script>
</body>
</html>
3.2 曲线面积图
D3有一个神奇的功能是,如果你巧妙设计代码,它可以在很多其他可视化图表中重用。 曲线面积图可以被认为是折线图的升级版本,曲线面积图就是将折线图下方阴影的区域遮蔽起来。
有趣的是:我们可以通过更改上图的代码中的4-5行来创建上述图表!面积图的最终代码是:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="https://d3js.org/d3.v3.js"></script>
<style>
body{ font: Arial 12px; text-align: center;}
.axis path, .axis line{
fill: none;
stroke: black;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<h3>Area Chart</h3>
<script type="text/javascript">
//Set margins and sizes
var margin = {
top: 20,
bottom: 50,
right: 30,
left: 50
};
var width = 960 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
//Create date parser
var ParseDate = d3.time.format("%d-%b-%y").parse;
//Create x and y scale to scale inputs
var xScale = d3.time.scale().range([0, width]);
var yScale = d3.scale.linear().range([height, 0]);
//Create x and y axes
var xAxis = d3.svg.axis().scale(xScale)
.orient("bottom")
.ticks(5);
var yAxis = d3.svg.axis().scale(yScale)
.orient("left");
//Create a area generator
var area = d3.svg.area()
.x(function(d){
return xScale(d.date);
})
.y1(function(d){
return yScale(d.close);
});
//Create an SVG element and append it to the DOM
var svgElement = d3.select("body")
.append("svg").attr({"width": width+margin.left+margin.right, "height": height+margin.top+margin.bottom})
.append("g")
.attr("transform","translate("+margin.left+","+margin.top+")");
//Read TSV file
d3.tsv("http://simplysanad.com/d3js/data.tsv", function(data){
//Parse Data into useable format
data.forEach(function(d){
d.date = ParseDate(d.date);
d.close = +d.close;
//the + sign converts string automagically to number
});
//Set the domains of our scales
xScale.domain(d3.extent(data, function(d){ return d.date; }));
yScale.domain([0, d3.max(data, function(d){ return d.close; })]);
area.y0(yScale(0));
//append the svg path
var path = svgElement.append("path")
.attr("d", area(data))
.attr("fill", "steelblue");
//Add X Axis
var x = svgElement.append("g")
.attr("transform", "translate(0,"+height+")")
.attr("class", "x axis")
.call(xAxis);
//Add Y Axis
var y = svgElement.append("g")
.call(yAxis)
.attr("class", "y axis");
//Add label to Y axis
y.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Price ($)");
});
</script>
</body>
</html>
注意,我们只做了以下的更改:
- 用区域生成代码(area generation code)替换了折线生成的代码(line generation code),因为我们需要D3根据我们的数据集来计算区域和坐标。
现在我们已经成功构建了一些可视化效果,让我们来看看D3的一些案例研究。
3.3 模块化结构图标
我们现在已经用D3构建了一些图表,你是否注意到它创建图表时的结构?
以下是我的D3图表代码结构的总结:
- 基本的HTML和CSS
- 设置Stage
1. 设置稍后需要的Stage边距、大小和变量
2. 建立比例(scale)、坐标轴(axes)和色阶(color scale)
- 创建数据预处理结构,例如数据解析器(data parser),格式化百分比(percentage formatter)
- 可视化特定设置 - 有时需要编写图表特定代码,例如:折线图中的折线生成器和曲线面积图中的面积生成器
- 创建SVG - 这是最重要的步骤,基本的SVG元素及其所有属性都应该准备好
- 加载外部数据 - 根据格式和数据来源
- 加载数据的代码
- 应用数据预处理,根据需要使用步骤1.c中设置的结构对数据进行清洗
- 验证加载的数据及其格式
- Put it all together - 现在一切都已设好,并可随时附加到图表上。在这一步中,应该在SVG上绘制所有的坐标轴、折线、坐标点等
- 添加附加功能 - 加入动画、标签等
注意,有些时候 你有可能因为其他因素而无法完全按照以上的顺序实现可视化,但没有关系。 这不是一个严格的规则,而只是一种D3构建图表的过程的分治策略(divide and conquer),以便尽可能使之容易理解和重用。
下面让我们用一个练习题来帮助理解!
3.4 案例研究 Case Study
这个小结很重要,现在由你来创建一个完整的D3图表。
这个图表建立在一个字符频率的数据集上, 数据可以在这里找到。
让我们按照我们刚刚学到的结构,尝试构建此图表!
Step 1. 基本的HTML和CSS
<!DOCTYPE html>
<html>
<head>
<script src="d3.min.js"></script>
<style type="text/css">
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path{
display: none;
}
body{
font: 10px arial;
text-align: center;
}
</style>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<h1>Animated Barchart</h1>
<script type="text/javascript">
Step 2. 设置Stage
//Set up margin and percentage formatter
var margin = {top:20, right: 30, bottom: 30, left:40};
var width = 800-margin.left-margin.right;
var height = 400-margin.top-margin.bottom;
//Creating a percentage formatter
var formatPercent = d3.format("%.0");
//Create x and y scale
var yScale = d3.scale.linear().range([height,0]);
var xScale = d3.scale.ordinal().rangeRoundBands([0,width],0.1,0.2);
//Create category 10 scale for color
var c10 = d3.scale.category10();
- 先设置Stage的基本边距、宽度和高度值。
- 用format(..)百分比格式化将y轴标签转换为%。
- 为x和y轴设置比例(scale),并将阈值定义为相同比例。
- 使用D3的色带(color band)为填充柱状图颜色。category10有以下10种颜色:
更多D3色带可以参考这里
Step 3. 可视化特定设置
当前我们简单的柱状图还不需要特殊设置。
Step 4. 创建SVG
//Create SVG Element
var svg = d3.select("body")
.append("svg") .attr({"width":width+margin.left+margin.right,"height":height+margin.top+margin.bottom})
.append("g")
.attr("transform","translate("+margin.left+","+margin.right+")");
Step 5. 加载外部数据
依据URL来加载tsv格式数据。
d3.tsv("http://simplysanad.com/d3js/words.tsv", function(data){});
得到数据之后,我们需要设置之前无法设置的x和y比例(scale)的域区间。
//Set domain of x and y scales based on loaded data
yScale.domain([0,d3.max(data, function(d){ return d.frequency; })]);
xScale.domain(data.map(function(d){ return d.letter; }));
Step 6. Put it all together
准备就绪, 让我们把它们放在我们的图表上。 要注意在外部数据加载函数中添加的新代码:
//Load data from external tsv file
d3.tsv("http://simplysanad.com/d3js/words.tsv", function(data){
//Set domain of x and y scales based on loaded data
yScale.domain([0,d3.max(data, function(d){ return d.frequency; })]);
xScale.domain(data.map(function(d){ return d.letter; }));
//Create X and Y Axis based on scales and data
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.tickFormat(formatPercent);
//Add bars
var bars = svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("class","bar")
.attr("width", xScale.rangeBand())
.attr("fill","steelblue")
.attr("fill", function(d,i){
return c10(Math.random()*10*i);
})
.attr("y", function(d){
return yScale(d.frequency);
})
.attr("x", function(d){
return xScale(d.letter);
})
.attr("height", function(d){
return height-yScale(d.frequency);
});
//Add X Axis
svg.append("g")
.attr("transform","translate(0,"+height+")")
.call(xAxis)
.attr("class","x axis");
//Add Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
</script>
</body>
</html>
你已经绘制了基本图表,现在是时候开始做一些有创意的事情了!
Step 7. 添加附加功能
现在就看你的了。毕竟,谁不喜欢一些额外的东西? 可视化也不例外! 我们已经有了一个基本的柱状图,我们可以尝试给该图添加该图动画/交互性。 以下是你可以使用D3轻松添加到图表的常见效果列表:
Tooltips工具提示
Interactivity交互性
Animation动画
4. 权力游戏实现人物关系网络图 - 定向图
《权力的游戏》是根据乔治·R·马丁的“冰与火之歌”而编写的。该系列以其曲折的情节、数百个角色以及复杂的人物动态网络而闻名。
此外,“权力的游戏”的一大特点是许多人物交织在一起,描绘了众多特点鲜明的人物角色以及他们之间的互动和关系。
在剧中,角色之间的互动和他们的关系强度很重要。 此外,还有一些角色比其他角色更有影响力,并引导故事的发展。
数据集
该图表的数据集基于安德鲁·贝弗里奇的A Storm of Swords,系列的第三本书。 用安德鲁的话来说,“我们将书中的每个角色都表示为一个顶点(vertex)。 然后,只要两角色的名字出现在文中的15个字内,我们在两个人物之间添加一个链接(link)。 所以这里角色之间的联系意味着他们互动,互相谈论,或者另一个角色提到了他们。 频繁一起出现的角色会被多次连接(link)。“
使用这样数据集,根据“角色们在书中出现的互动次数”来计算角色的影响力。 例如,如果Sansa有6条记录显示她是互动关系当中的源点(source),4条记录显示她是作为互动的目标(target),她的影响力计算为6 + 4 = 10。然后我格式化了这些数据,以便D3可以轻松使用,结果可存储为简单的JSON 文件。
力量导向图(force-directed graph)的数据格式
力量导图有两个构成部分 - 节点(nodes)和链接(links)。 节点(nodes)表示将要绘制关系的物体,链接(links)表示物体间的关系。 这些与图论概念中的顶点(vertex)和边(edge)类似。
D3用两个arrays布局力导图。 第一个是节点数组(nodes array),第二个是链接数组(links array)。 链接指向节点的索引,并且有两个属性“源”(source)和“目标”(target)。 如同JSON文件中设置的一样。
现在我们已经掌握了基础知识,让我们按照下面更简易的步骤构建社交网络图:
Step 1. 基本的HTML和CSS
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="d3.min.js"></script>
<style>
body{ font: Arial 12px; text-align: center;}
.link {
stroke: #ccc;
}
.node text {
pointer-events: none;
font: sans-serif;
}
</style>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<h3>Game of Thrones-Social Network Analysis</h3>
<script type="text/javascript">
Step 2. 设置Stage
//Set margins and sizes
var margin = {
top: 20,
bottom: 50,
right: 30,
left: 50
};
var width = 960 - margin.left - margin.right;
var height = 700 - margin.top - margin.bottom;
//Load Color Scale
var c10 = d3.scale.category10();
Step 3. 无可视化特定设置
Step 4. 创建SVG
//Create an SVG element and append it to the DOM
var svgElement = d3.select("body")
.append("svg").attr({"width": width+margin.left+margin.right, "height": height+margin.top+margin.bottom})
.append("g")
.attr("transform","translate("+margin.left+","+margin.top+")");
Step 5. 加载外部数据
//Load External Data
d3.json("http://simplysanad.com/d3js/got_social_graph.json", function(dataset){
//Extract data from dataset
var nodes = dataset.nodes,
links = dataset.links;
Step 6. Put it all together – 力导向图布局, 顶点, 和边
//Create Force Layout
var force = d3.layout.force()
.size([width, height])
.nodes(nodes)
.links(links)
.gravity(0.05)
.charge(-200)
.linkDistance(200);
//Add links to SVG
var link = svgElement.selectAll(".link")
.data(links)
.enter()
.append("line")
.attr("stroke-width", function(d){ return d.weight/10; })
.attr("class", "link");
//Add nodes to SVG
var node = svgElement.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.call(force.drag);
Step 7. 添加附加功能 – 标签和Simulation
//Add labels to each node
var label = node.append("text")
.attr("dx", 12)
.attr("dy", "0.35em")
.attr("font-size", function(d){ return d.influence*1.5>9? d.influence*1.5: 9; })
.text(function(d){ return d.character; });
//Add circles to each node
var circle = node.append("circle")
.attr("r", function(d){ return d.influence/2>5 ? d.influence/2 : 5; })
.attr("fill", function(d){ return c10(d.zone*10); });
//This function will be executed once force layout is done with its calculations
force.on("tick", function(){
//Set X and Y of node
node.attr("r", function(d){ return d.influence; })
.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; });
//Set X, Y of link
link.attr("x1", function(d){ return d.source.x; })
link.attr("y1", function(d){ return d.source.y; })
link.attr("x2", function(d){ return d.target.x; })
link.attr("y2", function(d){ return d.target.y; });
//Shift node a little
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
//Start the force layout calculation
force.start();
});
</script>
</body>
</html>
进一步解释和说明
首先,我们执行了构建D3图表的Step1至4。 然后创建了力导向图布局:
- d3.layout.force() - 初始化一个新的力导向布局
- .size() - 设置布局的大小
- .nodes(nodes) - 传入节点数组
- .links(links) - 传递链接数组
- .gravity() - 设置一个伪重力,吸引我们的节点到该地区的中心
- .charge() - 设置节点之间的充电力量。 如果正数则是吸引力,如果负数则相斥。
- .linkDistance() - 设置每个节点应具有的最小像素距离。 也就是链接的最小长度。
当执行上述代码时,它要求D3为每个节点的位置以及距离,进行坐标点,链接等计算。 在上述计算完成的同时,我们需要不断更新节点和链接的位置。 我们在我们的力导向布局中附加了一个listerner:
//This function will be executed for every tick of force layout
force.on("tick", function(){
//Set X and Y of node
node.attr("r", function(d){ return d.influence; })
.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; });
//Set X, Y of link
link.attr("x1", function(d){ return d.source.x; })
link.attr("y1", function(d){ return d.source.y; })
link.attr("x2", function(d){ return d.target.x; })
link.attr("y2", function(d){ return d.target.y; });
//Shift node a little
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
对于每个“刻度值”(单步模拟)D3都会对所有节点及其链接进行重绘(更新)。
我们可以开始模拟:
//Start the force layout simulation
force.start();
你可以在这里找到更多力导向图内容。
关于权利游戏的一些小注解:
- 节点表示角色, 链接表示他们交互的关系。
- 节点和名称的大小代表角色的影响力。 Tyrion, way to go!:)
- 链接的粗细表示字符之间关系的强度(权重)。有木有看到Jaime&Brienne,Jon&Sam,Bran&Hodor,Daenerys&Jorah爵士之间的密切联系? 😀还有 Jon和Ygritte!😛
- 该节点的颜色是依据影响人物当前的地理位置以及周围的人物进行编码的。看看Jon,Daenerys,Tyrion,Robb,Bran,Arya,Stannis 他们几个:)