Public
Edited
Oct 5, 2023
1 fork
Importers
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chart0 = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

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

const players = svg
.append("g")
.selectAll("circle")
.data(sample_data)
.join("circle")
.attr("cx", d => x(d.age)) // + Math.random() * 20)
.attr("cy", d => y(d.wage_eur))
.attr("r", d => r(d.overall))
.attr("stroke", d => color(d.body_type))
.attr("fill", d => color(d.body_type))
.attr("opacity", 0.75);

players.append("title").text(browserTooltip);

return svg.node();
}
Insert cell
Insert cell
Insert cell
chart1 = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

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

const players = svg
.append("g")
.selectAll("circle")
.data(sample_data)
.join("circle")
.attr("cx", d => x(d.age)) // + Math.random() * 20)
.attr("cy", d => y(d.wage_eur))
.attr("r", d => r(d.overall))
.attr("stroke", d => color(d.body_type))
.attr("fill", d => color(d.body_type))
.attr("opacity", 0.70)
.on('mouseenter', mouseEnter)
.on('mouseleave', mouseLeave);

function mouseEnter(event, d) {
d3.select(this)
.attr('r', d => r(d.overall) * 2)
.attr("stroke", "black");
}

function mouseLeave(event, d) {
d3.select(this)
.attr('r', d => r(d.overall))
.attr("stroke", d => color(d.body_type));
}

players.append("title").text(browserTooltip);

return svg.node();
}
Insert cell
Insert cell
chart2 = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

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

const players = svg
.append("g")
.selectAll("circle")
.data(sample_data)
.join("circle")
.attr('id', d => `${d.short_name}`)
.attr("cx", d => x(d.age)) // + Math.random() * 20)
.attr("cy", d => y(d.wage_eur))
.attr("r", d => r(d.overall))
.attr("stroke", d => color(d.body_type))
.attr("fill", d => color(d.body_type))
.attr("opacity", 0.70)
.on('click', clickHighlight);

const selected = new Map(sample_data.map(d => [d.short_name, true]));

function clickHighlight(event, d) {
const isSelected = selected.get(d.short_name);
//console.log(isSelected)
const player = d3.select(this);
if (isSelected) {
player
.attr("r", d => r(d.overall) * 2)
.attr("stroke", "black")
.attr("opacity", 0.9);
selected.set(d.short_name, !isSelected);
} else {
player
.attr("r", d => r(d.overall))
.attr("stroke", d => color(d.body_type))
.attr("opacity", 0.70);
selected.set(d.short_name, !isSelected);
}
}

players.append("title").text(browserTooltip);

return svg.node();
}
Insert cell
Insert cell
chart3 = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

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

const tooltip = new Tooltip();

const players = svg
.append("g")
.selectAll("circle")
.data(sample_data)
.join("circle")
.attr("cx", d => x(d.age)) // + Math.random() * 20)
.attr("cy", d => y(d.wage_eur))
.attr("r", d => r(d.overall))
.attr("stroke", d => color(d.body_type))
.attr("fill", d => color(d.body_type))
.attr("opacity", 0.70);

svg
.append("g")
.attr("fill", "none")
.attr("pointer-events", "all")
.selectAll("rect")
.data(sample_data)
.join("rect")
.attr("x", d => x(d.age))
.attr("y", d => y(d.wage_eur))
.attr("height", "8px")
.attr("width", "8px")
.on("mouseover", (event, d) => tooltip.show(d))
.on("mouseout", () => tooltip.hide());

svg.append(() => tooltip.node);

return svg.node();
}
Insert cell
Insert cell
Insert cell
chart4 = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

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

const players = svg
.append("g")
.selectAll("circle")
.data(sample_data)
.join("circle")
.attr("cx", d => x(d.age)) // + Math.random() * 20)
.attr("cy", d => y(d.wage_eur))
.attr("r", d => r(d.overall))
.attr("stroke", d => color(d.body_type))
.attr("fill", d => color(d.body_type))
.attr("opacity", 0.70)
.on('mouseenter', mouseEnter)
.on('mouseleave', mouseLeave);

// create tooltip
const tooltip = svg.append('g').attr('visibility', 'hidden');

const tooltipHeight = 25;

const tooltipRect = tooltip
.append('rect')
.attr('fill', 'white')
.attr('rx', 5)
.attr('height', tooltipHeight);

const tooltipText = tooltip
.append('text')
.attr('fill', 'black')
.attr('font-family', 'Ariel, Helvetica, sans-serif')
.attr('font-size', 10)
.attr('dy', 2)
.attr('dx', 2)
.attr('dominant-baseline', 'hanging');

// handle hovering over a circle
function mouseEnter(event, d) {
// make the circle larger
d3.select(this)
.attr("r", d => r(d.overall) * 2)
.attr("stroke", "black")
.attr("opacity", 0.9);

// update the text and get its width
tooltipText.html(d.short_name);
const labelWidth = tooltipText.node().getComputedTextLength();

// set the width of the tooltip's background rectangle
// to match the width of the label
tooltipRect.attr('width', labelWidth + 4);

// move the tooltip and make it visible
const xPos = x(d.age) + r(d.overall) * 3;
const yPos = y(d.wage_eur) - tooltipHeight / 2;

tooltip
.attr('transform', `translate(${xPos},${yPos})`)
.attr('visibility', 'visible');
}

// handle leaving a circle
function mouseLeave(event, d) {
// reset its size
d3.select(this)
.attr("r", d => r(d.overall))
.attr("stroke", d => color(d.body_type))
.attr("opacity", 0.70);

// make the tooltip invisible
tooltip.attr('visibility', 'hidden');
}

return svg.node();
}
Insert cell
Insert cell
Insert cell
// SOURCE: https://observablehq.com/@sjengle/interactivity?collection=@sjengle/interactive-scatterplot
vis = {
// wait until this cell is visible
await visibility();

const svg = d3_5.select(DOM.svg(width, height));
//svg.style("border", "1px solid #bbbbbb");

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

// data layer to keep circles
const g = svg.append("g").attr("id", "circles");

g.selectAll("circle")
.data(fewer_columns) // sample_data
.enter()
.append("circle")
.attr("cx", d => x(d.age)) // + Math.random() * 20)
.attr("cy", d => y(d.wage_eur))
.attr("r", d => r(d.overall))
.attr("fill", d => color(d.body_type))
.attr("opacity", 0.70);

// annotation layer to keep labels on top of data
svg.append("g").attr("id", "annotation");

return svg.node();
}
Insert cell
Insert cell
Insert cell
highlight = {
const svg = d3_5.select(vis);

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

// event.namespace allows us to add multiple functions for one event
// only needed in examples like this where some events trigger multiple functions
circles.on("mouseover.highlight", function(event, d) {
d3_5
.select(this)
.raise() // bring to front
.attr("r", d => r(d.overall) * 2)
.style("stroke", "black")
.style("stroke-width", 2);

// show what we interacted with
d3_5.select(status).text("highlight: " + d.short_name);
});

circles.on("mouseout.highlight", function(event, d) {
d3_5
.select(this)
.attr("r", d => r(d.overall))
.style("stroke", null);
d3_5.select(status).text("highlight: none");
});

return status;
}
Insert cell
hover2 = {
const svg = d3_5.select(vis);

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

circles.on("mouseover.hover2", function(d) {
let me = d3_5.select(this);
let div = d3_5.select("body").append("div");

div.attr("id", "details");
div.attr("class", "tooltip");

let rows = div
.append("table")
.selectAll("tr")
.data(Object.keys(d))
.enter()
.append("tr");

rows.append("th").text(key => capitalize(key));
rows.append("td").text(key => d[key]);

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

circles.on("mousemove.hover2", function(d) {
let div = d3_5.select("div#details");

// get height of tooltip
let bbox = div.node().getBoundingClientRect();

div.style("left", d3_5.event.clientX + "px");
div.style("top", d3_5.event.clientY - bbox.height + "px");
});

circles.on("mouseout.hover2", function(d) {
d3_5.selectAll("div#details").remove();
d3_5.select(status).text("hover: none");
});

return status;
}
Insert cell
html`
<style>
text, .tooltip {
font-family: Ariel, Helvetica, sans-serif;
font-size: 10pt;
}

.tooltip {
position: absolute;
width: auto;
height: auto;
padding: 8px;
background: #ddd;
pointer-events: none;
border: 1px solid #eee;
border-radius: 10px;
}
</style>
`
Insert cell
Insert cell
chart6 = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

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

const players = svg
.append("g")
.selectAll("circle")
.data(sample_data)
.join("circle")
.attr("cx", (d) => x(d.age)) // + Math.random() * 20)
.attr("cy", (d) => y(d.wage_eur))
.attr("r", (d) => r(d.overall))
.attr("stroke", (d) => color(d.body_type))
.attr("fill", (d) => color(d.body_type))
.attr("opacity", 0.7)
.on("click", hidePlayer);

function hidePlayer(event, d) {
d3.select(this).transition().duration(1500).attr("opacity", 0.0);
}

players.append("title").text(browserTooltip);

return svg.node();
}
Insert cell
Insert cell
x = d3
.scaleLinear()
.domain(d3.extent(sample_data, d => d.age))
.range([margin.left, width - margin.right])
.nice()
Insert cell
y = d3
.scaleLinear()
.domain(d3.extent(sample_data, d => d.wage_eur))
.range([height - margin.bottom, margin.top])
.nice()
Insert cell
r = d3
.scaleSqrt()
.domain(d3.extent(sample_data, d => d.overall))
.range([1, 8])
Insert cell
// d3.rollup(sample_data, v => v.length, d => d.body_type)
Insert cell
color = d3.scaleOrdinal(
["Lean", "Normal", "Stocky"], // [...new Set(sample_data.map(d => d.body_type))],
d3.schemeCategory10
)
Insert cell
Insert cell
Insert cell
xAxis = g =>
g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).ticks(width / 80))
.call(g => g.select(".domain").remove())
Insert cell
yAxis = g =>
g
.attr("transform", `translate(${margin.left}, 0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
Insert cell
legendBodyType = g =>
g
.attr(
"transform",
`translate(${((width - margin.left - margin.right) / 4) * 3.5},60)`
)
.call(g =>
g.append(() =>
legend({
color: color,
height: 50,
width: 150,
title: "Player's Body Type",
tickSize: 0
})
)
)
Insert cell
Insert cell
Insert cell
Insert cell
printTable(sample_data.slice(0, 10))
Insert cell
Insert cell
Insert cell
Insert cell
sample = (aArray, n = 2000) => d3.shuffle([...aArray]).slice(0, n)
Insert cell
capitalize = str =>
str.replace(
/\w\S*/g,
txt => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
)
Insert cell
selectColsByName = (receiverObj, supplierObj, colsToKeep) => {
if (Array.isArray(colsToKeep)) {
Object.keys(supplierObj).forEach(key => {
if (colsToKeep.includes(key)) receiverObj[key] = supplierObj[key];
});
return receiverObj;
} else {
console.log(`${colsToKeep} is not an array.`);
}
}
Insert cell
Insert cell
Insert cell
Insert cell
d3 = require("d3@6")
Insert cell
d3_5 = require("d3@5")
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