Scatter Plot
vizhub上实现的代码:
https://vizhub.com/Edward-Elric233/53807a1b35d94329b3689081cd2ea945
https://vizhub.com/Edward-Elric233/b9647d50899a4a0e8e917f913cd0a53a
https://vizhub.com/Edward-Elric233/8c6b50cd81a04f048f490f48e4fe6264
由前面的柱状图,我们对图形进行一定的修改就可以得到散点图:
只要将绑定数据以后enter进行添加矩形的操作换成圆形就可以了,不过需要注意的是不能够y坐标再使用scaleBand
了,而应该使用scalePoint
,如果继续使用scaleBand
的话需要调整圆的坐标(y坐标是Categorical的)。注意圆形的属性是cx,cy,r
分别表示圆心的坐标和半径。
index.html
<!DOCTYPE html>
<html><head><title>Scatter Plot</title><link rel="stylesheet" href="./styles.css"><script src="https://unpkg.com/d3@5.7.0/dist/d3.min.js"></script><!-- find D3 file on UNPKG d3.min.js-->
</head><body><svg width="960" height="500"></svg><script src="./index.js">// console.log(d3); test whether you have imported d3.js or not</script></body></html>
index.js
const svg = d3.select('svg');
// svg.style('background-color', 'red'); test
const width = +svg.attr('width');
const height = +svg.attr('height');const render = data => {const xValue = d => d.population;const yValue = d => d.country;const margin = { top: 60, right: 20, bottom: 80, left: 150 };const innerWidth = width - margin.left - margin.right;const innerHeight = height - margin.top - margin.bottom;const xScale = d3.scaleLinear().domain([0, d3.max(data, xValue)]).range([0, innerWidth]).nice(); //作用是如果值域的最大值不够整齐可以变得整齐// const yScale = scaleBand()const yScale = d3.scalePoint().domain(data.map(yValue)).range([0, innerHeight]).padding(0.5);const xAxisTickFormat = number => d3.format('.3s')(number).replace('G', 'B');const yAxis = d3.axisLeft(yScale).tickSize(-innerWidth);;const xAxis = d3.axisBottom(xScale).tickFormat(xAxisTickFormat).tickSize(-innerHeight); //设置tick-line的长度const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`);//yAxis(g.append('g'));const yAxisG = g.append('g').call(yAxis).selectAll('.domain').remove();const xAxisG = g.append('g').call(xAxis).attr('transform', `translate(0,${innerHeight})`);xAxisG.selectAll('.domain').remove();xAxisG.append('text').attr('class', 'axis-label').attr('y', 60).attr('x', innerWidth / 2).attr('fill', 'black').text('populiation');let colorSet = ['#eb2617', '#ffaa00', '#4dff00', '#00fbff', '#bb00ff', '#eeff00'];const createGetColor = (idx) => {var i = idx || -1;return {get: () => { i = (i + 1) % colorSet.length; return colorSet[i]; }};};const getColor = createGetColor();g.selectAll('circle').data(data).enter().append('circle').attr('cy', d => yScale(yValue(d))).attr('cx', d => xScale(xValue(d))).attr('r', 15).attr('fill', getColor.get);g.append('text').attr('class', 'title').attr('y', -20).text('Top 10 Most Population Countries');
};d3.csv("https://gist.githubusercontent.com/Edward-Elric233/23f3024c472ffd7e34e6a5ac04bad26c/raw/6ced2249ea6f5d12f72c1eb00b8c1278d2c86e95/every%2520countries'%2520population").then(data => {data.forEach(d => {d.population = +d.population * 1000;});render(data);// console.log(data);
});
styles.css
body {margin: 0px;overflow: hidden;font-family: manosapce;
}text {font-family: sans-serif;
}.tick text {font-size: 2em;fill: #8E8883
}.axis-label {fill: #8E8883;font-size: 2.5em
}.title {font-size: 3em;fill: #8E8883
}
虽然大概实现了散点图,但是上面的数据不适合用散点图来进行表示。因此我们不妨更换数据,使用更适合的数据。
这里因为y坐标也是Quantitative的,所以应该使用scaleLinear
。画的时候注意坐标的方向,如果方向不对应的画可以调整scaleLinear
的domain
或者range
(反向即可)。这里因为圆形比较密集就设置了一下圆形的透明度opacity
,看起来比较好看。
index.html
<!DOCTYPE html>
<html><head><title>Cars Scatter Plot</title><link rel="stylesheet" href="./styles.css"><script src="https://unpkg.com/d3@5.7.0/dist/d3.min.js"></script><!-- find D3 file on UNPKG d3.min.js-->
</head><body><svg width="960" height="500"></svg><script src="./index.js">// console.log(d3); test whether you have imported d3.js or not</script>
</body></html>
index.js
const svg = d3.select('svg');
// svg.style('background-color', 'red'); test
const width = +svg.attr('width');
const height = +svg.attr('height');//mpg cylinders displacement horsepower weight acceleration year origin nameconst render = data => {const title = 'Cars: Mpg vs. Horsepower';const xValue = d => d.mpg;const xAxisLabel = 'Mpg';const yValue = d => d.horsepower;const yAxisLabel = 'Horsepower';const margin = { top: 60, right: 20, bottom: 80, left: 100 };const innerWidth = width - margin.left - margin.right;const innerHeight = height - margin.top - margin.bottom;const circleRadius = 10;const xScale = d3.scaleLinear()//.domain([min(data, xValue), max(data, xValue)]) //和下面的写法等价.domain(d3.extent(data, xValue)).range([0, innerWidth]).nice(); //作用是如果值域的最大值不够整齐可以变得整齐// const yScale = scaleBand()const yScale = d3.scaleLinear().domain([d3.max(data, yValue), d3.min(data, yValue)]).range([0, innerHeight]).nice();//const xAxisTickFormat = number => format('.3s')(number).replace('G','B');const yAxis = d3.axisLeft(yScale).tickSize(-innerWidth).tickPadding(15);const xAxis = d3.axisBottom(xScale)//.tickFormat(xAxisTickFormat).tickSize(-innerHeight) //设置tick-line的长度.tickPadding(15); //通过设置Padding让x轴的数字离远一点const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`);//yAxis(g.append('g'));const yAxisG = g.append('g').call(yAxis);yAxisG.selectAll('.domain').remove();yAxisG.append('text').attr('class', 'axis-label').attr('y', -70).attr('x', -innerHeight / 2).attr('fill', 'black').attr('transform', `rotate(-90)`).attr('text-anchor', 'middle') //设置锚点在中心.text(yAxisLabel);const xAxisG = g.append('g').call(xAxis).attr('transform', `translate(0,${innerHeight})`);xAxisG.selectAll('.domain').remove();xAxisG.append('text').attr('class', 'axis-label').attr('y', 60).attr('x', innerWidth / 2).attr('fill', 'black').text(xAxisLabel);let colorSet = ['#eb2617', '#ffaa00', '#4dff00', '#00fbff', '#bb00ff', '#eeff00'];const createGetColor = (idx) => {var i = idx || -1;return {get: () => { i = (i + 1) % colorSet.length; return colorSet[i]; }};};const getColor = createGetColor();g.selectAll('circle').data(data).enter().append('circle').attr('cy', d => yScale(yValue(d))).attr('cx', d => xScale(xValue(d))).attr('r', circleRadius).attr('fill', getColor.get);g.append('text').attr('class', 'title').attr('y', -20).attr('x', innerWidth / 2).attr('text-anchor', 'middle').text(title);
};d3.csv('https://vizhub.com/curran/datasets/auto-mpg.csv').then(data => {data.forEach(d => {//得到的数据默认每个属性的值都是字符串,因此需要进行转换d.mpg = +d.mpg;d.cylinders = +d.cylinders;d.displacement = +d.displacement;d.horsepower = +d.horsepower;d.weight = +d.weight;d.acceleration = +d.acceleration;d.year = +d.year;});render(data);// console.log(data);
});
styles.css
body {margin: 0px;overflow:hidden;font-family: manosapce;}circle {opacity : 0.5;}text {font-family : sans-serif;}.tick text {font-size : 2em;fill : #8E8883}.tick line {stroke : #E5E2E0}.axis-label {fill : #8E8883;font-size: 2.5em}.title {font-size : 3em;fill : #8E8883}
对于时间轴的处理可以使用scaleTime
index.html
<!DOCTYPE html>
<html><head><title>Temperature in San Francisc Scatter Plot</title><link rel="stylesheet" href="./styles.css"><script src="https://unpkg.com/d3@5.7.0/dist/d3.min.js"></script><!-- find D3 file on UNPKG d3.min.js-->
</head><body><svg width="960" height="500"></svg><script src="./index.js">// console.log(d3); test whether you have imported d3.js or not</script></body></html>
html.js
const svg = d3.select('svg');
// svg.style('background-color', 'red'); test
const width = +svg.attr('width');
const height = +svg.attr('height');const render = data => {const title = 'A week in San Francisco';const xValue = d => d.timestamp;const xAxisLabel = 'Time';const yValue = d => d.temperature;const yAxisLabel = 'Temperature';const margin = { top: 60, right: 20, bottom: 80, left: 100 };const innerWidth = width - margin.left - margin.right;const innerHeight = height - margin.top - margin.bottom;const circleRadius = 6;const xScale = d3.scaleTime()//.domain([min(data, xValue), max(data, xValue)]) //和下面的写法等价.domain(d3.extent(data, xValue)).range([0, innerWidth]).nice(); //作用是如果值域的最大值不够整齐可以变得整齐// const yScale = scaleBand()const yScale = d3.scaleLinear().domain([d3.max(data, yValue), d3.min(data, yValue)]).range([0, innerHeight]).nice();//const xAxisTickFormat = number => format('.3s')(number).replace('G','B');const yAxis = d3.axisLeft(yScale).tickSize(-innerWidth).tickPadding(15);const xAxis = d3.axisBottom(xScale)//.tickFormat(xAxisTickFormat).tickSize(-innerHeight) //设置tick-line的长度.tickPadding(15); //通过设置Padding让x轴的数字离远一点const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`);//yAxis(g.append('g'));const yAxisG = g.append('g').call(yAxis);yAxisG.selectAll('.domain').remove();yAxisG.append('text').attr('class', 'axis-label').attr('y', -70).attr('x', -innerHeight / 2).attr('fill', 'black').attr('transform', `rotate(-90)`).attr('text-anchor', 'middle') //设置锚点在中心.text(yAxisLabel);const xAxisG = g.append('g').call(xAxis).attr('transform', `translate(0,${innerHeight})`);xAxisG.selectAll('.domain').remove();xAxisG.append('text').attr('class', 'axis-label').attr('y', 60).attr('x', innerWidth / 2).attr('fill', 'black').text(xAxisLabel);let colorSet = ['#eb2617', '#ffaa00', '#4dff00', '#00fbff', '#bb00ff', '#eeff00'];const createGetColor = (idx) => {var i = idx || -1;return {get: () => { i = (i + 1) % colorSet.length; return colorSet[i]; }};};const getColor = createGetColor();g.selectAll('circle').data(data).enter().append('circle').attr('cy', d => yScale(yValue(d))).attr('cx', d => xScale(xValue(d))).attr('r', circleRadius).attr('fill', 'red');g.append('text').attr('class', 'title').attr('y', -20).attr('x', innerWidth / 2).attr('text-anchor', 'middle').text(title);
};d3.csv('https://vizhub.com/curran/datasets/temperature-in-san-francisco.csv').then(data => {// console.log(data);data.forEach(d => {//得到的数据默认每个属性的值都是字符串,因此需要进行转换d.temperature = +d.temperature;d.timestamp = new Date(d.timestamp);});render(data);
});
styles.css
body {margin: 0px;overflow: hidden;font-family: manosapce;
}circle {opacity: 0.5;
}text {font-family: sans-serif;
}.tick text {font-size: 2em;fill: #8E8883
}.tick line {stroke: #E5E2E0
}.axis-label {fill: #8E8883;font-size: 2.5em
}.title {font-size: 3em;fill: #8E8883
}