Mercurial > repos > immport-devteam > cs_overview
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 + '"> </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 })(); |