Public
Edited
May 1
Insert cell
Insert cell
data = d3.csvParse(await FileAttachment("tested.csv").text(), d3.autoType) //load in data
Insert cell
// Groups and sums up the number of survivors in each passenger class and sex demographic (female and male)
function processSurvivorData(){
const demographics = {
sex: {},
class: {}
}
data.forEach(person => {
if(person.Survived == 1){
//Class of the survivor
if(person.Pclass in demographics.class){
demographics.class[person.Pclass].size += 1;
}
else{
demographics.class[person.Pclass] = {classType: person.Pclass, size: 1}
}
//Sex of the survivor
if(person.Sex in demographics.sex){
demographics.sex[person.Sex].size += 1;
}
else{
demographics.sex[person.Sex] = {sex_type: person.Sex, size: 1}
}
}
})
return demographics;
}
//REFERENCE: https://observablehq.com/@k-dasu/simple-visualization-example
Insert cell
// imports d3.js library and sankey diagram plugins
d3 = require("d3@7", "d3-sankey@0.12")
Insert cell
/**
@param {number} age - age of passenger
@returns a string that assigns age group to passenger depending on their age
*/
function getAgeGroup(age){
if(age < 18) return "youth";
if(age < 65) return "adult";
if(age >= 65) return "senior";
}
Insert cell
// sums up survivors in groups of the same sex, class, and age group
function organizeSurvivorsData(){
const survivors = data.filter(d => d.Survived == 1)
const counts = {};
survivors.forEach(d => {
const sex = `Sex: ${d.Sex}`;
const pclass = `Class: ${d.Pclass}`;
const ageGroup = `Age: ${getAgeGroup(d.Age)}`;
const sexToClass = `${sex}→${pclass}`;
const classToAgeGroup = `${pclass}→${ageGroup}`;
if (!counts[sexToClass]) {
counts[sexToClass] = { source: sex, target: pclass, value: 0 };
}
if (!counts[classToAgeGroup]) {
counts[classToAgeGroup] = { source: pclass, target: ageGroup, value: 0 };
}
counts[sexToClass].value += 1;
counts[classToAgeGroup].value += 1;
});
return Object.values(counts);
}
/** REFERENCES:
1. https://observablehq.com/@k-dasu/simple-visualization-example
2. Used ChatGPT for syntax on const sexToClass = `${sex}→${pclass}` and const classToAgeGroup = `${pclass}→${ageGroup}`
*/
Insert cell
// sums up passengers by the port/location they embarked the Titanic and their corresponding survival status
function organizeByEmbarkedAndSurvival(){
const group = {};
data.forEach(person => {
const embarked = person.Embarked;
const survived = person.Survived;

const key = `${embarked}-${survived}`;
if(!group[key]){
group[key] = {
Embarked: embarked,
Survived: survived,
count: 0
};
}
group[key].count += 1;
});
return Object.values(group);
}
//REFERENCE: https://observablehq.com/@k-dasu/simple-visualization-example
Insert cell
survivors_data = organizeSurvivorsData()
Insert cell
Insert cell
// generate an alluvial diagram for sex, class, and age of Titanic survivors
{
const { sankey, sankeyLinkHorizontal } = d3;

// get the unique node names from source and target properties of objects with passenger info in survivors_data
const nodes = Array.from(new Set(survivors_data.flatMap(d => [d.source, d.target])))
.map(name => ({ name }));

//map index to node
const nodeMap = new Map(nodes.map((d, i) => [d.name, i]));

const links = survivors_data.map(d => ({
source: nodeMap.get(d.source),
target: nodeMap.get(d.target),
value: d.value
}));

const sankeyGenerator = sankey()
.nodeWidth(85)
.nodePadding(10)
.extent([[20, 1], [850, 400]]);

const { nodes: layoutNodes, links: layoutLinks } = sankeyGenerator({
nodes: nodes.map(d => Object.assign({}, d)),
links: links.map(d => Object.assign({}, d))
});

const svg = d3.create("svg")
.attr("width", 900)
.attr("height", 420);

// Links
svg.append("g")
.selectAll("path")
.data(layoutLinks)
.join("path")
.attr("d", sankeyLinkHorizontal())
.attr("stroke", "steelblue")
//stroke-width/flow-area size reflects the quantity
.attr("stroke-width", d => d.width)
.attr("fill", "none")
.attr("opacity", 0.6);

// Nodes
svg.append("g")
.selectAll("rect")
.data(layoutNodes)
.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", "#a9cee8");

// Labels
svg.append("g")
.selectAll("text")
.data(layoutNodes)
.join("text")
.attr("fill", "#313a54")
.attr("x", d => d.x0)
.attr("y", d => (d.y1 + d.y0) / 2)
.attr("text-anchor", "start")
.attr("dy", "0.35em")
.text(d => d.name);

return svg.node();
}
/** REFERENCES:
1. https://observablehq.com/@d3/sankey/2
2. ChatGPT on guidance adjusting features + understanding the code from https://observablehq.com/@d3/sankey/2
*/
Insert cell
Insert cell
Insert cell
// create stacked bar chart to show breakdown of survivors among each group of passengers from different embarking locations
Plot.plot({
y: { label: "Passenger Count" },
x: { label: "Embarked", domain: ["C", "Q", "S"], tickFormat:(d) =>{
const label = { //labels for x-axis categories---referenced ChatGPT
"C": "Cherbourg, FR",
"Q": "Queenstown, IE",
"S": "Southampton, UK"
};
return label[d];
}},
color: {
legend: true,
label: "Survived",
type: "ordinal",
scheme: "blues", //the stacked bars will be shades of blue
domain: ["Did Not Survive", "Survived"], //legend for the different colors
labelFormat: d => d
},
marks: [
Plot.barY(organizeByEmbarkedAndSurvival().map(d => ({
...d,
Survived: d.Survived === 1 ? "Survived" : "Did Not Survive" // If their survived status is 1, they survived. If 0, they didn't survive
})), {
x: "Embarked",
y: "count",
fill: "Survived" // Color by the text-based "Survived" status
}),
Plot.ruleY([0]) // Adds horizontal line at y = 0 ---referenced ChatGPT
]
})
//REFERENCE: https://observablehq.com/@observablehq/plot-stacked-bar-chart
Insert cell
Insert cell
// sums up Titanic passengers in groups by exact age
function countAgeDemographic(){
const ages = {};
data.forEach(person => {
if(person.Age != null){
if(person.Age in ages){
ages[person.Age].count +=1;
}
else{
ages[person.Age] = {age: person.Age, count: 1};
}
}
});
const sortedAges = Object.values(ages).sort((a, b) => a.age - b.age);
return sortedAges;
}
//REFERENCE: https://observablehq.com/@k-dasu/simple-visualization-example
Insert cell
Insert cell
// d3 scatterplot of passengers by frequency of age

Plot.plot({
marks: [
Plot.dot(countAgeDemographic(), {
x: "age", //exact age of passengers
y: "count", //number of passengers
fill: "#8cb1ed", //color of circle mark
r: 4, //radius of circle mark
stroke: "black", //border outline of each mark
}),
Plot.frame() // Square border around the plot
],
x: { label: "Age" },
y: { label: "Number of Passengers",
domain: [0, 20], // y-axis goes from 0 to 20
ticks: d3.range(0, 20, 1) // each tick increments by 1 from 0 to 20
}
});
/**REFERENCE:
1. https://observablehq.com/@observablehq/plot-scatterplot/2
2. ChatGPT for syntax to fix details like adding a border to the mark, controlling size of the mark, and changing the tick increments
*/
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