An example of brush and zoom of a scatter plot.
This shows Wisconsin breast cancer dataset. Originally published at UCI Machine Learning Repository: Breast Cancer Wisconsin (Diagnostic) Data Set, this dataset was also published on Kaggle This data set contains information about benign or malignant and ten features computed for each cell nucleus, such as radius, texture, and perimeter.
The data visualization shows these two different clusters(B and M).
Forked from curran 's Stylized Scatter Plot with Color Legend.
forked from anqi-lu's block: Scatter Plot - Breast Cancer
Learned from Mike Bostock 's Brush & Zoom II
xxxxxxxxxx
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.24.0/d3-legend.min.js"></script>
<title>Basic Scatter Plot</title>
<style>
body {
margin: 0px;
}
.domain {
display: none;
}
.tick line {
stroke: #C0C0BB;
}
.tick text, .legendCells text {
fill: #8E8883;
font-size: 15pt;
font-family: sans-serif;
}
.x-axis-label, .y-axis-label, .legend-label {
fill: #635F5D;
font-size: 20pt;
font-family: sans-serif;
}
</style>
</head>
<body>
<svg width="960" height="500"></svg>
<script>
const xValue = d => d.radius_mean;
const xLabel = 'Radius Mean';
const yValue = d => d.texture_mean;
const yLabel = 'Texture Mean';
const diaName = { "M": "Malignant", "B": "Benign"}
const colorValue = d => diaName[d.diagnosis];
const colorLabel = 'Diagnosis';
const margin = { left: 120, right: 300, top: 20, bottom: 120 };
const svg = d3.select('svg');
const width = svg.attr('width');
const height = svg.attr('height');
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const g = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
const canvas = svg.append('g')
.attr('width', innerWidth)
.attr('height', innerHeight)
.attr('transform', `translate(${margin.left}, ${margin.top})`);
const xAxisG = g.append('g')
.attr('class', 'axis axis--x')
.attr('transform', `translate(0, ${innerHeight})`);
const yAxisG = g.append('g')
.attr('class', 'axis axis--y');
const colorLegendG = g.append('g')
.attr('transform', `translate(${innerWidth + 60}, 150)`);
xAxisG.append('text')
.attr('class', 'x-axis-label')
.attr('x', innerWidth / 2)
.attr('y', 100)
.text(xLabel);
yAxisG.append('text')
.attr('class', 'y-axis-label')
.attr('x', -innerHeight / 2)
.attr('y', -60)
.attr('transform', `rotate(-90)`)
.style('text-anchor', 'middle')
.text(yLabel);
colorLegendG.append('text')
.attr('class', 'legend-label')
.attr('x', -30)
.attr('y', -40)
.text(colorLabel);
const xScale = d3.scaleLinear();
const yScale = d3.scaleLinear();
const colorScale = d3.scaleOrdinal()
.range(d3.schemeCategory10);
var xAxis;
var yAxis;
const colorLegend = d3.legendColor()
.scale(colorScale)
.shape('circle');
const row = d => {
d.radius_mean = +d.radius_mean;
d.texture_mean = +d.texture_mean;
return d;
};
var x0, x, y, y0;
d3.csv('breast_cancer.csv', row, data => {
var area = d3.area()
.curve(d3.curveMonotoneX)
.x( d => xScale(xValue(d)))
.y0(height)
.y1( d => yScale(yValue(d)));
var brush = d3.brush().on("end", brushended),
idleTimeout,
idleDelay = 350;
x0 = d3.extent(data, xValue);
y0 = d3.extent(data, yValue)
x = xScale
.domain(d3.extent(data, xValue))
.range([0, innerWidth])
.nice();
y = yScale
.domain(d3.extent(data, yValue))
.range([innerHeight, 0])
.nice();
circles = g.selectAll('circle').data(data)
.enter().append('circle')
.attr('cx', d => xScale(xValue(d)))
.attr('cy', d => yScale(yValue(d)))
.attr('fill', d => colorScale(colorValue(d)))
.attr('fill-opacity', 0.6)
.attr('r', 5);
circles.attr("class", "area").attr("d", area);
xAxis = d3.axisBottom(x)
.tickPadding(15)
.tickSize(-10);
yAxis = d3.axisLeft(y)
.ticks(5)
.tickPadding(15)
.tickSize(-10);
xAxisG.call(xAxis);
yAxisG.call(yAxis);
colorLegendG.call(colorLegend)
.selectAll('.cell text')
.attr('dy', '0.1em');
g.selectAll(".domain")
.style("display", "none");
canvas.append("g")
.attr("class", "brush")
.call(brush);
function brushended() {
var s = d3.event.selection;
if (!s) {
if (!idleTimeout) return idleTimeout = setTimeout(idled, idleDelay);
x = xScale
.domain(d3.extent(data, xValue))
.range([0, innerWidth])
.nice();
y = yScale
.domain(d3.extent(data, yValue))
.range([innerHeight, 0])
.nice();
} else {
x.domain([s[0][0], s[1][0]].map(x.invert, x));
y.domain([s[1][1], s[0][1]].map(y.invert, y));
g.select(".brush").call(brush.move, null);
}
zoom();
}
function idled() {
idleTimeout = null;
}
function zoom() {
var t = svg.transition().duration(750);
g.select(".area").attr("d", area);
g.select(".axis--x").transition(t).call(xAxis);
g.select(".axis--y").transition(t).call(yAxis);
g.selectAll("circle").transition(t)
.attr("cx", d => xScale(xValue(d)))
.attr("cy", d => yScale(yValue(d)));
}
});
</script>
</body>
</html>
https://d3js.org/d3.v4.min.js
https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.24.0/d3-legend.min.js