source: Dev/trunk/d3/src/chart/qq.js @ 76

Last change on this file since 76 was 76, checked in by fpvanagthoven, 14 years ago

d3

File size: 6.6 KB
Line 
1// Based on http://vis.stanford.edu/protovis/ex/qqplot.html
2d3.chart.qq = function() {
3  var width = 1,
4      height = 1,
5      duration = 0,
6      domain = null,
7      tickFormat = null,
8      n = 100,
9      x = d3_chart_qqX,
10      y = d3_chart_qqY;
11
12  // For each small multiple

13  function qq(g) {
14    g.each(function(d, i) {
15      var g = d3.select(this),
16          qx = d3_chart_qqQuantiles(n, x.call(this, d, i)),
17          qy = d3_chart_qqQuantiles(n, y.call(this, d, i)),
18          xd = domain && domain.call(this, d, i) || [d3.min(qx), d3.max(qx)], // new x-domain
19          yd = domain && domain.call(this, d, i) || [d3.min(qy), d3.max(qy)], // new y-domain
20          x0, // old x-scale
21          y0; // old y-scale
22
23      // Compute the new x-scale.
24      var x1 = d3.scale.linear()
25          .domain(xd)
26          .range([0, width]);
27
28      // Compute the new y-scale.
29      var y1 = d3.scale.linear()
30          .domain(yd)
31          .range([height, 0]);
32
33      // Retrieve the old scales, if this is an update.
34      if (this.__chart__) {
35        x0 = this.__chart__.x;
36        y0 = this.__chart__.y;
37      } else {
38        x0 = d3.scale.linear().domain([0, Infinity]).range(x1.range());
39        y0 = d3.scale.linear().domain([0, Infinity]).range(y1.range());
40      }
41
42      // Stash the new scales.
43      this.__chart__ = {x: x1, y: y1};
44
45      // Update diagonal line.
46      var diagonal = g.selectAll("line.diagonal")
47          .data([null]);
48
49      diagonal.enter().append("svg:line")
50          .attr("class", "diagonal")
51          .attr("x1", x1(yd[0]))
52          .attr("y1", y1(xd[0]))
53          .attr("x2", x1(yd[1]))
54          .attr("y2", y1(xd[1]));
55
56      diagonal.transition()
57          .duration(duration)
58          .attr("x1", x1(yd[0]))
59          .attr("y1", y1(xd[0]))
60          .attr("x2", x1(yd[1]))
61          .attr("y2", y1(xd[1]));
62
63      // Update quantile plots.
64      var circle = g.selectAll("circle")
65          .data(d3.range(n).map(function(i) {
66            return {x: qx[i], y: qy[i]};
67          }));
68
69      circle.enter().append("svg:circle")
70          .attr("class", "quantile")
71          .attr("r", 4.5)
72          .attr("cx", function(d) { return x0(d.x); })
73          .attr("cy", function(d) { return y0(d.y); })
74          .style("opacity", 1e-6)
75        .transition()
76          .duration(duration)
77          .attr("cx", function(d) { return x1(d.x); })
78          .attr("cy", function(d) { return y1(d.y); })
79          .style("opacity", 1);
80
81      circle.transition()
82          .duration(duration)
83          .attr("cx", function(d) { return x1(d.x); })
84          .attr("cy", function(d) { return y1(d.y); })
85          .style("opacity", 1);
86
87      circle.exit().transition()
88          .duration(duration)
89          .attr("cx", function(d) { return x1(d.x); })
90          .attr("cy", function(d) { return y1(d.y); })
91          .style("opacity", 1e-6)
92          .remove();
93
94      var xformat = tickFormat || x1.tickFormat(4),
95          yformat = tickFormat || y1.tickFormat(4),
96          tx = function(d) { return "translate(" + x1(d) + "," + height + ")"; },
97          ty = function(d) { return "translate(0," + y1(d) + ")"; };
98
99      // Update x-ticks.
100      var xtick = g.selectAll("g.x.tick")
101          .data(x1.ticks(4), function(d) {
102            return this.textContent || xformat(d);
103          });
104
105      var xtickEnter = xtick.enter().append("svg:g")
106          .attr("class", "x tick")
107          .attr("transform", function(d) { return "translate(" + x0(d) + "," + height + ")"; })
108          .style("opacity", 1e-6);
109
110      xtickEnter.append("svg:line")
111          .attr("y1", 0)
112          .attr("y2", -6);
113
114      xtickEnter.append("svg:text")
115          .attr("text-anchor", "middle")
116          .attr("dy", "1em")
117          .text(xformat);
118
119      // Transition the entering ticks to the new scale, x1.
120      xtickEnter.transition()
121          .duration(duration)
122          .attr("transform", tx)
123          .style("opacity", 1);
124
125      // Transition the updating ticks to the new scale, x1.
126      xtick.transition()
127          .duration(duration)
128          .attr("transform", tx)
129          .style("opacity", 1);
130
131      // Transition the exiting ticks to the new scale, x1.
132      xtick.exit().transition()
133          .duration(duration)
134          .attr("transform", tx)
135          .style("opacity", 1e-6)
136          .remove();
137
138      // Update ticks.
139      var ytick = g.selectAll("g.y.tick")
140          .data(y1.ticks(4), function(d) {
141            return this.textContent || yformat(d);
142          });
143
144      var ytickEnter = ytick.enter().append("svg:g")
145          .attr("class", "y tick")
146          .attr("transform", function(d) { return "translate(0," + y0(d) + ")"; })
147          .style("opacity", 1e-6);
148
149      ytickEnter.append("svg:line")
150          .attr("x1", 0)
151          .attr("x2", 6);
152
153      ytickEnter.append("svg:text")
154          .attr("text-anchor", "end")
155          .attr("dx", "-.5em")
156          .attr("dy", ".3em")
157          .text(yformat);
158
159      // Transition the entering ticks to the new scale, y1.
160      ytickEnter.transition()
161          .duration(duration)
162          .attr("transform", ty)
163          .style("opacity", 1);
164
165      // Transition the updating ticks to the new scale, y1.
166      ytick.transition()
167          .duration(duration)
168          .attr("transform", ty)
169          .style("opacity", 1);
170
171      // Transition the exiting ticks to the new scale, y1.
172      ytick.exit().transition()
173          .duration(duration)
174          .attr("transform", ty)
175          .style("opacity", 1e-6)
176          .remove();
177    });
178  }
179
180  qq.width = function(x) {
181    if (!arguments.length) return width;
182    width = x;
183    return qq;
184  };
185
186  qq.height = function(x) {
187    if (!arguments.length) return height;
188    height = x;
189    return qq;
190  };
191
192  qq.duration = function(x) {
193    if (!arguments.length) return duration;
194    duration = x;
195    return qq;
196  };
197
198  qq.domain = function(x) {
199    if (!arguments.length) return domain;
200    domain = x == null ? x : d3.functor(x);
201    return qq;
202  };
203
204  qq.count = function(z) {
205    if (!arguments.length) return n;
206    n = z;
207    return qq;
208  };
209
210  qq.x = function(z) {
211    if (!arguments.length) return x;
212    x = z;
213    return qq;
214  };
215
216  qq.y = function(z) {
217    if (!arguments.length) return y;
218    y = z;
219    return qq;
220  };
221
222  qq.tickFormat = function(x) {
223    if (!arguments.length) return tickFormat;
224    tickFormat = x;
225    return qq;
226  };
227
228  return qq;
229};
230
231function d3_chart_qqQuantiles(n, values) {
232  var m = values.length - 1;
233  values = values.slice().sort(d3.ascending);
234  return d3.range(n).map(function(i) {
235    return values[~~(i * m / n)];
236  });
237}
238
239function d3_chart_qqX(d) {
240  return d.x;
241}
242
243function d3_chart_qqY(d) {
244  return d.y;
245}
Note: See TracBrowser for help on using the repository browser.