Public
Edited
May 26
1 fork
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
heatmap = function(matrix, options = {}){
const {
width: width = 200,
height: height = 200,
// dendograms
clusterCols: clusterCols = false,
clusterRows: clusterRows = false,
clusteringDistanceRows: clusteringDistanceRows = "euclidean",
clusteringDistanceCols: clusteringDistanceCols = "euclidean",
clusteringMethodCols: clusteringMethodCols = "complete",
clusteringMethodRows: clusteringMethodRows = "complete",
marginTop: marginTop = clusterCols ? 80 : 30,
marginLeft: marginLeft = clusterRows ? 120 : 30,
colPadding: colPadding = clusterCols ? 20 : 0,
rowPadding: rowPadding = clusterRows ? 20 : 0,
// heatmap
color: color = "red",
colorScale: colorScale = [0,100],
// hover tooltip
decimal: decimal = 2,
fontFamily: fontFamily= 'monospace'
} = options;
const margin = ({ top: marginTop,bottom:0,left:marginLeft,right:0})
const svg = d3.create("svg")

const data = matrix.data ? matrix.data: matrix

// Heatmap--------------------
const colHclustTree = new hclust.agnes(dist(transpose(data), distance[clusteringDistanceCols]), {
method:clusteringMethodCols,
isDistanceMatrix: true})
const root = d3.hierarchy(colHclustTree)
const clusterLayout = d3.cluster()
clusterLayout(root)
const rowHclustTree2 = new hclust.agnes(dist(data, distance[clusteringDistanceRows]), {
method: clusteringMethodRows,
isDistanceMatrix: true})
const root2 = d3.hierarchy(rowHclustTree2)
const clusterLayout2 = d3.cluster()
clusterLayout2(root2)

let colIdx = clusterCols ? root.leaves().map(x=>x.data.index): d3.range(data[0].length)//col clust
let rowIdx = clusterRows ? root2.leaves().map(x=>x.data.index) : d3.range(data.length)//row clust
console.log("rowIdx",rowIdx)
const newMatrix2 = transpose(colIdx.map(i => transpose(rowIdx.map(e => data[e])) [i]))
// if labels (truncated length) are not provided, indices are used
let colNames2 = matrix.colNames ? trimText(colIdx, matrix.colNames) : Array.from(new Array(data[0].length),(x,i)=> i + 1)
let rowNames2 = matrix.colNames ? trimText(rowIdx, matrix.rowNames) : Array.from(new Array(data[0].length),(x,i)=> i + 1)
console.log("rowNames2",rowNames2)

// max x and y label lengths to be used in dendogram heights
const colNames2Lengths = d3.max(colNames2.map(e => e.length))
const rowNames2Lengths = d3.max(rowNames2.map(e => e.length))

const color_scale = d3.scaleLinear()
.domain(colorScale)
.range(['#fff', `${color}`])
let x_scale = d3.scaleBand()
.domain(colNames2)
.range([0, width-margin.left-margin.right])
let y_scale = d3.scaleBand()
.domain(rowNames2)
.range([ 0, height-margin.top])

const g = svg
.attr('width', width )
.attr('height', height )
.append('g')
// move the entire graph down and right to accomodate labels
.attr('transform', `translate(${margin.left+margin.right}, ${margin.top+margin.bottom})`)
//text x axis
const xAxis = g.append('g')
.call(d3.axisTop(x_scale))
xAxis.selectAll('.tick').selectAll('line').remove()
xAxis.selectAll("text")
.style("text-anchor", "start")
.attr("dx", "2px")
.attr("dy", "1.1em")
.attr("transform", "rotate(-90)")
.attr("class", "xa")
//text y axis
let yAxis = g.append('g')
.call(d3.axisLeft(y_scale))
.attr("id", "ya")

yAxis.selectAll('.tick').selectAll('line').remove()
yAxis.selectAll("text")
.attr("dx", "7px")
.attr("dy", "0.3em")
.attr("class", "yaa")
const gPoints = g.append("g").attr("class", "gPoints");

const tooltip = d3tip()
.style('border', 'solid 3px black')
.style('background-color', 'white')
.style('border-radius', '10px')
.style('float', 'left')
.style('font-family', fontFamily)
.html((event, d) => `
<div style='float: right'>
value:${d.value.toFixed(decimal)} <br/>
row:${rowNames2[d.n]}, col:${colNames2[d.t] }
</div>`)
// Apply tooltip to our SVG
svg.call(tooltip)
gPoints.selectAll()
.data(buildData(newMatrix2))
.enter()
.append('rect')
.attr('x', (d) => x_scale(colNames2[d.t]))
.attr('y', (d) => y_scale(rowNames2[d.n]))
.attr('width', width/data[0].length)
.attr('height', height/data.length)
.attr('fill', (d) => color_scale(d.value))
.on('mouseover', tooltip.show)
.on('mouseout', tooltip.hide)
// Top dendogram---------------------------------
if (clusterCols== true){
// console.log(root.links())
const colMaxHeight = root.data.height;

const allNodes = root.descendants().reverse()
const leafs = allNodes.filter(d => !d.children)
leafs.sort((a,b) => a.x - b.x)
const leafHeight = (width-margin.left)/ leafs.length // spacing between leaves
leafs.forEach((d,i) => d.x = i*leafHeight + leafHeight/2)
allNodes.forEach(node => {
if (node.children) {
node.x = d3.mean(node.children, d => d.x)
}})

root.links().forEach((link,i) => {
svg
.append("path")
.attr("class", "link")
.attr("stroke", link.source.color || "blue")
.attr("stroke-width", `${5}px`)
.attr("fill", 'none')
.attr("transform", `translate(${margin.left},7)`)
.attr("d", colElbow(link,colMaxHeight,margin.top,colNames2Lengths))
})
}
// bottom dendogram----------------------

if (clusterRows== true){
const dendoTooltip = d3tip()
.style('border', 'solid 3px black')
.style('background-color', 'white')
.style('border-radius', '10px')
.style('float', 'left')
.style('font-family', 'monospace')
.html((event, d) => `
<div style='float: right'>
Height:${d.source.data.height.toFixed(3)} <br/>
</div>`)
const rowMaxHeight = root2.data.height;
const clusterLayout2 = d3.cluster()
clusterLayout2(root2)
const allNodes2 = root2.descendants().reverse()
const leafs2 = allNodes2.filter(d => !d.children)
leafs2.sort((a,b) => a.x - b.x)
const leafHeight2 = (height-margin.top)/ leafs2.length
leafs2.forEach((d,i) => d.x = i*leafHeight2 + leafHeight2/2)
allNodes2.forEach(node => {
if (node.children) {
node.x = d3.mean(node.children, d => d.x)
}})
// Apply tooltip to our SVG
svg.call(dendoTooltip)
// console.log(root2.links())
root2.links().forEach((link,i) => {
svg
.append("path")
.attr("class", "link")
.attr("stroke", link.source.color || "red")
.attr("stroke-width", `${5}px`)
.attr("fill", 'none')
.attr("transform", `translate(7,${margin.top})`)
.attr("d", rowElbow(link,rowMaxHeight,margin.left,rowNames2Lengths))
.on('mouseover', dendoTooltip.show)
// Hide the tooltip when "mouseout"
.on('mouseout', dendoTooltip.hide)
})
svg.selectAll('path')
.data(root2.links())
}
return svg.node()
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// plot4 = { const div11 = document.createElement("div")
// div11.appendChild(heatmap(matrix,))
// div11.appendChild(heatmap(matrix,{clusterRows: true}))
// div11.appendChild(heatmap(matrix,{clusterRows: true, clusterCols: true}))

// return div11
// }
Insert cell
Insert cell
// plot3 = {
// const div = document.createElement("div")

// div.appendChild(heatmap(matrix,{height:300,width:150,marginTop:100,marginLeft:50}))
// div.appendChild(heatmap(matrix,{height:300,marginTop:100,marginLeft:100,clusterRows: true}))
// div.appendChild(heatmap(matrix,{height:300,marginTop:100,marginLeft:100,clusterRows: true, clusterCols: true}))
// return div
// }
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
distCorMatRow = new Object({"rowNames": idx.map(i => dt.rowNames[i]), "colNames":idx.map(i => dt.rowNames[i]), "data":dist(idx.map(i => matrix[i]), similarity.pearson)})
Insert cell
Insert cell
Insert cell
Insert cell
distMatCol = new Object({"rowNames": idx2.map(i => dt.colNames[i]), "colNames":idx2.map(i => dt.colNames[i]), "data":dist(idx2.map(x=>transpose(matrix)[x]), distance.euclidean)})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
rowGraphNodes = networkNodes(rowCorrelation.data, rowCorrelation.rowNames)
Insert cell
Insert cell
rowNetwork = ForceGraph()(rowNetworkDiv).width(width/2).height(200)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
colNetworkDiv = document.createElement('div')
Insert cell
rowNetworkDiv = document.createElement('div')
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more