xxxxxxxxxx
<html lang="en">
<head>
<meta charset="utf-8">
<title>SCCA Survival Analysis</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style type="text/css">
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
stroke: #000;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
#title {
margin: auto;
font-family: sans-serif;
color: black;
stroke: black;
stroke-width: 1px;
text-align: center;
}
#survivalAllStages {
margin-top: 50px;
margin-left: auto;
margin-right: auto;
width: 960px;
height: 600px;
}
#stg1ConfIntBands {
margin: auto;
width: 960px;
height: 600px;
}
#stg2ConfIntBands {
margin: auto;
width: 960px;
height: 600px;
}
#stg3ConfIntBands {
margin: auto;
width: 960px;
height: 600px;
}
#stg4ConfIntBands {
margin: auto;
width: 960px;
height: 600px;
}
.selector {
margin: auto;
display: inline-block;
}
.button {
margin-top: 20px;
margin-left: auto;
margin-right: auto;
}
.container
{
text-align:center;
}
.centerwrapper
{
margin: auto;
}
#disease {
display: inline-block;
}
#race {
display: inline-block;
}
#sex {
display: inline-block;
}
#age {
display: inline-block;
}
#run {
display: inline-block;
}
#stg1CenterLine {
fill: none;
stroke: #1f77b4;
stroke-width: 1.5px;
}
#stg1UpperLine {
fill: none;
stroke: #1f77b4;
stroke-width: 1.5px;
stroke-dasharray: 2;
}
#stg1LowerLine {
fill: none;
stroke: #1f77b4;
stroke-width: 1.5px;
stroke-dasharray: 2;
}
#stg1Bands {
fill: #1f77b4;
fill-opacity: 0.3;
stroke: none;
}
#stg2CenterLine {
fill: none;
stroke: #ff7f0e;
stroke-width: 1.5px;
}
#stg2UpperLine {
fill: none;
stroke: #ff7f0e;
stroke-width: 1.5px;
stroke-dasharray: 2;
}
#stg2LowerLine {
fill: none;
stroke: #ff7f0e;
stroke-width: 1.5px;
stroke-dasharray: 2;
}
#stg2Bands {
fill: #ff7f0e;
fill-opacity: 0.3;
stroke: none;
}
#stg3CenterLine {
fill: none;
stroke: #2ca02c;
stroke-width: 1.5px;
}
#stg3UpperLine {
fill: none;
stroke: #2ca02c;
stroke-width: 1.5px;
stroke-dasharray: 2;
}
#stg3LowerLine {
fill: none;
stroke: #2ca02c;
stroke-width: 1.5px;
stroke-dasharray: 2;
}
#stg3Bands {
fill: #2ca02c;
fill-opacity: 0.3;
stroke: none;
}
#stg4CenterLine {
fill: none;
stroke: #d62728;
stroke-width: 1.5px;
}
#stg4UpperLine {
fill: none;
stroke: #d62728;
stroke-width: 1.5px;
stroke-dasharray: 2;
}
#stg4LowerLine {
fill: none;
stroke: #d62728;
stroke-width: 1.5px;
stroke-dasharray: 2;
}
#stg4Bands {
fill: #d62728;
fill-opacity: 0.3;
stroke: none;
}
</style>
</head>
<body>
<div id="title">
<h1>SCCA Survival Analysis</h1>
</div>
<div class = "container">
<div class="centerwrapper">
<div class='selector'>
<select id='disease'>
<option value=''>Select a disease:</option>
<option value='Breast'>Breast</option>
<option value='lungandBronchus'>Lung & Bronchus</option>
<option value='pancreas'>Pancreas</option>
</select>
</div>
<div class='selector'>
<select id='race'>
<option value=''>Select a race:</option>
<option value='all'>All</option>
<option value='white'>White</option>
<option value='other'>Other</option>
</select>
</div>
<div class='selector'>
<select id='sex'>
<option value=''>Select a gender:</option>
<option value='all'>All</option>
<option value='female'>Female</option>
<option value='male'>Male</option>
</select>
</div>
<div class='selector'>
<select id='age'>
<option value=''>Select an age group:</option>
<option value='all'>All</option>
<option value='18-64'>18 - 64</option>
<option value='18-90'>18 - 90</option>
<option value='over_65'>Over 65</option>
</select>
</div>
<div class='button'>
<div id='run'>
<button>Run Analysis</button>
</div>
</div>
</div>
</div>
<div id='survivalAllStages'></div>
<div id='stg1ConfIntBands'></div>
<div id='stg2ConfIntBands'></div>
<div id='stg3ConfIntBands'></div>
<div id='stg4ConfIntBands'></div>
</body>
</html>
<!-- D3 Survival Analysis Curves -->
<script>
var margin = {
top: 20,
right: 80,
bottom: 40,
left: 50},
width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scaleLinear()
.domain([0, 10])
.range([0, width]);
var y = d3.scaleLinear()
.domain([0, 1])
.range([height, 0]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
var xAxis = d3.axisBottom()
.scale(x)
.tickValues([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
var yAxis = d3.axisLeft()
.scale(y);
var line = d3.line()
.x(function(d) {
return x(d.years);
})
.y(function(d) {
return y(d.survivalPct);
});
var stg1BandArea = d3.area()
.x(function(d) { return x(d.years); })
.y0(function(d) { return y(d.stage1Lower); })
.y1(function(d) { return y(d.stage1Upper)});
var stg2BandArea = d3.area()
.x(function(d) { return x(d.years); })
.y0(function(d) { return y(d.stage2Lower); })
.y1(function(d) { return y(d.stage2Upper)});
var stg3BandArea = d3.area()
.x(function(d) { return x(d.years); })
.y0(function(d) { return y(d.stage3Lower); })
.y1(function(d) { return y(d.stage3Upper)});
var stg4BandArea = d3.area()
.x(function(d) { return x(d.years); })
.y0(function(d) { return y(d.stage4Lower); })
.y1(function(d) { return y(d.stage4Upper)});
// ~~~~~~~~~~~~~~~~~~~~~~~
// SVG for all stages plot
// ~~~~~~~~~~~~~~~~~~~~~~~
var svg = d3.select("#survivalAllStages")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// plot title
svg.append("text")
.attr("transform", "translate(" + width / 2 + ", 0)")
.attr("text-anchor", "middle")
.text("Survival Rate for All Stages")
.style("font-size", 18)
.style("font-family", "sans-serif");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// label for x axis
svg.append("text")
.attr("transform", "translate(" + width / 2 + ", " + (height + margin.top + 12) + ")")
.attr("text-anchor", "middle")
.text("Years Since Diagnosis")
.style("font-size", 14)
.style("font-family", "sans-serif");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end");
// label for y axis
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Survival Rate")
.style("font-size", 14)
.style("font-family", "sans-serif");
// ~~~~~~~~~~~~~~~~~~~~~~~
// SVG for Stage 1 Bands
// ~~~~~~~~~~~~~~~~~~~~~~~
var svgS1 = d3.select("#stg1ConfIntBands")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// plot title
svgS1.append("text")
.attr("transform", "translate(" + width / 2 + ", 0)")
.attr("text-anchor", "middle")
.text("Stage 1 Survival Rate with 95% Confidence Interval")
.style("font-size", 18)
.style("font-family", "sans-serif");
svgS1.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// label for x axis
svgS1.append("text")
.attr("transform", "translate(" + width / 2 + ", " + (height + margin.top + 12) + ")")
.attr("text-anchor", "middle")
.text("Years Since Diagnosis")
.style("font-size", 14)
.style("font-family", "sans-serif");
svgS1.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end");
// label for y axis
svgS1.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Survival Rate")
.style("font-size", 14)
.style("font-family", "sans-serif");
// ~~~~~~~~~~~~~~~~~~~~~~~
// SVG for Stage 2 Bands
// ~~~~~~~~~~~~~~~~~~~~~~~
var svgS2 = d3.select("#stg2ConfIntBands")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// plot title
svgS2.append("text")
.attr("transform", "translate(" + width / 2 + ", 0)")
.attr("text-anchor", "middle")
.text("Stage 2 Survival Rate with 95% Confidence Interval")
.style("font-size", 18)
.style("font-family", "sans-serif");
svgS2.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// label for x axis
svgS2.append("text")
.attr("transform", "translate(" + width / 2 + ", " + (height + margin.top + 12) + ")")
.attr("text-anchor", "middle")
.text("Years Since Diagnosis")
.style("font-size", 14)
.style("font-family", "sans-serif");
svgS2.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end");
// label for y axis
svgS2.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Survival Rate")
.style("font-size", 14)
.style("font-family", "sans-serif");
// ~~~~~~~~~~~~~~~~~~~~~~~
// SVG for Stage 3 Bands
// ~~~~~~~~~~~~~~~~~~~~~~~
var svgS3 = d3.select("#stg3ConfIntBands")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// plot title
svgS3.append("text")
.attr("transform", "translate(" + width / 2 + ", 0)")
.attr("text-anchor", "middle")
.text("Stage 3 Survival Rate with 95% Confidence Interval")
.style("font-size", 18)
.style("font-family", "sans-serif");
svgS3.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// label for x axis
svgS3.append("text")
.attr("transform", "translate(" + width / 2 + ", " + (height + margin.top + 12) + ")")
.attr("text-anchor", "middle")
.text("Years Since Diagnosis")
.style("font-size", 14)
.style("font-family", "sans-serif");
svgS3.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end");
// label for y axis
svgS3.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Survival Rate")
.style("font-size", 14)
.style("font-family", "sans-serif");
// ~~~~~~~~~~~~~~~~~~~~~~~
// SVG for Stage 4 Bands
// ~~~~~~~~~~~~~~~~~~~~~~~
var svgS4 = d3.select("#stg4ConfIntBands")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// plot title
svgS4.append("text")
.attr("transform", "translate(" + width / 2 + ", 0)")
.attr("text-anchor", "middle")
.text("Stage 4 Survival Rate with 95% Confidence Interval")
.style("font-size", 18)
.style("font-family", "sans-serif");
svgS4.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// label for x axis
svgS4.append("text")
.attr("transform", "translate(" + width / 2 + ", " + (height + margin.top + 12) + ")")
.attr("text-anchor", "middle")
.text("Years Since Diagnosis")
.style("font-size", 14)
.style("font-family", "sans-serif");
svgS4.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end");
// label for y axis
svgS4.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Survival Rate")
.style("font-size", 14)
.style("font-family", "sans-serif");
// row conversion functions
var rowConverter = function(d) {
return {
years: parseFloat(d['years']),
stg1Samples: parseInt(d['Stage_1_sample_size']),
stage1: parseFloat(d['Stage_1']),
stg2Samples: parseInt(d['Stage_2_sample_size']),
stage2: parseFloat(d['Stage_2']),
stg3Samples: parseInt(d['Stage_3_sample_size']),
stage3: parseFloat(d['Stage_3']),
stg4Samples: parseInt(d['Stage_4_sample_size']),
stage4: parseFloat(d['Stage_4'])
}
};
var rowConverterStg1 = function(d) {
return {
samples: parseInt(d['Stage_1_sample_size']),
years: parseFloat(d['years']),
stage1: parseFloat(d['Stage_1']),
stage1Upper: parseFloat(d['Stage_1_upper']),
stage1Lower: parseFloat(d['Stage_1_lower'])
}
};
var rowConverterStg2 = function(d) {
return {
samples: parseInt(d['Stage_2_sample_size']),
years: parseFloat(d['years']),
stage2: parseFloat(d['Stage_2']),
stage2Upper: parseFloat(d['Stage_2_upper']),
stage2Lower: parseFloat(d['Stage_2_lower'])
}
};
var rowConverterStg3 = function(d) {
return {
samples: parseInt(d['Stage_3_sample_size']),
years: parseFloat(d['years']),
stage3: parseFloat(d['Stage_3']),
stage3Upper: parseFloat(d['Stage_3_upper']),
stage3Lower: parseFloat(d['Stage_3_lower'])
}
};
var rowConverterStg4 = function(d) {
return {
samples: parseInt(d['Stage_4_sample_size']),
years: parseFloat(d['years']),
stage4: parseFloat(d['Stage_4']),
stage4Upper: parseFloat(d['Stage_4_upper']),
stage4Lower: parseFloat(d['Stage_4_lower'])
}
};
d3.select("#run")
.on("click", function() {
svg.selectAll(".line").remove();
svg.selectAll(".stage").remove();
svg.selectAll(".text").remove();
svg.selectAll(".circle").remove();
svg.selectAll(".mouse-per-line").remove();
// construct the filename to open from the selector input
var diseaseSect = document.getElementById("disease");
var disease = diseaseSect.options[diseaseSect.selectedIndex].value;
var raceSect = document.getElementById("race");
var race = raceSect.options[raceSect.selectedIndex].value;
var sexSect = document.getElementById("sex");
var sex = sexSect.options[sexSect.selectedIndex].value;
var ageSect = document.getElementById("age");
var age = ageSect.options[ageSect.selectedIndex].value;
var filename = disease + "_race_" + race + "_sex_" + sex + "_age_" + age + ".csv"
// ~~~~~~~~~~~~~~~~~~~~
// Draw All Stages plot
// ~~~~~~~~~~~~~~~~~~~~
d3.csv(filename, rowConverter, function(data) {
// sample size
var sampleSize = data[0]['stg1Samples'] + data[0]['stg2Samples'] + data[0]['stg3Samples'] + data[0]['stg4Samples'];
color.domain(d3.keys(data[0]).filter(function(key) {
return key !== "years" && key !== "stg1Samples" && key !== "stg2Samples" && key !== "stg3Samples" && key !== "stg3Samples" && key !== "stg4Samples";
}));
var stages = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
years: d.years,
survivalPct: +d[name]
};
})
};
});
var legend = svg.selectAll(".legend")
.data(stages)
.enter()
.append('g')
.attr('class', 'legend');
legend.append('rect')
.attr('x', 20)
.attr('y', function(d, i) {
return i * 20 + 350;
})
.attr('width', 10)
.attr('height', 10)
.style('fill', function(d) {
console.log(color(d.name));
return color(d.name);
});
legend.append('text')
.attr('x', 35)
.attr('y', function(d, i) {
return (i * 20) + 9 + 350;
})
.text(function(d) {
return d.name;
});
var stage = svg.selectAll(".stage")
.data(stages)
.enter().append("g")
.attr("class", "stage");
stage.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.values);
})
.style("stroke", function(d) {
return color(d.name);
});
var mouseG = svg.append("g")
.attr("class", "mouse-over-effects");
var lines = document.getElementsByClassName('line');
var mousePerLine = mouseG.selectAll('.mouse-per-line')
.data(stages)
.enter()
.append("g")
.attr("class", "mouse-per-line");
mousePerLine.append("circle")
.attr("r", 7)
.style("stroke", function(d) {
return color(d.name);
})
.style("fill", "none")
.style("stroke-width", "1px")
.style("opacity", "0");
mousePerLine.append("text")
.attr("transform", "translate(10,3)");
mouseG.append('svg:rect') // append a rect to catch mouse movements on canvas
.attr('width', width) // can't catch mouse events on a g element
.attr('height', height)
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseout', function() { // on mouse out hide line, circles and text
d3.select(".mouse-line")
.style("opacity", "0");
d3.selectAll(".mouse-per-line circle")
.style("opacity", "0");
d3.selectAll(".mouse-per-line text")
.style("opacity", "0");
})
.on('mouseover', function() { // on mouse in show line, circles and text
d3.select(".mouse-line")
.style("opacity", "1");
d3.selectAll(".mouse-per-line circle")
.style("opacity", "1");
d3.selectAll(".mouse-per-line text")
.style("opacity", "1");
})
.on('mousemove', function() { // mouse moving over canvas
var mouse = d3.mouse(this);
d3.select(".mouse-line")
.attr("d", function() {
var d = "M" + mouse[0] + "," + height;
d += " " + mouse[0] + "," + 0;
return d;
});
d3.selectAll(".mouse-per-line")
.attr("transform", function(d, i) {
var xDate = x.invert(mouse[0]),
bisect = d3.bisector(function(d) { return d.date; }).right;
idx = bisect(d.values, xDate);
var beginning = 0,
end = lines[i].getTotalLength(),
target = null;
while (true){
target = Math.floor((beginning + end) / 2);
pos = lines[i].getPointAtLength(target);
if ((target === end || target === beginning) && pos.x !== mouse[0]) {
break;
}
if (pos.x > mouse[0]) end = target;
else if (pos.x < mouse[0]) beginning = target;
else break; //position found
}
d3.select(this).select('text')
.text(y.invert(pos.y).toFixed(2));
return "translate(" + mouse[0] + "," + pos.y +")";
});
});
/*
// write sample size
svg.append("text")
.attr("x", x(1))
.attr("y", y(0.1))
.attr("class", "text")
.style("font-family", "sans-serif")
.style("font-size", 14)
.text("Sample Size: ", +sampleSize);
*/
});
// ~~~~~~~~~~~~~~~~~~~~
// Stage 1 Band Plot
// ~~~~~~~~~~~~~~~~~~~~
svgS1.selectAll(".line").remove();
svgS1.selectAll("#stg1Bands").remove();
svgS1.selectAll(".stage").remove();
svgS1.selectAll(".text").remove();
svgS1.selectAll(".circle").remove();
svgS1.selectAll(".mouse-per-line").remove();
d3.csv(filename, rowConverterStg1, function(data) {
// sample size
var sampleSize = data[0]['samples'];
color.domain(d3.keys(data[0]).filter(function(key) {
return key !== "years";
}));
var stages = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
years: d.years,
survivalPct: +d[name]
};
})
};
});
// append stages data
var stage1 = svgS1.selectAll(".stage")
.data(stages)
.enter().append("g")
.attr("class", "stage");
// draw area between lines to be shaded
svgS1.append("path")
.datum(data)
.attr("id", "stg1Bands")
.attr("d", stg1BandArea );
// draw lines (central, upper and lower)
stage1.append("path")
.attr("class", "line")
.attr("id", function(d) {
if (d.name == "stage1") {
return "stg1CenterLine";
} else if (d.name == "stage1Upper") {
return "stg1UpperLine";
} else if (d.name == "stage1Lower") {
return "stg1LowerLine";
}})
.attr("d", function(d) {
return line(d.values);
});
var mouseG1 = svgS1.append("g")
.attr("class", "mouse-over-effects");
var lines = document.getElementsByClassName('line');
var mousePerLine = mouseG1.selectAll('.mouse-per-line')
.data(stages)
.enter()
.append("g")
.attr("class", "mouse-per-line");
mousePerLine.append("circle")
.attr("r", 7)
.style("stroke", function(d) {
return "#1f77b4";
})
.style("fill", "none")
.style("stroke-width", "1px")
.style("opacity", "0");
mousePerLine.append("text")
.attr("transform", "translate(10,3)");
mouseG1.append('svg:rect') // append a rect to catch mouse movements on canvas
.attr('width', width) // can't catch mouse events on a g element
.attr('height', height)
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseout', function() { // on mouse out hide line, circles and text
d3.select(".mouse-line")
.style("opacity", "0");
d3.selectAll(".mouse-per-line circle")
.style("opacity", "0");
d3.selectAll(".mouse-per-line text")
.style("opacity", "0");
})
.on('mouseover', function() { // on mouse in show line, circles and text
d3.select(".mouse-line")
.style("opacity", "1");
d3.selectAll(".mouse-per-line circle")
.style("opacity", "1");
d3.selectAll(".mouse-per-line text")
.style("opacity", "1");
})
.on('mousemove', function() { // mouse moving over canvas
var mouse = d3.mouse(this);
d3.select(".mouse-line")
.attr("d", function() {
var d = "M" + mouse[0] + "," + height;
d += " " + mouse[0] + "," + 0;
return d;
});
d3.selectAll(".mouse-per-line")
.attr("transform", function(d, i) {
var xDate = x.invert(mouse[0]),
bisect = d3.bisector(function(d) { return d.date; }).right;
idx = bisect(d.values, xDate);
var beginning = 0,
end = lines[i].getTotalLength(),
target = null;
while (true){
target = Math.floor((beginning + end) / 2);
pos = lines[i].getPointAtLength(target);
if ((target === end || target === beginning) && pos.x !== mouse[0]) {
break;
}
if (pos.x > mouse[0]) end = target;
else if (pos.x < mouse[0]) beginning = target;
else break; //position found
}
d3.select(this).select('text')
.text(y.invert(pos.y).toFixed(2));
return "translate(" + mouse[0] + "," + pos.y +")";
});
});
// write sample size
svgS1.append("text")
.attr("x", x(1))
.attr("y", y(0.1))
.attr("class", "text")
.style("font-family", "sans-serif")
.style("font-size", 14)
.text("Sample Size: " + sampleSize);
});
// ~~~~~~~~~~~~~~~~~~~~
// Stage 2 Band Plot
// ~~~~~~~~~~~~~~~~~~~~
svgS2.selectAll(".line").remove();
svgS2.selectAll("#stg2Bands").remove();
svgS2.selectAll(".stage").remove();
svgS2.selectAll(".text").remove();
svgS2.selectAll(".circle").remove();
svgS2.selectAll(".mouse-per-line").remove();
d3.csv(filename, rowConverterStg2, function(data) {
// sample size
var sampleSize = data[0]['samples'];
color.domain(d3.keys(data[0]).filter(function(key) {
return key !== "years";
}));
var stages = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
years: d.years,
survivalPct: +d[name]
};
})
};
});
// append stages data
var stage2 = svgS2.selectAll(".stage")
.data(stages)
.enter().append("g")
.attr("class", "stage");
// draw area between lines to be shaded
svgS2.append("path")
.datum(data)
.attr("id", "stg2Bands")
.attr("d", stg2BandArea );
// draw lines (central, upper and lower)
stage2.append("path")
.attr("class", "line")
.attr("id", function(d) {
if (d.name == "stage2") {
return "stg2CenterLine";
} else if (d.name == "stage2Upper") {
return "stg2UpperLine";
} else if (d.name == "stage2Lower") {
return "stg2LowerLine";
}})
.attr("d", function(d) {
return line(d.values);
});
var mouseG2 = svgS2.append("g")
.attr("class", "mouse-over-effects");
var lines = document.getElementsByClassName('line');
var mousePerLine = mouseG2.selectAll('.mouse-per-line')
.data(stages)
.enter()
.append("g")
.attr("class", "mouse-per-line");
mousePerLine.append("circle")
.attr("r", 7)
.style("stroke", function(d) {
return "#ff7f0e";
})
.style("fill", "none")
.style("stroke-width", "1px")
.style("opacity", "0");
mousePerLine.append("text")
.attr("transform", "translate(10,3)");
mouseG2.append('svg:rect') // append a rect to catch mouse movements on canvas
.attr('width', width) // can't catch mouse events on a g element
.attr('height', height)
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseout', function() { // on mouse out hide line, circles and text
d3.select(".mouse-line")
.style("opacity", "0");
d3.selectAll(".mouse-per-line circle")
.style("opacity", "0");
d3.selectAll(".mouse-per-line text")
.style("opacity", "0");
})
.on('mouseover', function() { // on mouse in show line, circles and text
d3.select(".mouse-line")
.style("opacity", "1");
d3.selectAll(".mouse-per-line circle")
.style("opacity", "1");
d3.selectAll(".mouse-per-line text")
.style("opacity", "1");
})
.on('mousemove', function() { // mouse moving over canvas
var mouse = d3.mouse(this);
d3.select(".mouse-line")
.attr("d", function() {
var d = "M" + mouse[0] + "," + height;
d += " " + mouse[0] + "," + 0;
return d;
});
d3.selectAll(".mouse-per-line")
.attr("transform", function(d, i) {
var xDate = x.invert(mouse[0]),
bisect = d3.bisector(function(d) { return d.date; }).right;
idx = bisect(d.values, xDate);
var beginning = 0,
end = lines[i].getTotalLength(),
target = null;
while (true){
target = Math.floor((beginning + end) / 2);
pos = lines[i].getPointAtLength(target);
if ((target === end || target === beginning) && pos.x !== mouse[0]) {
break;
}
if (pos.x > mouse[0]) end = target;
else if (pos.x < mouse[0]) beginning = target;
else break; //position found
}
d3.select(this).select('text')
.text(y.invert(pos.y).toFixed(2));
return "translate(" + mouse[0] + "," + pos.y +")";
});
});
// write sample size
svgS2.append("text")
.attr("x", x(1))
.attr("y", y(0.1))
.attr("class", "text")
.style("font-family", "sans-serif")
.style("font-size", 14)
.text("Sample Size: " + sampleSize);
});
// ~~~~~~~~~~~~~~~~~~~~
// Stage 3 Band Plot
// ~~~~~~~~~~~~~~~~~~~~
svgS3.selectAll(".line").remove();
svgS3.selectAll("#stg3Bands").remove();
svgS3.selectAll(".stage").remove();
svgS3.selectAll(".text").remove();
svgS3.selectAll(".circle").remove();
svgS3.selectAll(".mouse-per-line").remove();
d3.csv(filename, rowConverterStg3, function(data) {
// sample size
var sampleSize = data[0]['samples'];
color.domain(d3.keys(data[0]).filter(function(key) {
return key !== "years";
}));
var stages = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
years: d.years,
survivalPct: +d[name]
};
})
};
});
// append stages data
var stage3 = svgS3.selectAll(".stage")
.data(stages)
.enter().append("g")
.attr("class", "stage");
// draw area between lines to be shaded
svgS3.append("path")
.datum(data)
.attr("id", "stg3Bands")
.attr("d", stg3BandArea );
// draw lines (central, upper and lower)
stage3.append("path")
.attr("class", "line")
.attr("id", function(d) {
if (d.name == "stage3") {
return "stg3CenterLine";
} else if (d.name == "stage3Upper") {
return "stg3UpperLine";
} else if (d.name == "stage3Lower") {
return "stg3LowerLine";
}})
.attr("d", function(d) {
return line(d.values);
});
var mouseG3 = svgS3.append("g")
.attr("class", "mouse-over-effects");
var lines = document.getElementsByClassName('line');
var mousePerLine = mouseG3.selectAll('.mouse-per-line')
.data(stages)
.enter()
.append("g")
.attr("class", "mouse-per-line");
mousePerLine.append("circle")
.attr("r", 7)
.style("stroke", function(d) {
return "#2ca02c";
})
.style("fill", "none")
.style("stroke-width", "1px")
.style("opacity", "0");
mousePerLine.append("text")
.attr("transform", "translate(10,3)");
mouseG3.append('svg:rect') // append a rect to catch mouse movements on canvas
.attr('width', width) // can't catch mouse events on a g element
.attr('height', height)
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseout', function() { // on mouse out hide line, circles and text
d3.select(".mouse-line")
.style("opacity", "0");
d3.selectAll(".mouse-per-line circle")
.style("opacity", "0");
d3.selectAll(".mouse-per-line text")
.style("opacity", "0");
})
.on('mouseover', function() { // on mouse in show line, circles and text
d3.select(".mouse-line")
.style("opacity", "1");
d3.selectAll(".mouse-per-line circle")
.style("opacity", "1");
d3.selectAll(".mouse-per-line text")
.style("opacity", "1");
})
.on('mousemove', function() { // mouse moving over canvas
var mouse = d3.mouse(this);
d3.select(".mouse-line")
.attr("d", function() {
var d = "M" + mouse[0] + "," + height;
d += " " + mouse[0] + "," + 0;
return d;
});
d3.selectAll(".mouse-per-line")
.attr("transform", function(d, i) {
var xDate = x.invert(mouse[0]),
bisect = d3.bisector(function(d) { return d.date; }).right;
idx = bisect(d.values, xDate);
var beginning = 0,
end = lines[i].getTotalLength(),
target = null;
while (true){
target = Math.floor((beginning + end) / 2);
pos = lines[i].getPointAtLength(target);
if ((target === end || target === beginning) && pos.x !== mouse[0]) {
break;
}
if (pos.x > mouse[0]) end = target;
else if (pos.x < mouse[0]) beginning = target;
else break; //position found
}
d3.select(this).select('text')
.text(y.invert(pos.y).toFixed(2));
return "translate(" + mouse[0] + "," + pos.y +")";
});
});
// write sample size
svgS3.append("text")
.attr("x", x(1))
.attr("y", y(0.1))
.attr("class", "text")
.style("font-family", "sans-serif")
.style("font-size", 14)
.text("Sample Size: " + sampleSize);
});
// ~~~~~~~~~~~~~~~~~~~~
// Stage 4 Band Plot
// ~~~~~~~~~~~~~~~~~~~~
svgS4.selectAll(".line").remove();
svgS4.selectAll("#stg4Bands").remove();
svgS4.selectAll(".stage").remove();
svgS4.selectAll(".text").remove();
svgS4.selectAll(".circle").remove();
svgS4.selectAll(".mouse-per-line").remove();
d3.csv(filename, rowConverterStg4, function(data) {
// sample size
var sampleSize = data[0]['samples'];
color.domain(d3.keys(data[0]).filter(function(key) {
return key !== "years";
}));
var stages = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
years: d.years,
survivalPct: +d[name]
};
})
};
});
// append stages data
var stage4 = svgS4.selectAll(".stage")
.data(stages)
.enter().append("g")
.attr("class", "stage");
// draw area between lines to be shaded
svgS4.append("path")
.datum(data)
.attr("id", "stg4Bands")
.attr("d", stg4BandArea );
// draw lines (central, upper and lower)
stage4.append("path")
.attr("class", "line")
.attr("id", function(d) {
if (d.name == "stage4") {
return "stg4CenterLine";
} else if (d.name == "stage4Upper") {
return "stg4UpperLine";
} else if (d.name == "stage4Lower") {
return "stg4LowerLine";
}})
.attr("d", function(d) {
return line(d.values);
});
var mouseG4 = svgS4.append("g")
.attr("class", "mouse-over-effects");
var lines = document.getElementsByClassName('line');
var mousePerLine = mouseG4.selectAll('.mouse-per-line')
.data(stages)
.enter()
.append("g")
.attr("class", "mouse-per-line");
mousePerLine.append("circle")
.attr("r", 7)
.style("stroke", function(d) {
return "#d62728";
})
.style("fill", "none")
.style("stroke-width", "1px")
.style("opacity", "0");
mousePerLine.append("text")
.attr("transform", "translate(10,3)");
mouseG4.append('svg:rect') // append a rect to catch mouse movements on canvas
.attr('width', width) // can't catch mouse events on a g element
.attr('height', height)
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseout', function() { // on mouse out hide line, circles and text
d3.select(".mouse-line")
.style("opacity", "0");
d3.selectAll(".mouse-per-line circle")
.style("opacity", "0");
d3.selectAll(".mouse-per-line text")
.style("opacity", "0");
})
.on('mouseover', function() { // on mouse in show line, circles and text
d3.select(".mouse-line")
.style("opacity", "1");
d3.selectAll(".mouse-per-line circle")
.style("opacity", "1");
d3.selectAll(".mouse-per-line text")
.style("opacity", "1");
})
.on('mousemove', function() { // mouse moving over canvas
var mouse = d3.mouse(this);
d3.select(".mouse-line")
.attr("d", function() {
var d = "M" + mouse[0] + "," + height;
d += " " + mouse[0] + "," + 0;
return d;
});
d3.selectAll(".mouse-per-line")
.attr("transform", function(d, i) {
var xDate = x.invert(mouse[0]),
bisect = d3.bisector(function(d) { return d.date; }).right;
idx = bisect(d.values, xDate);
var beginning = 0,
end = lines[i].getTotalLength(),
target = null;
while (true){
target = Math.floor((beginning + end) / 2);
pos = lines[i].getPointAtLength(target);
if ((target === end || target === beginning) && pos.x !== mouse[0]) {
break;
}
if (pos.x > mouse[0]) end = target;
else if (pos.x < mouse[0]) beginning = target;
else break; //position found
}
d3.select(this).select('text')
.text(y.invert(pos.y).toFixed(2));
return "translate(" + mouse[0] + "," + pos.y +")";
});
});
// write sample size
svgS4.append("text")
.attr("x", x(1))
.attr("y", y(0.1))
.attr("class", "text")
.style("font-family", "sans-serif")
.style("font-size", 14)
.text("Sample Size: " + sampleSize);
});
});
</script>
https://d3js.org/d3.v4.min.js