Published
Edited
Jun 24, 2020
Importers
1 star
Insert cell
Insert cell
Insert cell
load_data = {
const text = await FileAttachment("mock_sales_data.csv").text();
const parseDate = d3.utcParse("%d/%m/%Y");
return d3.csvParse(text, ({date, sales, department}) => ({
date: parseDate(date),
sales: +sales.substr(1),
department: department
}));
}
Insert cell
Insert cell
data = Object.assign(d3.nest()
.key(function(d) { return d.department; })
.rollup(function(v) { return (d3.sum(v, function(d) { return d.sales; })); })
.entries(load_data)).sort(function(x ,y){
return d3.ascending(x.value, y.value);
});
Insert cell
Insert cell
x = d3.scaleBand()
.domain(data.map(d => d.key))
.rangeRound([margin.left, width - margin.right])
.padding(0.1)
Insert cell
y = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)]).nice()
.range([height - margin.bottom, margin.top])
Insert cell
md `## Initialize axes`
Insert cell
xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).tickSizeOuter(0))
Insert cell
yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y))
Insert cell
Insert cell
chart = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);

svg.append("g")
.attr("fill", color)
.selectAll("rect")
.data(data)
.join("rect")
.attr("x", d => x(d.key))
.attr("y", d => y(d.value))
.attr("height", d => y(0) - y(d.value))
.attr("width", x.bandwidth());
svg.append("g")
.call(xAxis);

svg.append("g")
.call(yAxis);

return svg.node();
}
Insert cell
Insert cell
Insert cell
labelAnnotations = {
return [{
id: "movies",
// If you don't provide a custom "type" attribute in your options dictionary, ,
// the default type in the getAnnotations function will be used.
note: {
label: "a label for movies",
title: "Movies"
},
data: get_department_data("Movies", data),
dx: -79,
dy: -57,
},{
id: "beauty",
note: {
label: "a label for beauty",
title: "Beauty",
},
data: get_department_data("Beauty", data),
dx: -42,
dy: -88,
}]
}
Insert cell
Insert cell
getAnnotations = function(annotationList, x, y, debug){
/*
Return a list of d3.annotation objects
Arguments
annotationList - a list of dictionaries used to configure each annotation
x: a d3.scale
y: a d3.scale
debug: a boolean flag
*/
chart // reactive hack to force annotations to render whenever the chart is redrawn
let makeLabelAnnotations = d3.annotation()
.editMode(debug) // GLOBAL VARIABLE
.type(d3.annotationLabel) // Adjust this arg to adjust the default annotation styling.
.accessors({
x: d => {
// console.log(x(d.key));
return x(d.key)},
y: d => {
// console.log(d.value)
return y(d.value)}// If you use a new dataset, you may need to update this accessor.
})
.accessorsInverse({
key: d => (x.invert(d.x)),
value: d => y.invert(d.y)
})
.annotations(annotationList)
return makeLabelAnnotations
}
Insert cell
md `
** C. ** Prepare the annotations by defining a *labels* function
- calls the getAnnotations function
`
Insert cell
// This function is responsible for converting the list of dictionaries into actual d3.annotation objects
labels = getAnnotations(labelAnnotations, x, y, debug)
Insert cell
Insert cell
render_barchart = function(data, xAxis, yAxis, debug) {
/* Return a DOM node with containing a line chart.

Arguments:
data: a list of data objects (dictionaries or iterables)
xAxis: a d3.axis
yAxis: a d3.axis
debug: variable that forces the chart to redraw every time this step reruns (for observable notebook purposes)
*/
const svg = d3.select(DOM.svg(width, height));
svg.append("g")
.call(xAxis);

svg.append("g")
.call(yAxis);
svg.append("g")
.attr("fill", color)
.selectAll("rect")
.data(data)
.join("rect")
.attr("x", d => x(d.key))
.attr("y", d => y(d.value))
.attr("height", d => y(0) - y(d.value))
.attr("width", x.bandwidth());
return svg.node();
}
Insert cell
md `
#### 3. Create an applyAnnotations function
- function that draws d3.annotation objects onto a designated DOM node`
Insert cell
applyAnnotations = function(annotations, target) {
/*Draws d3.annotation objects onto a designated DOM node */
d3.select(target)
.append("g")
.attr("class", "annotation-group")
.call(annotations)
return annotations
}
Insert cell
Insert cell
applyAnnotations(labels, chart2)
Insert cell
Insert cell
chart2 = {
labelAnnotations // Make this cell dependent on annotations so the chart will redraw on annotation updates
return render_barchart(data, xAxis, yAxis, debug)
}
Insert cell
Insert cell
viewof CHECKED = checkbox(
{
options: [
{ value: 'toggle', label: '<b>Edit the annotation positions?</b>' }
],
value: ''
})
Insert cell
debug = {
return (CHECKED == "toggle")
}
Insert cell
annotations_display = {
let collection = labels.collection()
if (collection) {
return collection.annotations
} else {
return "Manually rerun this cell to check the annotation marked by annotation_number"
}
}
Insert cell
Insert cell
// Helper function to lookup where annotations match values
get_department_data = function(department, data) {
const filtered = data.filter(x => (x.key === department))
return {
key: department,
value: filtered[0].value
}
}
Insert cell
Insert cell
Insert cell
import { checkbox } from "@jashkenas/inputs"
Insert cell
d3_base = require("d3@5")
Insert cell
d3_svg_annotation = require("d3-svg-annotation")
Insert cell
d3 = {
// Add
const bundle = Object.assign({}, d3_base, d3_svg_annotation);
return bundle;
}
Insert cell
margin = ({top: 30, right: 0, bottom: 30, left: 40})
Insert cell
height = 500
Insert cell
color = "#69b3a2"
Insert cell
d3_annotation = require("d3-svg-annotation")
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