Published
Edited
Jun 7, 2020
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
render_data_table(table_info)
Insert cell
Insert cell
md`## Filter Interaction`
Insert cell
marathonDta = Object.assign(
d3.csvParse(
await FileAttachment("marathonData.csv").text(),
({ rate, gender }) => {
if (gender == gen || gen == "All") {
return rate;
}
}
),
{ x: "Age", y: "Frequency" }
)
Insert cell
bins = d3
.histogram()
.domain(x.domain())
.thresholds(x.ticks(40))(marathonDta)
Insert cell
x = d3
.scaleLinear()
.domain(d3.extent(marathonDta))
.nice()
.range([margin.left, width - margin.right])
Insert cell
y = d3
.scaleLinear()
.domain([0, d3.max(bins, d => d.length)])
.nice()
.range([height - margin.bottom, margin.top])
Insert cell
xAxis = g =>
g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(
d3
.axisBottom(x)
.ticks(width / 80)
.tickSizeOuter(0)
)
.call(g =>
g
.append("text")
.attr("x", width - margin.right)
.attr("y", -4)
.attr("fill", "currentColor")
.attr("font-weight", "bold")
.attr("text-anchor", "end")
.text(marathonDta.x)
)
Insert cell
yAxis = g =>
g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).ticks(height / 40))
.call(g => g.select(".domain").remove())
.call(g =>
g
.select(".tick:last-of-type text")
.clone()
.attr("x", 4)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(marathonDta.y)
)
Insert cell
viewof gen = html`<select>
<option>M
<option>W
<option>All
</select>`
Insert cell
filterChart = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

svg
.append("g")
.attr("fill", "steelblue")
.selectAll("rect")
.data(bins)
.join("rect")
.attr("x", d => x(d.x0) + 1)
.attr("width", d => Math.max(0, x(d.x1) - x(d.x0) - 1))
.attr("y", d => y(d.length))
.attr("height", d => y(0) - y(d.length));

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

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

return svg.node();
}
Insert cell
md`## Other Graph`
Insert cell
marginScatter = ({ top: 25, right: 20, bottom: 35, left: 40 })
Insert cell
marathonDtaParsed = Object.assign(
d3.csvParse(
await FileAttachment("marathonData.csv").text(),
({ rate, gender, final, split }) => {
if (parseInt(rate) == 33) {
var d = {};
var timing = final.split(":");
var splitTime = split.split(":");
d.x =
parseInt(3600 * timing[0]) +
parseInt(timing[1]) * 60 +
parseInt(timing[2]);
d.y =
parseInt(3600 * splitTime[0]) +
parseInt(splitTime[1]) * 60 +
parseInt(splitTime[2]);
d.name = gender;
return d;
}
}
),
{ y: "Split Time →", x: "Final Time" }
)
Insert cell
xScatter = d3
.scaleLinear()
.domain(d3.extent(marathonDtaParsed, d => d.x))
.nice()
.range([marginScatter.left, width - margin.right])
Insert cell
yScatter = d3
.scaleLinear()
.domain(d3.extent(marathonDtaParsed, d => d.y))
.nice()
.range([height - marginScatter.bottom, marginScatter.top])
Insert cell
md`### Hover over to see final completion time (bottom of circle)`
Insert cell
scatterChart = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

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

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

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

svg
.append("g")
.attr("id", "circles")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("fill", "none")
.selectAll("circle")
.data(marathonDtaParsed)
.join("circle")
.attr("cx", d => xScatter(d.x))
.attr("cy", d => yScatter(d.y))
.attr("r", 10);

svg.append("g").attr("id", "annotation");

return svg.node();
}
Insert cell
xAxisScatter = g =>
g
.attr("transform", `translate(0,${height - marginScatter.bottom})`)
.call(d3.axisBottom(xScatter).ticks(width / 80))
.call(g => g.select(".domain").remove())
.call(g =>
g
.append("text")
.attr("x", width)
.attr("y", marginScatter.bottom - 4)
.attr("fill", "currentColor")
.attr("text-anchor", "end")
.text(marathonDtaParsed.x)
)
Insert cell
yAxisScatter = g =>
g
.attr("transform", `translate(${marginScatter.left},0)`)
.call(d3.axisLeft(yScatter))
.call(g => g.select(".domain").remove())
.call(g =>
g
.append("text")
.attr("x", -marginScatter.left)
.attr("y", 10)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text(marathonDtaParsed.y)
)
Insert cell
grid = g =>
g
.attr("stroke", "currentColor")
.attr("stroke-opacity", 0.1)
.call(g =>
g
.append("g")
.selectAll("line")
.data(xScatter.ticks())
.join("line")
.attr("x1", d => 0.5 + xScatter(d))
.attr("x2", d => 0.5 + xScatter(d))
.attr("y1", marginScatter.top)
.attr("y2", height - marginScatter.bottom)
)
.call(g =>
g
.append("g")
.selectAll("line")
.data(yScatter.ticks())
.join("line")
.attr("y1", d => 0.5 + yScatter(d))
.attr("y2", d => 0.5 + yScatter(d))
.attr("x1", marginScatter.left)
.attr("x2", width - marginScatter.right)
)
Insert cell
circles = d3
.select(scatterChart)
.select("#circles")
.selectAll("circle")
Insert cell
annotations = d3.select(scatterChart).select("g#annotation")
Insert cell
hover = {
const svg = d3.select(scatterChart);

// used to test out interactivity in this cell
const status = html`<code>hover: none</code>`;

circles.on("mouseover.hover1", function(d) {
let me = d3.select(this);

annotations
.insert("text")
.attr("id", "label")
.attr("x", me.attr("cx"))
.attr("y", me.attr("cy"))
.attr("text-anchor", "middle")
.text(d.x, d.y);

// show what we interacted with
d3.select(status).text("hover: " + d.name);
});

circles.on("mouseout.hover1", function(d) {
annotations.select("text#label").remove();
d3.select(status).text("hover: none");
});

return status;
}
Insert cell
md`## Last Graph`
Insert cell
marathonDta2 = Object.assign(
d3.csvParse(
await FileAttachment("marathonData.csv").text(),
({ rate, final }) => {
if (parseInt(rate) > 30 && ageT == "Over 30") {
var hour = final.split(":")[0];
var minute = final.split(":")[1];
var second = final.split(":")[2];
return parseInt(3600 * hour + 60 * minute + second);
} else if (parseInt(rate) <= 30 && ageT == "Under 30") {
var hour = final.split(":")[0];
var minute = final.split(":")[1];
var second = final.split(":")[2];
return parseInt(3600 * hour + 60 * minute + second);
}
}
),
{ x: "Final", y: "Frequency" }
)
Insert cell
bins2 = d3
.histogram()
.domain(x2.domain())
.thresholds(x2.ticks(100))(marathonDta2)
Insert cell
x2 = d3
.scaleLinear()
.domain(d3.extent(marathonDta2))
.nice()
.range([margin.left, width - margin.right])
Insert cell
y2 = d3
.scaleLinear()
.domain([0, d3.max(bins2, d => d.length)])
.nice()
.range([height - margin.bottom, margin.top])
Insert cell
xAxis2 = g =>
g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(
d3
.axisBottom(x2)
.ticks(width / 80)
.tickSizeOuter(0)
)
.call(g =>
g
.append("text")
.attr("x", width - margin.right)
.attr("y", -4)
.attr("fill", "currentColor")
.attr("font-weight", "bold")
.attr("text-anchor", "end")
.text(marathonDta2.x)
)
Insert cell
yAxis2 = g =>
g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y2).ticks(height / 40))
.call(g => g.select(".domain").remove())
.call(g =>
g
.select(".tick:last-of-type text")
.clone()
.attr("x", 4)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(marathonDta2.y)
)
Insert cell
md`### Histogram of finish times (in seconds)`
Insert cell
viewof ageT = html`<select>
<option>Over 30
<option>Under 30
</select>`
Insert cell
filterChart2 = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

svg
.append("g")
.attr("fill", "steelblue")
.selectAll("rect")
.data(bins2)
.join("rect")
.attr("x", d => x2(d.x0) + 1)
.attr("width", d => Math.max(0, x2(d.x1) - x2(d.x0) - 1))
.attr("y", d => y2(d.length))
.attr("height", d => y2(0) - y2(d.length));

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

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

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
d3 = require("d3@5")
Insert cell
Insert cell
height = 600
Insert cell
width = 954
Insert cell
margin = ({ top: 50, right: 30, bottom: 40, left: 40 })
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