概要 实例 介绍 源码

箱线图

源文件:index.html

  1. <!DOCTYPE html>
  2. <meta charset="utf-8">
  3. <style>
  4. body {
  5. font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  6. }
  7. .box {
  8. font: 10px sans-serif;
  9. }
  10. .box line,
  11. .box rect,
  12. .box circle {
  13. fill: #fff;
  14. stroke: #000;
  15. stroke-width: 1.5px;
  16. }
  17. .box .center {
  18. stroke-dasharray: 3,3;
  19. }
  20. .box .outlier {
  21. fill: none;
  22. stroke: #ccc;
  23. }
  24. </style>
  25. <body>
  26. <script src="//d3js.org/d3.v3.min.js"></script>
  27. <script src="box.js"></script>
  28. <script>
  29. var margin = {top: 10, right: 50, bottom: 20, left: 50},
  30. width = 120 - margin.left - margin.right,
  31. height = 400 - margin.top - margin.bottom;
  32. var min = Infinity,
  33. max = -Infinity;
  34. var chart = d3.box()
  35. .whiskers(iqr(1.5))
  36. .width(width)
  37. .height(height);
  38. d3.csv("morley.csv", function(error, csv) {
  39. if (error) throw error;
  40. var data = [];
  41. csv.forEach(function(x) {
  42. var e = Math.floor(x.Expt - 1),
  43. r = Math.floor(x.Run - 1),
  44. s = Math.floor(x.Speed),
  45. d = data[e];
  46. if (!d) d = data[e] = [s];
  47. else d.push(s);
  48. if (s > max) max = s;
  49. if (s < min) min = s;
  50. });
  51. chart.domain([min, max]);
  52. var svg = d3.select("body").selectAll("svg")
  53. .data(data)
  54. .enter().append("svg")
  55. .attr("class", "box")
  56. .attr("width", width + margin.left + margin.right)
  57. .attr("height", height + margin.bottom + margin.top)
  58. .append("g")
  59. .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
  60. .call(chart);
  61. setInterval(function() {
  62. svg.datum(randomize).call(chart.duration(1000));
  63. }, 2000);
  64. });
  65. function randomize(d) {
  66. if (!d.randomizer) d.randomizer = randomizer(d);
  67. return d.map(d.randomizer);
  68. }
  69. function randomizer(d) {
  70. var k = d3.max(d) * .02;
  71. return function(d) {
  72. return Math.max(min, Math.min(max, d + k * (Math.random() - .5)));
  73. };
  74. }
  75. // Returns a function to compute the interquartile range.
  76. function iqr(k) {
  77. return function(d, i) {
  78. var q1 = d.quartiles[0],
  79. q3 = d.quartiles[2],
  80. iqr = (q3 - q1) * k,
  81. i = -1,
  82. j = d.length;
  83. while (d[++i] < q1 - iqr);
  84. while (d[--j] > q3 + iqr);
  85. return [i, j];
  86. };
  87. }
  88. </script>

源文件:morley.csv

  1. Expt,Run,Speed
  2. 1,1,850
  3. 1,2,740
  4. 1,3,900
  5. 1,4,1070
  6. 1,5,930
  7. 1,6,850
  8. 1,7,950
  9. 1,8,980
  10. 1,9,980
  11. 1,10,880
  12. 1,11,1000
  13. 1,12,980
  14. 1,13,930
  15. 1,14,650
  16. 1,15,760
  17. 1,16,810
  18. 1,17,1000
  19. 1,18,1000
  20. 1,19,960
  21. 1,20,960
  22. 2,1,960
  23. 2,2,940
  24. 2,3,960
  25. 2,4,940
  26. 2,5,880
  27. 2,6,800
  28. 2,7,850
  29. 2,8,880
  30. 2,9,900
  31. 2,10,840
  32. 2,11,830
  33. 2,12,790
  34. 2,13,810
  35. 2,14,880
  36. 2,15,880
  37. 2,16,830
  38. 2,17,800
  39. 2,18,790
  40. 2,19,760
  41. 2,20,800
  42. 3,1,880
  43. 3,2,880
  44. 3,3,880
  45. 3,4,860
  46. 3,5,720
  47. 3,6,720
  48. 3,7,620
  49. 3,8,860
  50. 3,9,970
  51. 3,10,950
  52. 3,11,880
  53. 3,12,910
  54. 3,13,850
  55. 3,14,870
  56. 3,15,840
  57. 3,16,840
  58. 3,17,850
  59. 3,18,840
  60. 3,19,840
  61. 3,20,840
  62. 4,1,890
  63. 4,2,810
  64. 4,3,810
  65. 4,4,820
  66. 4,5,800
  67. 4,6,770
  68. 4,7,760
  69. 4,8,740
  70. 4,9,750
  71. 4,10,760
  72. 4,11,910
  73. 4,12,920
  74. 4,13,890
  75. 4,14,860
  76. 4,15,880
  77. 4,16,720
  78. 4,17,840
  79. 4,18,850
  80. 4,19,850
  81. 4,20,780
  82. 5,1,890
  83. 5,2,840
  84. 5,3,780
  85. 5,4,810
  86. 5,5,760
  87. 5,6,810
  88. 5,7,790
  89. 5,8,810
  90. 5,9,820
  91. 5,10,850
  92. 5,11,870
  93. 5,12,870
  94. 5,13,810
  95. 5,14,740
  96. 5,15,810
  97. 5,16,940
  98. 5,17,950
  99. 5,18,800
  100. 5,19,810
  101. 5,20,870

源文件:box.js

  1. (function() {
  2. // Inspired by http://informationandvisualization.de/blog/box-plot
  3. d3.box = function() {
  4. var width = 1,
  5. height = 1,
  6. duration = 0,
  7. domain = null,
  8. value = Number,
  9. whiskers = boxWhiskers,
  10. quartiles = boxQuartiles,
  11. tickFormat = null;
  12. // For each small multiple…
  13. function box(g) {
  14. g.each(function(d, i) {
  15. d = d.map(value).sort(d3.ascending);
  16. var g = d3.select(this),
  17. n = d.length,
  18. min = d[0],
  19. max = d[n - 1];
  20. // Compute quartiles. Must return exactly 3 elements.
  21. var quartileData = d.quartiles = quartiles(d);
  22. // Compute whiskers. Must return exactly 2 elements, or null.
  23. var whiskerIndices = whiskers && whiskers.call(this, d, i),
  24. whiskerData = whiskerIndices && whiskerIndices.map(function(i) { return d[i]; });
  25. // Compute outliers. If no whiskers are specified, all data are "outliers".
  26. // We compute the outliers as indices, so that we can join across transitions!
  27. var outlierIndices = whiskerIndices
  28. ? d3.range(0, whiskerIndices[0]).concat(d3.range(whiskerIndices[1] + 1, n))
  29. : d3.range(n);
  30. // Compute the new x-scale.
  31. var x1 = d3.scale.linear()
  32. .domain(domain && domain.call(this, d, i) || [min, max])
  33. .range([height, 0]);
  34. // Retrieve the old x-scale, if this is an update.
  35. var x0 = this.__chart__ || d3.scale.linear()
  36. .domain([0, Infinity])
  37. .range(x1.range());
  38. // Stash the new scale.
  39. this.__chart__ = x1;
  40. // Note: the box, median, and box tick elements are fixed in number,
  41. // so we only have to handle enter and update. In contrast, the outliers
  42. // and other elements are variable, so we need to exit them! Variable
  43. // elements also fade in and out.
  44. // Update center line: the vertical line spanning the whiskers.
  45. var center = g.selectAll("line.center")
  46. .data(whiskerData ? [whiskerData] : []);
  47. center.enter().insert("line", "rect")
  48. .attr("class", "center")
  49. .attr("x1", width / 2)
  50. .attr("y1", function(d) { return x0(d[0]); })
  51. .attr("x2", width / 2)
  52. .attr("y2", function(d) { return x0(d[1]); })
  53. .style("opacity", 1e-6)
  54. .transition()
  55. .duration(duration)
  56. .style("opacity", 1)
  57. .attr("y1", function(d) { return x1(d[0]); })
  58. .attr("y2", function(d) { return x1(d[1]); });
  59. center.transition()
  60. .duration(duration)
  61. .style("opacity", 1)
  62. .attr("y1", function(d) { return x1(d[0]); })
  63. .attr("y2", function(d) { return x1(d[1]); });
  64. center.exit().transition()
  65. .duration(duration)
  66. .style("opacity", 1e-6)
  67. .attr("y1", function(d) { return x1(d[0]); })
  68. .attr("y2", function(d) { return x1(d[1]); })
  69. .remove();
  70. // Update innerquartile box.
  71. var box = g.selectAll("rect.box")
  72. .data([quartileData]);
  73. box.enter().append("rect")
  74. .attr("class", "box")
  75. .attr("x", 0)
  76. .attr("y", function(d) { return x0(d[2]); })
  77. .attr("width", width)
  78. .attr("height", function(d) { return x0(d[0]) - x0(d[2]); })
  79. .transition()
  80. .duration(duration)
  81. .attr("y", function(d) { return x1(d[2]); })
  82. .attr("height", function(d) { return x1(d[0]) - x1(d[2]); });
  83. box.transition()
  84. .duration(duration)
  85. .attr("y", function(d) { return x1(d[2]); })
  86. .attr("height", function(d) { return x1(d[0]) - x1(d[2]); });
  87. // Update median line.
  88. var medianLine = g.selectAll("line.median")
  89. .data([quartileData[1]]);
  90. medianLine.enter().append("line")
  91. .attr("class", "median")
  92. .attr("x1", 0)
  93. .attr("y1", x0)
  94. .attr("x2", width)
  95. .attr("y2", x0)
  96. .transition()
  97. .duration(duration)
  98. .attr("y1", x1)
  99. .attr("y2", x1);
  100. medianLine.transition()
  101. .duration(duration)
  102. .attr("y1", x1)
  103. .attr("y2", x1);
  104. // Update whiskers.
  105. var whisker = g.selectAll("line.whisker")
  106. .data(whiskerData || []);
  107. whisker.enter().insert("line", "circle, text")
  108. .attr("class", "whisker")
  109. .attr("x1", 0)
  110. .attr("y1", x0)
  111. .attr("x2", width)
  112. .attr("y2", x0)
  113. .style("opacity", 1e-6)
  114. .transition()
  115. .duration(duration)
  116. .attr("y1", x1)
  117. .attr("y2", x1)
  118. .style("opacity", 1);
  119. whisker.transition()
  120. .duration(duration)
  121. .attr("y1", x1)
  122. .attr("y2", x1)
  123. .style("opacity", 1);
  124. whisker.exit().transition()
  125. .duration(duration)
  126. .attr("y1", x1)
  127. .attr("y2", x1)
  128. .style("opacity", 1e-6)
  129. .remove();
  130. // Update outliers.
  131. var outlier = g.selectAll("circle.outlier")
  132. .data(outlierIndices, Number);
  133. outlier.enter().insert("circle", "text")
  134. .attr("class", "outlier")
  135. .attr("r", 5)
  136. .attr("cx", width / 2)
  137. .attr("cy", function(i) { return x0(d[i]); })
  138. .style("opacity", 1e-6)
  139. .transition()
  140. .duration(duration)
  141. .attr("cy", function(i) { return x1(d[i]); })
  142. .style("opacity", 1);
  143. outlier.transition()
  144. .duration(duration)
  145. .attr("cy", function(i) { return x1(d[i]); })
  146. .style("opacity", 1);
  147. outlier.exit().transition()
  148. .duration(duration)
  149. .attr("cy", function(i) { return x1(d[i]); })
  150. .style("opacity", 1e-6)
  151. .remove();
  152. // Compute the tick format.
  153. var format = tickFormat || x1.tickFormat(8);
  154. // Update box ticks.
  155. var boxTick = g.selectAll("text.box")
  156. .data(quartileData);
  157. boxTick.enter().append("text")
  158. .attr("class", "box")
  159. .attr("dy", ".3em")
  160. .attr("dx", function(d, i) { return i & 1 ? 6 : -6 })
  161. .attr("x", function(d, i) { return i & 1 ? width : 0 })
  162. .attr("y", x0)
  163. .attr("text-anchor", function(d, i) { return i & 1 ? "start" : "end"; })
  164. .text(format)
  165. .transition()
  166. .duration(duration)
  167. .attr("y", x1);
  168. boxTick.transition()
  169. .duration(duration)
  170. .text(format)
  171. .attr("y", x1);
  172. // Update whisker ticks. These are handled separately from the box
  173. // ticks because they may or may not exist, and we want don't want
  174. // to join box ticks pre-transition with whisker ticks post-.
  175. var whiskerTick = g.selectAll("text.whisker")
  176. .data(whiskerData || []);
  177. whiskerTick.enter().append("text")
  178. .attr("class", "whisker")
  179. .attr("dy", ".3em")
  180. .attr("dx", 6)
  181. .attr("x", width)
  182. .attr("y", x0)
  183. .text(format)
  184. .style("opacity", 1e-6)
  185. .transition()
  186. .duration(duration)
  187. .attr("y", x1)
  188. .style("opacity", 1);
  189. whiskerTick.transition()
  190. .duration(duration)
  191. .text(format)
  192. .attr("y", x1)
  193. .style("opacity", 1);
  194. whiskerTick.exit().transition()
  195. .duration(duration)
  196. .attr("y", x1)
  197. .style("opacity", 1e-6)
  198. .remove();
  199. });
  200. d3.timer.flush();
  201. }
  202. box.width = function(x) {
  203. if (!arguments.length) return width;
  204. width = x;
  205. return box;
  206. };
  207. box.height = function(x) {
  208. if (!arguments.length) return height;
  209. height = x;
  210. return box;
  211. };
  212. box.tickFormat = function(x) {
  213. if (!arguments.length) return tickFormat;
  214. tickFormat = x;
  215. return box;
  216. };
  217. box.duration = function(x) {
  218. if (!arguments.length) return duration;
  219. duration = x;
  220. return box;
  221. };
  222. box.domain = function(x) {
  223. if (!arguments.length) return domain;
  224. domain = x == null ? x : d3.functor(x);
  225. return box;
  226. };
  227. box.value = function(x) {
  228. if (!arguments.length) return value;
  229. value = x;
  230. return box;
  231. };
  232. box.whiskers = function(x) {
  233. if (!arguments.length) return whiskers;
  234. whiskers = x;
  235. return box;
  236. };
  237. box.quartiles = function(x) {
  238. if (!arguments.length) return quartiles;
  239. quartiles = x;
  240. return box;
  241. };
  242. return box;
  243. };
  244. function boxWhiskers(d) {
  245. return [0, d.length - 1];
  246. }
  247. function boxQuartiles(d) {
  248. return [
  249. d3.quantile(d, .25),
  250. d3.quantile(d, .5),
  251. d3.quantile(d, .75)
  252. ];
  253. }
  254. })();