This example is inspired by Co-occurrence Matrix, Trulia Trends, and Selection Frame.
Changing orders
Selecting cells
xxxxxxxxxx
<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="https://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>
</select>
<div id="chart" style='overflow:auto; width:960px; height:480px;'></div>
<script type="text/javascript">
var margin = { top: 150, right: 10, bottom: 50, left: 100 },
cellSize=12;
col_number=60;
row_number=50;
width = cellSize*col_number, // - margin.left - margin.right,
height = cellSize*row_number , // - margin.top - margin.bottom,
//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 = [49,11,30,4,18,6,12,20,19,33,32,26,44,35,38,3,23,41,22,10,2,15,16,36,8,25,29,7,27,34,48,31,45,43,14,9,39,1,37,47,42,21,40,5,28,46,50,17,24,13], // change to gene name or probe id
hccol = [6,5,41,12,42,21,58,56,14,16,43,15,17,46,47,48,54,49,37,38,25,22,7,8,2,45,9,20,24,44,23,19,13,40,11,1,39,53,10,52,3,26,27,60,50,51,59,18,31,32,30,4,55,28,29,57,36,34,33,35], // change to gene name or probe id
rowLabel = ['1759080_s_at','1759302_s_at','1759502_s_at','1759540_s_at','1759781_s_at','1759828_s_at','1759829_s_at','1759906_s_at','1760088_s_at','1760164_s_at','1760453_s_at','1760516_s_at','1760594_s_at','1760894_s_at','1760951_s_at','1761030_s_at','1761128_at','1761145_s_at','1761160_s_at','1761189_s_at','1761222_s_at','1761245_s_at','1761277_s_at','1761434_s_at','1761553_s_at','1761620_s_at','1761873_s_at','1761884_s_at','1761944_s_at','1762105_s_at','1762118_s_at','1762151_s_at','1762388_s_at','1762401_s_at','1762633_s_at','1762701_s_at','1762787_s_at','1762819_s_at','1762880_s_at','1762945_s_at','1762983_s_at','1763132_s_at','1763138_s_at','1763146_s_at','1763198_s_at','1763383_at','1763410_s_at','1763426_s_at','1763490_s_at','1763491_s_at'], // change to gene name or probe id
colLabel = ['con1027','con1028','con1029','con103','con1030','con1031','con1032','con1033','con1034','con1035','con1036','con1037','con1038','con1039','con1040','con1041','con108','con109','con110','con111','con112','con125','con126','con127','con128','con129','con130','con131','con132','con133','con134','con135','con136','con137','con138','con139','con14','con15','con150','con151','con152','con153','con16','con17','con174','con184','con185','con186','con187','con188','con189','con191','con192','con193','con194','con199','con2','con200','con201','con21']; // change to contrast name
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", 0)
.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", 0)
.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>
Modified http://d3js.org/d3.v3.min.js to a secure url
https://d3js.org/d3.v3.min.js