Skip to content

Instantly share code, notes, and snippets.

@avrasgoldman
Last active November 22, 2019 19:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save avrasgoldman/f2bc5d01c02d12075a3d9bc971d76d27 to your computer and use it in GitHub Desktop.
Save avrasgoldman/f2bc5d01c02d12075a3d9bc971d76d27 to your computer and use it in GitHub Desktop.
bean explorer
license: mit
ID food_group beans Calcium, Ca (mg) Carbohydrate, by difference (g) Total lipid (fat) (g) Fiber, total dietary (g) Iron, Fe (mg) Magnesium, Mg (mg) Protein (g) Riboflavin (mg) Sugars, total (g)
1 Legumes black 27 23.71 0.54 8.7 2.1 0.444 8.86 0.059 0.32
2 Legumes kidney 28 22.8 0.5 6.4 2.94 0.477 8.67 0.058 0.32
3 Legumes red kidney 28 22.8 0.5 7.4 2.94 0.477 8.67 0.058 0.32
4 Legumes navy 69 26.05 0.62 10.5 2.36 0.527 8.23 0.066 0.37
5 Legumes pink 52 27.91 0.49 5.3 2.3 0.548 9.06 0.063 0.36
6 Legumes pinto 46 26.22 0.65 9 2.09 0.453 9.01 0.062 0.34
7 Legumes white 90 25.09 0.35 6.3 3.7 0.636 9.73 0.046 0.34
8 Legumes yeloow 62 25.28 1.08 10.4 2.48 0.455 9.16 0.103 0.34
9 Legumes fava 36 19.65 0.4 5.4 1.5 0.421 7.6 0.089 1.82
10 Legumes garbanzo 49 27.42 2.59 7.6 2.89 1.03 8.86 0.063 4.8
11 Legumes blackeyed 24 20.76 0.53 6.5 2.51 0.475 7.73 0.055 3.3
12 Legumes lentils 19 19.54 0.38 7.9 3.33 0.494 9.02 0.073 1.8
13 Legumes lima 17 20.88 0.38 7 2.39 0.516 7.8 0.055 2.9
14 Legumes mung 27 19.15 0.38 7.6 1.4 0.298 7.02 0.061 2
15 Legumes peanuts 55 21.26 22.01 8.8 1.01 1.023 13.5 0.063 2.47
16 Legumes peas 14 20.51 0.39 8.3 1.29 0.396 8.34 0.056 2.9
17 Legumes soybeans 102 8.36 8.97 6 5.14 0.824 18.21 0.285 3
<!DOCTYPE html>
<meta charset="utf-8">
<title>Nutrient Parallel Coordinates IV</title>
<body>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://bl.ocks.org/syntagmatic/raw/3341641/render-queue.js"></script>
<link rel="stylesheet" href="style.css"></link>
<script>
var margin = {top: 66, right: 110, bottom: 20, left: 188},
width = document.body.clientWidth - margin.left - margin.right,
height = 340 - margin.top - margin.bottom,
innerHeight = height - 2;
var devicePixelRatio = window.devicePixelRatio || 1;
var 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"]);
var 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])
}
};
var dimensions = [
{
key: "beans",
description: "Beans",
type: types["String"],
axis: d3.axisLeft()
.tickFormat(function(d,i) {
return d;
})
},
{
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: "Riboflavin (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: "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])
}
];
var xscale = d3.scalePoint()
.domain(d3.range(dimensions.length))
.range([0, width]);
var yAxis = d3.axisLeft();
var container = d3.select("body").append("div")
.attr("class", "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", function(d) { return "axis " + d.key.replace(/ /g, "_"); })
.attr("transform", function(d,i) { return "translate(" + xscale(i) + ")"; });
d3.csv("beans.csv", function(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);
});
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", "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", "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);
d3.selectAll(".axis.beans .tick text")
.style("fill", color);
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])];
});
};
function draw(d) {
ctx.strokeStyle = color(d.beans);
ctx.beginPath();
var coords = project(d);
coords.forEach(function(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();
}
function brushstart() {
d3.event.sourceEvent.stopPropagation();
}
// Handles a brush event, toggling the display of foreground lines.
function brush() {
render.invalidate();
var actives = [];
svg.selectAll(".axis .brush")
.filter(function(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)));
}
});
function d3_functor(v) {
return typeof v === "function" ? v : function() { return v; };
};
</script>
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;
}
.axis.manufac_name .tick text,
.axis.beans .tick text {
opacity: 1;
}
//.axis:hover line,
//.axis:hover path,
.axis.active line,
.axis.active path {
fill: none;
stroke: #222;
stroke-width: 1px;
}
.key:hover .beans {
font-weight: bold;
}
.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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment