Published
Edited
Mar 9, 2021
5 stars
Insert cell
Insert cell
Insert cell
Insert cell
md`#### What we need for creating Tree:
- Hierachical relationship organized into node and links.
- Node location
- Links: Connection between the nodes. Source node and Target node.
`
Insert cell
Insert cell
md`### Sunburst Chart
*a.k.a.* Ring Chart, Multi-level Pie Chart, Belt Chart, Radial Treemap.

Quoting [datavizcatalogue](https://datavizcatalogue.com/methods/sunburst_diagram.html)
> ... shows hierarchy through a series of rings, that are sliced for each category node. Each ring corresponds to a level in the hierarchy, with the central circle representing the root node and the hierarchy moving outwards from it.

> Rings are sliced up and divided based on their hierarchical relationship to the parent slice. The angle of each slice is either divided equally under its parent node or can be made proportional to a value.

> Color can be used to highlight hierarchal groupings or specific categories.
`
Insert cell
Insert cell
md`### Tree Maps`
Insert cell
Insert cell
Insert cell
Insert cell
md`#### What do we need for creating Circle Packing:
- Hierachical relationship
- Some measurable quantity to define part of the whole.
- Layout: algorithm to organize the hierachical data into a circular area
- Nodes and links:
- center of circle
- radius of the circle
`
Insert cell
Tuesday = md`### Week 8 TuesDay`
Insert cell
Table(athletes)
Insert cell
// Data source : https://observablehq.com/@d3/d3-group
athletes = d3.csvParse(
await FileAttachment("atheletes.csv").text(),
d3.autoType
)
Insert cell
grtAthletes = d3.group(athletes, d => d.nation, d => d.sport)
Insert cell
aroot = d3.hierarchy(grtAthletes)
Insert cell
Insert cell
Insert cell
aroot.depth
Insert cell
aroot.children[0].depth
Insert cell
aroot.children[0].height
Insert cell
aroot.height
Insert cell
aroot.children[0].children[0].children[0]
Insert cell
Insert cell
aroot.descendants()
Insert cell
aroot.links()
Insert cell
Insert cell
aroot.descendants()
Insert cell
aroot.leaves()
Insert cell
Insert cell
atree = d3.tree().size([W, H])(aroot.copy())
Insert cell
drawTree(aroot)
Insert cell
Insert cell
drawTreeH(aroot)
Insert cell
Insert cell
Insert cell
Insert cell
drawTreeHorizontal(aroot)
Insert cell
Insert cell
md`#### Draw Tree: Cluster Layout
The cluster layout is very similar to the tree layout the main difference being all leaf nodes are placed at the same depth. For example:
~~~
clusterLayout = d3.cluster().size([W, H])(root.copy())
~~~
Same tree drawing method used above can be use for it drawing.
Cluster layouts are used for Radial Dendogram drawing
`
Insert cell
radialLayoutDraw(aroot.copy())
Insert cell
Insert cell
yelpReview = d3.json(
"https://gist.githubusercontent.com/MargretWG/b3f9e0a383408c6e6a45fc652e83a26c/raw/8756e2320d05a774e96983234beff81b01409315/hierarchy.json",
d3.autoType
)
Insert cell
yelpReviewHierarchy = d3.hierarchy(yelpReview)
Insert cell
yelpReviewHierarchy.children[0].children[0].children[0].children[0]
Insert cell
Insert cell
Insert cell
Insert cell
aroot.sum(d => d.earnings)
Insert cell
feedback
Insert cell
drawTreeMap(aroot, "earnings")
Insert cell
drawTreeMap(yelpReviewHierarchy, "size")
Insert cell
Insert cell
Insert cell
md`### Circle Packing: d3.pack()
Like in Treemap layout, before applying this layout to our hierarchy we must run .sum() on the hierarchy. This traverses the tree and sets .value on each node to the sum of its children
~~~
root.sum(d => d.population)
or
root.count()
~~~

See example: https://observablehq.com/@d3/circle-packing
`
Insert cell
temp = drawPack(aroot, "earnings")
Insert cell
feedback
Insert cell
Insert cell
feedback
Insert cell
drawPack(yelpReviewHierarchy, "size")
Insert cell
md`### Sunburst chart: d3.partition()
See example: https://observablehq.com/@d3/sunburst
`
Insert cell
drawPartition(aroot, "earnings")
Insert cell
feedback
Insert cell
drawPartition = (root, attribute) => {
const newRoot = (valueAs == "count"
? root.copy().count()
: root.copy().sum(d => d[attribute])
).sort((a, b) => b.value - a.value);

const partitionLayout = d3.partition().size([2 * Math.PI, H / 2])(newRoot);
mutable feedback = partitionLayout;
const [svgNode, svg] = getSVG();
const radius = H / 2;
const arcGenerator = d3
.arc()
.startAngle(d => d.x0)
.endAngle(d => d.x1)
.innerRadius(d => d.y0)
.outerRadius(d => d.y1);

const chart = svg
.append("g")
.attr("transform", `translate(${W / 2},${H / 2})`);

chart
.selectAll('path')
.data(partitionLayout.descendants().filter(d => d.depth))
.join('path')
.attr('d', arcGenerator)
.style("stroke", "white")
.style("fill", d => {
while (d.depth > 1) d = d.parent;
//mutable feedback = d.data.name || d.data[0];
return colorScale(d.data.name || d.data[0]);
});

chart
.append("g")
.attr("text-anchor", "middle")
.attr("font-size", 10)
.attr("font-family", "sans-serif")
.selectAll("text")
.data(
partitionLayout
.descendants()
.filter(d => d.depth && ((d.y0 + d.y1) / 2) * (d.x1 - d.x0) > 10)
)
.join("text")
.attr("transform", function(d) {
const x = (((d.x0 + d.x1) / 2) * 180) / Math.PI;
const y = (d.y0 + d.y1) / 2;
return `rotate(${x - 90}) translate(${y},0) rotate(${x < 180 ? 0 : 180})`;
})
.attr("dy", "0.35em")
.text(d => d.data.name || d.data[0] || "");

return svgNode;
}
Insert cell
drawPartition(yelpReviewHierarchy, "size")
Insert cell
md`### World data
Let us look at the world map data. World may be thought of as a collection of *region*'s, regions as a collection of *subregion*'s, subregions as collection of *countries*, and so on.`
Insert cell
root.leaves().map(d => d.data) // The original data is in the hierarchy’s leaves and can be accessed directly. Compare below with countryData
Insert cell
countryData
Insert cell
md`We can find a node based on some criterion, and seek the shortest path from that node to another.`
Insert cell
northAmerica = root.find(d => d.data[0] === "Northern America")
Insert cell
india = root.find(d => d.data.name === "India")
Insert cell
northAmerica.path(india).map(d => d.data[0] || d.data.name || "^root")
Insert cell
india.path(northAmerica).map(d => d.data[0] || d.data.name || "^root")
Insert cell
root.descendants()
Insert cell
md` root.links() returns a flat array of objects containing all the parent-child links`
Insert cell
root.links()
Insert cell
leaves = root.leaves()
Insert cell
countryData = {
const data = await d3.json(
"https://raw.githubusercontent.com/dr5hn/countries-states-cities-database/master/countries%2Bstates%2Bcities.json",
d3.autoType
);
return data.map(d => {
d.population = latestPopulationGroupedByCountryCode.get(d.iso3)
? latestPopulationGroupedByCountryCode.get(d.iso3).Value
: 0;
return d;
});
}
Insert cell
latestPopulationGroupedByCountryCode = {
const D = d3.group(
await d3.csv(
"https://datahub.io/core/population/r/population.csv",
d3.autoType
),
d => d["Country Code"]
);
[...D.keys()].forEach(key => {
const A = D.get(key);
D.set(key, A[A.length - 1]);
});
return D;
}
Insert cell
Insert cell
root = d3.hierarchy(group)
Insert cell
md`#### Draw Tree: Tree Layout: Using d3.linkVertical()`
Insert cell
Insert cell
md`#### Draw Tree: Tree Layout: Using d3.linkHorizontal()`
Insert cell
Insert cell
radialLayoutDraw(root.copy())
Insert cell
feedback
Insert cell
treeWithCounts = root.copy().count()
Insert cell
totalCounts = treeWithCounts.value
Insert cell
treeWithCounts.descendants().map(d => d.value)
Insert cell
treeWithPopulation = root.copy().sum(d => d.population)
Insert cell
treeWithPopulation.descendants().map(d => d.value)
Insert cell
treeWithPopulation.find(d => d.data[0] === "Northern America").value
Insert cell
treeWithPopulation.find(d => d.data.name === "China").value
Insert cell
treeWithPopulation.find(d => d.data.name === "United States").value
Insert cell
drawTreeMap(root, "population")
Insert cell
drawPack(root, "population")
Insert cell
Insert cell
mutable feedback = 0
Insert cell
md`## Data`
Insert cell
regions = [...new Set(countryData.map(d => d.region))]
Insert cell
Insert cell
countryData.filter(d => d.population == 0)
Insert cell
d3.csv("https://datahub.io/core/population/r/population.csv", d3.autoType)
Insert cell
height = 600
Insert cell
W = width - margin.left - margin.right
Insert cell
H = height - margin.top - margin.bottom
Insert cell
margin = ({ top: 20, right: 30, bottom: 30, left: 40 })
Insert cell
md`## Useful Functions`
Insert cell
colorScale = d3
.scaleOrdinal()
.domain(regions)
.range(d3.schemeCategory10)
Insert cell
getSVG = (options = { width: width, height: height, margin: margin }) => {
const svgElement = DOM.svg(options.width, options.height);

const svg = d3
.select(svgElement)
.append("g")
.attr(
"transform",
`translate(${options.margin.left},${options.margin.top})`
);
return [svgElement, svg];
}
Insert cell
md`### Libraries & Imports`
Insert cell
import {serialize} from "@palewire/saving-csv"
Insert cell
import { radio, select, slider, button } from "@jashkenas/inputs"
Insert cell
import { Table } from "@observablehq/inputs"
Insert cell
d3 = require("d3@6")
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