initial commit

This commit is contained in:
Colin Megill
2017-08-23 16:28:01 -07:00
commit f6d7cc3e23
40 changed files with 1661 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
import React from 'react';
import _ from "lodash";
import cells from "../../../data/GBM_metadata.js";
import drawParallelCoordinates from "./drawParallelCoordinates";
import createContinuousRanges from "./createContinuousRanges";
const Continuous = () => {
drawParallelCoordinates(cells)
return (
<div>
<h3> Continuous Metadata </h3>
</div>
)
};
export default Continuous;
@@ -0,0 +1,25 @@
import * as globals from "../../globals.js"
/*
end result:
ranges: {
Total_reads: {
min: 11111,
max: 33333
},
Genes_detected: {
min: 444,
max: 555
}
}
*/
const createContinuousRanges = (cells) => {
const ranges = {};
return ranges;
}
export default createContinuousRanges;
@@ -0,0 +1,314 @@
import styles from './parallelCoordinates.css';
import {
margin,
width,
height,
innerHeight,
color,
dimensions,
types,
xscale,
yAxis,
} from "./util";
/*****************************************
******************************************
Canvas Parallel coordinates with svg brushing via /* via https://bl.ocks.org/syntagmatic/05a5b0897a48890133beb59c815bd953
******************************************
******************************************/
/*****************************************
******************************************
Render Queue via http://bl.ocks.org/syntagmatic/raw/3341641/render-queue.js
******************************************
******************************************/
const renderQueue = (function(callback1234) {
var _queue = [], // data to be rendered
_rate = 1000, // number of calls per frame
_invalidate = function() {}, // invalidate last render queue
_clear = function() {}; // clearing function
var rq = function(data) {
if (data) rq.data(data);
_invalidate();
_clear();
rq.render();
};
rq.render = function() {
var valid = true;
_invalidate = rq.invalidate = function() {
valid = false;
};
function doFrame() {
if (!valid) return true;
var chunk = _queue.splice(0,_rate);
chunk.map(callback1234);
timer_frame(doFrame);
}
doFrame();
};
rq.data = function(data) {
_invalidate();
_queue = data.slice(0); // creates a copy of the data
return rq;
};
rq.add = function(data) {
_queue = _queue.concat(data);
};
rq.rate = function(value) {
if (!arguments.length) return _rate;
_rate = value;
return rq;
};
rq.remaining = function() {
return _queue.length;
};
// clear the canvas
rq.clear = function(func) {
if (!arguments.length) {
_clear();
return rq;
}
_clear = func;
return rq;
};
rq.invalidate = _invalidate;
var timer_frame = window.requestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.oRequestAnimationFrame
|| window.msRequestAnimationFrame
|| function(callback) { setTimeout(callback, 17); };
return rq;
});
/*****************************************
******************************************
MAIN: drawParallelCoordinates
******************************************
******************************************/
const drawParallelCoordinates = (data) => {
const d3_functor = (v) => {
return typeof v === "function" ? v : function() { return v; };
};
/*****************************************
******************************************
Setup SVG & Canvas elements
******************************************
******************************************/
var container = d3.select("body").append("div")
.attr("class", styles.parcoords)
.style("width", width + margin.left + margin.right + "px")
.style("height", height + margin.top + margin.bottom + "px");
var svg = container.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var canvas = container.append("canvas")
.attr("width", width * devicePixelRatio)
.attr("height", height * devicePixelRatio)
.style("width", width + "px")
.style("height", height + "px")
.style("margin-top", margin.top + "px")
.style("margin-left", margin.left + "px");
var ctx = canvas.node().getContext("2d");
ctx.globalCompositeOperation = 'darken';
ctx.globalAlpha = 0.15;
ctx.lineWidth = 1.5;
ctx.scale(devicePixelRatio, devicePixelRatio);
var output = d3.select("body").append("pre");
var axes = svg.selectAll(".axis")
.data(dimensions)
.enter().append("g")
.attr("class", styles.axis)
.attr("transform", function(d,i) { return "translate(" + xscale(i) + ")"; });
/*****************************************
******************************************
Setup SVG & Canvas elements
******************************************
******************************************/
d3.csv("https://gist.githubusercontent.com/syntagmatic/05a5b0897a48890133beb59c815bd953/raw/310a68d713eb4d351f809b6cfbcffe3d9d96a205/nutrient.csv", (error, data) => {
if (error) throw error;
// shuffle the data!
data = d3.shuffle(data);
data.forEach(function(d) {
dimensions.forEach(function(p) {
d[p.key] = !d[p.key] ? null : p.type.coerce(d[p.key]);
});
// truncate long text strings to fit in data table
for (var key in d) {
if (d[key] && d[key].length > 35) d[key] = d[key].slice(0,36);
}
});
// type/dimension default setting happens here
dimensions.forEach(function(dim) {
if (!("domain" in dim)) {
// detect domain using dimension type's extent function
dim.domain = d3_functor(dim.type.extent)(data.map(function(d) { return d[dim.key]; }));
}
if (!("scale" in dim)) {
// use type's default scale for dimension
dim.scale = dim.type.defaultScale.copy();
}
dim.scale.domain(dim.domain);
});
const draw = (d) => {
ctx.strokeStyle = "rgba(0,0,0,.4)" /* color(d.food_group); */
ctx.beginPath();
var coords = project(d);
coords.forEach((p,i) => {
// this tricky bit avoids rendering null values as 0
if (p === null) {
// this bit renders horizontal lines on the previous/next
// dimensions, so that sandwiched null values are visible
if (i > 0) {
var prev = coords[i-1];
if (prev !== null) {
ctx.moveTo(prev[0],prev[1]);
ctx.lineTo(prev[0]+6,prev[1]);
}
}
if (i < coords.length-1) {
var next = coords[i+1];
if (next !== null) {
ctx.moveTo(next[0]-6,next[1]);
}
}
return;
}
if (i == 0) {
ctx.moveTo(p[0],p[1]);
return;
}
ctx.lineTo(p[0],p[1]);
});
ctx.stroke();
}
/*****************************************
******************************************
Handles a brush event, toggling the display of foreground lines.
******************************************
******************************************/
const brush = () => {
render.invalidate();
var actives = [];
svg.selectAll(".axis .brush")
.filter((d) => {
return d3.brushSelection(this);
})
.each(function(d) {
actives.push({
dimension: d,
extent: d3.brushSelection(this)
});
});
var selected = data.filter(function(d) {
if (actives.every(function(active) {
var dim = active.dimension;
// test if point is within extents for each active brush
return dim.type.within(d[dim.key], active.extent, dim);
})) {
return true;
}
});
ctx.clearRect(0,0,width,height);
ctx.globalAlpha = d3.min([0.85/Math.pow(selected.length,0.3),1]);
render(selected);
output.text(d3.tsvFormat(selected.slice(0,24)));
console.log('fires brush', actives)
}
function brushstart() {
d3.event.sourceEvent.stopPropagation();
}
var render = renderQueue(draw).rate(50);
ctx.clearRect(0,0,width,height);
ctx.globalAlpha = d3.min([0.85/Math.pow(data.length,0.3),1]);
render(data);
axes.append("g")
.each(function(d) {
var renderAxis = "axis" in d
? d.axis.scale(d.scale) // custom axis
: yAxis.scale(d.scale); // default axis
d3.select(this).call(renderAxis);
})
.append("text")
.attr("class", styles.title)
.attr("text-anchor", "start")
.text(function(d) { return "description" in d ? d.description : d.key; });
// Add and store a brush for each axis.
axes.append("g")
.attr("class", styles.brush)
.each(function(d) {
d3.select(this).call(d.brush = d3.brushY()
.extent([[-10,0], [10,height]])
.on("start", brushstart)
.on("brush", brush)
.on("end", brush)
)
})
.selectAll("rect")
.attr("x", -8)
.attr("width", 16);
output.text(d3.tsvFormat(data.slice(0,24)));
function project(d) {
return dimensions.map(function(p,i) {
// check if data element has property and contains a value
if (
!(p.key in d) ||
d[p.key] === null
) return null;
return [xscale(i),p.scale(d[p.key])];
});
};
});
}
export default drawParallelCoordinates;
@@ -0,0 +1,91 @@
/*
This code can be found: https://bl.ocks.org/syntagmatic/05a5b0897a48890133beb59c815bd953
body {
min-width: 760px;
}
*/
.parcoords {
display: block;
}
.parcoords svg,
.parcoords canvas {
font: 10px sans-serif;
position: absolute;
}
.parcoords canvas {
opacity: 0.9;
pointer-events: none;
}
.axis .title {
font-size: 10px;
transform: rotate(-21deg) translate(-5px,-6px);
fill: #222;
}
.axis line,
.axis path {
fill: none;
stroke: #ccc;
stroke-width: 1px;
}
.axis .tick text {
fill: #222;
opacity: 0;
pointer-events: none;
}
/*
old, from reference bl.ocks
.axis.manufac_name .tick text,
.axis.food_group .tick text {
opacity: 1;
}
*/
.axis:hover line,
.axis:hover path,
.axis.active line,
.axis.active path {
fill: none;
stroke: #222;
stroke-width: 1px;
}
.axis:hover .title {
font-weight: bold;
}
.axis:hover .tick text {
opacity: 1;
}
.axis.active .title {
font-weight: bold;
}
.axis.active .tick text {
opacity: 1;
font-weight: bold;
}
.brush .extent {
fill-opacity: .3;
stroke: #fff;
stroke-width: 1px;
}
pre {
width: 100%;
height: 300px;
margin: 6px 12px;
tab-size: 40;
font-size: 10px;
overflow: auto;
}
+162
View File
@@ -0,0 +1,162 @@
export const margin = {top: 66, right: 110, bottom: 20, left: 188};
export const width = document.body.clientWidth - margin.left - margin.right;
export const height = 340 - margin.top - margin.bottom;
export const innerHeight = height - 2;
export const devicePixelRatio = window.devicePixelRatio || 1;
export const color = d3.scaleOrdinal()
.range(["#5DA5B3","#D58323","#DD6CA7","#54AF52","#8C92E8","#E15E5A","#725D82","#776327","#50AB84","#954D56","#AB9C27","#517C3F","#9D5130","#357468","#5E9ACF","#C47DCB","#7D9E33","#DB7F85","#BA89AD","#4C6C86","#B59248","#D8597D","#944F7E","#D67D4B","#8F86C2"]);
export const types = {
"Number": {
key: "Number",
coerce: function(d) { return +d; },
extent: d3.extent,
within: function(d, extent, dim) { return extent[0] <= dim.scale(d) && dim.scale(d) <= extent[1]; },
defaultScale: d3.scaleLinear().range([innerHeight, 0])
},
"String": {
key: "String",
coerce: String,
extent: function (data) { return data.sort(); },
within: function(d, extent, dim) { return extent[0] <= dim.scale(d) && dim.scale(d) <= extent[1]; },
defaultScale: d3.scalePoint().range([0, innerHeight])
},
"Date": {
key: "Date",
coerce: function(d) { return new Date(d); },
extent: d3.extent,
within: function(d, extent, dim) { return extent[0] <= dim.scale(d) && dim.scale(d) <= extent[1]; },
defaultScale: d3.scaleTime().range([0, innerHeight])
}
};
export const dimensions = [
{
key: "Total lipid (fat) (g)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Sugars, total (g)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Calcium, Ca (mg)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Sodium, Na (mg)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Phosphorus, P (mg)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Potassium, K (mg)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Thiamin (mg)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Riboflavin (mg)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Niacin (mg)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Iron, Fe (mg)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Magnesium, Mg (mg)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Protein (g)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Zinc, Zn (mg)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Vitamin B-6 (mg)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Vitamin B-12 (mcg)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Folic acid (mcg)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Selenium, Se (mcg)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Vitamin A, IU (IU)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Vitamin K (phylloquinone) (mcg)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Vitamin C, total ascorbic acid (mg)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Vitamin D (IU)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Cholesterol (mg)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Fiber, total dietary (g)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
},
{
key: "Carbohydrate, by difference (g)",
type: types["Number"],
scale: d3.scaleSqrt().range([innerHeight, 0])
}
];
export const xscale = d3.scalePoint()
.domain(d3.range(dimensions.length))
.range([0, width]);
export const yAxis = d3.axisLeft();