maxv) { maxv = val; }
515 | }
516 | if (minb) { minv = Math.floor(minv / step) * step; }
517 | if (maxb) { maxv = Math.ceil(maxv / step) * step; }
518 | }
519 | // compute index array
520 | var a = [], lut = (a.lut = []),
521 | range = (maxv - minv), unique = Math.ceil(range / step);
522 | for (i = 0; i < N; ++i) {
523 | val = values[i];
524 | if (val < minv || val > maxv) { a.push(-1); }
525 | else if (val == maxv) { a.push(unique - 1); }
526 | else { a.push(~~((values[i] - minv) / step)); }
527 | }
528 | for (i = 0; i < unique; ++i) {
529 | // multiply b/c adding garners round-off error
530 | lut.push(minv + i * step);
531 | }
532 | return a;
533 | };
534 | op.step = function(x) {
535 | if (x === undefined) return step;
536 | step = x;
537 | return op;
538 | };
539 | op.min = function(x) {
540 | if (x === undefined) return min;
541 | min = x;
542 | return op;
543 | };
544 | op.max = function(x) {
545 | if (x === undefined) return max;
546 | max = x;
547 | return op;
548 | };
549 | op.value = expr;
550 | return op;
551 | };
552 |
553 | dv.quantile = function(expr, n) {
554 | function search(array, value) {
555 | var low = 0, high = array.length - 1;
556 | while (low <= high) {
557 | var mid = (low + high) >> 1, midValue = array[mid];
558 | if (midValue < value) { low = mid + 1; }
559 | else if (midValue > value) { high = mid - 1; }
560 | else { return mid; }
561 | }
562 | var i = -low - 1;
563 | return (i < 0) ? (-i - 1) : i;
564 | }
565 |
566 | var op = {};
567 | op.array = function(values) {
568 | // get sorted data values
569 | var i, d = values.sorted;
570 | if (!d) {
571 | var cmp;
572 | if (values.type && values.type === "numeric") {
573 | cmp = function(a,b) { return a - b; }
574 | } else {
575 | cmp = function(a,b) { return a < b ? -1 : a > b ? 1 : 0; }
576 | }
577 | values.sorted = (d = values.slice().sort(cmp));
578 | }
579 | // compute quantile boundaries
580 | var q = [d[0]], a = [], lut = (a.lut = []);
581 | for (i = 1; i <= n; ++i) {
582 | q[i] = d[~~(i * (d.length - 1) / n)];
583 | lut.push(i - 1);
584 | }
585 | // iterate through data and label quantiles
586 | for (i = 0; i < values.length; ++i) {
587 | a.push(Math.max(0, search(q, values[i]) - 1));
588 | }
589 | return a;
590 | }
591 | op.bins = function(x) {
592 | if (x === undefined) return n;
593 | n = x;
594 | return op;
595 | }
596 | op.value = expr;
597 | return op;
598 | };
599 |
600 | return dv; })();
--------------------------------------------------------------------------------
/examples/profile/benchmark.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Profiler Benchmarks
4 |
5 |
6 |
7 |
24 |
25 |
26 | Data Profiler Benchmarks
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/examples/profile/dv.profile.js:
--------------------------------------------------------------------------------
1 | dv.EPSILON = 1e-9;
2 |
3 | dv.logFloor = function(x, b) {
4 | return (x > 0)
5 | ? Math.pow(b, Math.floor(Math.log(x) / Math.log(b)))
6 | : -Math.pow(b, -Math.floor(-Math.log(-x) / Math.log(b)));
7 | };
8 |
9 | function dv_bins(data, bins, min, max, step) {
10 | var bmin = min !== undefined,
11 | bmax = max !== undefined;
12 | min = bmin ? min : d3.min(data);
13 | max = bmax ? max : d3.max(data);
14 | var span = max - min;
15 |
16 | /* Special case: empty, invalid or infinite span. */
17 | if (!span || !isFinite(span)) return [min, min, 1];
18 |
19 | var s = Math.pow(10, Math.round(Math.log(span) / Math.log(10)) - 1),
20 | d = [Math.floor(min / s) * s, Math.ceil(max / s) * s];
21 | if (bmin) d[0] = min;
22 | if (bmax) d[1] = max;
23 |
24 | if (step === undefined) {
25 | step = dv.logFloor((d[1]-d[0])/bins, 10);
26 | var err = bins / ((d[1]-d[0]) / step);
27 | if (err <= .15) step *= 10;
28 | else if (err <= .35) step *= 5;
29 | else if (err <= .75) step *= 2;
30 | }
31 | d.push(step);
32 |
33 | return d;
34 | }
35 |
36 | // query for brushing & linking data, checking cache first
37 | function dv_profile_cache(evt, query) {
38 | //if (!dv.profile.cache) return evt.data.query(query);
39 |
40 | var cmp = function(a,b) {
41 | return keys[a] < keys[b] ? -1 : (keys[a] > keys[b] ? 1 : 0);
42 | }
43 | var dims = query.dims, idx = [], i, dat
44 | keys = dims.map(function(d,i) { idx.push(i); return d/*.key*/; });
45 | idx.sort(cmp);
46 | var key = idx.map(function(j) { return keys[j]; }).join("__");
47 |
48 | if (!(dat = evt.cache[key])) {
49 | // cache miss: execute the query
50 | dims = idx.map(function(j) { return dims[j]; });
51 | window.dat = {evt:evt, dims:dims, vals:query.vals};
52 | dat = evt.data.query({dims:dims, vals:query.vals});
53 | evt.cache[key] = dat;
54 | }
55 | // return table columns in correct order
56 | idx.push(idx.length); // include count column
57 | return idx.map(function(j) { return dat[j]; });
58 | }
59 |
60 | // Profiler instace to manage plots and coordinate linked selections
61 | dv.profile = function(data) {
62 | var g = [],
63 | add = function(vis) { qdata = null; g.push(vis); return vis; },
64 | qdata = null, // rolled-up data to serve as base for all queries
65 | timeoutID;
66 |
67 | // retrieve query data
68 | g.qdata = function() { return qdata; };
69 |
70 | // compute rollup of query data
71 | function qrollup(g, data) {
72 | // first collect all the bins
73 | var bins = {}, keys, qd;
74 | g.forEach(function(p) { p.fields().forEach(function(f,i) {
75 | bins[f] = p.query().dims[i];
76 | }); });
77 | keys = d3.keys(bins).sort();
78 | qd = data.sparse_query({
79 | dims: keys.map(function(k) { return bins[k]; }),
80 | vals: [dv.count("*")]
81 | });
82 | var table = dv.table();
83 | qd.forEach(function(c,i) {
84 | table.addColumn(i, c, dv.type[i==qd.length-1?"numeric":"ordinal"]);
85 | });
86 | return table;
87 | }
88 |
89 | // initialize this profiler instance
90 | g.init = function() {
91 | qdata = qrollup(g, data);
92 | g.forEach(function(p) { p.update(); });
93 | }
94 |
95 | // run a performance benchmark on the current instance
96 | g.benchmark = function(callback) {
97 | if (!console || !window.webkitRequestAnimationFrame) {
98 | alert("Sorry, the benchmarks require Google Chrome.");
99 | return;
100 | }
101 | var bd = {};
102 | bd.frames = [];
103 | bd.select = [];
104 | bd.render = [];
105 |
106 | var gi=-1, f;
107 | var bins = qdata[0].lut, idx=0, inc=0, count=0;
108 | var t0, t1, t2;
109 |
110 | function printstats() {
111 | var af = d3.sum(bd.frames) / bd.frames.length;
112 | var as = d3.sum(bd.select) / bd.select.length;
113 | var ar = d3.sum(bd.render) / bd.render.length;
114 | var sf = bd.frames.reduce(function(a,b) { return a + b*b; }, 0);
115 | var ss = bd.select.reduce(function(a,b) { return a + b*b; }, 0);
116 | var sr = bd.render.reduce(function(a,b) { return a + b*b; }, 0);
117 | var df = Math.sqrt(sf/bd.frames.length - af*af);
118 | var ds = Math.sqrt(ss/bd.frames.length - as*as);
119 | var dr = Math.sqrt(sr/bd.frames.length - ar*ar);
120 | console.log([af,df,as,ds,ar,dr]
121 | .map(function(d) { return d.toFixed(3); }).join(","));
122 | }
123 |
124 | var next = function() {
125 | for (++gi; g[gi] && g[gi].fields().length==2; ++gi);
126 | if (gi >= g.length) {
127 | printstats();
128 | callback();
129 | return;
130 | }
131 | f = g[gi].fields()[0];
132 | bins = qdata[f].lut;
133 | idx = 0; count = 0; inc = bins[1]-bins[0];
134 | step();
135 | }
136 |
137 | var step = function() {
138 | t0 = Date.now();
139 | if (count > 0) {
140 | bd.frames.push(t0-t1);
141 | bd.render.push(t0-t2);
142 | }
143 | var r = {};
144 | r[f] = [bins[idx], bins[idx]+inc];
145 | r[f].ex = (idx == bins.length-1);
146 | idx = (idx + 1) % (bins.length);
147 | count++;
148 | t1 = Date.now();
149 | g.select({source:g[gi], range:r}, -1);
150 | t2 = Date.now();
151 | if (count < 5*(qdata[f].lut.length)+1) {
152 | bd.select.push(t2-t1);
153 | window.webkitRequestAnimationFrame(step);
154 | } else { next(); }
155 | }
156 | next();
157 | return bd;
158 | }
159 |
160 | // add a plot to this profiler instance
161 | g.plot = function() {
162 | var type = arguments[0], args = [];
163 | for (var i=1; i v || v > x[1] || (!x.ex && v == x[1]))
195 | return false;
196 | }
197 | return true;
198 | };
199 | }
200 | e.data = qdata.where(filter);
201 | }
202 | for (var i=0; i 0 && val < 2 ? 2 : val;
302 | });
303 | } else {
304 | vis.selectAll("rect.brush")
305 | .attr("height", 0);
306 | }
307 | };
308 |
309 | hist.rollup = function() { return roll; };
310 |
311 | hist.fields = function() {
312 | if (arguments.length == 0) return fields;
313 | fields = arguments;
314 | field = fields[0];
315 | return hist;
316 | };
317 |
318 | hist.options = function() {
319 | if (arguments.length == 0) return fields;
320 | opt = arguments[0];
321 | bins = opt.bins || bins;
322 | w = opt.width || w;
323 | h = opt.height || h;
324 | hist.update();
325 | return hist;
326 | };
327 |
328 | hist.type = function() { return "histogram"; };
329 |
330 | hist.initUI();
331 | hist.initBins();
332 | return hist;
333 | };
334 |
335 | // Binned scatterplot visualization component
336 | dv.scatter = function(id, fields, opt)
337 | {
338 | var group = this,
339 | scat = {},
340 | data = group.data, roll, sroll,
341 | xfield = fields[0],
342 | yfield = fields[1],
343 | bins = opt.bins || 10,
344 | xbins = 0, ybins = 0,
345 | w = opt.width || 400,
346 | h = opt.height || 400,
347 | bx, by, xstep, ystep, q, qb,
348 | x, y, o, vis, xmin, xmax, ymin, ymax, squareWidth, squareHeight;
349 |
350 | scat.query = function() { return q; };
351 |
352 | function indices(t) {
353 | var idx = [], len = t[2].length;
354 | for (var i=0; i 0) idx.push(i);
356 | }
357 | return idx;
358 | }
359 |
360 | scat.initUI = function() {
361 | d3.select("#"+id+" svg").remove();
362 | vis = d3.select("#"+id).append("svg:svg")
363 | .attr("width", w)
364 | .attr("height", h);
365 | };
366 |
367 | scat.initBins = function() {
368 | var xbin = dv_bins(data[xfield], bins,
369 | opt.xmin, opt.xmax, opt.xstep);
370 | xmin = xbin[0]; xmax = xbin[1]; xstep = xbin[2];
371 | bx = dv.bin(xfield, xstep, xmin, xmax);
372 |
373 | var ybin = dv_bins(data[yfield], bins,
374 | opt.ymin, opt.ymax, opt.ystep);
375 | ymin = ybin[0]; ymax = ybin[1]; ystep = ybin[2];
376 | by = dv.bin(yfield, ystep, ymin, ymax);
377 |
378 | scat.xbin = xbin;
379 | scat.ybin = ybin;
380 | xbins = Math.ceil((xmax-xmin)/xstep);
381 | ybins = Math.ceil((ymax-ymin)/ystep);
382 |
383 | q = {dims:[bx, by], vals:[dv.count("*")]};
384 | qb = {dims:[xfield, yfield], vals:[dv.sum(data.length)]};
385 | };
386 |
387 | scat.update = function() {
388 | function opacity(i) {
389 | var v = roll[2][i];
390 | return v==0 ? 0 : o(v);
391 | }
392 | function mouseout() {
393 | d3.select(this)
394 | .style("fill", null)
395 | .attr("fill-opacity", opacity);
396 | group.select({source:scat});
397 | }
398 | function mouseover(i) {
399 | d3.select(this)
400 | .style("fill", "red")
401 | .attr("fill-opacity", 1);
402 | var vx = roll[0][i], vy = roll[1][i], r = {};
403 | r[xfield] = [vx, vx + xstep];
404 | r[xfield].ex = Math.abs(vx + xstep - xmax) < dv.EPSILON;
405 | r[yfield] = [vy, vy + ystep];
406 | r[yfield].ex = Math.abs(vy + ystep - ymax) < dv.EPSILON;
407 | group.select({source:scat, range: r});
408 | }
409 |
410 | roll = group.qdata().query(qb); //data.query(q);
411 |
412 | var sx = Math.floor((w-10)/xbins + 0.5),
413 | sy = Math.floor(h/ybins + 0.5),
414 | sw = sx * xbins,
415 | sh = sy * ybins;
416 |
417 | x = d3.scale.linear().domain([xmin, xmax]).range([0, sw]);
418 | y = d3.scale.linear().domain([ymin, ymax]).range([sh-sy, -sy]);
419 | o = d3.scale.linear().domain([0, d3.max(roll[2])]).range([0.15,1]);
420 |
421 | var sel = vis.selectAll("rect.base")
422 | .data(indices(roll));
423 | sel.enter().append("svg:rect")
424 | .attr("class", "base")
425 | .on("mouseover", mouseover)
426 | .on("mouseout", mouseout);
427 | sel.exit().remove();
428 |
429 | vis.selectAll("rect.base")
430 | .attr("x", function(i) { return x(roll[0][i]); })
431 | .attr("y", function(i) { return y(roll[1][i]); })
432 | .attr("width", sx)
433 | .attr("height", sy)
434 | .attr("fill-opacity", opacity);
435 |
436 | squareWidth = Math.floor((w-10)/xbins + 0.5)
437 | squareHeight = Math.floor(h/ybins + 0.5)
438 | vis.selectAll("rect.brush")
439 | .data(indices(roll))
440 | .enter().append("svg:rect")
441 | .attr("class", "brush")
442 | .attr("pointer-events", "none")
443 | .attr("fill-opacity", 0)
444 | .attr("x", 0)
445 | .attr("y", 0)
446 | .attr("width", squareWidth)
447 | .attr("height", squareHeight);
448 | };
449 |
450 | scat.select = function(e) {
451 | if (e.data) {
452 | var sd = dv_profile_cache(e, qb); // selected data
453 | var c = d3.scale.linear()
454 | .domain([0,d3.max(sd[2])])
455 | .range([0.15,1]);
456 | var sel = vis.selectAll("rect.brush")
457 | .data(indices(sd))
458 | .attr("x", function(i) { return x(sd[0][i]); })
459 | .attr("y", function(i) { return y(sd[1][i]); })
460 | .attr("fill-opacity", function(i) { return c(sd[2][i]); });
461 | sel.exit()
462 | .attr("fill-opacity", 0);
463 | } else {
464 | vis.selectAll("rect.brush")
465 | .attr("fill-opacity", 0);
466 | }
467 | };
468 |
469 | scat.rollup = function() { return roll; };
470 |
471 | scat.fields = function() {
472 | if (arguments.length == 0) return fields;
473 | fields = arguments;
474 | xfield = fields[0];
475 | yfield = fields[1] || xfield;
476 | return scat;
477 | };
478 |
479 | scat.options = function() {
480 | if (arguments.length == 0) return fields;
481 | opt = arguments[0];
482 | bins = opt.bins || bins;
483 | w = opt.width || w;
484 | h = opt.height || h;
485 | scat.update();
486 | return scat;
487 | };
488 |
489 | scat.type = function() { return "scatter"; };
490 |
491 | scat.initUI();
492 | scat.initBins();
493 | return scat;
494 | };
--------------------------------------------------------------------------------
/examples/profile/splom.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Profiler Scatterplot Matrix
4 |
5 |
6 |
7 |
24 |
25 |
26 | Data Profiler Scatter Plot Matrix (SPLOM)
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/examples/query/benchmark.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Datavore Query Benchmarks
4 |
5 |
6 |
7 |
11 |
12 |
13 |
15 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/examples/query/generate_data.js:
--------------------------------------------------------------------------------
1 | function generate_data(N) {
2 | function randn(n) {
3 | return Math.max(0, Math.floor(n*(Math.random()-0.001)));
4 | }
5 |
6 | // generate synthetic data set
7 | var cols = [[],[],[]], // data columns
8 | am = ["a","b","c"], // domain of 1st col
9 | bm = d3.range(1,6); // domain of 2nd col
10 |
11 | // generate rows from random data
12 | for (var i=0; i
2 |
3 | Datavore Query Examples
4 |
5 |
6 |
7 |
12 |
13 |
14 |
104 |
105 |
106 |