Public
Edited
May 1
Insert cell
Insert cell
Insert cell
Insert cell
titanicDataSet = d3.csv(await FileAttachment("tested.csv").url(), d3.autoType)
Insert cell
// Want to set up a system for defining age ranges to be used later in the sankey
ageRange = age => {
if (age == null)
return "Age Not Known";
if(age < 13)
return "Child";
if(age < 18)
return "Teenager";
if (age < 45)
return "Adult";
if (age < 60)
return "Middle-Age";
return "Senior";
}
Insert cell
Insert cell
// Now process the titanic dataset
processData = {
const sankeyMap = new Map();
const totalMap = new Map();
let nodeID = 0;

const getNodeId = name => {
if (!sankeyMap.has(name)) {
sankeyMap.set(name, { id: nodeID++, name });
}
return sankeyMap.get(name).id;
};

titanicDataSet.forEach(d => {
const pClass = `Pclass ${d.Pclass}`;
const sex = d.Sex;
const age = ageRange(d.Age);
const survivorStatus = d.Survived === 1 ? "Survived" : "Died";
const fullSankeyLink = [pClass, sex, age, survivorStatus];
for (let i = 0; i < fullSankeyLink.length - 1; i++) {
const source = getNodeId(fullSankeyLink[i]);
const target = getNodeId(fullSankeyLink[i + 1]);
const key = `${source}->${target}`;
totalMap.set(key, (totalMap.get(key) || 0) + 1);
}
});

const nodes = Array.from(sankeyMap.values());
const links = Array.from(totalMap.entries()).map(([key, value]) => {
const [source, target] = key.split("->").map(Number);
return { source, target, value };
});

return { nodes, links };
}

Insert cell
Insert cell
height = 600
Insert cell
width = 960
Insert cell
d3sankey = require("d3-sankey")
Insert cell
createDiagram = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
const { nodes, links } = processData;
const sankey = d3sankey.sankey()
.nodeWidth(15)
.nodePadding(10)
.extent([[1, 1], [width - 1, height - 6]]);
const graph = sankey({
nodes: nodes.map(d => Object.assign({}, d)),
links: links.map(d => Object.assign({}, d))
});
const color = d3.scaleOrdinal(d3.schemeCategory10);
svg.append("g")
.selectAll("rect")
.data(graph.nodes)
.join("rect")
.attr("x", d => d.x0)
.attr("y", d => d.y0)
.attr("height", d => d.y1 - d.y0)
.attr("width", d => d.x1 - d.x0)
.attr("fill", d => color(d.name))
.append("title")
.text(d => `${d.name}\n${d.value}`);

svg.append("g")
.attr("fill", "none")
.attr("stroke-opacity", 0.5)
.selectAll("path")
.data(graph.links)
.join("path")
.attr("d", d3sankey.sankeyLinkHorizontal())
.attr("stroke", d => color(d.source.name))
.attr("stroke-width", d => Math.max(1, d.width))
.append("title")
.text(d => `${d.source.name} → ${d.target.name}\n${d.value}`);

svg.append("g")
.style("font", "10px sans-serif")
.selectAll("text")
.data(graph.nodes)
.join("text")
.attr("x", d => d.x0 < width / 2 ? d.x1 + 6 : d.x0 - 6)
.attr("y", d => (d.y1 + d.y0) / 2)
.attr("dy", "0.35em")
.attr("text-anchor", d => d.x0 < width / 2 ? "start" : "end")
.text(d => d.name);

return svg.node();
}


Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
// Define all the chart dimensions
const chartWidth = 700;
const chartHeight = 450;
const paddingTop = 30;
const paddingRight = 25;
const paddingBottom = 40;
const paddingLeft = 50;

// Set the scales for the X and Y axes
const xScale = d3.scaleLinear()
.domain(d3.extent(titanicDataSet, d => d.Age)) // X-axis will be based on Age
.nice()
.range([paddingLeft, chartWidth - paddingRight]);

const yScale = d3.scaleLinear()
.domain(d3.extent(titanicDataSet, d => d.Fare)) // Y-axis will be based on Fare
.nice()
.range([chartHeight - paddingBottom, paddingTop]);

// Create the SVG container for the chart
const svgContainer = d3.create("svg")
.attr("width", chartWidth)
.attr("height", chartHeight)
.attr("viewBox", `0 0 ${chartWidth} ${chartHeight}`)
.style("max-width", "100%")
.style("height", "auto");

// Add the X-axis at the bottom of the chart
svgContainer.append("g")
.attr("transform", `translate(0,${chartHeight - paddingBottom})`)
.call(d3.axisBottom(xScale));
// Add the Y-axis on the left side of the chart
svgContainer.append("g")
.attr("transform", `translate(${paddingLeft},0)`)
.call(d3.axisLeft(yScale));

// Add X-axis label (Age)
svgContainer.append("text")
.attr("x", chartWidth / 2)
.attr("y", chartHeight - paddingBottom + 30) // Positioning below the X-axis
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.attr("fill", "#000")
.text("Age of Passenger");

// Add Y-axis label (Fare)
svgContainer.append("text")
.attr("x", -chartHeight / 2)
.attr("y", paddingLeft - 35) // Positioning to the left of the Y-axis
.attr("transform", "rotate(-90)") // Rotate to make it vertical
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.attr("fill", "#000")
.text("Fare of the Ticket");

// Add customized circles for each data point in scatterplot
svgContainer.append("g")
.selectAll("circle")
.data(titanicDataSet)
.join("circle")
.filter(d => d.Age && d.Fare) // Only use rows where both Age and Fare are there
.attr("cx", d => xScale(d.Age)) // X position based on Age
.attr("cy", d => yScale(d.Fare)) // Y position based on Fare
.attr("r", 4) // Radius of the circles
// Color based on survival status (green for survived, red for not)
.style("fill", d => d.Survived ? "#2ca02c" : "#d62728")
// Slight transparency for better visualization of overlapping points
.style("opacity", 0.7);

// Return the SVG container to render the chart
return svgContainer.node();
}

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof pieChart = {
// Set up the dimensions of the pie chart
const width = 500, height = 400, radius = Math.min(width, height) / 2;
const filtered = titanicDataSet.filter(d => d.Embarked);
const total = filtered.length;
// Count how many passengers boarded at each port ('Embarked')
const data = d3.rollup(filtered, v => v.length, d => d.Embarked);
const entries = Array.from(data.entries()).map(([key, value]) => ({
key,
value,
percent: ((value / total) * 100).toFixed(1)
}));

// Generate the pie chart arcs based on the data
const arcs = d3.pie().value(d => d.value)(entries);
// Define the arc shape for each pie slice
const arc = d3.arc().innerRadius(0).outerRadius(radius - 10);
const color = d3.scaleOrdinal()
.domain(entries.map(d => d.key))
.range(d3.schemeCategory10);

// Create the SVG container for the chart
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height]);

// Append the pie slices (paths) to the SVG container
svg.append("g")
.selectAll("path")
.data(arcs)
.join("path")
.attr("d", arc)
.attr("fill", d => color(d.data.key))
.append("title")
.text(d => `${d.data.key}: ${d.data.value} passengers (${d.data.percent}%)`);

// Add the labels (text) inside each of the segments of the slices
svg.append("g")
.selectAll("text")
.data(arcs)
.join("text")
.attr("transform", d => `translate(${arc.centroid(d)})`)
.attr("text-anchor", "middle")
.attr("dy", "0.35em")
.style("font-size", "11px")
.text(d => `${d.data.key}: ${d.data.value} (${d.data.percent}%)`);

return svg.node();
}

Insert cell
Insert cell
Insert cell
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