Skip to content

Instantly share code, notes, and snippets.

@micahstubbs
Last active April 12, 2019 04:36
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 micahstubbs/89522076fa3f862b55b6ade8c69312b9 to your computer and use it in GitHub Desktop.
Save micahstubbs/89522076fa3f862b55b6ade8c69312b9 to your computer and use it in GitHub Desktop.
Dendrogram Small Dataset
height: 500
border: no
license: Apache-2.0

an iteration that draws a smaller dataset.

If your tree dataset is small, then this example should be useful for showing how your dataset might look as a dendrogram.

See also the Dendrogram Large Dataset and react-app examples.


D3’s cluster layout produces node-link diagrams with leaf nodes at equal depth. These are less compact than tidy trees, but are useful for dendrograms, hierarchical clustering and phylogenetic trees. See also the radial variant.`


inspired by

{
"name": "analytics",
"children": [
{
"name": "cluster",
"children": [
{ "name": "AgglomerativeCluster", "value": 3938 },
{ "name": "CommunityStructure", "value": 3812 },
{ "name": "HierarchicalCluster", "value": 6714 },
{ "name": "MergeEdge", "value": 743 }
]
},
{
"name": "graph",
"children": [
{ "name": "BetweennessCentrality", "value": 3534 },
{ "name": "LinkDistance", "value": 5731 },
{ "name": "MaxFlowMinCut", "value": 7840 },
{ "name": "ShortestPaths", "value": 5914 },
{ "name": "SpanningTree", "value": 3416 }
]
},
{
"name": "optimization",
"children": [{ "name": "AspectRatioBanker", "value": 7074 }]
}
]
}
<html>
<head>
<title>Cluster Dendrogram - Large Dataset</title>
<meta charset="utf-8" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.9.2/d3.min.js"></script>
</head>
<body>
<script src="index.js"></script>
</body>
</html>
// define some constants
const width = 960
const height = 500
const margin = { top: 40, left: 40, bottom: 0, right: 0 }
const innerWidth = width - margin.left - margin.right
const innerHeight = height - margin.top - margin.bottom
const fontSize = 12
const magicWidthDivisor = 1.35
// load the data
d3.json('analytics.json').then(data => draw(data))
function draw(data) {
// define the tree layout function
const tree = data => {
const root = d3
.hierarchy(data)
.sort(
(a, b) => a.height - b.height || a.data.name.localeCompare(b.data.name)
)
console.log('root.height', root.height)
console.log('root', root)
root.dx = 10
root.dy = innerWidth / (root.height + 1)
return d3.cluster().size([innerHeight, innerWidth / magicWidthDivisor])(root)
}
// call the tree layout function on the data
const root = tree(data)
// draw the visualization
let x0 = Infinity
let x1 = -x0
root.each(d => {
if (d.x > x1) x1 = d.x
if (d.x < x0) x0 = d.x
})
const svg = d3
.select('body')
.append('svg')
.attr('width', width)
.attr('height', height)
// a gray border for debugging the layout
// svg
// .append('rect')
// .attr('width', width)
// .attr('height', height)
// .style('fill', 'none')
// .style('stroke', 'gray')
// .style('stroke-width', '1px')
const magicXTranslateDivisor = 6
const xTranslate = root.dy / magicXTranslateDivisor + margin.left
const yTranslate = root.dx - x0 + margin.top
const g = svg
.append('g')
.attr('font-family', 'sans-serif')
.attr('font-size', fontSize)
.attr(
'transform',
`translate(${xTranslate},${yTranslate})`
)
const link = g
.append('g')
.attr('fill', 'none')
.attr('stroke', '#555')
.attr('stroke-opacity', 0.4)
.attr('stroke-width', 1.5)
.selectAll('path')
.data(root.links())
.join('path')
.attr(
'd',
d => `
M${d.target.y},${d.target.x}
C${d.source.y + root.dy / 2},${d.target.x}
${d.source.y + root.dy / 2},${d.source.x}
${d.source.y},${d.source.x}
`
)
const node = g
.append('g')
.attr('stroke-linejoin', 'round')
.attr('stroke-width', 3)
.selectAll('g')
.data(root.descendants().reverse())
.join('g')
.attr('transform', d => `translate(${d.y},${d.x})`)
node
.append('circle')
.attr('fill', d => (d.children ? '#555' : '#999'))
.attr('r', 2.5)
node
.append('text')
.attr('dy', '0.31em')
.attr('x', d => (d.children ? -6 : 6))
.text(d => d.data.name)
.filter(d => d.children)
.attr('text-anchor', 'end')
.clone(true)
.lower()
.attr('stroke', 'white')
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment