This is a dataset listing anonymized patients who received an MRI from the Kaggle dataset MRI and Alzheimers, which was original gathered from the Open Access Series of Imaging Studies (OASIS) project. This set actually includes two data files, a cross sectional set, including patients of diverse ages, and a longitudinal set of older patients which have been classified with as demented or non-demented. This block incluedes both datasets, however the summary statistics display are shown for only the longitudinal data set.
This visualization supports panning, zooming, and resize. You can zoom by using the mouse wheel, and pan by clicking and dragging within the graph area. Resizing works when using the popout window, but the graph enforces a minimum size, and will maintain its aspect ratio at larger sizes, based on the width of the browser window.
forked from:
xxxxxxxxxx
<html>
<head>
<meta charset="utf-8">
<title>MRI and Alzheimers</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body {
margin: 0px;
background-color: #000000;
}
body svg {
background-color: #000000;
}
.tick text {
fill: white;
font-size: 10pt;
font-family: sans-serif;
}
.tick line {
stroke: #3d3d3d;
}
.axis-label {
fill: white;
font-size: 16pt;
font-family: sans-serif;
}
.legend-label {
fill: white;
font-size: 12pt;
font-family: sans-serif;
}
.legend-text {
fill: white;
font-size: 8pt;
font-family: sans-serif;
}
.marker {
opacity: 0.75;
}
#marksG {
clip-path: url(#viewClip);
}
#zoomCatcher {
fill: transparent;
stroke: none;
}
</style>
</head>
<body>
<div id="chart">
<svg width="960" height="500">
<defs>
<clipPath id="viewClip">
<rect id="viewBox"/>
</clipPath>
</defs>
<g id="mainG">
<g id="xAxisG"/>
<g id="yAxisG"/>
<g id="clippingG" clip-path="url(#viewClip)">
<g id="marksG"/>
</g>
<g id="legendG"/>
</g>
<rect id="zoomCatcher"/>
</svg>
</div>
<script>
const xValue = d => d.Age;
const xLabel = 'Age';
const yValue = d => d.nWBV;
const yLabel = 'Brain Volume (normalized)';
const margin = { left: 60, right: 120, top: 90, bottom: 30 };
const xTicks = 8;
const yTicks = 5;
const vRatio = 500 / 960;
const colorValue = d => d["CDR"];
const colorLabel = "Dementia";
const sizeValue = d => d["Educ"];
const sizeLabel = "Education";
const chartDiv = document.getElementById("chart");
const svg = d3.select('svg');
const g = d3.select("#mainG");
const xAxisG = d3.select("#xAxisG");
const yAxisG = d3.select("#yAxisG");
const marksG = d3.select("#marksG");
const legendG = d3.select("#legendG");
const viewBox = d3.select("#viewBox");
const zoomCatcher = d3.select("#zoomCatcher");
const xScale = d3.scaleLinear().domain([9.9, 100]);
const yScale = d3.scaleLinear().domain([0.60, 0.90]);
const colorScale = d3.scaleOrdinal()
.domain(["Unassessed", "None", "Very Mild", "Mild", "Moderate"])
.range(["#01665e", "#5ab4ac", "#f6e8c3", "#d8b365", "#8c510a"]);
const sizeScale = d3.scaleOrdinal()
.domain(["Unknown", "< High School", "High School Grad", "Some College", "College Grad", "Beyond College"])
.range([50, 25, 50, 100, 150, 200]);
const xAxis = d3.axisTop()
.scale(xScale)
.ticks(xTicks)
.tickPadding(15);
const yAxis = d3.axisRight()
.scale(yScale)
.ticks(yTicks)
.tickPadding(15);
const xAxisText = xAxisG.append('text');
const yAxisText = yAxisG.append('text')
function drawLegend() {
legendG.append('rect')
.attr('x', -50)
.attr('y', -50)
.attr('width', 350)
.attr('height', 300)
.attr('fill', 'black')
.attr('opacity', 0.75);
legendG.append('line')
.attr('x1', -20)
.attr('y1', -12)
.attr('x2', 260)
.attr('y2', -12)
.attr('stroke', 'white');
const colorLegendG = legendG.append('g')
.attr('transform', `translate(160, 0)`);
const sizeLegendG = legendG.append('g')
.attr('transform', `translate(0, 0)`);
colorLegendG.append('text')
.attr('class', 'legend-label')
.attr('x', -2)
.attr('y', -18)
.text(colorLabel);
sizeLegendG.append('text')
.attr('class', 'legend-label')
.attr('x', -2)
.attr('y', -18)
.text(sizeLabel);
for(i in colorScale.range()) {
const label = colorScale.domain()[i];
const color = colorScale.range()[i];
const area = 150;
var item = colorLegendG.append('circle');
updateCircleMark(item, 10, i*18, area, color);
if(i == 0) { updateGhostMark(item, color); }
colorLegendG.append('text')
.attr('x', 25)
.attr('y', i*18 + 3)
.attr('class', 'legend-text')
.text(label);
}
for(i in sizeScale.range()) {
const label = sizeScale.domain()[i];
const area = sizeScale.range()[i];
const color = '#01665e';
var item1 = sizeLegendG.append('rect');
var item2 = sizeLegendG.append('circle');
updateSquareMark(item1, 10, i*18, area, color);
updateCircleMark(item2, 30, i*18, area, color);
if(i == 0) {
updateGhostMark(item1, color);
updateGhostMark(item2, color);
}
sizeLegendG.append('text')
.attr('x', 50)
.attr('y', i*18 + 3)
.attr('class', 'legend-text')
.text(label);
}
sizeLegendG
.append('text')
.attr('x', 5)
.attr('y', i*18 + 21)
.attr('class', 'legend-text')
.text('M');
sizeLegendG
.append('text')
.attr('x', 27)
.attr('y', i*18 + 21)
.attr('class', 'legend-text')
.text('F');
}
function updateCircleMark(item, cx, cy, area, color) {
const r = Math.sqrt(area / Math.PI);
return item
.attr('cx', cx)
.attr('cy', cy)
.attr('r', r)
.attr('fill', color);
}
function updateSquareMark(item, cx, cy, area, color) {
const r = Math.sqrt(area / 4);
return item
.attr('x', cx - r)
.attr('y', cy - r)
.attr('width', 2*r)
.attr('height', 2*r)
.attr('fill', color);
}
function updateGhostMark(item, color) {
return item
.attr('fill', 'None')
.attr('stroke', color)
.attr('stroke-width', 1.5);
}
// customize appearance of axis
function customAxis(g, axis) {
g.call(axis);
g.select(".domain").remove();
}
const row = d => {
d.Age = +d.Age;
d.nWBV = +d.nWBV;
const cdr = +d.CDR;
if(d.CDR == "") { d.CDR = "Unrated" }
else if(cdr == 0) { d.CDR = "None" }
else if(cdr == 0.5) { d.CDR = "Very Mild" }
else if(cdr == 1.0) { d.CDR = "Mild" }
else if(cdr == 2.0) { d.CDR = "Moderate" }
const educ = +d.Educ;
if(d.Educ == "") { d.Educ = "Unknown" }
else if(educ == 1) { d.Educ = "< High School" }
else if(educ == 2) { d.Educ = "High School" }
else if(educ == 3) { d.Educ = "Some College" }
else if(educ == 4) { d.Educ = "College Grad" }
else if(educ == 5) { d.Educ = "Beyond College" }
else { d.Educ = "Unknown" }
return d;
};
const zoom = d3.zoom()
.scaleExtent([1, 10]);
function redraw(data) {
const width = Math.max(800, chartDiv.clientWidth);
const height = width * vRatio;
svg
.attr("width", width)
.attr("height", height);
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const xoff = margin.left;
const yoff = margin.top;
zoom
.extent([[0, 0], [innerWidth, innerHeight]])
.translateExtent([[0, 0], [innerWidth, innerHeight]])
.on("zoom", zoomed);
g.attr('transform', `translate(${margin.left},${margin.top})`);
xAxisG.attr('transform', `translate(0, ${innerHeight})`);
legendG.attr('transform', `translate(20, ${innerHeight - 100})`);
xAxisText.attr('class', 'axis-label')
.attr('x', innerWidth / 2)
.attr('y', -(innerHeight + 50))
.text(xLabel);
yAxisText
.attr('class', 'axis-label')
.attr('x', innerHeight / 2)
.attr('y', -innerWidth - 70)
.attr('transform', `rotate(90)`)
.style('text-anchor', 'middle')
.text(yLabel);
xScale
.range([0, innerWidth]);
yScale
.range([innerHeight, 0]);
xAxis.tickSize(innerHeight);
yAxis.tickSize(innerWidth);
function updateMarkers(xScale, yScale) {
const squareMarkers = marksG.selectAll('rect.marker')
.data(data.filter(d => d["M/F"] == "M"));
const circleMarkers = marksG.selectAll('circle.marker')
.data(data.filter(d => d["M/F"] != "M"));
squareMarkers.enter()
.append('rect')
.attr('class', 'marker')
.merge(squareMarkers)
.each(function(d) {
var item = d3.select(this);
var cx = xScale(xValue(d));
var cy = yScale(yValue(d));
var area = sizeScale(sizeValue(d));
var color = colorScale(colorValue(d));
updateSquareMark(item, cx, cy, area, color);
if(sizeValue(d) == "Unknown") {
updateGhostMark(item, color);
}
});
circleMarkers.enter()
.append('circle')
.attr('class', 'marker')
.merge(circleMarkers)
.each(function(d) {
var item = d3.select(this);
var cx = xScale(xValue(d));
var cy = yScale(yValue(d));
var area = sizeScale(sizeValue(d));
var color = colorScale(colorValue(d));
updateCircleMark(item, cx, cy, area, color);
if(sizeValue(d) == "Unknown") {
updateGhostMark(item, color);
}
});
}
updateMarkers(xScale, yScale);
function zoomed() {
const t = d3.event.transform;
const zoomXscale = t.rescaleX(xScale);
const zoomYscale = t.rescaleY(yScale);
xAxisG.call(xAxis.scale(zoomXscale));
yAxisG.call(yAxis.scale(zoomYscale));
updateMarkers(zoomXscale, zoomYscale);
}
xAxisG.call(g => customAxis(g, xAxis));
yAxisG.call(g => customAxis(g, yAxis));
viewBox
.attr('x', 0)
.attr('y', 0)
.attr('width', innerWidth)
.attr('height', innerHeight);
zoomCatcher
.attr('transform', `translate(${margin.left}, ${margin.top})`)
.attr('width', innerWidth)
.attr('height', innerHeight)
.call(zoom);
}
drawLegend();
var theData = null;
d3.csv('oasis_cross-sectional.csv', row, data => {
theData = data;
redraw(data);
});
// Redraw based on the new size whenever the browser window is resized.
window.addEventListener("resize", function () {
if(theData != null) { redraw(theData); }
});
</script>
</body>
</html>
https://d3js.org/d3.v4.min.js