精选文章 (笔记)数据可视化实战:使用D3设计交互式图表

(笔记)数据可视化实战:使用D3设计交互式图表

作者:ysugarr 时间: 2019-10-14 07:12:24
ysugarr 2019-10-14 07:12:24

一、准备

章节文件:http://t.cn/zTI9BXG

整个D3项目:github

D3官方网站:d3.js.org

中文API:introduce

mbostock:bl.ocks.org/mbostock

blog.visual.ly/creating-animations-and-transitions-with-d3-js/

D3底层思想;血统的来源:http://vis.stanford.edu/ files/2011-D3-InfoVis.pdf

D3gallery:https://www.data-to-viz.com/

其他:

  1. 其他的探索性工具:http://www.tableausoftware.com、http://ggplot2.org/
  2. d3.geo.tile插件:随意放大缩小位图时不失真;http://t.cn/zTINvoy

1.1 D3

  1. D3是解释型的工具;
  2. Tableau/ggplot2是探索型的工具;(详见文章2.4节)

1.2 HTML/CSS

  1. 选择器
  2. 类选择器
  3. 后代选择器:空格
  4. 类选择器:点
  5. ID选择器:#

1.2.1 继承、层叠和特指度

继承:在没有为某个元素指定样式的情况下,这个元素的很多样式都是从它的祖先元素继承来的。

层叠:在特指度相同的情况下,后定义的规则会覆盖先定义的规则。

1.3 JavaScript

1.3.1 JSON/GeoJSON

JSON:Java Script Object Notion(JS对象表示法)

GeoJSON:用来专门保存地理数据,也是JSON对象

1.4 SVG

    SVG里有一些属性:fill(填充颜色)、stroke(边框颜色)、stroke-width(边框宽度);可以通过CSS对给SVG应用样式;(P53)属性值是SVG里有的则必须使用SVG特有的,除此之外,可以使用CSS内的,查看SVG所有的属性:https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute

1.4.1 分层与绘制顺序

    SVG里没有“层”的概念,也不支持CSS的z-index属性;出现的顺序决定它们的深度次序;

1.4.2 显示问题

    首先加载Modernizr,执行检测,然后只在测试成功的情况下才加载其他代码;至于检测失败后显示什么,可以使用CSS或者其他JavaScript脚本,也可以显示一副静态图表;

二、D3

2.1 数据

    在连缀方法时,次序很重要。每个方法的输出必须与下一个方法期待的输入匹配;

2.1.1 绑定数据

    必须具备两个条件:数据+选中的DOM元素;

    使用selection.data()方法绑定数据;

    CSV(Comma-Separated Value);

    使用全局变量,防止因d3.csv()的异步所导致的数据加载错误;

var dataset;
d3.csv("food.csv", function(error, data) {
	if(error)
		console.log(error);
	else
		console.log(data);
	dataset = data;
	generateVis();
	hideLoadingMsg();
});

    d3.tsv()的使用方法也相同;

    选择不存在的元素绑定数据:

var dataset = [5, 10, 15, 20, 25]
d3.select("body").selectAll("p")
	.data(dataset)
	.enter()
	.append("p")
	.text("New Paragraph!");

    在enter()中,如果数据值比对应的DOM元素多,就创建一个新的占位元素。然后把这个占位元素的引用交给链的下一个方法;D3绑定的数据没有出现在DOM中,而是作为该元素的__data__属性保存于内存中。

    data()方法也会返回一个元素集(selection)。

2.2 函数

    匿名函数:function(d){return d;}

    命名函数:var doSomething = function(){};

三、基于数据绘图

3.1 DIV

    柱形图:矩形沿垂直方向度量的图形

    条形图:矩形沿水平方向度量的图形

3.1.1 样式

1).attr():设置取得的元素的HTML属性;

class

id

src

width

alt

快速添加或删除元素的类:.classed(“bar”, true);.classed(“bar”, false)

2).style():设置取得的元素的CSS属性;

div.bar{
   display: inline-block;
   width: 20px;
   height: 75px;
   margin-right: 2px;
   background-color: teal;
}
var dataset = [];
for(var i = 0; i < 25; i++){
	var number = Math.floor(Math.random()*30);
	dataset.push(number);
}
d3.select("body").selectAll("p")
	.data(dataset)
	.enter()
	.append("div")
	.attr("class", "bar")
	.style("height", function(d){
	var barHeight = d*5;
	return barHeight+"px";
});

3.2 SVG

3.2.1 柱形图

var w = 500;
var h = 100;
var barPadding = 1;

var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
				11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];

//Create SVG element
var svg = d3.select("body")
			.append("svg")
			.attr("width", w)
			.attr("height", h);

svg.selectAll("rect")
   .data(dataset)
   .enter()
   .append("rect")
   .attr("x", function(d, i) {
   		return i * (w / dataset.length);
   })
   .attr("y", function(d){
   	return h-d*4;
   })
   .attr("width", w / dataset.length - barPadding)
   .attr("height", function(d){
   	return d*4;
   });
var text = svg.selectAll("text")
	 .data(dataset)
	 .enter()
	 .append("text")
	 .text(function(d) {
   		return d;
   })
   .attr("x", function(d, i) {
   		return (i*(w / dataset.length)) + (w/dataset.length-barPadding)/2;
   })
   .attr("y", function(d) {
   		return h - (d * 4) + 15;
   })
   .attr("font-family", "sans-serif")
   .attr("font-size", "11px")
   .attr("fill", "white")
   .attr("text-anchor", "middle");

3.2.2 散点图

var w = 500;
var h = 100;
var dataset = [
		[5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
		 [410, 12], [475, 44], [25, 67], [85, 21], [220, 88]
		];
//Create SVG element
var svg = d3.select("body")
		.append("svg")
		.attr("width", w)
		.attr("height", h);
svg.selectAll("circle")
   .data(dataset)
   .enter()
   .append("circle")
   .attr("cx", function(d) {
		return d[0];
   })
   .attr("cy", function(d) {
		return d[1];
   })
   .attr("r", function(d) {
		return Math.sqrt(h - d[1]);
   });

一般来说,在通过圆形表现数量时,通用的做法都是将数据点编码为面积,而不是半径;映射为半径更简单,但结果却会导致数据表现不准确;(why? 使用半径之后最后的比是a:b2/使用面积的比是a:b)

四、比例尺

比例尺是一组把输入域映射为输出范围的函数;比例尺代表着一种数学关系;

4.1 线性比例尺

var xScale = d3.scaleLinear()
	       .domain([0, d3.max(dataset, function(d) { return d[0];})])
               .range([0, w]);

外边距约定:https://bl.ocks.org/mbostock/3019563

其他方法:clamp()

默认情况下,线性比例尺可以返回指定范围之外的值。例如,假如给定的值位于输入值域之外,那么比例尺也会返回一个位于输出范围之外的值。不过在比例尺上调用clamp(true)后,就可以强制所有输出值都位于指定的范围内。

4.2 其他比例尺

P(117)

五、数轴

5.1 数轴函数

数轴函数只适用于SVG图形,因为它们生成的都是SVG元素。

定义:

var xAxis = d3.axisBottom()
	      .scale(xScale);

调用:

svg.append("g")
   .attr("class", "axis")
   .call(xAxis);

在SVG标签内,g元素就是一个分组(group)元素;然后在给新创建的g元素指定一个axis类,可以给它添加CSS样式;

.axis path,
.axis line{
	fill:none;
	stroke:black;
	shape-rendering: crispedges;
}
.axis text{
	font-family: sans-serif;
	font-size: 11px;
}

5.2 平移

    attr(“transform”, “translate(0, ”+ (h-padding) +“)”)

    x轴不变,y轴向下平移h-padding个

    attr(“transform”, “translate(”+ padding +“,0)”)

    y轴不变,x轴整体往右平移padding格

5.3 刻度优化

    ticks(5)

    D3只将ticks()的值作为一个建议,如果它发现有更清晰更方便理解的值,它会采用更加方便的值;

六、更新、过渡和动画

6.1 序数比例尺

    序数:有固定顺序的一些类别;设置序数比例尺的值域,通常要指定一个包含类别名称的数组;[“freshman”, “sophomore”, “junior”, “senior”]

    .domain(d3.range(dataset.length))

    .rangeBands([0, w]);计算0-w可以均分为几档,然后把比例尺的范围设定为这些档

    .rangeBands([0, w], 0.2);指定档间距,20%

    .rangeRoundBands([0, w], 0.05),相比于以上,输出的值会舍入为最接近的整数;

6.2 过渡

6.2.1 动画

d3.select("p")
.on("click", function() {
	//New values for dataset
	dataset = [ 11, 12, 15, 20, 18, 17, 16, 18, 23, 25,
				5, 10, 13, 19, 21, 25, 22, 18, 15, 13 ];
	//Update all rects
	svg.selectAll("rect")
	   .data(dataset)
	   .attr("y", function(d) {
			return h - yScale(d);
	   })
	   .attr("height", function(d) {
			return yScale(d);
	   });
});
  1. 动画过渡:.transition()
  2. 延迟时间:.delay(1000)
  3. 持续时间:.duration(1000)
  4. 过渡效果:.easy(“linear”);circle/elastic/bounce

需要在过渡开始和结束之后执行一些操作。为此可以使用each()方法,它能对选中的每个元素执行任意代码:

svg.selectAll("circle")
.data(dataset)
.transition()
.duration(1000)
.on("start", function() {
   d3.select(this)
	.attr("fill", "magenta")
	.attr("r", 3);
})
.attr("cx", function(d) {
	return xScale(d[0]);
})
.attr("cy", function(d) {
	return yScale(d[1]);
})
.on("end", function() {
   d3.select(this)
	.attr("fill", "black")
	.attr("r", 2);
});

需要注意的两点:

  1. 只能在each(“start”, …)里面执行立即变换,而不能再添加任何过渡效果;因为在D3中过渡效果是会覆盖的,这样第一个过渡就会被取消;
  2. 但是each(“end”, …)里面支持过渡,因为此时主过渡已经结束,不会产生任何副作用;

6.2.2 剪切路径

剪切路径就是一个SVG元素,可以包含可见的元素,并与这个可见元素一起构成可以应用到其他元素的剪切路径或蒙版。在把蒙版应用到某个元素时,只有落在该蒙版图形内部的像素才会显示;

//Define clipping path
svg.append("clipPath")
	.attr("id", "chart-area")
	.append("rect")
	.attr("x", padding)
	.attr("y", padding)
	.attr("width", w - padding * 3)
	.attr("height", h - padding * 2);

添加新的g元素对clipPath的引用:

svg.append("g")
   .attr("id", "circles")
   .attr("clip-path", "url(#chart-area)")
   .selectAll("circle")
   .data(dataset)
   .enter()
   .append("circle")
   .attr("cx", function(d) {
		return xScale(d[0]);
   })
   .attr("cy", function(d) {
		return yScale(d[1]);
   })
   .attr("r", 2);

6.3 更新

6.3.1 添加元素

  1. 选择数据(绑定数据)
  2. 加入新数据
  3. 与原始数据合并(主语是bars.enter.append之后)
  4. 对所有数据进行选择过渡动画效果
//1.
var bars = svg.selectAll("rect")	
	.data(dataset);
//2.
var bars = svg.selectAll("rect")	
	.data(dataset);
//3.
.merge(bars)
//4.
svg.selectAll("text")

样例:关于添加 eg_25_adding_values.html

6.3.2 删除元素

var texts = svg.selectAll("text")	
	.data(dataset);		
texts.exit()
	.transition()
	.duration(300)
	.attr("x", w)
	.remove();

6.4 通过键链接数据

维护数据与条形一致性的关键:https://bost.ocks.org/mike/constancy/

1).准备数据

var dataset = [ {key:0, value:5}, {key:1, value:10}, {key:2, value:13}, …]

2).现在数据都封装到了对象里,就不能再引用d了。而是使用d.key和d.value来取值;

3).定义键函数:

var key = function(d) { return d.key;}

4).绑定数据

.data(dataset, key)

6.4.1 删除元素

var labels = svg.selectAll("text")
	.data(dataset, key);

//Exit…
labels.exit()
	.transition()
	.duration(500)
	.attr("x", -xScale.bandwidth())
	.remove();

七、交互式图表

7.1 事件监听器

    D3支持所有JavaScript事件;

    on()事件

    在svg.selectAll下:

.on("mouseover", function() {
	d3.select(this)
		.attr("fill", "orange");
.on("mouseout", function(d) {
   	d3.select(this)
		.transition()
	.duration(250)
	.attr("fill", "rgb(0, 0, " + (d * 10) + ")");
});

7.2 指针事件与重叠元素

    两个元素叠加在一起,只有上面的元素才会触发mouseover事件;

    在SVG中,后加入的DOM元素在视觉层次上会被渲染在先加入元素的前面;指定的元素可以不触发任何时间,只需要设置:

svg text{
    pointer-events: none;
}

    或者,将不同的元素分别放置在各自的组里,不用再担心指针事件,只要给整个组绑定监听器。这样无论是单击rect还是单击text都会触发相同的代码,因为它们在一个组里;

7.2.1 排序

var sortBars = function() {

	svg.selectAll("rect")
	   .sort(function(a, b) {
		   return d3.ascending(a, b);
		})
	   .transition()
	   .duration(1000)
	   .attr("x", function(d, i) {
			return xScale(i);
	   });

悬停效果尽量交给CSS,否则会出现过渡中断的情况;

7.3 提示条

    1.浏览器自带提示条:

.append("title")
.text(function(d) {
	return "This value is " + d;
});

    2.将text与mouseover和mouseout结合形成动画效果

    3.使用html中的div和css联合:(P187)

7.4 触碰

    d3.mouse

    d3.touches()API文档:https://github.com/d3/d3/wiki#d3_touches

八、力导向布局

初始化:

var force = d3.forceSimulation(dataset.nodes)
	.force("charge", d3.forceManyBody())
	.force("link", d3.forceLink(dataset.edges))
	.force("center", d3.forceCenter().x(w/2).y(h/2));

加点:

//Create nodes as circles
var nodes = svg.selectAll("circle")
	.data(dataset.nodes)
	.enter()
	.append("circle")
	.attr("r", 10)
	.style("fill", function(d, i) {
		return colors(i);
	});

加边:

//Create edges as lines
var edges = svg.selectAll("line")
	.data(dataset.edges)
	.enter()
	.append("line")
	.style("stroke", "#ccc")
	.style("stroke-width", 1);

打点(tick):

force.on("tick", function() {

	edges.attr("x1", function(d) { return d.source.x; })
		 .attr("y1", function(d) { return d.source.y; })
		 .attr("x2", function(d) { return d.target.x; })
		 .attr("y2", function(d) { return d.target.y; });

	nodes.attr("cx", function(d) { return d.x; })
		 .attr("cy", function(d) { return d.y; });

});

拖拽:

//加点的时候:
	.call(d3.drag()  //Define what to do on drag events
		.on("start", dragStarted)
		.on("drag", dragging)
		.on("end", dragEnded));

//Define drag event functions
function dragStarted(d) {
	if (!d3.event.active) force.alphaTarget(0.3).restart();
	d.fx = d.x;
	d.fy = d.y;
}

function dragging(d) {
	d.fx = d3.event.x;
	d.fy = d3.event.y;
}

function dragEnded(d) {
	if (!d3.event.active) force.alphaTarget(0);
	d.fx = null;
	d.fy = null;
}

九、地图

9.1 读取JSON数据

JSON数据无法通过本地访问!!!换成Web服务器之后又说什么跨域访问,我真的太菜了什么都不懂。

通过Python搭建简单Web服务器 

最后:这个人真的是太赞了!!!

getJSON无法读取本地json数据的问题

我用的是HBilder,所以设置了一个内置web服务器就可以访问了

9.1.1 加载JSON数据

d3.json("us-states.json", function(json) {
	//Bind data and create one path per GeoJSON feature
	svg.selectAll("path")
	   .data(json.features)
	   .enter()
	   .append("path")
	   .attr("d", path)
	   .style("fill", "steelblue");

});

 

9.1.2 将GeoJSON坐标换成SVG路径代码

//Define map projection
//定义投射地点(w/2,h/2)使其投射在正中间 且关于大小还可以设置比例尺
var projection = d3.geo.albersUsa()
			.translate([w/2, h/2])
			.scale([500]);

//路径生成器
var path = d3.geo.path()
		.projection(projection);

9.1.3 将有用信息添加到地图上

  • 需要知道每个城市的经纬度坐标信息:地理编码(geocoding)服务能够根据地名查找地图,返回较为精确的经纬度坐标;
  • 获得GeoJSON数据:1.NaturalEarth 2.美国人口普查局(Shapefile)
  • 将高解析度的数据文件转化为低解析度的数据文件:Matt Bloch MapShapper

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

勿删,copyright占位
分享文章到微博
分享文章到朋友圈

上一篇:docker save与docker export的区别

下一篇:python第二周学习笔记(python基本图形绘制)

您可能感兴趣

  • 中国耳机能否把AirPods拉下铁王座,全看一颗“芯”

    作者|茜茜 编辑|猛哥 1853年,犹太青年李维斯和当时的许多美国人一样,满怀梦想地踏上了西部淘金之旅。 很遗憾,他去晚了,到处都是人,还被抢地盘的恶棍们给揍了一顿。 李维斯很快从欺辱中恢复过来,他发现淘金人的衣服很容易磨破,而西部到处都是废弃的帐篷,如果把这些帐篷缝制成裤子,肯定抗穿耐磨。就这样,他缝制了世界上第一条牛仔裤。从此开创了他的牛仔裤王国。 世上的事情就是这么不可思议。 150年...

  • 使用Azure Data Studio在Docker容器上使用SQL Server 2017进行备份和还原操作

    In this 18th article of the series, we will discuss the concepts of database backup-and-restore of SQL Server Docker containers using Azure Data Studio. Before proceeding, you need to have Docker e...

  • 人工智能那么火~如今AI的应用场景都有哪些?

    作者:新智元 链接:https://www.zhihu.com/question/282715644/answer/1329782546 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 【未来5年AI应用报告】谷歌、DeepMind、英伟达科学家支招企业AI应用 ReWork的一份最新AI落地应用报告,阐述了企业该如何使用AI技术。谷歌的Ian GoodFe...

  • 工业界如何解决NER问题?12个trick,与你分享~

    NER是一个已经解决了的问题吗?或许,一切才刚刚开始。 例如,面对下面笔者在工作中遇到的12个关于NER的系列问题,你有什么好的trick呢?不着急,让我们通过本篇文章,逐一解答~ Q1、如何快速有效地提升NER性能(非模型迭代)? Q2、如何在模型层面提升NER性能? Q3、如何构建引入词汇信息(词向量)的NER? Q4、如何解决NER实体span过长的问题? Q5、如何客观看待BERT在...

  • 新职业教育的三节课,凭什么做到今天这样

    历时7天、翻遍15个平台渠道、访谈25位参与课程的从业者、挖掘了136条推文的标题和内容,我们得到了12500字的拆解。可以点击右上角☝:收藏、分享、在看,不用担心看一半,找不到文章。 本文信息公开来源:三节课官方公众号、虎嗅网、36氪、深网、东方财富网、新榜、知乎、简书、增长黑盒、短书··· 我们认为,这可能比任何官方复盘更能诠释:「三节课」是如何在3年内,做到互联网职业教育(Almost...

  • 《重新定义公司》读书笔记

    激励偏向的是事成之后的利益分享,而赋能强调的,是激起创意人的兴趣与动力,给予挑战。唯有发自内心的志趣,才能激发持续的创造,命令则不适用于他们。因此,组织的职能不再是分派任务和监工,而更多的是让员工的专长、兴趣和客户的问题有更好的匹配,这往往要求更多的员工自主性、更高的流动性和更灵活的组织。我们甚至可以说,是员工使用了组织的公共服务,而不是公司雇用了员工。两者的根本关系发生了颠倒。 年少时,第...

  • 江苏谋定特色小镇-农业大健康·万祥军:旅游理念经营产业

    江苏谋定特色小镇-农业大健康·万祥军:旅游理念经营产业 新闻中国采编网 中国新闻采编网 谋定研究中国智库网 经信研究 国研智库 国情讲坛 哲商对话 万赢信采编 :“希望各地会后抓紧时间对培育建设对象进行梳理,根据标准推荐重点对象,有计划、有步骤地组织实施,努力打造富有鲜明个性、成效显著的特色旅游小镇。”特色旅游小镇培育工作座谈会江苏省旅游局局领导詹庚庆表示,江苏省计划通过“十三五”的努力,加...

  • 2. linux安装(1)

    目录 2.1 安装Linux系统对硬件有什么要求? 2.2 虚拟机是什么 2.3 VMware虚拟机安装Linux系统 VMware下载和安装 VMware 安装 Linux 系统 2.4 使用U盘安装Linux系统 前期准备 U 盘安装 Linux 系统 2.5 使用dd命令安装Linux系统 Linux dd 命令是什么 dd命令安装Linux详细步骤 2.6 使用LiveCD从光盘直接...

华为云40多款云服务产品0元试用活动

免费套餐,马上领取!
CSDN

CSDN

中国开发者社区CSDN (Chinese Software Developer Network) 创立于1999年,致力为中国开发者提供知识传播、在线学习、职业发展等全生命周期服务。