D3.js 산포도 그래프를 단계별 그리며 설명 - scaleLinear(), domain(), range(), axisBottom()
산포도 그래프 작성 방법
간단한 그래프 샘플로 산포도를 그려보자. 만듭니다.
산포도(散布度) 또는 변산도(變散度)는 변량이 흩어져 있는 정도를 하나의 수로 나타낸 값이다.
좌표점 찍기
이번에는 다음 데이터를 사용한다.
var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
[410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ];
[ ]
는 배열로, [[x좌표1, y좌표1], [x좌표2, y좌표2], ...]
와 같은 형식의 2차원 배열의 데이터이다.
우선, 좌표를 원을 표시하는 “circle"를 사용해 표시해 보겠다.
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) { return d[0]; })
.attr("cy", function(d) { return d[1]; })
.attr("r", 4);
전체 코드는 아래와 같다.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 Test</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<script>
var svg = d3.select("body").append("svg").attr("width", 700).attr("height", 100);
var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
[410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ];
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) { return d[0]; })
.attr("cy", function(d) { return d[1]; })
.attr("r", 4);
</script>
</body>
</html>
“circle"에 할당한 데이터는 2차원 배열이기 때문에 “circle"에는 [x 좌표, y 좌표]의 데이터가 할당된다. 예를 들면, 첫 번째의 “circle"에는 [5, 20]
이 할당된다. 이 데이터는 cx
,cy
속성에 각각 설정하고 있다.
좌표점 텍스트 표시
다음으로는 좌표 위치를 x 좌표, y 좌표의 형식으로 텍스트로 표시해 보겠다.
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.attr("x", function(d) { return d[0]; })
.attr("y", function(d) { return d[1]; })
.attr("fill", "red")
.text(function(d) {
return d[0] + "," + d[1];
});
전체 코드는 아래와 같다.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 Test</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<script>
var svg = d3.select("body").append("svg").attr("width", 700).attr("height", 100);
var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
[410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ];
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) { return d[0]; })
.attr("cy", function(d) { return d[1]; })
.attr("r", 4);
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.attr("x", function(d) { return d[0]; })
.attr("y", function(d) { return d[1]; })
.attr("fill", "red")
.text(function(d) {
return d[0] + "," + d[1];
});
</script>
</body>
</html>
x
좌표는 오른쪽을 향하며 증가하지만, y
좌표는 아래를 향하며 증가하고 있다는 것을 주위해야 한다.
스케일 변환
스케일의 변환에는 다음의 D3의 함수를 사용한다.
var scale = d3.scaleLinear()
.domain([0, 500])
.range([0, 100]);
이 함수는 domain
으로 지정한 범위를 range
로 지정한 범위로 변환하는 함수를 scale
라는 변수로 지정한다.
예를 들어, 아래와 같이 호출할 수 있는데, 호출을 하게 되면 20이 반환된다.
scale(100);
이는 [0,500]
의 범위를 [0,100]
의 범위로 변경, 즉, 좌표를 1/5로 한 결과이다. (100/5 = 20)
이것을 그래프에 적용해보자.
그래프의 너비와 높이를 아래와 같이 설정한다.
var width = 400;
var height = 300;
비율을 4:3으로 설정하였다.
var xScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d[0]; })])
.range([0, width]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d[1]; })])
.range([height, 0]);
여기서 데이터의 최대치를 구하기 위해서 d3.max(..)
를 사용하였다. d3.max
는 첫 번째 인수로 배열을 지정하고 두 번째 인수로 반환하고 함수로 지정할 수 있다.
y 좌표는 반대로 하려고 하기 때문에, range의 첫 번째 인수에 큰 값을 넣었다.
이 함수를 사용하여 다시 그린다.
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) { return xScale(d[0]); })
.attr("cy", function(d) { return yScale(d[1]); })
.attr("r", 4);
전체 코드는 아래와 같다.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 Test</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<script>
// 여기에 코드를 작성한다.
var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
[410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ];
var width = 400;
var height = 300;
var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);
var xScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d[0]; })])
.range([0, width]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d[1]; })])
.range([height, 0]);
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) { return xScale(d[0]); })
.attr("cy", function(d) { return yScale(d[1]); })
.attr("r", 4);
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.attr("x", function(d) { return xScale(d[0]); })
.attr("y", function(d) { return yScale(d[1]); })
.attr("fill", "red")
.text(function(d) {
return d[0] + "," + d[1];
});
</script>
</body>
</html>
y
좌표는 아래쪽이 작아지고, 종횡비도 4:3이 되었다.
축 그리기
다음으로 축을 그려보자. 여기에는 D3의 함수가 준비되어 있다.
var axisX = d3.axisBottom(xScale);
svg.append("g")
.call(axisX);
앞에서의 scale과 함께 사용한다. 아래쪽 축을 나타내는 d3.axisBottom
외에도 d3.axisTop
, d3.axisRight
, d3.axisLeft
가 있다. 이것을 call 함수로 호출하면 아래와 같이 축을 그려준다.
위에 코드를 적용한 전체 코드는 아래와 같다. 축이 있기에 text 표시는 지웠다.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 Test</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<script>
var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
[410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ];
var width = 400;
var height = 300;
var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);
var xScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d[0]; })])
.range([0, width]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d[1]; })])
.range([height, 0]);
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) { return xScale(d[0]); })
.attr("cy", function(d) { return yScale(d[1]); })
.attr("r", 4);
var axisX = d3.axisBottom(xScale);
svg.append("g")
.call(axisX);
</script>
</body>
</html>
좌표점과 축 위치 조정
위치와 축을 표시하는 너비를 고려하지 않다. 그래서 다음과 같이 위치를 이동한다.
var axisX = d3.axisBottom(xScale);
var padding = 30;
svg.append("g")
.attr("transform", "translate(" + 0 + "," + (height-padding) + ")")
.call(axisX);
이것을 고려하면 그래프를 그리는 코드는 아래와 같이 된다.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 Test</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<script>
var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
[410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ];
var width = 400;
var height = 300;
var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);
var padding = 30;
var xScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d[0]; })])
.range([padding, width - padding]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d[1]; })])
.range([height - padding, padding]);
var axisx = d3.axisBottom(xScale);
var axisy = d3.axisLeft(yScale);
svg.append("g")
.attr("transform", "translate(" + 0 + "," + (height - padding) + ")")
.call(axisx);
svg.append("g")
.attr("transform", "translate(" + padding + "," + 0 + ")")
.call(axisy);
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) { return xScale(d[0]); })
.attr("cy", function(d) { return yScale(d[1]); })
.attr("fill", "SkyBlue")
.attr("r", 4);
</script>
</body>
</html>
이것으로 산포도가 완성되었다. 원의 색도 하늘색으로 변경해 봤다.