Skip to content

Instantly share code, notes, and snippets.

@baramuyu
Created September 8, 2016 07:12
Show Gist options
  • Select an option

  • Save baramuyu/b34effc730e3ca1338f82c2d8c7cd469 to your computer and use it in GitHub Desktop.

Select an option

Save baramuyu/b34effc730e3ca1338f82c2d8c7cd469 to your computer and use it in GitHub Desktop.

Revisions

  1. baramuyu revised this gist Sep 8, 2016. No changes.
  2. baramuyu created this gist Sep 8, 2016.
    1 change: 1 addition & 0 deletions .block
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    license: mit
    20 changes: 20 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,20 @@
    This example is inspired by [Co-occurrence Matrix](http://bost.ocks.org/mike/miserables/ "Les Misérables Co-occurrence"), [Trulia Trends](http://trends.truliablog.com/2011/09/house-hunter-by-day-not-so-much-after-midnight/ "Trulia Trends"), and [Selection Frame](http://bl.ocks.org/lgersman/5311083 "Selection Frame").

    Changing orders

    + cluster: hierarchical clustering
    + probe name and contrast name: sort by probe name and contrast name
    + probe name: sort by probe name
    + contrast name: sort by contrast name
    + click a row label: sort cell values with the same row label
    + click a column label: sort cell values with the same column label
    + click a cell: select the row label of corresponding cell

    Selecting cells

    + Using mouse to select cells, corresponding row and column labels will be highlighed as well
    + Press Alt Key to select multiple cell blocks

    forked from <a href='http://bl.ocks.org/paulgovan/'>paulgovan</a>'s block: <a href='http://bl.ocks.org/paulgovan/6ca7a08a16b33d419ba0'>heatmap </a>

    forked from <a href='http://bl.ocks.org/anonymous/'>anonymous</a>'s block: <a href='http://bl.ocks.org/anonymous/c4bf12702e59a69339f07ac9670bbc5f'>heatmap </a>
    3,001 changes: 3,001 additions & 0 deletions data_heatmap.tsv
    3,001 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
    408 changes: 408 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,408 @@
    <meta charset="utf-8">
    <style>
    /* disable text selection */
    svg *::selection {
    background : transparent;
    }

    svg *::-moz-selection {
    background:transparent;
    }

    svg *::-webkit-selection {
    background:transparent;
    }
    rect.selection {
    stroke : #333;
    stroke-dasharray: 4px;
    stroke-opacity : 0.5;
    fill : transparent;
    }

    rect.cell-border {
    stroke: #eee;
    stroke-width:0.3px;
    }

    rect.cell-selected {
    stroke: rgb(51,102,153);
    stroke-width:0.5px;
    }

    rect.cell-hover {
    stroke: #F00;
    stroke-width:0.3px;
    }

    text.mono {
    font-size: 9pt;
    font-family: Consolas, courier;
    fill: #aaa;
    }

    text.text-selected {
    fill: #000;
    }

    text.text-highlight {
    fill: #c00;
    }
    text.text-hover {
    fill: #00C;
    }
    #tooltip {
    position: absolute;
    width: 200px;
    height: auto;
    padding: 10px;
    background-color: white;
    -webkit-border-radius: 10px;
    -moz-border-radius: 10px;
    border-radius: 10px;
    -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
    -moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
    box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
    pointer-events: none;
    }

    #tooltip.hidden {
    display: none;
    }

    #tooltip p {
    margin: 0;
    font-family: sans-serif;
    font-size: 12px;
    line-height: 20px;
    }
    </style>

    </head>
    <div id="tooltip" class="hidden">
    <p><span id="value"></p>
    </div>
    <script src="http://d3js.org/d3.v3.min.js"></script>
    Order:
    <select id="order">
    <option value="hclust">by cluster</option>
    <option value="probecontrast">by probe name and contrast name</option>
    <option value="probe">by probe name</option>
    <option value="contrast">by contrast name</option>
    <option value="custom">by log2 ratio</option>
    </select>
    <div id="chart" style='overflow:auto; width:960px; height:480px;'></div>

    <script type="text/javascript">
    var margin = { top: 150, right: 20, bottom: 50, left: 200 },
    cellSize=12;

    //gridSize = Math.floor(width / 24),
    legendElementWidth = cellSize*2.5,
    colorBuckets = 21,
    colors = ['#005824','#1A693B','#347B53','#4F8D6B','#699F83','#83B09B','#9EC2B3','#B8D4CB','#D2E6E3','#EDF8FB','#FFFFFF','#F1EEF6','#E6D3E1','#DBB9CD','#D19EB9','#C684A4','#BB6990','#B14F7C','#A63467','#9B1A53','#91003F'];
    hcrow =d3.range(1,17), // change to gene name or probe id
    colLabel = ['smtp080','smtp302','smtp502','smtp540','smtp781','smtp828','smtp829','smtp906','smtp088','smtp164','smtp453','smtp516','smtp594','smtp894','smtp951','smtp030','smtp145','smtp160','smtp189','smtp222','smtp245','smtp277','smtp434','smtp553','smtp620','smtp873','smtp884','smtp944','smtp105','smtp118','smtp151','smtp388','smtp401','smtp633','smtp701','smtp787','smtp819','smtp880','smtp945','smtp983','smtp132','smtp138','smtp146','smtp198','smtp410','smtp426','smtp490','smtp491'], // change to gene name or probe id
    rowLabel= ['rc shell','public SMTP','private SMTP','Dashboard','No inbound flow','mail 452','pending queue','Spooling Outbound','Spooling Inbound','Spooling Outbox','Latency Ountbound','Latency Inbound','Latency Outbox','Hibernate','High CPU','Polling Error']; // change to contrast name
    hccol = d3.range(1,colLabel.length), // change to gene name or probe id
    col_number=colLabel.length;
    row_number=rowLabel.length;
    width = cellSize*col_number, // - margin.left - margin.right,
    height = cellSize*row_number , // - margin.top - margin.bottom,
    d3.tsv("data_heatmap.tsv",
    function(d) {
    return {
    row: +d.row_idx,
    col: +d.col_idx,
    value: +d.log2ratio
    };
    },
    function(error, data) {
    var colorScale = d3.scale.quantile()
    .domain([ -10 , 0, 10])
    .range(colors);

    var svg = d3.select("#chart").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 rowSortOrder=false;
    var colSortOrder=false;
    var rowLabels = svg.append("g")
    .selectAll(".rowLabelg")
    .data(rowLabel)
    .enter()
    .append("text")
    .text(function (d) { return d; })
    .attr("x", -10)
    .attr("y", function (d, i) { return hcrow.indexOf(i+1) * cellSize; })
    .style("text-anchor", "end")
    .attr("transform", "translate(-6," + cellSize / 1.5 + ")")
    .attr("class", function (d,i) { return "rowLabel mono r"+i;} )
    .on("mouseover", function(d) {d3.select(this).classed("text-hover",true);})
    .on("mouseout" , function(d) {d3.select(this).classed("text-hover",false);})
    .on("click", function(d,i) {rowSortOrder=!rowSortOrder; sortbylabel("r",i,rowSortOrder);d3.select("#order").property("selectedIndex", 4).node().focus();;})
    ;

    var colLabels = svg.append("g")
    .selectAll(".colLabelg")
    .data(colLabel)
    .enter()
    .append("text")
    .text(function (d) { return d; })
    .attr("x", 10)
    .attr("y", function (d, i) { return hccol.indexOf(i+1) * cellSize; })
    .style("text-anchor", "left")
    .attr("transform", "translate("+cellSize/2 + ",-6) rotate (-90)")
    .attr("class", function (d,i) { return "colLabel mono c"+i;} )
    .on("mouseover", function(d) {d3.select(this).classed("text-hover",true);})
    .on("mouseout" , function(d) {d3.select(this).classed("text-hover",false);})
    .on("click", function(d,i) {colSortOrder=!colSortOrder; sortbylabel("c",i,colSortOrder);d3.select("#order").property("selectedIndex", 4).node().focus();;})
    ;

    var heatMap = svg.append("g").attr("class","g3")
    .selectAll(".cellg")
    .data(data,function(d){return d.row+":"+d.col;})
    .enter()
    .append("rect")
    .attr("x", function(d) { return hccol.indexOf(d.col) * cellSize; })
    .attr("y", function(d) { return hcrow.indexOf(d.row) * cellSize; })
    .attr("class", function(d){return "cell cell-border cr"+(d.row-1)+" cc"+(d.col-1);})
    .attr("width", cellSize)
    .attr("height", cellSize)
    .style("fill", function(d) { return colorScale(d.value); })
    /* .on("click", function(d) {
    var rowtext=d3.select(".r"+(d.row-1));
    if(rowtext.classed("text-selected")==false){
    rowtext.classed("text-selected",true);
    }else{
    rowtext.classed("text-selected",false);
    }
    })*/
    .on("mouseover", function(d){
    //highlight text
    d3.select(this).classed("cell-hover",true);
    d3.selectAll(".rowLabel").classed("text-highlight",function(r,ri){ return ri==(d.row-1);});
    d3.selectAll(".colLabel").classed("text-highlight",function(c,ci){ return ci==(d.col-1);});

    //Update the tooltip position and value
    d3.select("#tooltip")
    .style("left", (d3.event.pageX+10) + "px")
    .style("top", (d3.event.pageY-10) + "px")
    .select("#value")
    .text("lables:"+rowLabel[d.row-1]+","+colLabel[d.col-1]+"\ndata:"+d.value+"\nrow-col-idx:"+d.col+","+d.row+"\ncell-xy "+this.x.baseVal.value+", "+this.y.baseVal.value);
    //Show the tooltip
    d3.select("#tooltip").classed("hidden", false);
    })
    .on("mouseout", function(){
    d3.select(this).classed("cell-hover",false);
    d3.selectAll(".rowLabel").classed("text-highlight",false);
    d3.selectAll(".colLabel").classed("text-highlight",false);
    d3.select("#tooltip").classed("hidden", true);
    })
    ;

    // var legend = svg.selectAll(".legend")
    // .data([-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10])
    // .enter().append("g")
    // .attr("class", "legend");

    // legend.append("rect")
    // .attr("x", function(d, i) { return legendElementWidth * i; })
    // .attr("y", height+(cellSize*2))
    // .attr("width", legendElementWidth)
    // .attr("height", cellSize)
    // .style("fill", function(d, i) { return colors[i]; });

    // legend.append("text")
    // .attr("class", "mono")
    // .text(function(d) { return d; })
    // .attr("width", legendElementWidth)
    // .attr("x", function(d, i) { return legendElementWidth * i; })
    // .attr("y", height + (cellSize*4));

    // Change ordering of cells

    function sortbylabel(rORc,i,sortOrder){
    var t = svg.transition().duration(3000);
    var log2r=[];
    var sorted; // sorted is zero-based index
    d3.selectAll(".c"+rORc+i)
    .filter(function(ce){
    log2r.push(ce.value);
    })
    ;
    if(rORc=="r"){ // sort log2ratio of a gene
    sorted=d3.range(col_number).sort(function(a,b){ if(sortOrder){ return log2r[b]-log2r[a];}else{ return log2r[a]-log2r[b];}});
    t.selectAll(".cell")
    .attr("x", function(d) { return sorted.indexOf(d.col-1) * cellSize; })
    ;
    t.selectAll(".colLabel")
    .attr("y", function (d, i) { return sorted.indexOf(i) * cellSize; })
    ;
    }else{ // sort log2ratio of a contrast
    sorted=d3.range(row_number).sort(function(a,b){if(sortOrder){ return log2r[b]-log2r[a];}else{ return log2r[a]-log2r[b];}});
    t.selectAll(".cell")
    .attr("y", function(d) { return sorted.indexOf(d.row-1) * cellSize; })
    ;
    t.selectAll(".rowLabel")
    .attr("y", function (d, i) { return sorted.indexOf(i) * cellSize; })
    ;
    }
    }

    d3.select("#order").on("change",function(){
    order(this.value);
    });

    function order(value){
    if(value=="hclust"){
    var t = svg.transition().duration(3000);
    t.selectAll(".cell")
    .attr("x", function(d) { return hccol.indexOf(d.col) * cellSize; })
    .attr("y", function(d) { return hcrow.indexOf(d.row) * cellSize; })
    ;

    t.selectAll(".rowLabel")
    .attr("y", function (d, i) { return hcrow.indexOf(i+1) * cellSize; })
    ;

    t.selectAll(".colLabel")
    .attr("y", function (d, i) { return hccol.indexOf(i+1) * cellSize; })
    ;

    }else if (value=="probecontrast"){
    var t = svg.transition().duration(3000);
    t.selectAll(".cell")
    .attr("x", function(d) { return (d.col - 1) * cellSize; })
    .attr("y", function(d) { return (d.row - 1) * cellSize; })
    ;

    t.selectAll(".rowLabel")
    .attr("y", function (d, i) { return i * cellSize; })
    ;

    t.selectAll(".colLabel")
    .attr("y", function (d, i) { return i * cellSize; })
    ;

    }else if (value=="probe"){
    var t = svg.transition().duration(3000);
    t.selectAll(".cell")
    .attr("y", function(d) { return (d.row - 1) * cellSize; })
    ;

    t.selectAll(".rowLabel")
    .attr("y", function (d, i) { return i * cellSize; })
    ;
    }else if (value=="contrast"){
    var t = svg.transition().duration(3000);
    t.selectAll(".cell")
    .attr("x", function(d) { return (d.col - 1) * cellSize; })
    ;
    t.selectAll(".colLabel")
    .attr("y", function (d, i) { return i * cellSize; })
    ;
    }
    }
    //
    var sa=d3.select(".g3")
    .on("mousedown", function() {
    if( !d3.event.altKey) {
    d3.selectAll(".cell-selected").classed("cell-selected",false);
    d3.selectAll(".rowLabel").classed("text-selected",false);
    d3.selectAll(".colLabel").classed("text-selected",false);
    }
    var p = d3.mouse(this);
    sa.append("rect")
    .attr({
    rx : 0,
    ry : 0,
    class : "selection",
    x : p[0],
    y : p[1],
    width : 1,
    height : 1
    })
    })
    .on("mousemove", function() {
    var s = sa.select("rect.selection");

    if(!s.empty()) {
    var p = d3.mouse(this),
    d = {
    x : parseInt(s.attr("x"), 10),
    y : parseInt(s.attr("y"), 10),
    width : parseInt(s.attr("width"), 10),
    height : parseInt(s.attr("height"), 10)
    },
    move = {
    x : p[0] - d.x,
    y : p[1] - d.y
    }
    ;

    if(move.x < 1 || (move.x*2<d.width)) {
    d.x = p[0];
    d.width -= move.x;
    } else {
    d.width = move.x;
    }

    if(move.y < 1 || (move.y*2<d.height)) {
    d.y = p[1];
    d.height -= move.y;
    } else {
    d.height = move.y;
    }
    s.attr(d);

    // deselect all temporary selected state objects
    d3.selectAll('.cell-selection.cell-selected').classed("cell-selected", false);
    d3.selectAll(".text-selection.text-selected").classed("text-selected",false);

    d3.selectAll('.cell').filter(function(cell_d, i) {
    if(
    !d3.select(this).classed("cell-selected") &&
    // inner circle inside selection frame
    (this.x.baseVal.value)+cellSize >= d.x && (this.x.baseVal.value)<=d.x+d.width &&
    (this.y.baseVal.value)+cellSize >= d.y && (this.y.baseVal.value)<=d.y+d.height
    ) {

    d3.select(this)
    .classed("cell-selection", true)
    .classed("cell-selected", true);

    d3.select(".r"+(cell_d.row-1))
    .classed("text-selection",true)
    .classed("text-selected",true);

    d3.select(".c"+(cell_d.col-1))
    .classed("text-selection",true)
    .classed("text-selected",true);
    }
    });
    }
    })
    .on("mouseup", function() {
    // remove selection frame
    sa.selectAll("rect.selection").remove();

    // remove temporary selection marker class
    d3.selectAll('.cell-selection').classed("cell-selection", false);
    d3.selectAll(".text-selection").classed("text-selection",false);
    })
    .on("mouseout", function() {
    if(d3.event.relatedTarget.tagName=='html') {
    // remove selection frame
    sa.selectAll("rect.selection").remove();
    // remove temporary selection marker class
    d3.selectAll('.cell-selection').classed("cell-selection", false);
    d3.selectAll(".rowLabel").classed("text-selected",false);
    d3.selectAll(".colLabel").classed("text-selected",false);
    }
    })
    ;
    });
    </script>