Published
Edited
Mar 13, 2021
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
generations = {
replay;
const gs = []
const root = {name: "Root", parent: ""}
gs.push([root])
const gen1 = d3.range(N).map(i => ({
name: `${i+1}`, genotype: `${i+1}`, parent: "Root"
}))
gs.push(gen1)
while (true) {
const nextGen = []
const lastGen = gs[gs.length - 1]
lastGen.forEach(parent => {
const numDaughters = d3.randomBinomial(2, 0.5)()
d3.range(numDaughters).forEach(i => {
nextGen.push({ name: `${parent.name}_${i+1}`, genotype: parent.genotype, parent: parent.name })
})
})
if (nextGen.length === 0) {
// No female children left
console.log('No female children left!')
break
}
gs.push(nextGen)
if (gs.length >= maxGenerations)
break
if (breakAfterConvergence && nextGen.every(d => d.genotype === nextGen[0].genotype))
break
}
return gs
}
Insert cell
Insert cell
colorBy = (d) => d.genotype
Insert cell
color = {
const domain = generations[1].map(colorBy)
const colors = domain.map((v,i) => d3.interpolateSinebow(i/N))
const s = d3.scaleOrdinal(domain, colors)
return s
}
Insert cell
dx = 12
Insert cell
dy = 20
Insert cell
width
Insert cell
tree = d3.tree().size([400, width - 50])
Insert cell
Insert cell
function graph(root, {
label = d => d.data.id,
highlight = () => false,
nodeColor = d => color(d.data.genotype),
linkColor = d => color(d.target.data.genotype),
marginLeft = 40
} = {}) {
root = tree(root);

let x0 = Infinity;
let x1 = -x0;
root.each(d => {
if (d.x > x1) x1 = d.x;
if (d.x < x0) x0 = d.x;
});
const dy = 10

const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, x1 - x0 + dx * 2])
.style("overflow", "visible");
const g = svg.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("transform", `translate(${marginLeft},${dx - x0})`);
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("stroke", linkColor)
// .attr("stroke-opacity", d => highlight(d.source) && highlight(d.target) ? 1 : null)
.attr("d", treeLink);
const node = g.append("g")
.attr("stroke-linejoin", "round")
.attr("stroke-width", 3)
.selectAll("g")
.data(root.descendants())
.join("g")
.attr("transform", d => `translate(${d.y},${d.x})`);

node.append("circle")
.attr("fill", nodeColor)
.attr("r", 2.5);

node.append("text")
.attr("fill", d => highlight(d) ? "red" : null)
.attr("dy", "0.31em")
.attr("x", d => d.children ? -6 : 6)
.attr("text-anchor", d => d.children ? "end" : "start")
.text(d => !d.children ? label(d) : "")
.clone(true).lower()
.attr("stroke", "white");
return svg.node();
}
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