Published
Edited
Feb 22, 2021
Insert cell
Insert cell
Insert cell
// load dataset
raw_data = d3.csv(
"https://raw.githubusercontent.com/EliGrosman/info474-data/main/states_yearly.csv"
)
Insert cell
Insert cell
// choose variable to plot on y axis
y_variable = "population"
Insert cell
// choose variable to plot on x axis
x_variable = "year"
Insert cell
// color of the bars
color_variable = "population"
Insert cell
Insert cell
x_domain = raw_data.columns
.filter(d => d !== "NAME")
.sort(function(a, b) {
return parseInt(a) - parseInt(b);
})
Insert cell
// organize data so each row is the population in a certain state in a given year
chart_data = {
const new_data = [];
raw_data.forEach(d => {
x_domain.forEach(year => {
new_data.push({ year: year, state: d.NAME, population: d[year] });
});
});
return new_data;
}
Insert cell
// the chart will only display data for the state chosen by the user
// this state 'state' is chosen with a dropdown below
data = chart_data.filter(d => d.state == state)
Insert cell
Insert cell
// we already have the x domain
x_domain
Insert cell
min_y = d3.min(data, d => +d[y_variable])
Insert cell
max_y = d3.max(data, d => +d[y_variable])
Insert cell
Insert cell
margin = ({ top: 50, bottom: 50, left: 90, right: 20 })
Insert cell
x_scale = d3
.scaleBand()
.rangeRound([margin.left, width - margin.right])
.domain(x_domain)
Insert cell
y_scale = d3
.scaleLinear()
.domain([min_y - (max_y - min_y) / 4, max_y])
.range([height - margin.top - margin.bottom, 0])
Insert cell
color_scale = d3
.scaleLinear()
.domain([min_y - (max_y - min_y) / 4, max_y])
.range(["lightblue", "black"])
.interpolate(d3.interpolateRgb.gamma(1.9))
Insert cell
Insert cell
x_axis = d3.axisBottom().scale(x_scale)
Insert cell
y_axis = d3.axisLeft().scale(y_scale)
Insert cell
import { legend } from "@d3/color-legend"
Insert cell
legend({
color: color_scale,
title: "Population"
})
Insert cell
{
const svg = d3
.create("svg")
.attr('width', width)
.attr('height', height);

const g_x_axis = svg
.append("g")
.attr("class", "xaxis")
.attr("transform", `translate(0, ${height - margin.bottom})`);

const g_y_axis = svg
.append("g")
.attr("class", "yaxis")
.attr("transform", `translate(${margin.left}, ${margin.bottom})`);

g_y_axis.call(y_axis);

g_x_axis.call(x_axis);

return svg.node();
}
Insert cell
Insert cell
{
const svg = d3
.create('svg')
.attr("width", width)
.attr("height", height);
const bars = svg.selectAll('rect').data(data, d => d[x_variable]);

bars.join(
enter =>
enter
.append("rect")
.attr("x", d => x_scale(d[x_variable]))
// add width and height just to see that they render
.attr("width", 20)
.attr("height", 10),

exit => exit.remove()
);

return svg.node();
}
Insert cell
Insert cell
{
const svg = d3
.create('svg')
.attr("width", width)
.attr("height", height);
const bars = svg.selectAll('rect').data(data, d => d[x_variable]);

bars.join(
enter =>
enter
.append("rect")
.attr("x", d => x_scale(d[x_variable]))
.attr("y", d => y_scale(d[y_variable]) + margin.bottom - 1)
.attr("width", 20)
.attr("height", 10),

exit => exit.remove()
);

return svg.node();
}
Insert cell
Insert cell
{
const svg = d3
.create('svg')
.attr("width", width)
.attr("height", height);
const bars = svg.selectAll('rect').data(data, d => d[x_variable]);

bars.join(
enter =>
enter
.append("rect")
.attr("x", d => x_scale(d[x_variable]))
.attr("width", x_scale.bandwidth() - 1)
.attr("y", d => y_scale(d[y_variable]) + margin.bottom - 1)
.attr(
"height",
d => height - margin.bottom - margin.top - y_scale(d[y_variable])
)

.attr("fill", d => color_scale(d[color_variable])),

exit => exit.remove()
);

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
let svg, g_x_axis, g_y_axis, title, xlabel, ylabel;

if (!this) {
svg = d3
.create("svg")
.attr('width', width)
.attr('height', height);

g_x_axis = svg
.append("g")
.attr("class", "xaxis")
.attr("transform", `translate(0, ${height - margin.bottom})`);

g_y_axis = svg
.append("g")
.attr("class", "yaxis")
.attr("transform", `translate(${margin.left}, ${margin.bottom})`);

title = svg
.append("text")
.attr("class", "title")
.attr("x", width / 2)
.attr("y", margin.top / 3)
.style("text-anchor", "middle");

xlabel = svg
.append("text")
.attr("class", "xlabel")
.attr("x", width / 2)
.attr("y", height - margin.bottom / 3)
.style("text-anchor", "middle")
.text("Year");

ylabel = svg
.append("text")
.attr("transform", "rotate(-90)")
.attr("class", "ylabel")
.attr("x", -height / 2)
.attr("y", margin.left / 4)
.style("text-anchor", "middle")
.text("Population (number of residents)");
} else {
svg = d3.select(this);
g_x_axis = svg.select(".xaxis");
g_y_axis = svg.select(".yaxis");
title = svg.select(".title");
}

g_y_axis
.transition()
.duration(500)
.call(y_axis);

g_x_axis.call(x_axis);

title.text(`Population of ${state}`);

const bars = svg.selectAll('rect').data(data, d => d[x_variable]);

bars.join(
enter =>
enter
.append("rect")
.attr("x", d => x_scale(d[x_variable]))
.attr("width", x_scale.bandwidth() - 1)
.attr("y", d => y_scale(d[y_variable]) + margin.bottom - 1)
.attr(
"height",
d => height - margin.bottom - margin.top - y_scale(d[y_variable])
)
.attr("fill", d => color_scale(d[color_variable])),
update =>
update.call(g =>
g
.transition()
.duration(1000)
.attr("y", d => y_scale(d[y_variable]) + margin.bottom)
.attr(
"height",
d => height - margin.bottom - margin.top - y_scale(d[y_variable])
)
.attr("fill", d => color_scale(d[color_variable]))
),
exit => exit.remove()
);

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
height = 600
Insert cell
import { select } from "@jashkenas/inputs"
Insert cell
import {
assignment_instructions,
observable_challenges
} from "@uw-info474/utilities"
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