comparison js/boxplots.js @ 2:a64ece32a01a draft default tip

"planemo upload for repository https://github.com/ImmPortDB/immport-galaxy-tools/tree/master/flowtools/cs_overview commit a46097db0b6056e1125237393eb6974cfd51eb41"
author azomics
date Tue, 28 Jul 2020 08:32:36 -0400
parents
children
comparison
equal deleted inserted replaced
1:bca68066a957 2:a64ece32a01a
1 // Copyright (c) 2016 Northrop Grumman.
2 // All rights reserved.
3
4 var displayMarkerTable = function(plotconfig){
5 var nbm = plotconfig.mrkrNames.length + 1;
6 $(plotconfig.mtable).empty();
7 plotconfig.allMarkers.map(function(v) {
8 $(plotconfig.mtable).append('<tr>'
9 + '<td><span style="background-color:rgba(0,0,0,' + (v + 1 )/ nbm + ')'
10 + '">&nbsp;&nbsp;&nbsp;</span></td>'
11 + '<td title="' + plotconfig.mrkrNames[v] + '">'
12 + plotconfig.mrkrNames[v] + '</td>'
13 + '<td align="center"><input type="checkbox" checked class='
14 + plotconfig.mrkrSelect + ' value=' + v + '/></td></tr>');
15 });
16 if (nbm > 5) {
17 $(plotconfig.mrkrSelectAll).prop('checked', false);
18 $(plotconfig.mrkrSelectAll).prop('disabled', true);
19 $('#markerWarning').show();
20 $(plotconfig.mrkrSelectj).each(function() {
21 var selectedMrkr = parseInt(this.value);
22 if (selectedMrkr > 4){
23 this.checked = false;
24 this.disabled = true;
25 } else {
26 this.checked = true;
27 }
28 });
29 }
30
31 $(plotconfig.mrkrSelectAll).click(function() {
32 var checkAll = $(plotconfig.mrkrSelectAll).prop('checked');
33 if (checkAll) {
34 $(plotconfig.mrkrSelectj).prop("checked", true);
35 } else {
36 $(plotconfig.mrkrSelectj).prop("checked", false);
37 }
38 updateCSplots(plotconfig);
39 });
40
41 $(plotconfig.mrkrSelectj).click(function() {
42 if (nbm < 6){
43 if ($(plotconfig.mrkrSelectj).length == $(plotconfig.mrkrSelectCheck).length) {
44 $(plotconfig.mrkrSelectAll).prop("checked",true);
45 } else {
46 $(plotconfig.mrkrSelectAll).prop("checked",false);
47 }
48 } else {
49 var nbSelected = 0;
50 $(plotconfig.mrkrSelectj).each(function() {
51 if (this.checked) {nbSelected++}
52 });
53 if (nbSelected < 5) {
54 $(plotconfig.mrkrSelectj).prop('disabled', false);
55 } else {
56 $(plotconfig.mrkrSelectj).each(function() {
57 if (!this.checked) {
58 this.disabled = true;
59 }
60 });
61 }
62 }
63 updateCSplots(plotconfig);
64 });
65 };
66
67 var updateBoxplot = function(plotconfig){
68 var margin = {top: 30, right: 10, bottom: 50, left: 60},
69 h = 0,
70 w = 0,
71 width = 0,
72 height = 0,
73 labels = false, // show the text labels beside individual boxplots?
74 mfi_option = false,
75 min = Infinity,
76 max = -Infinity,
77 checkLabels = $(plotconfig.displayvalues).prop("checked"),
78 checkMFI = $(plotconfig.displayMFI).prop("checked"),
79 dataToPlot = [],
80 tmp = [],
81 nbm = plotconfig.mrkrNames.length + 1,
82 maxRange = 0,
83 minRange = 0,
84 domainx = [],
85 domainx1 = [];
86
87 $(plotconfig.plotdivj).empty();
88 h = $(window).height() - 200;
89 $(plotconfig.plotdivj).height(h);
90 w = $(plotconfig.plotdivj).width();
91 width = w - margin.left - margin.right;
92 height = h - margin.top - margin.bottom;
93
94 var svg = d3.select(plotconfig.plotdivj).append("svg")
95 .attr("width", w)
96 .attr("height", h)
97 .attr("class", "box")
98 .append("g")
99 .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
100
101 if (checkLabels) {
102 labels = true;
103 };
104 if (checkMFI) {
105 mfi_option = true;
106 };
107 /* Get the data in proper shape to feed to the boxplot function
108 want [Object, Object, ..., Object] where Object:
109 'population': pop
110 'Data' : [Object, Object, ..., Object] where Object:
111 'Marker' : marker name
112 'outliers' : outliers
113 */
114
115 if (plotconfig.view == 'p') {
116 plotconfig.selectedPopulations.forEach(function(p) {
117 tmpPlot = [];
118 plotconfig.selectedMarkers.forEach(function(m) {
119 var markernm = plotconfig.mrkrNames[m],
120 qtmp = [
121 +plotconfig.csdata.q1[markernm][p],
122 +plotconfig.csdata.q2[markernm][p],
123 +plotconfig.csdata.q3[markernm][p]
124 ],
125 wtmp = [
126 +plotconfig.csdata.lower[markernm][p],
127 +plotconfig.csdata.upper[markernm][p]
128 ];
129 tmp = [];
130 // Get min and max while we're here
131 plotconfig.csdata.outliers[markernm][p].forEach(function(outl) {
132 tmp.push(+outl);
133 if (+outl > max) {max = +outl};
134 if (+outl < min) {min = +outl};
135 });
136 if (+plotconfig.csdata.upper[markernm][p] > max) {
137 max = +plotconfig.csdata.upper[markernm][p];
138 };
139 if (+plotconfig.csdata.lower[markernm][p] < min) {
140 min = +plotconfig.csdata.lower[markernm][p];
141 };
142 tmpPlot.push({
143 marker: markernm,
144 outliers: tmp,
145 quartiles: qtmp,
146 whiskers: wtmp,
147 config: [m,p, nbm],
148 mfi: +plotconfig.csdata.mfi[markernm][p]
149 });
150 });
151 dataToPlot.push({population:p, popdata: tmpPlot});
152 });
153 } else {
154 plotconfig.selectedMarkers.forEach(function(m) {
155 var markernm = plotconfig.mrkrNames[m];
156 tmpPlot = [];
157 plotconfig.selectedPopulations.forEach(function(p) {
158 var qtmp = [
159 +plotconfig.csdata.q1[markernm][p],
160 +plotconfig.csdata.q2[markernm][p],
161 +plotconfig.csdata.q3[markernm][p]
162 ],
163 wtmp = [
164 +plotconfig.csdata.lower[markernm][p],
165 +plotconfig.csdata.upper[markernm][p]
166 ];
167 tmp = [];
168 // Get min and max while we're here
169 plotconfig.csdata.outliers[markernm][p].forEach(function(outl) {
170 tmp.push(+outl);
171 if (+outl > max) {max = +outl};
172 if (+outl < min) {min = +outl};
173 });
174 if (+plotconfig.csdata.upper[markernm][p] > max) {
175 max = +plotconfig.csdata.upper[markernm][p];
176 };
177 if (+plotconfig.csdata.lower[markernm][p] < min) {
178 min = +plotconfig.csdata.lower[markernm][p];
179 };
180 tmpPlot.push({
181 population:p,
182 outliers: tmp,
183 quartiles: qtmp,
184 whiskers: wtmp,
185 config: [m,p, nbm],
186 mfi: +plotconfig.csdata.mfi[markernm][p]
187 });
188 });
189 dataToPlot.push({marker: markernm, popdata: tmpPlot});
190 });
191 };
192 maxRange = max + 30;
193 minRange = min - 30;
194
195 if (plotconfig.view == 'p') {
196 domainx = plotconfig.selectedPopulations;
197 domainx1 = plotconfig.selectedMarkers.map(function(d){
198 return plotconfig.mrkrNames[d];
199 });
200 } else {
201 domainx1 = plotconfig.selectedPopulations;
202 domainx = plotconfig.selectedMarkers.map(function(d){
203 return plotconfig.mrkrNames[d];
204 });
205 }
206 // axes
207 var xScale = d3.scale.ordinal()
208 .domain(domainx)
209 .rangeRoundBands([0 , width], 0.2, 0.02);
210
211 var x1Scale = d3.scale.ordinal()
212 .domain(domainx1)
213 .rangeRoundBands([0, xScale.rangeBand()], 0.1);
214
215 var xAxis = d3.svg.axis()
216 .scale(xScale)
217 .orient("bottom");
218
219 // the y-axis
220 var yScale = d3.scale.linear()
221 .domain([minRange, maxRange])
222 .range([height + margin.top, 0 + margin.top]);
223
224 var yAxis = d3.svg.axis()
225 .scale(yScale)
226 .orient("left")
227 .tickFormat(d3.format("d"));
228
229 svg.append("g")
230 .attr("class", "x axisbp")
231 .attr("transform", "translate(0," + (height + margin.top) + ")")
232 .call(xAxis);
233
234 svg.append("g")
235 .attr("class", "y axisbp")
236 .call(yAxis)
237 .append("text")
238 .attr("class", "ylabel")
239 .attr("transform", "rotate(-90)")
240 .attr("y", 0 - margin.left)
241 .attr("x", 0 - (height / 2))
242 .attr("dy", "1em")
243 .style("text-anchor", "middle")
244 .text("MFI values");
245
246 var boxplot = d3.box()
247 .width(x1Scale.rangeBand())
248 .height(height + margin.top)
249 .domain([minRange, maxRange])
250 .showLabels(labels)
251 .showMFI(mfi_option);
252
253 if (plotconfig.view == 'p'){
254 var group = svg.selectAll(".groups")
255 .data(dataToPlot)
256 .enter().append("g")
257 .attr("class", "group")
258 .attr("transform", function(d) {
259 return "translate(" + xScale(d.population) + ",0)";
260 });
261
262 group.selectAll(".box")
263 .data(function(d) { return d.popdata; })
264 .enter().append("g")
265 .attr("transform", function(d) {return "translate(" + x1Scale(d.marker) + ",0)"; })
266 .call(boxplot);
267 } else {
268 var group = svg.selectAll(".groups")
269 .data(dataToPlot)
270 .enter().append("g")
271 .attr("class", "group")
272 .attr("transform", function(d) {
273 return "translate(" + xScale(d.marker) + ",0)";
274 });
275
276 group.selectAll(".box")
277 .data(function(d) { return d.popdata; })
278 .enter().append("g")
279 .attr("transform", function(d) { return "translate(" + x1Scale(d.population) + ",0)"; })
280 .call(boxplot);
281 }
282 };
283
284 (function() {
285 // Inspired by http://informationandvisualization.de/blog/box-plot
286 // Modified to fit our data structure.
287 d3.box = function() {
288 var width = 1,
289 height = 1,
290 duration = 0,
291 domain = null,
292 value = Number,
293 showLabels = true, // whether or not to show text labels
294 numBars = 4,
295 curBar = 1,
296 showMFI = true, // display MFI ?
297 tickFormat = null,
298 margin = {top: 30, right: 10, bottom: 50, left: 60};
299
300 // For each small multiple…
301 function box(g) {
302 g.each(function(data, i) {
303 var d = data.outliers.sort(d3.ascending),
304 g = d3.select(this),
305 n = d.length,
306 min = Infinity,
307 max = -Infinity;
308 if (n > 0){
309 min = d[0],
310 max = d[n - 1];
311 }
312 // Readjust min and max with upper and lower values
313 if (data.whiskers[0] < min) {min = data.whiskers[0]}
314 if (data.whiskers[1] > max) {max = data.whiskers[1]}
315 // Compute quartiles. Must return exactly 3 elements.
316 var quartileData = data.quartiles;
317 // Compute whiskers. Must return exactly 2 elements, or null.
318 var whiskerData = data.whiskers;
319 // Compute outliers. here all data in d is an outlier.
320 // We compute the outliers as indices, so that we can join across transitions!
321 var outlierIndices = d3.range(n);
322 var mfiData = data.mfi;
323 // this is the scale for ONE SET of values
324 // Compute the new x-scale.
325 var x1 = d3.scale.linear()
326 .domain(domain && domain.call(this, d, i) || [min, max])
327 .range([height , 0 + margin.top ]);
328 // Retrieve the old x-scale, if this is an update.
329 var x0 = this.__chart__ || d3.scale.linear()
330 .domain([0, Infinity])
331 .range(x1.range());
332
333 // Stash the new scale.
334 this.__chart__ = x1;
335 // Note: the box, median, and box tick elements are fixed in number,
336 // so we only have to handle enter and update. In contrast, the outliers
337 // and other elements are variable, so we need to exit them! Variable
338 // elements also fade in and out.
339 // Update center line: the vertical line spanning the whiskers.
340 var center = g.selectAll("line.center")
341 .data(whiskerData ? [whiskerData] : []);
342
343 //vertical line
344 center.enter().insert("line", "rect")
345 .attr("class", "center")
346 .attr("x1", width / 2)
347 .attr("y1", function(d) { return x0(d[0]); })
348 .attr("x2", width / 2)
349 .attr("y2", function(d) { return x0(d[1]); })
350 .style("opacity", 1e-6)
351 .style("stroke", function(d) { return color_palette[0][data.config[1]][3]; })
352 .transition()
353 .duration(duration)
354 .style("opacity", 1)
355 .attr("y1", function(d) { return x1(d[0]); })
356 .attr("y2", function(d) { return x1(d[1]); });
357
358 center.transition()
359 .duration(duration)
360 .style("opacity", 1)
361 .attr("y1", function(d) { return x1(d[0]); })
362 .attr("y2", function(d) { return x1(d[1]); });
363
364 center.exit().transition()
365 .duration(duration)
366 .style("opacity", 1e-6)
367 .attr("y1", function(d) { return x1(d[0]); })
368 .attr("y2", function(d) { return x1(d[1]); })
369 .remove();
370
371 // Update innerquartile box.
372 var box = g.selectAll("rect.box")
373 .data([quartileData]);
374
375 box.enter().append("rect")
376 .attr("class", "box")
377 .style("fill", function(d) {
378 var nbm = data.config[2],
379 pop = data.config[1],
380 mrkr = data.config[0];
381 var color = color_palette[0][pop][1] + (mrkr + 1 )/ nbm + ')';
382 return color; })
383 .style("stroke", function(d) { return color_palette[0][data.config[1]][3]; })
384 .attr("x", 0)
385 .attr("y", function(d) { return x0(d[2]); })
386 .attr("width", width)
387 .attr("height", function(d) { return x0(d[0]) - x0(d[2]); })
388 .transition()
389 .duration(duration)
390 .attr("y", function(d) { return x1(d[2]); })
391 .attr("height", function(d) { return x1(d[0]) - x1(d[2]); });
392
393 box.transition()
394 .duration(duration)
395 .attr("y", function(d) { return x1(d[2]); })
396 .attr("height", function(d) { return x1(d[0]) - x1(d[2]); });
397
398 // Update median line.
399 var medianLine = g.selectAll("line.median")
400 .data([quartileData[1]]);
401
402 medianLine.enter().append("line")
403 .attr("class", "median")
404 .attr("x1", 0)
405 .attr("y1", x0)
406 .attr("x2", width)
407 .attr("y2", x0)
408 .style("stroke", function(d) { return color_palette[0][data.config[1]][3]; })
409 .transition()
410 .duration(duration)
411 .attr("y1", x1)
412 .attr("y2", x1);
413
414 medianLine.transition()
415 .duration(duration)
416 .attr("y1", x1)
417 .attr("y2", x1);
418
419 // Update MFI line.
420 var MFILine = g.selectAll("line.mfi")
421 .data([mfiData]);
422 if (showMFI == true) {
423 MFILine.enter().append("line")
424 .attr("class", "mfi")
425 .style("stroke", function(d){ return color_palette[0][data.config[1]][2]; })
426 .attr("x1", 0)
427 .attr("y1", x0)
428 .attr("x2", width)
429 .attr("y2", x0)
430 .transition()
431 .duration(duration)
432 .attr("y1", x1)
433 .attr("y2", x1);
434
435 MFILine.transition()
436 .duration(duration)
437 .attr("y1", x1)
438 .attr("y2", x1);
439 }
440
441 // Update whiskers.
442 var whisker = g.selectAll("line.whisker")
443 .data(whiskerData || []);
444
445 whisker.enter().insert("line", "circle, text")
446 .attr("class", "whisker")
447 .attr("x1", 0)
448 .attr("y1", x0)
449 .attr("x2", 0 + width)
450 .attr("y2", x0)
451 .style("opacity", 1e-6)
452 .style("stroke", function(d) { return color_palette[0][data.config[1]][3]; })
453 .transition()
454 .duration(duration)
455 .attr("y1", x1)
456 .attr("y2", x1)
457 .style("opacity", 1);
458
459 whisker.transition()
460 .duration(duration)
461 .attr("y1", x1)
462 .attr("y2", x1)
463 .style("opacity", 1);
464
465 whisker.exit().transition()
466 .duration(duration)
467 .attr("y1", x1)
468 .attr("y2", x1)
469 .style("opacity", 1e-6)
470 .remove();
471
472 // Update outliers.
473 var outlier = g.selectAll("circle.outlier")
474 .data(outlierIndices, Number);
475
476 outlier.enter().insert("circle", "text")
477 .attr("class", "outlier")
478 .attr("r", 3)
479 .attr("cx", function(d){
480 return Math.floor(Math.random() * width);
481 })
482 .attr("cy", function(i) { return x0(d[i]); })
483 .style("opacity", 1e-6)
484 .style("fill", function(d) {
485 var nbm = data.config[2],
486 pop = data.config[1],
487 mrkr = data.config[0];
488 var color = color_palette[0][pop][1] + (mrkr + 1 )/ nbm + ')';
489 return color; })
490 .style("stroke", function(d) { return color_palette[0][data.config[1]][3]; })
491 .transition()
492 .duration(duration)
493 .attr("cy", function(i) { return x1(d[i]); })
494 .style("opacity", 1);
495
496 outlier.transition()
497 .duration(duration)
498 .attr("cy", function(i) { return x1(d[i]); })
499 .style("opacity", 1);
500
501 outlier.exit().transition()
502 .duration(duration)
503 .attr("cy", function(i) { return x1(d[i]); })
504 .style("opacity", 1e-6)
505 .remove();
506
507 // Compute the tick format.
508 var format = tickFormat || x1.tickFormat(8);
509
510 // Update box ticks.
511 var boxTick = g.selectAll("text.box")
512 .data(quartileData);
513 if(showLabels == true) {
514 boxTick.enter().append("text")
515 .attr("class", "box")
516 .attr("dy", ".3em")
517 .attr("dx", function(d, i) { return i & 1 ? 6 : -6 })
518 .attr("x", function(d, i) { return i & 1 ? + width : 0 })
519 .attr("y", x0)
520 .attr("text-anchor", function(d, i) { return i & 1 ? "start" : "end"; })
521 .text(format)
522 .transition()
523 .duration(duration)
524 .attr("y", x1);
525 }
526
527 boxTick.transition()
528 .duration(duration)
529 .text(format)
530 .attr("y", x1);
531
532 // Update whisker ticks. These are handled separately from the box
533 // ticks because they may or may not exist, and we want don't want
534 // to join box ticks pre-transition with whisker ticks post-.
535 var whiskerTick = g.selectAll("text.whisker")
536 .data(whiskerData || []);
537 if(showLabels == true) {
538 whiskerTick.enter().append("text")
539 .attr("class", "whisker")
540 .attr("dy", ".3em")
541 .attr("dx", 6)
542 .attr("x", width)
543 .attr("y", x0)
544 .text(format)
545 .style("opacity", 1e-6)
546 .transition()
547 .duration(duration)
548 .attr("y", x1)
549 .style("opacity", 1);
550 }
551 whiskerTick.transition()
552 .duration(duration)
553 .text(format)
554 .attr("y", x1)
555 .style("opacity", 1);
556
557 whiskerTick.exit().transition()
558 .duration(duration)
559 .attr("y", x1)
560 .style("opacity", 1e-6)
561 .remove();
562 });
563 d3.timer.flush();
564 }
565
566 box.width = function(x) {
567 if (!arguments.length) return width;
568 width = x;
569 return box;
570 };
571 box.height = function(x) {
572 if (!arguments.length) return height;
573 height = x;
574 return box;
575 };
576 box.tickFormat = function(x) {
577 if (!arguments.length) return tickFormat;
578 tickFormat = x;
579 return box;
580 };
581 box.duration = function(x) {
582 if (!arguments.length) return duration;
583 duration = x;
584 return box;
585 };
586 box.domain = function(x) {
587 if (!arguments.length) return domain;
588 domain = x == null ? x : d3.functor(x);
589 return box;
590 };
591 box.value = function(x) {
592 if (!arguments.length) return value;
593 value = x;
594 return box;
595 };
596 box.showLabels = function(x) {
597 if (!arguments.length) return showLabels;
598 showLabels = x;
599 return box;
600 };
601 box.showMFI = function(x) {
602 if (!arguments.length) return showMFI;
603 showMFI = x;
604 return box;
605 };
606 return box;
607 };
608 })();