Public
Edited
Jul 19, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
toc()
Insert cell
Insert cell
Insert cell
Insert cell
EVdata = FileAttachment("Electric_Vehicle_Population_Data.csv").csv({typed: true})
Insert cell
import { Table } from "@observablehq/inputs"
Insert cell
Table(EVdata.slice(0, 5))
Insert cell
Insert cell
Insert cell
vanilaBarChart = {

// Data manipulation
const rollupEVdata = d3.rollup(d3.filter(EVdata, d => d.State == "WA"), v => v.length, d => d.County)

const aggData = d3.filter(Array.from(rollupEVdata, ([county, count]) => ({ county, count })), d => d.count > 600)

// Define chart dimensions
const height = 500

const margins = ({
top: 30,
right: 10,
bottom: 20,
left: 45
})

// Define scales
const x = d3.scaleBand()
// .domain: data range, sorted first by electric vehicle population, and then county name
.domain(d3.groupSort(aggData, ([d]) => -d.count, (d) => d.county))
// .range: canvas range
.range([margins.left, width - margins.right])
// We are using a little bit of padding to make the graph look nicer
.padding(0.1)

const y = d3.scaleLinear()
.domain([0, d3.max(aggData, d=> d.count)])
.range([height - margins.bottom, margins.top])
.nice()
// Create the SVG container
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto;");

// Add y-axis and label
svg.append("g")
.attr("transform", `translate(${margins.left}, 0)`)
.call(d3.axisLeft(y).tickSizeOuter(0))
.call(g => g.select(".domain").remove()) // remove the axis but preserve the ticks
.call(g => g.append("text")
.attr("x", -margins.left)
.attr("y", 10)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text("↑ Count"))

const barColor = "pink"
const barHighlightColor = "lightblue"
const bars = svg.append("g")
.selectAll()
.data(aggData)
.join("rect")
.attr("x", d => x(d.county))
.attr("y", d => y(d.count))
.attr("height", d => y(0) - y(d.count)) // must be y(0) - y(d.country)
.attr("width", x.bandwidth())
.attr("fill", barColor)
.attr("class", "bars")

const label = svg.append("text")

bars
.on("mouseover", function(e,d){
d3.selectAll(".bars")
.transition()
.duration(200)
.style("opacity", 0.5)

d3.select(this)
.transition()
.duration(200)
.attr("fill", barHighlightColor)
.attr("opacity", 1)

const labelText = `${d.count}`

label
.attr("display", null)
.attr("font-size", "13px")
.style("opacity", 1)
.attr("transform", `translate(${d3.select(this).attr("x")},${d3.select(this).attr("y")})`)
.attr("dx", +33)
.attr("dy", -5)
.text(d => labelText)
.attr("text-anchor", "end")
})
.on("mousemove", function(e, d){
d3.selectAll(".bars")
.transition()
.duration(10)
.attr("fill", barColor)
.style("opacity", 1)
d3.select(this)
.transition()
.duration(10)
.attr("fill", barHighlightColor)
.attr("opacity", 1)
})
.on("mouseleave", function(e, d){

d3.selectAll(".bars")
.transition()
.duration(200)
.attr("fill", barColor)
.style("opacity", 1)

label
.attr("display", "none")
})

// Add x-axis and label
svg.append("g")
.attr("transform", `translate(0,${height - margins.bottom})`)
.call(d3.axisBottom(x).tickSizeOuter(0))

// Add a title
const title = svg.append("g")
.append("text")
.attr("x", width / 2)
.attr("y", (margins.top / 2))
.attr("text-anchor", "middle")
.attr("front-weight", "bold")
.attr("font-family", "Helvetica Neue, Arial")
.attr("font-size", "20px")
.text("Electric vehicle ownership distribution, Washington State")

return svg.node()
}
Insert cell
Insert cell
Insert cell
viewof animatedorder = {
const select = Inputs.select(
new Map([
["Alphabetical", (a, b) => a.county.localeCompare(b.county)],
["Count, ascending", (a, b) => a.count - b.count],
["Count, descending", (a, b) => b.count - a.count]
]),
{ label: "Order" }
);
return select;
}
Insert cell
Insert cell
Insert cell
updateAnimation = animateChart.updateChart(animatedorder)
Insert cell
Insert cell
animateChart = {

const rollupEVdata = d3.rollup(d3.filter(EVdata, d => d.State == "WA"), v => v.length, d => d.County)

const aggData = d3.filter(Array.from(rollupEVdata, ([county, count]) => ({ county, count })), d => d.count > 600)

const height = 500

const margins = ({
top: 30,
right: 10,
bottom: 20,
left: 45
})

const x = d3.scaleBand()
// .domain: data range, sorted first by electric vehicle population, and then county name
.domain(d3.groupSort(aggData, ([d]) => -d.count, (d) => d.county))
// .range: canvas range
.range([margins.left, width - margins.right])
// We are using a little bit of padding to make the graph look nicer
.padding(0.1)

const y = d3.scaleLinear()
.domain([0, d3.max(aggData, d=> d.count)])
.range([height - margins.bottom, margins.top])
.nice()
// Create the SVG container
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto;");

// Add y-axis and label
const gy = svg.append("g")
.attr("transform", `translate(${margins.left}, 0)`)
.call(d3.axisLeft(y).tickSizeOuter(0))
.call(g => g.select(".domain").remove()) // remove the axis but preserve the ticks
.call(g => g.append("text")
.attr("x", -margins.left)
.attr("y", 10)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text("↑ Count"))

const barColor = "pink"
const barHighlightColor = "lightblue"
const bars = svg.append("g")
.selectAll()
.data(aggData)
.join("rect")
.attr("x", d => x(d.county))
.attr("y", d => y(d.count))
.attr("height", d => y(0) - y(d.count)) // must be y(0) - y(d.country)
.attr("width", x.bandwidth())
.attr("fill", barColor)
.attr("class", "bars")

const label = svg.append("text")

bars
.on("mouseover", function(e,d){
d3.selectAll(".bars")
.transition()
.duration(200)
.style("opacity", 0.5)

d3.select(this)
.transition()
.duration(200)
.attr("fill", barHighlightColor)
.attr("opacity", 1)

const labelText = `${d.count}`

label
.attr("display", null)
.attr("font-size", "13px")
.style("opacity", 1)
.attr("transform", `translate(${d3.select(this).attr("x")},${d3.select(this).attr("y")})`)
.attr("dx", +33)
.attr("dy", -5)
.text(d => labelText)
.attr("text-anchor", "end")
})
.on("mousemove", function(e, d){
d3.selectAll(".bars")
.transition()
.duration(10)
.attr("fill", barColor)
.style("opacity", 1)
d3.select(this)
.transition()
.duration(10)
.attr("fill", barHighlightColor)
.attr("opacity", 1)
})
.on("mouseleave", function(e, d){

d3.selectAll(".bars")
.transition()
.duration(200)
.attr("fill", barColor)
.style("opacity", 1)

label
.attr("display", "none")
})

const xAxis = d3.axisBottom(x).tickSizeOuter(0);
// Add x-axis and label
const gx = svg.append("g")
.attr("transform", `translate(0,${height - margins.bottom})`)
.call(xAxis)

// Add a title
const title = svg.append("g")
.append("text")
.attr("x", width / 2)
.attr("y", (margins.top / 2))
.attr("text-anchor", "middle")
.attr("front-weight", "bold")
.attr("font-family", "Helvetica Neue, Arial")
.attr("font-size", "20px")
.text("Electric vehicle ownership distribution, Washington State")

// update chart
function updateChart(order) {
x.domain(aggData.sort(order).map(d => d.county));

const t = svg.transition()
.duration(750);

bars.data(aggData, d => d.county)
.order()
.transition(t)
.delay((d, i) => i * 20)
.attr("x", d => x(d.county));

gx.transition(t)
.call(xAxis)
.selectAll(".tick")
.delay((d, i) => i * 20);
}
return Object.assign(svg.node(), {updateChart});
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
import {toc} from "@jonfroehlich/collapsible-toc"
Insert cell
import {textcolor} from "@observablehq/text-color-annotations-in-markdown"
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