Published
Edited
Sep 12, 2021
Insert cell
Insert cell
d3 = require("d3@6")
Insert cell
//Diamond dataset
data = Object.assign(d3.csvParse(await FileAttachment("diamonds-2.csv").text()))
Insert cell
Insert cell
dataGroup = d3.groups(data, d => d.cut)
Insert cell
//the number of occurences of each cut can be found by the length of their group array
mutable dataCount = dataGroup.map(([cut, count]) => ({cut: cut, count: count.length}))
//as dataCount is defined as mutable, its value can be changed
Insert cell
Insert cell
Insert cell
//These are the padding values
margin = ({top: 100, right: 0, bottom: 20, left: 40}) //top as 100px for graphTitle and y-axis Title, bottom as 20 for placing x-Axis and left as 40 for placing y-Axis along with their labels
Insert cell
height = 600 //SVG height
Insert cell
xRange = [margin.left, widthModified - margin.right] //replace widthModified by width to set the bar-plot of entire width
Insert cell
yRange = [height - margin.bottom, margin.top]
Insert cell
x = d3.scaleBand() //Band scales are used for charts with an ordinal or categorical dimension
.domain(dataCount.map(d => d.cut))
.rangeRound(xRange) //for non-continuous datas, generally, rangeRound is used instead of range
.padding(0.1) //inserts gap between the bars
Insert cell
y = d3.scaleLinear() //Linear scales are used for continuous datas
.domain([0, d3.max(dataCount, d => d.count)]).nice() //nice is put to give a nicer rounded starting and end values to the axes
.range(yRange) //For continuous datas, generally range is used instead of rangeRound
Insert cell
Insert cell
//Adds scale 'x' to xAxis
xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`) //moves the axis to the bottom of chart (leaving the padding space)
.call(d3.axisBottom(x))
.call(g => g.select(".domain").remove()) //removes the opaque line visible across the entire axis
.call(g => g.select(".tick:last-of-type text").clone() //The :last-of-type selector allows you to move the position to the last occurence of xAxis label (or tick), while clone() makes a copy of the selected elements and inserts the duplicate of the selection so that the upper limit of the axis-scale is properly inserted
.attr("x", 50)
.attr("font-size", 15)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text("Cut →")) //sets the text title for the x-axis
Insert cell
//Adds scale 'y' to yAxis
yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y))
Insert cell
yTitle = g => g.append("text")
.attr("font-size", 15)
.attr("font-weight", "bold")
.attr("y", 70)
.text("↑ Counts of Records")
Insert cell
graphTitle = g => g.append("text")
.attr("font-size", 40)
.attr("y", 30)
.text("Number of diamonds for each CUT quality")

//makes the title center-aligned
.attr("x", width/2)
.attr("text-anchor", "middle")
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
//One can use the following method too to create a SVG element
//const svg = d3.select(DOM.svg(width, height));

svg.append("g")
.attr("fill", "rgb(158,158,225)")
.attr("stroke", "rgb(8,48,107)")
.attr("stroke-width", 1.5)
.attr("fill-opacity","0.6")
.selectAll("rect") //selects all the rectangles
.data(dataCount) //binds the data to the selected "rect" elements
.join("rect") //specifies rectangle is added to the DOM as data changes
//x and y attribute positions the rectangle, while height and width represents size
.attr("x", d => x(d.cut))
.attr("y", d => y(d.count))
.attr("height", d => y(0) - y(d.count)) //subtracts (the distance of the position of maximum height of bar from origin x-axis) from (the height of the plot area) to get the height of each bar [Reminder: origin is at top left]
.attr("width", x.bandwidth()) //x.bandwidth() returns the width of each bar.
//similar to tooltip feature of vegalite; it shows the detail on the plot on mouse hovering
.append("title")
.text(d => `${d.cut} = ${d.count}`);

//.call(function) applies the function
svg.append("g")
.call(xAxis)
svg.append("g")
.call(yAxis);
svg.call(yTitle);
svg.call(graphTitle);
return svg.node();
}
Insert cell
Insert cell
Insert cell
choice = ["No-sort", "Ascending", "Descending"]
Insert cell
{
if (sorting == "No-sort")
{
mutable dataCount = dataGroup.map(([cut, count]) => ({cut: cut, count: count.length}))
}
else if (sorting == "Descending")
{
//sort based on the count order
mutable dataCount = dataCount.sort(function(a, b) {
return d3.descending(a.count, b.count)
});
}
else if (sorting == "Ascending")
{
mutable dataCount = dataCount.sort(function(a, b) {
return d3.ascending(a.count, b.count)
});
}
}
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