Public
Edited
Apr 15
Fork of OG Charts
Insert cell
Insert cell
Insert cell
//import the raw data from a weblink
heart_data_raw = d3.csv(
"https://raw.githubusercontent.com/balleromair12/DataViz_finalproject/refs/heads/main/heart_2020_cleaned.csv",
d3.autoType
)
Insert cell
viewof selectedGender = Inputs.select(
["All", "Male", "Female"],
{ label: "Select Gender:", value: "All" }
)
Insert cell
viewof selectedRace = Inputs.select(
["All", "White", "Black", "Asian", "American Indian/Alaskan Native", "Other"],
{ label: "Select Race:", value: "All" }
)
Insert cell
viewof selectedAge = Inputs.select(
["All", "18-24", "25-29", "30-34", "35-39", "40-44", "45-49", "50-54", "55-59", "60-64", "65-69", "70-74", "75-79", "80 or older"],
{ label: "Select Age Category:", value: "All" }
)
Insert cell
filteredData = {
return heart_data_raw.filter(d =>
(selectedGender === "All" || d.Sex === selectedGender) &&
(selectedRace === "All" || d.Race === selectedRace) &&
(selectedAge === "All" || d.AgeCategory === selectedAge)
)
}
Insert cell
diseaseCounts = {
const diseases = ["HeartDisease", "Stroke", "Asthma", "KidneyDisease", "Diabetic", "SkinCancer"];
const total = filteredData.length;
return diseases.map(disease => {
const count = filteredData.filter(d => d[disease] === "Yes").length;
return {
disease,
count,
percent: total > 0 ? (count / total) * 100 : 0
};
});
}
Insert cell
chart = {

const title = `Prevalence of Diseases${
selectedGender !== "All" ? ` for ${selectedGender}` : ""
}${
selectedRace !== "All" ? `, ${selectedRace}` : ""
}${
selectedAge !== "All" ? `, aged ${selectedAge}` : ""
}`;

if (selectedGender === "All") {
// 🧠 Compute grouped data by gender
const groups = ["Male", "Female"];
const diseases = ["HeartDisease", "Stroke", "Asthma", "KidneyDisease", "Diabetic", "SkinCancer"];

const groupedCounts = groups.flatMap(group => {
const subset = heart_data_raw.filter(d =>
d.Sex === group &&
(selectedRace === "All" || d.Race === selectedRace) &&
(selectedAge === "All" || d.AgeCategory === selectedAge)
);
const total = subset.length;

return diseases.map(disease => ({
group,
disease,
percent: total > 0 ? subset.filter(d => d[disease] === "Yes").length / total * 100 : 0
}));
});

return Plot.plot({
title,
y: { label: "Prevalence (%)" },
x: { label: "Disease" },
color: { legend: true, label: "Gender" },
marks: [
Plot.barY(groupedCounts, {
x: "disease",
y: "percent",
fill: "group",
order: "descending", // keep Male on bottom in stack
stack: true,
tip: true
}),
Plot.ruleY([0])
]
});

} else {
// 👤 Show a standard (non-stacked) bar chart
return Plot.plot({
title,
y: { label: "Prevalence (%)" },
x: { label: "Disease", domain: diseaseCounts.map(d => d.disease) },
marks: [
Plot.barY(diseaseCounts, {
x: "disease",
y: "percent",
fill: "#4e79a7",
tip: true
}),
Plot.ruleY([0])
]
});
}
}

Insert cell
trackedDiseases = ["HeartDisease", "Stroke", "Asthma", "KidneyDisease", "Diabetic", "SkinCancer"]

Insert cell
diseaseTreeData = {

const filtered = heart_data_raw.filter(d =>
(selectedGender === "All" || d.Sex === selectedGender) &&
(selectedRace === "All" || d.Race === selectedRace) &&
(selectedAge === "All" || d.AgeCategory === selectedAge)
);

const root = { name: "All", count: 0, children: [] };

for (const person of filtered) {
const diseases = trackedDiseases.filter(disease => person[disease] === "Yes").sort();
if (diseases.length < 2) continue;

let current = root;
current.count++;

for (const disease of diseases) {
if (!current.children) current.children = [];
let child = current.children.find(c => c.name === disease);
if (!child) {
child = { name: disease, count: 0, children: [] };
current.children.push(child);
}
child.count++;
current = child;
}
}

return root;
}

Insert cell
diseaseTreeChart = {
const width = 800;
const dx = 50; // vertical spacing (increase to separate rows more)
const dy = 140; // horizontal spacing

const root = d3.hierarchy(diseaseTreeData);
const treeLayout = d3.tree().nodeSize([dx, dy]);
treeLayout(root);

// Compute tree height dynamically based on max Y and X coordinates
const x0 = d3.min(root.descendants(), d => d.x);
const x1 = d3.max(root.descendants(), d => d.x);
const y0 = d3.min(root.descendants(), d => d.y);
const y1 = d3.max(root.descendants(), d => d.y);

const svg = d3.create("svg")
.attr("viewBox", [y0 - 100, x0 - dx - 60, y1 - y0 + 200, x1 - x0 + 160]) // extra space above and below
.attr("preserveAspectRatio", "xMidYMid meet")
.style("font", "12px sans-serif")
.style("overflow", "visible")
.style("width", "100%")
.style("height", "auto");

const g = svg.append("g");

// Chart Title
g.append("text")
.attr("x", (y1 - y0) / 2 + y0)
.attr("y", x0 - dx - 30) // move higher above the topmost node
.attr("text-anchor", "middle")
.attr("font-size", "25px")
.attr("font-weight", "bold")
.text("Common Disease Co-Occurrence Tree");



// Links
g.append("g")
.selectAll("path")
.data(root.links())
.join("path")
.attr("fill", "none")
.attr("stroke", "#ccc")
.attr("stroke-width", 2)
.attr("d", d3.linkHorizontal()
.x(d => d.y)
.y(d => d.x)
);

// Nodes
const node = g.append("g")
.selectAll("g")
.data(root.descendants())
.join("g")
.attr("transform", d => `translate(${d.y},${d.x})`);

node.append("circle")
.attr("fill", "#4e79a7")
.attr("r", 5);

node.append("text")
.attr("dy", "0.31em")
.attr("x", d => d.children ? -10 : 10)
.attr("text-anchor", d => d.children ? "end" : "start")
.text(d => `${d.data.name} (${d.data.count})`)
.clone(true).lower()
.attr("stroke", "white");

return svg.node();
}

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