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 version of the visualization has been modified to correctly respond to resize events. However, it will not scale below a minimum size, and it continues to maintain its aspect ratio at larger sizes, based on the width of the browser window.
forked from curran's block: Data Table Summary
forked from dbeach24's block: MRI and Alzheimers Vis
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;
}
</style>
</head>
<body>
<div id="chart">
<svg width="960" height="500"/>
</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 = svg.append('g');
const xAxisG = g.append('g');
const yAxisG = g.append('g');
const marksG = g.append('g');
const legendG = g.append('g');
const xScale = d3.scaleLinear();
const yScale = d3.scaleLinear();
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');
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;
};
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;
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
.domain(d3.extent(data, xValue))
.range([0, innerWidth])
.nice(xTicks);
yScale
.domain(d3.extent(data, yValue))
.range([innerHeight, 0])
.nice(yTicks);
xAxis.tickSize(innerHeight);
yAxis.tickSize(innerWidth);
const squareMarkers = marksG.selectAll('rect')
.data(data.filter(d => d["M/F"] == "M"));
const circleMarkers = marksG.selectAll('circle')
.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);
}
})
xAxisG.call(g => customAxis(g, xAxis));
yAxisG.call(g => customAxis(g, yAxis));
}
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