Chromosome subset viewer with sample data. Object constancy is maintained each time the page loads. Page loops through random subsets of the data. Click to stop. Refresh page to start again. Designed as a way to discover new trends in a 80,000+ entry dataset.
xxxxxxxxxx
<meta charset="utf-8">
<style>
.data_paths {
stroke: #000;
stroke-width: 3.5px;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: black;
stroke-width: 1.5px;
shape-rendering: crispEdges;
}
text { font-family: sans-serif; }
.axis text { font-size: 11px; }
.key {
font-size: 12px;
shape-rendering: crisp-edges;
}
.enter { stroke: green; }
.update { stroke: #333; }
.exit { stroke: brown; }
</style>
<body>
<script src="https://d3js.org/d3.v3.js"></script>
<script>
// margins
var margin = {top: 20, right: 30, bottom: 30, left: 90, axis_label: 5},
w = 960 - margin.left - margin.right,
h = 500 - margin.top - margin.bottom,
key_area = 150,
size = 15; // size of subset
// scales
var xScale = d3.scale.linear()
.range([0, w]);
var yScale = d3.scale.ordinal()
.rangePoints([h, 0]);
var color = d3.scale.category20() // do i need to set the domain here?
.domain(["1", "2", "3", "4", "5", "6", "7", "8", "9", "M", "X", "Y",
"10", "11", "12", "13", "14", "15", "16", "17"]);
var keyScale = d3.scale.ordinal()
.rangeRoundBands([0,h]);
// axes
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(10);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(size);
// svg
var svg = d3.select("body").append("svg")
.attr("height", h + margin.top + margin.bottom)
.attr("width", w + margin.left + margin.right + key_area);
// plot
var plot = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// line generator
var line = d3.svg.line()
.x(function(d) { return xScale(d.tx); })
.y(function(d) { return yScale(d.i); });
/**** THE CALLBACK FUNCTION ****/
d3.csv("chrom_sample1.csv", form_the_data, function(error, data) {
/**** this stuff doesn't change each tick ****/
xScale.domain([d3.min(data, function(d) { return d.x1; }),
d3.max(data, function(d) { return d.x2; })]);
yScale.domain(d3.range(size));
// draw axes
plot.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis) // places the axis
.append("text") // creates the axis label
.attr("transform", "translate(" + (w - margin.axis_label) + "," + margin.bottom + ")")
.style("text-anchor", "start")
.style("fill", "black")
.text("tx");
plot.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "translate(" + margin.axis_label*-16 + "," + margin.axis_label*-1 + ") rotate(-90)")
.style("text-anchor", "end")
.style("fill", "black")
.text("gene");
// add key, with all chromosomes listed (by explicitly setting domain at top)
keyScale.domain(color.domain());
var keyData = toObjArr(keyScale.domain());
var legend = svg.append("g")
.attr("transform", "translate(" + (w + margin.left + margin.right*2) + "," + margin.top + ")");
var entries = legend.selectAll(".key")
.data(keyData)
.enter().append("g")
.attr("class", "key")
.attr("transform", function(d){ return "translate(0," + keyScale(d.valueOf()) + ")"; });
entries.append("circle")
.attr("cx", 0).attr("cy", 0).attr("r", 5)
.attr("fill", function(d){ return color(d.valueOf()); });
entries.append("text")
.attr("x", 7).attr("y", 4)
.text(function(d){ return "chr" + d.valueOf(); })
/*********************************/
// this is looking pretty good. needs a little work. and code needs tidying.
// some axis tick marks are thicker than others...
// looks good. i just don't understand transitions. yet.
/**************************/
// add an index to the data, for making subsets
var index = 0;
data.forEach(function(d) {
d.index = index;
index += 1;
});
// make and draw first subset right here. keeps key function from returning undefined
var subset = [],
candidate = 0,
flag = true;
// make the subset. no duplicates.
for (var i = 0; i < size; i++) {
flag = true;
while (flag == true) {
flag = false;
candidate = Math.floor(Math.random()*(data.length - 1 - 0 + 1) + 0);
subset.forEach(function(d) {
if (d.index == candidate) {
flag = true;
}
});
}
subset.push(data[candidate]);
}
var paths = plot.selectAll(".data_paths")
.data(subset.map(function(d,i) {
return [
{ tx: d.x1, i: i, gene_name: d.name, chr: d.chrom_val },
{ tx: d.x2, i: i }
]
}))
.enter()
.append("path")
.attr("class", "data_paths")
.attr("d", line)
.style("stroke", function(d) {
// color lines according to the chromosome they're on
return color(d[0].chr);
})
.append("title")
.text(function(d) {
return "gene: " + d[0].gene_name + " chr: " + d[0].chr + " tx: " + d[0].tx + "-" + d[1].tx;
});
plot.selectAll(".y.axis text")
.data(subset)
.text(function(d) { return d.name; })
// run until on click or browser close
var run = setInterval(function() {
var subset = [],
candidate = 0,
flag = true;
// make the subset. no duplicates. ultimately have this inside a tick function
for (var i = 0; i < size; i++) {
flag = true;
while (flag == true) {
flag = false;
candidate = Math.floor(Math.random()*(data.length - 1 - 0 + 1) + 0);
subset.forEach(function(d) {
if (d.index == candidate) {
flag = true;
}
});
}
subset.push(data[candidate]);
}
display(subset);
}, 1500);
window.addEventListener("click", function(){
clearInterval(run);
});
});
function display(subset) { // this is everything that changes each tick
// Data Join
var paths = plot.selectAll(".data_paths")
.data(subset.map(function(d,i) {
return [
{ tx: d.x1, i: i, gene_name: d.name, chr: d.chrom_val },
{ tx: d.x2, i: i }
]
}), function(d) { return d[0].gene_name; }); // we assume gene_name is a unique key for the moment
// Update
paths.attr("class", "data_paths")
.attr("d", line)
.style("stroke", function(d) {
// color lines according to the chromosome they're on
return color(d[0].chr);
})
.append("title")
.text(function(d) {
return "gene: " + d[0].gene_name + " chr: " + d[0].chr + " tx: " + d[0].tx + "-" + d[1].tx;
});
// Enter
paths
.enter()
.append("path")
.attr("class", "data_paths")
.attr("d", line)
.style("stroke", function(d) {
// color lines according to the chromosome they're on
return color(d[0].chr);
})
.append("title")
.text(function(d) {
return "gene: " + d[0].gene_name + " chr: " + d[0].chr + " tx: " + d[0].tx + "-" + d[1].tx;
});
// Exit
paths.exit()
.attr("class", "data_paths")
.remove();
// label lines as to what gene they are on
// color labels according to chromosome?
plot.selectAll(".y.axis text")
.data(subset)
.text(function(d) { return d.name; });
//.style("fill", function(d) { return color(d.chr); })
}
function form_the_data(d) {
d.x1 = +d.txStart;
d.x2 = +d.txEnd;
d.chrom_val = (d.chrom.length == 4) ? d.chrom.slice(-1) : d.chrom.slice(-2);
return d;
}
function toObjArr(arr) {
var objArr = [], i = 0;
while(arr.length != 0) {
objArr.push(Object(arr.shift()));
i+= 1;
}
return objArr;
}
</script>
Modified http://d3js.org/d3.v3.js to a secure url
https://d3js.org/d3.v3.js