Skip to content

Instantly share code, notes, and snippets.

@dhoboy
Created March 17, 2015 02:09
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 dhoboy/66dc096d42919755d0fb to your computer and use it in GitHub Desktop.
Save dhoboy/66dc096d42919755d0fb to your computer and use it in GitHub Desktop.
This works

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.

name chrom strand txStart txEnd
uc001aaa.3 chr1 + 11873 14409
uc010nxr.1 chr2 + 11873 14409
uc010nxq.1 chr3 + 11873 14409
uc009vis.3 chr4 - 14361 16765
uc009vjc.1 chr5 - 16857 17751
uc009vjd.2 chr6 - 15795 18061
uc009vit.3 chr7 - 14361 19759
uc009viu.3 chr8 - 14361 19759
uc001aae.4 chr9 - 14361 19759
uc001aai.1 chrM - 16857 19759
uc001aah.4 chrX - 14361 29370
uc009vir.3 chrY - 14361 29370
uc009viq.3 chr10 - 14361 29370
uc001aac.4 chr11 - 14361 29370
uc009viv.2 chr12 - 14406 29370
uc009viw.2 chr13 - 14406 29370
uc009vix.2 chr14 - 15602 29370
uc009viy.2 chr15 - 16606 29370
uc009viz.2 chr16 - 16606 29370
uc010nxs.1 chr17 - 16857 29370
uc009vje.2 chr1 - 17232 29370
uc009vjf.2 chr2 - 17605 29370
uc009vjb.1 chr3 - 16857 29961
uc001aak.3 chr4 - 34610 36081
uc001aal.1 chr5 + 69090 70008
uc021oeg.2 chr6 - 134772 140566
uc001aaq.2 chr7 + 321083 321115
uc001aar.2 chr8 + 321145 321207
uc021oeh.1 chr9 + 324287 325896
uc009vjk.2 chrM + 322036 326938
uc021oei.1 chrX + 327545 328439
uc001aau.3 chrY + 323891 328581
uc010nxu.2 chr10 + 367658 368597
uc001aax.1 chr11 + 420205 421839
uc021oej.1 chr1 - 566092 566115
<!DOCTYPE html>
<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="http://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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment