D3.js 中 Box Plots详解

2024-02-01 06:20
文章标签 详解 js box d3 plots

本文主要是介绍D3.js 中 Box Plots详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Box Plot

聊聊box plot,将自己阅读、理解d3.js的箱形图源码的过程和思路记录下来……

箱形图(Box-plot)又称为盒须图、盒式图或箱线图,是一种用作显示一组数据分散情况资料的统计图。因形状如箱子而得名。在各种领域也经常被使用,常见于品质管理。 —— [ 百度百科 ]

话不多少,先睹为快,用d3实现的箱形图如图1所示:

图1 箱形图的样图

眨一看,箱形图有点难懂,图中的盒子,以及各种线段上标注的数字,还有那莫名奇妙的小空心圆圈什么意思?其实,我第一眼看也是一脸蒙,接着就查阅相关资料,搞明白了这张神秘的图,请看图2,保证您一秒懂什么是箱形图。

图2 箱形图关键参数标注图

所以,箱形图主要包含六个数据节点,将一组数据从大到小排列,分别计算出他的上边缘(largest value),上四分位数Q3(upper quartile),中位数(median),下四分位数Q1(lower quartile),下边缘(smallest value),还有一个异常值(图中的空心小圆圈所示)

粗略了解了箱形图怎么回事之后,开始研究d3画箱形图的方式,其中包括了三个文件,分别是index.html文件;box.js文件以及样例数据文件morley.csv,下面分别对整个实现过程进行详细解释。

box.js文件解读 —— [ 源码 ]

(function() {// Inspired by http://informationandvisualization.de/blog/box-plot//@onlywan  在该立即执行函数中定义以下一系列操作d3.box = function() {var width = 1,height = 1,duration = 0,domain = null,value = Number,whiskers = boxWhiskers,quartiles = boxQuartiles,   //@onlywan 声明获取四分位数的函数tickFormat = null;// For each small multiple…function box(g) {//@onlywan 以下计算绘制箱形图所需的各种数据参数:// 第一步:对每个数组进行排序;// 第二步:计算每个数组的 上四分位数、下四分位数、中位数、最大值、最小值;// 第三步:获取每个数组最大值、最小值的index;// 第四步:计算每个数组异常值的index;// 第五步:定义比例尺;// 第六步:给各种元素绑定数据并设置相关的属性;g.each(function(d, i) {//@onlywan 先将d数组进行数值类型的映射转换,再按照升序顺序对d数组进行排序d = d.map(value).sort(d3.ascending);   var g = d3.select(this),n = d.length,    //@onlywan 记录数组的长度min = d[0],      //@onlywan 记录数组的最小值max = d[n - 1];  //@onlywan 记录数组的最大值// Compute quartiles. Must return exactly 3 elements.// @onlywan 获取数组d的三个分位数,见boxQuartiles()函数var quartileData = d.quartiles = quartiles(d);// Compute whiskers. Must return exactly 2 elements, or null.// @onlywan 获取数组d的最大值和最小值在数组中的下标,见boxWhiskers()函数,此处命名为//          boxWhiskers,盒子的胡须,自我理解应该是因为箱形图中每个盒子的两端用虚线来//          标识上下边缘数值,看起来长得像胡须,因此命名为whisker吧var whiskerIndices = whiskers && whiskers.call(this, d, i),whiskerData = whiskerIndices && whiskerIndices.map(function(i) { return d[i]; });// Compute outliers. If no whiskers are specified, all data are "outliers".// We compute the outliers as indices, so that we can join across transitions!// @onlywan 获取异常值的下标,即箱形图中的那些小空心圆圈//          d3.range([start,]stop[,step]);返回等差数列函数;如d3.range(6)返回//          [0,1,2,3,4,5]var outlierIndices = whiskerIndices? d3.range(0, whiskerIndices[0]).concat(d3.range(whiskerIndices[1] + 1, n)): d3.range(n);// Compute the new x-scale.//@onlywan d3.scale.linear()来指定比例尺为线性的,返回线性比例尺;//         后面的domain()和range()分别表示 定义域和值域,类似于数学函数中,x的取值范围//         称为定义域,y的取值范围称为值域;//         通过计算得出,此处定义域指定为[min,max],即当前数组的最小值到最大值的区间;//         此处值域指定为[1,0],此处的height是在函数开始定义的,值为1;//         最终比例尺的线性关系满足如 当x=min时,y=height,当x=max时,y=0这样的关系var x1 = d3.scale.linear().domain(domain && domain.call(this, d, i) || [min, max]).range([height, 0]);// Retrieve the old x-scale, if this is an update.//@onlywan 若是某种更新,要恢复旧比例尺,结果要么是图像现有的,要么是定义域为[0,Infinity]//         值域为上边x1中值域var x0 = this.__chart__ || d3.scale.linear().domain([0, Infinity]).range(x1.range());// Stash the new scale.//@onlywan 将新的比例尺函数存储在__chart__变量中;this.__chart__ = x1;// Note: the box, median, and box tick elements are fixed in number,// so we only have to handle enter and update. In contrast, the outliers// and other elements are variable, so we need to exit them! Variable// elements also fade in and out.//@onlywan 上面英文注释中的enter、exit动作是指 d3中的函数 enter()函数和//         exit()函数,enter()函数是选中那些缺少的虚拟dom,exit()函数的//         的作用是选中那些多余的dom元素;两个函数都是选择器;// Update center line: the vertical line spanning the whiskers.//@onlywan 设置中心线的绑定数组,这里将[min,max]数组绑定到中心线上var center = g.selectAll("line.center").data(whiskerData ? [whiskerData] : []);//@onlywan 为缺少中心线的添加 中心线,绑定class、x1,y1,x2,y2属性,并且指//         动画动作;center.enter().insert("line", "rect").attr("class", "center").attr("x1", width / 2).attr("y1", function(d) { return x0(d[0]); }).attr("x2", width / 2).attr("y2", function(d) { return x0(d[1]); }) .style("opacity", 1e-6) .transition() .duration(duration) .style("opacity", 1) .attr("y1", function(d) { return x1(d[0]); }) .attr("y2", function(d) { return x1(d[1]); }); //@onlywan 为中心线添加动画动作以及y坐标的值 center.transition() .duration(duration) .style("opacity", 1) .attr("y1", function(d) { return x1(d[0]); }) .attr("y2", function(d) { return x1(d[1]); }); //@onlywan 为多余出来的中心线元素添加移除动画动作 center.exit().transition() .duration(duration) .style("opacity", 1e-6) .attr("y1", function(d) { return x1(d[0]); }) .attr("y2", function(d) { return x1(d[1]); }) .remove(); // Update innerquartile box. //@onlywan 为box元素绑定数据,数据为三个,上四分位数,下四分位数和中位数 var box = g.selectAll("rect.box") .data([quartileData]); //@onlywan 补充添加缺少的box元素,并且为其添加相关属性及动画 box.enter().append("rect") .attr("class", "box") .attr("x", 0) .attr("y", function(d) { return x0(d[2]); }) .attr("width", width) .attr("height", function(d) { return x0(d[0]) - x0(d[2]); }) .transition() .duration(duration) .attr("y", function(d) { return x1(d[2]); }) .attr("height", function(d) { return x1(d[0]) - x1(d[2]); }); //@onlywan 为box添加动画 box.transition() .duration(duration) .attr("y", function(d) { return x1(d[2]); }) .attr("height", function(d) { return x1(d[0]) - x1(d[2]); }); // Update median line. //@onlywan 为中位线绑定数据 var medianLine = g.selectAll("line.median") .data([quartileData[1]]); //@onlywan 补充缺少的中位线,并且给其添加相关属性及动画 medianLine.enter().append("line") .attr("class", "median") .attr("x1", 0) .attr("y1", x0) .attr("x2", width) .attr("y2", x0) .transition() .duration(duration) .attr("y1", x1) .attr("y2", x1); //@onlywan 为中位线添加动作 medianLine.transition() .duration(duration) .attr("y1", x1) .attr("y2", x1); // Update whiskers. //@onlywan 为盒子两端虚线绑定数据 var whisker = g.selectAll("line.whisker") .data(whiskerData || []); //@onlywan 补充缺少的线,并为其绑定相关属性及动画 whisker.enter().insert("line", "circle, text") .attr("class", "whisker") .attr("x1", 0) .attr("y1", x0) .attr("x2", width) .attr("y2", x0) .style("opacity", 1e-6) .transition() .duration(duration) .attr("y1", x1) .attr("y2", x1) .style("opacity", 1); //@onlywan 为盒子两端虚线绑定动作 whisker.transition() .duration(duration) .attr("y1", x1) .attr("y2", x1) .style("opacity", 1); //@onlywan 去除多余的线 whisker.exit().transition() .duration(duration) .attr("y1", x1) .attr("y2", x1) .style("opacity", 1e-6) .remove(); // Update outliers. //@onlyan 为异常值绑定数据 var outlier = g.selectAll("circle.outlier") .data(outlierIndices, Number); //@onlywan 填补缺少的异常值圆圈,并设定其圆心,半径以及动画等属性 outlier.enter().insert("circle", "text") .attr("class", "outlier") .attr("r", 5) .attr("cx", width / 2) .attr("cy", function(i) { return x0(d[i]); }) .style("opacity", 1e-6) .transition() .duration(duration) .attr("cy", function(i) { return x1(d[i]); }) .style("opacity", 1); //@onlywan 为异常值点绑定动画 outlier.transition() .duration(duration) .attr("cy", function(i) { return x1(d[i]); }) .style("opacity", 1); //@onlywan 去除多余的异常值元素 outlier.exit().transition() .duration(duration) .attr("cy", function(i) { return x1(d[i]); }) .style("opacity", 1e-6) .remove(); // Compute the tick format. //@onlywan 坐标轴刻度格式化函数定义 var format = tickFormat || x1.tickFormat(8); // Update box ticks. //@onlywan 为盒子坐标刻度绑定数据 var boxTick = g.selectAll("text.box") .data(quartileData); //填补缺少的刻度文字及相关属性 boxTick.enter().append("text") .attr("class", "box") .attr("dy", ".3em") .attr("dx", function(d, i) { return i & 1 ? 6 : -6 }) .attr("x", function(d, i) { return i & 1 ? width : 0 }) .attr("y", x0) .attr("text-anchor", function(d, i) { return i & 1 ? "start" : "end"; }) .text(format) .transition() .duration(duration) .attr("y", x1); //@onlywan 定义盒子刻度格式 boxTick.transition() .duration(duration) .text(format) .attr("y", x1); // Update whisker ticks. These are handled separately from the box // ticks because they may or may not exist, and we want don't want // to join box ticks pre-transition with whisker ticks post-. //@onlywan 虚线刻度数据绑定 var whiskerTick = g.selectAll("text.whisker") .data(whiskerData || []); //@onlywan 填补虚线刻度缺少的文字并设置相关属性 whiskerTick.enter().append("text") .attr("class", "whisker") .attr("dy", ".3em") .attr("dx", 6) .attr("x", width) .attr("y", x0) .text(format) .style("opacity", 1e-6) .transition() .duration(duration) .attr("y", x1) .style("opacity", 1); //@onlywan 虚线刻度设置相关属性 whiskerTick.transition() .duration(duration) .text(format) .attr("y", x1) .style("opacity", 1); //@onlywan 去除多余的虚线刻度 whiskerTick.exit().transition() .duration(duration) .attr("y", x1) .style("opacity", 1e-6) .remove(); }); //@onlywan 立刻执行当前没有延迟的计时,常用来处理闪屏问题 d3.timer.flush(); } //@onlywan box的宽度属性设置函数 box.width = function(x) { if (!arguments.length) return width; width = x; return box; }; //@onlywan box的高度属性设置函数 box.height = function(x) { if (!arguments.length) return height; height = x; return box; }; //@onlywan box的刻度格式设置函数 box.tickFormat = function(x) { if (!arguments.length) return tickFormat; tickFormat = x; return box; }; //@onlywan box的动作间隔设置函数 box.duration = function(x) { if (!arguments.length) return duration; duration = x; return box; }; //@onlywan box的定义域设置函数 box.domain = function(x) { if (!arguments.length) return domain; domain = x == null ? x : d3.functor(x); return box; }; //@onlywan box的value属性设置函数 box.value = function(x) { if (!arguments.length) return value; value = x; return box; }; //@onlywan box的虚线设置函数 box.whiskers = function(x) { if (!arguments.length) return whiskers; whiskers = x; return box; }; //@onlywan box的分位数设置函数 box.quartiles = function(x) { if (!arguments.length) return quartiles; quartiles = x; return box; }; //@onlywan 最终返回box对象 return box; }; //@onlywan 获取whisker值在数组d中的index值 function boxWhiskers(d) { return [0, d.length - 1]; } //@onlywan d3.quantile(array, p[, accessor])函数是用来获取排好序的数组的一个分位数 function boxQuartiles(d) { return [ d3.quantile(d, .25), //@onlywan 获取已排序数组d的 下四分位数 d3.quantile(d, .5), //@onlywan 获取已排序数组d的 中位数 d3.quantile(d, .75) //@onlywan 获取已排序数组d的 上四分位数 ]; } })();

index.html文件解读 —— [ 源码 ]

<!DOCTYPE html>
<meta charset="utf-8">
<style>body {font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}.box {font: 10px sans-serif;
}.box line,
.box rect,
.box circle {fill: #fff;stroke: #000;stroke-width: 1.5px;
}.box .center {stroke-dasharray: 3,3;
}.box .outlier {fill: none;stroke: #ccc;
}</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="box.js"></script>
<script>var margin = {top: 10, right: 50, bottom: 20, left: 50},width = 120 - margin.left - margin.right,height = 500 - margin.top - margin.bottom;var min = Infinity,max = -Infinity;//@onlywan  初始化箱形图
var chart = d3.box().whiskers(iqr(1.5)).width(width).height(height);d3.csv("morley.csv", function(error, csv) {if (error) throw error;var data = [];//@onlywan 生成 data[]数组csv.forEach(function(x) {var e = Math.floor(x.Expt - 1),r = Math.floor(x.Run - 1),s = Math.floor(x.Speed),d = data[e];if (!d) d = data[e] = [s];else d.push(s);if (s > max) max = s;if (s < min) min = s;});//@onlywan 定义箱形图的 定义域chart.domain([min, max]);//@onlywan 生成svg元素,给其绑定数据为data数组,并且设置相关的属性var svg = d3.select("body").selectAll("svg").data(data).enter().append("svg").attr("class", "box").attr("width", width + margin.left + margin.right).attr("height", height + margin.bottom + margin.top).append("g") //@onlywan 此处追加一个group元素//@onlywan 转换坐标.attr("transform", "translate(" + margin.left + "," + margin.top + ")").call(chart);//@onlywan 将svg元素给chart函数setInterval(function() {//@onlywan 为svg赋予新的随机数组值,此处用datum函数来一次性为5个svg图赋值,并执行chart函数svg.datum(randomize).call(chart.duration(1000));}, 2000);
});//@onlywan 功能函数,用于变换d
function randomize(d) {if (!d.randomizer) d.randomizer = randomizer(d);return d.map(d.randomizer);
}
//@onlywan 功能函数,用于变换d
function randomizer(d) {var k = d3.max(d) * .02;return function(d) {return Math.max(min, Math.min(max, d + k * (Math.random() - .5)));};
}// Returns a function to compute the interquartile range.
//@onlywan 功能函数,用来计算四分位差,interquartile range ,即IQR,表示四分位差
function iqr(k) {return function(d, i) {var q1 = d.quartiles[0],q3 = d.quartiles[2],iqr = (q3 - q1) * k,i = -1,j = d.length;while (d[++i] < q1 - iqr);while (d[--j] > q3 + iqr);return [i, j];};
}</script>

d3官网对于box plot 图给出的数据是 著名的迈克尔逊-莫雷实验 迈克耳孙-莫雷实验的数据,共5组实验,每组中20个数据,5组实验对应箱形图中5个盒子。【个人理解】

终于把第一个图读完了,今天是黑色星期五之后的星期日,天气还算可以。

这篇关于D3.js 中 Box Plots详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/666322

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

K8S(Kubernetes)开源的容器编排平台安装步骤详解

K8S(Kubernetes)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。以下是K8S容器编排平台的安装步骤、使用方式及特点的概述: 安装步骤: 安装Docker:K8S需要基于Docker来运行容器化应用程序。首先要在所有节点上安装Docker引擎。 安装Kubernetes Master:在集群中选择一台主机作为Master节点,安装K8S的控制平面组件,如AP

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件

EasyPlayer.js网页H5 Web js播放器能力合集

最近遇到一个需求,要求做一款播放器,发现能力上跟EasyPlayer.js基本一致,满足要求: 需求 功性能 分类 需求描述 功能 预览 分屏模式 单分屏(单屏/全屏) 多分屏(2*2) 多分屏(3*3) 多分屏(4*4) 播放控制 播放(单个或全部) 暂停(暂停时展示最后一帧画面) 停止(单个或全部) 声音控制(开关/音量调节) 主辅码流切换 辅助功能 屏

嵌入式Openharmony系统构建与启动详解

大家好,今天主要给大家分享一下,如何构建Openharmony子系统以及系统的启动过程分解。 第一:OpenHarmony系统构建      首先熟悉一下,构建系统是一种自动化处理工具的集合,通过将源代码文件进行一系列处理,最终生成和用户可以使用的目标文件。这里的目标文件包括静态链接库文件、动态链接库文件、可执行文件、脚本文件、配置文件等。      我们在编写hellowor

LabVIEW FIFO详解

在LabVIEW的FPGA开发中,FIFO(先入先出队列)是常用的数据传输机制。通过配置FIFO的属性,工程师可以在FPGA和主机之间,或不同FPGA VIs之间进行高效的数据传输。根据具体需求,FIFO有多种类型与实现方式,包括目标范围内FIFO(Target-Scoped)、DMA FIFO以及点对点流(Peer-to-Peer)。 FIFO类型 **目标范围FIFO(Target-Sc