Public
Edited
May 1
Insert cell
Insert cell
ds = require("d3@7")
Insert cell
Insert cell
data = FileAttachment("best_video_games.csv").csv()
Insert cell
Insert cell
platformToManufacturer = ({
"3DS": "Nintendo",
"Dreamcast": "SEGA",
"Game Boy Advance": "Nintendo",
"GameCube": "Nintendo",
"Nintendo 64": "Nintendo",
"PC": "PC",
"PlayStation": "Sony",
"PlayStation 2": "Sony",
"PlayStation 3": "Sony",
"PlayStation 4": "Sony",
"PlayStation 5": "Sony",
"Switch": "Nintendo",
"Wii": "Nintendo",
"Wii U": "Nintendo",
"Xbox": "Microsoft",
"Xbox 360": "Microsoft",
"Xbox One": "Microsoft",
"Xbox Series X": "Microsoft"
})
Insert cell
Insert cell
platformCounts =
{
const counts = d3.rollup(
data,
v => v.length,
d => d.Platform
);

const result = Array.from(counts, ([platform, count]) => ({
platform,
count,
manufacturer: platformToManufacturer[platform]
}));
return result.sort((a, b) => d3.descending(a.count, b.count));
}
Insert cell
Insert cell
manufacturerColor = d3.scaleOrdinal()
.domain([...new Set(platformCounts.map(d => d.manufacturer))])
.range(d3.schemeCategory10);
Insert cell
Insert cell
barChart = {
const width = 800;
const height = 600;
const margin = {top: 30, right: 30, bottom: 50, left: 50};

const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height + 100])
.attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;")

const x = d3.scaleBand()
.domain(platformCounts.map(d => d.platform))
.range([margin.left, width - margin.right])
.padding(0.1);

const y = d3.scaleLinear()
.domain([0, d3.max(platformCounts, d => d.count)])
.nice()
.range([height - margin.bottom, margin.top])

svg.append("g")
.selectAll("rect")
.data(platformCounts)
.join("rect")
.attr("x", d => x(d.platform))
.attr("y", d => y(d.count))
.attr("height", d => y(0) - y(d.count))
.attr("width", x.bandwidth())
.attr("fill", d => manufacturerColor(d.manufacturer))
.append("title")
.text(d => `${d.platform} (${d.manufacturer}): ${d.count} games`)
;

svg.append("g")
.attr("transform", `translate(0, ${height - margin.bottom})`)
.call(d3.axisBottom(x))
.selectAll("text")
.attr("transform", "rotate(-45)")
.style("text-anchor", "end");

svg.append("g")
.attr("transform", `translate(${margin.left})`)
.call(d3.axisLeft(y));

svg.append("text")
.attr("x", width / 2)
.attr("y", margin.top / 2)
.attr("text-anchor", "middle")
.attr("font-size","16px")
.attr("font-weight", "bold")
.text("Number of Top-Ranked Games by Platform (Colored by Manufacturer)");

const legendGroup = svg.append("g")
.attr("transform", `translate(${margin.left}, ${height - margin.bottom + 80})`);

[...new Set(
platformCounts
.map(d => d.manufacturer)
.filter(m => m !== undefined && m !== "Other")
)].forEach((manufacturer, i) => {
const group = legendGroup
.append("g")
.attr("transform", `translate(${i * 120}, 0)`);

group.append("rect")
.attr("width", 15)
.attr("height", 15)
.attr("fill", manufacturerColor(manufacturer));

group.append("text")
.attr("x", 20)
.attr("y", 12)
.text(manufacturer)
.attr("font-size", "12px")
.attr("alignment-baseline", "middle");
});

return svg.node();
}
Insert cell
Insert cell
scatterPlot = {

const width = 800;
const height = 600;
const margin = {top: 30, right: 30, bottom: 60, left: 60};

const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height + 60])
.attr("style", "max-width: 100%, height: auto, font: 10px sans-serif");

const parseDate = d3.timeParse("%d-%b-%y");

const dateData = data
.filter(d => d["Date"] && d.Metascore)
.map(d => {
const parsedDate = parseDate(d["Date"]);
const metascore = parseFloat(d.Metascore);
const platform = d.Platform;
const manufacturer = platformToManufacturer[platform];
return{
date: parsedDate,
metascore: metascore,
platform: platform,
manufacturer: manufacturer
};
})
.filter(d => !isNaN(d.date) && !isNaN(d.metascore) && d.manufacturer);

const dateExtent = d3.extent(dateData, d => d.date)
const timePadding = (dateExtent[1] - dateExtent[0]) * 0.05;
const x = d3.scaleTime()
.domain([new Date(dateExtent[0].getTime() - timePadding), new Date(dateExtent[1].getTime() + timePadding)])
.range([margin.left, width - margin.right]);

const y = d3.scaleLinear()
.domain([90, d3.max(dateData, d => d.metascore)])
.nice()
.range([height - margin.bottom, margin.top]);

svg.append("g")
.attr("stroke", "black")
.selectAll("circle")
.data(dateData)
.join("circle")
.attr("cx", d => x(d.date))
.attr("cy", d => y(d.metascore))
.attr("r", 5)
.attr("opacity", 0.85)
.attr("fill", d => manufacturerColor(d.manufacturer))
.append("title").text(d => `${d.platform} (${d.manufacturer}): ${d.metascore}`);

svg.append("g")
.attr("transform", `translate(0, ${height - margin.bottom})`)
.call(d3.axisBottom(x));

svg.append("g")
.attr("transform", `translate(${margin.left}, 0)`)
.call(d3.axisLeft(y));

svg.append("text")
.attr("x", width / 2)
.attr("y", margin.top / 2)
.attr("text-anchor", "middle")
.attr("font-size", "16px")
.attr("font-weight", "bold")
.text("Metascore Over Time (Colored by Manufacturer)");

const legendGroup = svg.append("g")
.attr("transform", `translate(${margin.left}, ${height - margin.bottom + 40})`);

[...new Set(
platformCounts
.map(d => d.manufacturer)
.filter(m => m !== undefined && m !== "Other")
)].forEach((manufacturer, i) => {
const group = legendGroup.append("g").attr("transform", `translate(${i * 120}, 0)`);
group.append("rect").attr("width", 20).attr("height", 20).attr("fill", manufacturerColor(manufacturer));

group.append("text")
.attr("x", 25)
.attr("y", 15)
.text(manufacturer)
.attr("font-size", "16px")
.attr("alignment-baseline", "middle");
});

return svg.node();

}
Insert cell
Insert cell
dendogramData = {
const root = {name: "Top 100 Games", children: [] };
const manufacturerMap = new Map();

data.forEach(d => {
const manufacturer = platformToManufacturer[d.Platform];
const platform = d.Platform;
const game = d.Name;

if(!manufacturerMap.has(manufacturer)){
manufacturerMap.set(manufacturer, new Map());
}
const platformMap = manufacturerMap.get(manufacturer);

if(!platformMap.has(platform))
{
platformMap.set(platform, []);
}
platformMap.get(platform).push({name: game});
});

manufacturerMap.forEach((platformMap, manufacturer) => {
const platformChildren = [];
platformMap.forEach((games, platform) => {
platformChildren.push({name: platform, children: games});
});
root.children.push({name: manufacturer, children: platformChildren});
});
return root;
}
Insert cell
Insert cell
dendogramChart = {
const width = 800;
const dx = 10;
const dy = width / 6;
const tree = d3.tree().nodeSize([dx, dy]);
const diagonal = d3.linkHorizontal().x(d => d.y).y(d => d.x);

const root = d3.hierarchy(dendogramData);
root.x0 = dy / 2;
root.y0 = 0;
tree(root);

let x0 = Infinity;
let x1 = -x0;
root.each(d => {
if(d.x > x1)
{
x1 = d.x;
}
if(d.x < x0){
x0 = d.x;
}
});

const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, x1 - x0 + dx * 2])
.style("font", "10px sans-serif")
.style("user-select", "none");

const g = svg.append("g").attr("transform", `translate(${dy / 3}, ${dx - x0})`);

const link = g.append("g")
.attr("fill", "none")
.attr("stroke", "#555")
.attr("stroke-opacity", 0.4)
.selectAll("path")
.data(root.links())
.join("path").attr("stroke", d => {
const topManufacturer = d.source.depth === 0 ? d.target.data.name : d.source.ancestors().find(a => a.depth === 1)?.data.name;
return manufacturerColor(topManufacturer);
})
.attr("stroke-width", 1.5)
.attr("d", diagonal);

const node = g.append("g")
.attr("stroke-linejoin", "round")
.attr("stroke-width", 1)
.selectAll("g")
.data(root.descendants())
.join("g")
.attr("transform", d => `translate(${d.y},${d.x})`)

node.append("circle")
.attr("fill", d => d.depth == 1 ? manufacturerColor(d.data.name) : "#999")
.attr("r", 2.5);

node.append("text")
.attr("dx", d => d.children ? -6 : 6)
.attr("dy", "0.31em")
.attr("text-anchor", d => d.children ? "end" : "start")
.text(d => d.data.name)
.clone(true)
.lower()
.attr("stroke", "white");

const legend = svg.append("g")
.attr("transform", `translate(10, 10)`);

manufacturerColor.domain()
.filter(m => m !== undefined && m !== "Other").forEach((manufacturer, i) => {
const group = legend.append("g")
.attr("transform", `translate(0, ${i * 20})`);

group.append("rect")
.attr("width", 15)
.attr("height", 15)
.attr("fill", manufacturerColor(manufacturer));

group.append("text")
.attr("x", 20)
.attr("y", 12)
.text(manufacturer)
.attr("font-size", "12px")
.attr("alignment-baseline", "middle");
});

return svg.node();
}
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