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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more