{
let w = 800;
let h = 500;
let pad = 30;
let svg = d3
.create("svg")
.attr("viewBox", `0 0 ${w} ${h}`)
.style("max-width", `${w}px`)
.style("border", "solid 1px black");
let [xmin, xmax] = d3.extent(data, (o) => o.AverageSalary);
let [ymin, ymax] = [0, 100];
let x_scale = d3
.scaleLinear()
.domain([xmin, xmax])
.range([pad, w - pad]);
let y_scale = d3
.scaleLinear()
.domain([ymin, ymax])
.range([h - pad, pad]);
let points = svg.append("g");
points
.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", (d) => x_scale(d.AverageSalary))
.attr("cy", (d) => y_scale(d.RetentionRate))
.attr("r", (d) => (d.UnitID == 199111 ? 5 : 3))
.attr("fill", (d) => (d.UnitID == 199111 ? "#002366" : "lightgray"))
.attr("fill-opacity", (d) => (d.UnitID == 199111 ? 1 : 0.6))
.attr("stroke-width", 2)
.on("pointerenter", function () {
d3.select(this).attr("stroke", "black");
})
.on("pointerleave", function () {
d3.select(this).attr("stroke", null);
})
.each(function (datum) {
tippy(this, {
theme: "light-border",
allowHTML: true,
content: `<span style="font-weight: bold">${
datum.University
}</span>:<br> Average Salary: ${
datum.AverageSalary
}<br> Retention Rate: ${datum.RetentionRate / 100}`
});
});
let regression_result = regression
.regressionLinear()
.x((d) => d.AverageSalary)
.y((d) => d.RetentionRate)(data);
let [x1, y1, x2, y2] = regression_result.flat();
svg
.append("line")
.attr("x1", x_scale(x1))
.attr("y1", y_scale(y1))
.attr("x2", x_scale(x2))
.attr("y2", y_scale(y2))
.attr("stroke", d3.schemeCategory10[0])
.attr("stroke-width", 3);
svg
.append("g")
.attr("transform", `translate(${pad})`)
.call(d3.axisLeft(y_scale).tickSizeOuter(0));
svg
.append("g")
.attr("transform", `translate(0,${h - pad})`)
.call(d3.axisBottom(x_scale).tickSizeOuter(0));
return svg.node();
}