Published
Edited
Feb 13, 2021
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
// replay;

const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

const l = length(line(data));

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

svg
.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 2.5)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-dasharray", `0,${l}`)
.attr("d", line)
.transition()
.duration(5000)
.ease(d3.easeLinear)
.attr("stroke-dasharray", `${l},${l}`);

svg
.append("g")
.attr("fill", "white")
.attr("stroke", "black")
.attr("stroke-width", 2)
.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", d => xScale(xAccessor(d)))
.attr("cy", d => yScale(yAccessor(d)))
.attr("r", 5);

const label = svg
.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 14)
.selectAll("g")
.data(data)
.join("g")
.attr("transform", d => `translate(${xScale(d.x)},${yScale(d.y)})`)
.attr("opacity", 0);

label
.append("text")
.text(d => d.name)
.each(function(d) {
const t = d3.select(this);
switch (d.orient) {
case "top":
t.attr("text-anchor", "middle").attr("dy", "-0.7em");
break;
case "right":
t.attr("dx", "0.5em")
.attr("dy", "0.32em")
.attr("text-anchor", "start");
break;
case "bottom":
t.attr("text-anchor", "middle").attr("dy", "1.4em");
break;
case "left":
t.attr("dx", "-0.5em")
.attr("dy", "0.32em")
.attr("text-anchor", "end");
break;
}
});

label
.transition(5000)
.delay((d, i) => (length(line(data.slice(0, i + 1))) / l) * (5000 - 50))
.attr("opacity", 1);

return svg.node();
replay;
}
Insert cell
Insert cell
tooltip = {
chart;

const tooltip = d3
.select("body")
.append("div")
.attr("class", "svg-tooltip")
.style("position", "absolute")
.style("visibility", "hidden");

d3.selectAll("circle")
.on("mouseover", function(d, i) {
// change the selection style
d3.select(this)
.attr('stroke-width', '4')
.attr("stroke", "black")
.style('fill', "gray");

tooltip
.style("visibility", "visible")
.text(
`Year: ${data[i].name}\nFemale Median Income: $${data[i].x}\nMedian Home Price: $${data[i].y}`
);
})
.on("mousemove", function(d, i) {
tooltip
.style("top", d3.event.pageY - 100 + "px")
.style("left", d3.event.pageX - 100 + "px");
})
.on("mouseout", function(d, i) {
// change the selection style
d3.select(this)
.style("fill", "white")
.attr("stroke", "black")
.attr("stroke-width", 2);

tooltip.style("visibility", "hidden");
});
}
Insert cell
Insert cell
Insert cell
data = Object.assign(
(await FileAttachment("median_female_income_homes_clean2@2.csv").csv()).map(
({ year, f_income_median, median_home, side }) => ({
name: year,
x: +f_income_median,
y: +median_home,
orient: side
})
),
{ x: "Median female income", y: "Median home price" }
)
Insert cell
Insert cell
xAccessor = d => d.x
Insert cell
yAccessor = d => d.y
Insert cell
Insert cell
xAccessor(data[0])
Insert cell
yAccessor(data[0])
Insert cell
Insert cell
line = d3
.line()
.curve(d3.curveCatmullRom)
.x(d => xScale(xAccessor(d)))
.y(d => yScale(yAccessor(d)))
Insert cell
Insert cell
margin = ({
top: 20,
bottom: 30,
left: 60,
right: 30
})
Insert cell
height = width * 0.75
Insert cell
width // built in ObservableHQ
Insert cell
Insert cell
xScale = d3
.scaleLinear()
.domain(d3.extent(data, xAccessor))
.nice()
.range([margin.left, width - margin.right])
Insert cell
yScale = d3
.scaleLinear()
.domain(d3.extent(data, yAccessor))
.nice()
.range([height - margin.bottom, margin.top])
Insert cell
Insert cell
xAxis = g =>
g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(xScale).ticks(width / 80, "$,f"))
.call(g => g.select(".domain").remove())
.call(g =>
g
.selectAll(".tick line")
.clone()
.attr("y2", -height)
.attr("stroke-opacity", 0.1)
)
.call(g =>
g
.append("text")
.attr("x", width - 4)
.attr("y", -4)
.attr("font-weight", "bold")
.attr("font-size", 14)
.attr("text-anchor", "end")
.attr("fill", "black")
.text(data.x)
)
// .call(halo))
Insert cell
yAxis = g =>
g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(yScale).ticks(null, "$,f"))
.call(g => g.select(".domain").remove())
.call(g =>
g
.selectAll(".tick line")
.clone()
.attr("x2", width)
.attr("stroke-opacity", 0.1)
)
.call(g =>
g
.select(".tick:last-of-type text")
.clone()
.attr("x", 4)
.attr("font-size", 14)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.attr("fill", "black")
.text(data.y)
)
Insert cell
function length(path) {
return d3
.create("svg:path")
.attr("d", path)
.node()
.getTotalLength();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3 = require("d3@5")
Insert cell
import { chart as bostockChart } from '@d3/connected-scatterplot'
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