Public
Edited
Feb 18
Insert cell
Insert cell
Insert cell
Insert cell
teams_stats21 = FileAttachment("teams_stats2@1.csv").csv()
Insert cell
teams_stats21
Insert cell
Insert cell
// define margins and size
plotWidth =710
Insert cell
plotHeight = 670
Insert cell
plotMargin = ({top: 30, right: 30, bottom: 30, left: 30})
Insert cell
Insert cell
goals_forExtent = d3.extent(teams_stats21, d => d.goals_for)
Insert cell
// Scale for x-axis (encode goals_for here)
xScale = d3.scaleLinear()
.domain([0, d3.max(teams_stats21, d => +d.goals_for)])
.range([plotMargin.left, plotWidth - plotMargin.right]);

// Didn't use .nice() as scale ranged until max value
Insert cell
goals_againstExtent = d3.extent(teams_stats21, d => d.goals_against)
Insert cell
// Scale for y-axis (encode goals_against here)
yScale = d3.scaleLinear()
.domain([0, d3.max(teams_stats21, d => +d.goals_against)])
.range([plotHeight-plotMargin.bottom, plotMargin.top]);

// Didn't use .nice() as scale ranged until max value
Insert cell
// Create the colorScale for points (Hint: you need to use d3.scaleSequential to encode color for points, use the "d3.interpolateYlGn" interpolator already provided by d3)
// https://d3js.org/d3-scale/sequential (see the first example)

colorScale = d3.scaleSequential(d3.interpolateYlGn)
.domain(d3.extent(teams_stats21, d => +d.points));

Insert cell
// Scale for radius of circles (should be between 5 - 20, with the lowest point total corresponding to 5, highest point total corresponding to 20)
radiusScale = d3.scaleSqrt()
.domain(d3.extent(teams_stats21, d => +d.points))
.range([5, 20]);

//Used ScaleSqrt because it ensures that area of circle is proportional to data, making comparisons intuitive
//Using scaleLinear would have caused larger values to look disproportionately large
// Question for TA: Is it always a general practice to use scaleSqrt? or it depends on the data?
Insert cell
Insert cell
Insert cell
{
const svg = d3.create("svg")
.attr("width", plotWidth + plotMargin.left + plotMargin.right)
.attr("height", plotHeight + plotMargin.top + plotMargin.bottom);

const mainGroup = svg.append("g")
.attr("transform", `translate(${plotMargin.left}, ${plotMargin.top})`);
// Create axes groups and append axes
const XAxisGroup = mainGroup.append("g")
.attr("transform", `translate(0, ${plotHeight - plotMargin.bottom})`)
.call(d3.axisBottom(xScale));


//XAxis Labels
XAxisGroup.append("text")
.attr("x", plotWidth /2)
.attr("y", 40)
.attr("fill", "black")
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.text("goals_for");

const YAxisGroup = mainGroup.append("g")
.attr("transform", `translate(${plotMargin.left}, 0)`)
.call(d3.axisLeft(yScale));


//YAxis Labels
YAxisGroup.append("text")
.attr("transform", "rotate(-90)")
.attr("x", -plotHeight/2)
.attr("y", -40)
.attr("fill", "black")
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.text("goals_against");

const circlesGroup = mainGroup.append("g")

// Render your visualization by calling render_data
render_data(circlesGroup, teams_stats21);
return svg.node();
}

Insert cell
Insert cell
Insert cell
render_data = (group, data) => {
// render elements. Remember to use the Data Join
group.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", d => xScale(+d.goals_for))
.attr("cy", d => yScale(+d.goals_against))
.attr("r", d => radiusScale(+d.points))
.attr("fill", d => colorScale(+d.points))
.attr("stroke", "gray")
.attr("stroke-width", 2);

// create labels for teams
group.selectAll("text")
.data(data)
.join("text")
.attr("x", d => xScale(+d.goals_for))
.attr("y", d => yScale(+d.goals_against) - radiusScale(+d.points) - 5)
.attr("fill", "gray")
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.text(d => d.team);
}

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