Public
Edited
Nov 21
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
return embed(spec, {
renderer: "svg", // Explicitly use SVG renderer
config: {
// Use system fonts
text: {font: "system-ui, -apple-system, sans-serif"},
style: {
"guide-label": {font: "system-ui, -apple-system, sans-serif"},
"guide-title": {font: "system-ui, -apple-system, sans-serif"}
}
}
});
}
Insert cell
visWidth = width - 160
Insert cell
dataFilteredGender = data.filter(d => d.Gender === selectedGender)
Insert cell
stats = {
const currentAgeRow = dataFilteredGender.find(d => d.Year === selectedYear && d.Age === currentAge);
const meanAge = currentAge + currentAgeRow['e(x)'];
const survivalProbs = processedData.filter(d => d.Type === "Survival Probability");
const medianAge = survivalProbs.find(d => d.Probability <= 0.5)?.Age;
return {
meanAge,
medianAge,
currentAge
};
}
Insert cell
processedData = {
// Filter for selected year and age
const yearData = dataFilteredGender.filter(d => d.Year === selectedYear && d.Age >= currentAge);
// Calculate survival probabilities
let survivalProb = 1.0;
const probabilities = [];
yearData.forEach(row => {
probabilities.push({
Age: row.Age,
Probability: survivalProb,
Type: "Survival Probability"
});
survivalProb *= (1 - row['q(x)']);
});
// Calculate death probability density
const deathProbs = probabilities.map(d => d.Probability);
const ages = probabilities.map(d => d.Age);
const density = gradient(deathProbs.map(p => -p), ages);
const deathProbabilities = ages.map((age, i) => ({
Age: age,
Probability: density[i],
Type: "Death Probability Density"
}));
// Combine both sets of data
return [...probabilities, ...deathProbabilities];
}
Insert cell
// Function to calculate gradient
function gradient(values, domain) {
// Calculate central differences
const deltas = Array(values.length);
// Forward difference for first point
deltas[0] = (values[1] - values[0]) / (domain[1] - domain[0]);
// Central differences for middle points
for (let i = 1; i < values.length - 1; i++) {
deltas[i] = (values[i + 1] - values[i - 1]) / (domain[i + 1] - domain[i - 1]);
}
// Backward difference for last point
const last = values.length - 1;
deltas[last] = (values[last] - values[last - 1]) / (domain[last] - domain[last - 1]);
return deltas;
}
Insert cell
genders = {
return [...new Set(data.map(d => d.Gender))].sort();
}
Insert cell
years = {
return [...new Set(dataFilteredGender.map(d => d.Year))].sort();
}
Insert cell
visWidthMin = Math.max(visWidth, 500)
Insert cell
spec = {
const baseSpec = {
"width": visWidthMin,
"height": .4*visWidthMin,
"data": {"values": processedData},
"encoding": {
"x": {"field": "Age", "type": "quantitative", "title": "Age"}
},
"config": {
"legend": {"orient": "top", "layout": {"top": {"anchor": "middle"}}, "title": null}
},
"layer": [
{
// Survival probability line
"transform": [{"filter": "datum.Type === 'Survival Probability'"}],
"mark": "line",
"encoding": {
"y": {
"field": "Probability",
"type": "quantitative",
"title": "Survival Probability",
"axis": {"format": "%"}
},
"color": {
"field": "Type",
"type": "nominal",
"scale": {
"domain": ["Survival Probability", "Death Probability Density"],
"range": ["blue", "red"]
}
},
"tooltip": [
{"field": "Age", "type": "quantitative", "title": "Age"},
{"field": "Probability", "type": "quantitative", "title": "Probability", "format": ".1%"},
{"field": "Type", "type": "nominal"}
]
}
},
{
// Death probability density
"transform": [{"filter": "datum.Type === 'Death Probability Density'"}],
"mark": {"type": "area", "opacity": 0.3},
"encoding": {
"y": {
"field": "Probability",
"type": "quantitative",
"title": "Death Probability Density",
"axis": {"format": "%"},
"scale": {"zero": true}
},
"color": {"field": "Type", "type": "nominal"},
"tooltip": [
{"field": "Age", "type": "quantitative", "title": "Age"},
{"field": "Probability", "type": "quantitative", "title": "Probability", "format": ".2%"},
{"field": "Type", "type": "nominal"}
]
}
},
{
// Mean age line
"mark": {"type": "rule", "color": "#00AA00", "size": 2},
"encoding": {
"x": {"datum": stats.meanAge}
}
},
{
// Mean age label
"mark": {"type": "text", "align": "left", "dx": 5, "fill": "#00AA00"},
"encoding": {
"x": {"datum": stats.meanAge},
"y": {"value": 20}, // Position from top
"text": {"value": `Mean Age: ${stats.meanAge.toFixed(1)}`}
}
},
{
// Median age line
"mark": {"type": "rule", "color": "#FF4500", "size": 2},
"encoding": {
"x": {"datum": stats.medianAge}
}
},
{
// Median age label
"mark": {"type": "text", "align": "left", "dx": 5, "fill": "#FF4500"},
"encoding": {
"x": {"datum": stats.medianAge},
"y": {"value": 40}, // Position from top
"text": {"value": `Median Age: ${stats.medianAge.toFixed(1)}`}
}
}
],
"resolve": {
"scale": {
"y": "independent" // Make y-scales independent
}
}
};
return baseSpec;
}
Insert cell
data = FileAttachment("all.json").json()
Insert cell
embed = require("vega-embed@6")
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