block by dhoboy 66dc096d42919755d0fb

This works

Full Screen

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.

index.html

<!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="//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>

chrom_sample1.csv

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