프로필사진
owgno6
CODELIB
Recent Posts
Recent Comments
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Archives
Today
Total

티스토리 뷰

D3.js

07 원형 차트(Pie & Doughnut Chart)

owgno6 2018. 6. 15. 11:46

지금까지는 데이터를 그래픽으로 표현하여 

바 차트의 막대, 라인 차트의 선을 다루었다.

이번엔 파이 차트의 조각과 같은 데이터로 나타내려고한다.


원그림 또는 파이차트는 범주별 구성비율을 단위 원에 부채꼴로 표현한 그래프이다. 

이 그래프에서 부채꼴의 중심각은 구성비율에 비례한다. 

즉, 원그래프라고도 한다.


다양한 차트가 있지만,

그 중에서도 비율을 이야기할 때 많이 쓰이는 유형이

파이차트다.


파이차트의 데이터 구성은

지금까지 데이터 구성방식과 많이 다르다.

가장 큰 차이는

데이터를 담는 형식과

데이터의 시각화를 담당하는

Scale이 사라진 점이다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<div class="graph"></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var w = 250, h = 250;
d3.select(".graph")
    .append("svg")
    .attr("width", w)
    .attr("height", h)
    .attr("id""graphWrap");
var graphWrap = d3.select("#graphWrap");
 
var graphData = [50301253];
var pie = d3.pie();
var arc = d3.arc().innerRadius(0).outerRadius(100);
var colors = d3.scaleOrdinal(d3.schemeCategory10);
 
var oneGraph = graphWrap.selectAll("path").data(pie(graphData));
oneGraph.enter()
    .append("path")
    .attr("class""pie")
    .attr("transform""translate("+(w/2)+","+(h/2)+")")
    .style("fill"function(d, i) {
        return colors(graphData[i]);})
    .transition()
    .duration(1000)
    .delay(function(d, i) {
        return i * 1000;
    })
    .attrTween("d"function(d, i) {
        var interpolate = d3.interpolate(
            {startAngle : d.startAngle, endAngle : d.startAngle},
            {startAngle : d.startAngle, endAngle : d.endAngle}
        );
        return function(t){
            return arc(interpolate(t));
        }
    });
 
</script>
cs


[line 10 ~ 16]

SVG 250 x 250 크기의 <svg id="graphWrap"></svg> 를 div.graph 에 추가한다.


[line 18]

데이터 변수 설정을 보면 

지금까지 데이터설정(dataset) 방식과 다르다는 것을 알 수 있다.

원형차트의 경우 x축과 y축이 없기때문에 단순배열로 설정된다.

( 데이터의 값이 합 100이 아니여도 원이 생성된다. )


d3.pie를 생성하며 [line 19].

안쪽반지름(innerRadius), 바깥쪽반지름(outerRadius)을 설정한다. [line 20].

( innerRadius(0).outerRadius(100)의 경우 200x200의 원 그래프가 생성된다. )


[line 23 ~ 29].

그래프 속성설정을 하고,

색상은 return값에 따로 설정할 수 있지만

지금까지 예제에 계속 사용했던 d3표준 색상을 사용했다. (d3.schemeCategory10) [line 21].


[line 30 ~ 43].

애니매이션 설정부분이다.

애니매이션에 대한 부분은 d3 홈페이지에

Transitions 에서 자세하게 다루고 있다.

https://github.com/zziuni/d3/wiki/API-Reference


대략 해석해보면

duration는 지속시간이며

delay는 생성되는 원 그래프의 시간을 어긋나게 표시한다.



 .attrTween("d", function(d, i) {  // attrTween은 두 속성값 사이를 부드럽게 트랜지션 한다.(보간처리)

var interpolate = d3.interpolate(

{startAngle : d.startAngle, endAngle : d.startAngle}, // 각 부분의 시작 각도

{startAngle : d.startAngle, endAngle : d.endAngle} // 각 부분의 종료 각도

);

return function(t){

return arc(interpolate(t)); // 시간에 따라 처리









위에는 파이(pie차트를 보였지만,

이번에는 아래그림과 같이 도넛(donut)차트를 구현해 본다.


도넛차트의 구현방식은 간단하다.

위 소스에서 [line 20].

var arc = d3.arc().innerRadius(0).outerRadius(100); 에서

var arc = d3.arc().innerRadius(40).outerRadius(100); 으로

안쪽반지름(innerRadius) 값을 설정해주면 된다.






여기서 테두리도 설정을 해보면
[line 26] 아래에 
.attr("stroke", "black")을 추가시키면
원형차트에서 검은색의 테두리가 생성된다.









다음에는 차트에 text를 표현해본다.




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<div class="one-graph"></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var w = 300, h = 300;
var graphData = [50301253];
var colorData = ["red""orange""yellow""blue""purple"];
var pie = d3.pie();
var arc = d3.arc().innerRadius(40).outerRadius(150); 
 
var svg = d3.select(".one-graph")
    .append("svg")
    .attr("width", w)
    .attr("height", h)
    .attr("id""graphWrap");
 
var g = svg.selectAll(".pie")
    .data(pie(graphData))
    .enter()
    .append("g")
    .attr("class""pie")
    .attr("transform","translate("+w/2+","+h/2+")");
 
g.append("path")
    .style("fill"function(d, i) {
        return colorData[i];
    }) 
    .transition()
    .duration(500)
    .delay(function(d, i) { 
        return i * 500;
    })
    .attrTween("d"function(d, i) { 
        var interpolate = d3.interpolate(
            {startAngle : d.startAngle, endAngle : d.startAngle}, 
            {startAngle : d.startAngle, endAngle : d.endAngle} 
        );
        return function(t){
            return arc(interpolate(t)); 
        }
    });
 
g.append("text")
    .attr("transform"function(d) { return "translate(" + arc.centroid(d) + ")"; })
    .attr("dy"".35em")
    .style("text-anchor""middle")
    .text(function(d, i) {
        return graphData[i] + "%";
    });
 
 
svg.append("text")
    .attr("class""total")
    .attr("transform""translate("+(w/2-35)+", "+(h/2+5)+")")
    .text("합계:" + d3.sum(graphData));
 

</script>



차트를 보기 쉽게 전체적으로 크기를 키웠다.

SVG 크기를 키우고 [line 10].

그에 맞게 arc값의 바깥반지름(outerRadius)도 키워준다. [line 14].

그리고 원하는 색상을 넣어보았다. [line 12].

( 이전 소스와 구현 방식이 다르다. )


전 소스 변수 oneGraph에서 g로 보기쉽게 변경했고,

애니매이션의 속도를 조절했다.


text태그로 배열에 값을 넣고 [line 48 ~ 54].

차트에 정중앙에 text를 출력해본다. (합계) [line 57 ~ 60].

호의 중심값 계산은 arc.centroid로 정의한다. [line 49].

https://github.com/zziuni/d3/wiki/API-Reference Shapes 부분 참조











하지만 위 차트는 데이터를 출력하는데

치명적인 결함이 있었다.


데이터값(graphData)을 [80, 50, 12, 5, 3] 을 입력하여

'합 100'이 넘어가자 %가 올바르지 않게 이상하게 출력됐다.



[line 53]을 보면


 

return graphData[i] + "%";

데이터를 순차적으로 출력하고 뒤에 %만 붙였기 때문이다.






다음은 위의 결함을 보완한 예제이다.



기존 text태그에서 배열 값 넣는 방식을 변경했다.


g.append("text")

.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })

.attr("dy", ".35em")

.style("text-anchor", "middle")

.text(function(d, i) {

return graphData[i] + "%";

});


g.append("text")

.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })

.attr("dy", ".35em")

.style("text-anchor", "middle")

.text(function(d, i) {

return  d.endAngle-d.startAngle > 0.2 ?

" (" + Math.round(1000*(d.endAngle-d.startAngle)/(Math.PI*2))/10 + "%)" : ""

}); 







다음은 위 데이터의 이름(출처,라벨)을 달아주고 마친다.





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<div class="one-graph"></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var w = 400, h = 400;
var dataName = ["e trend""dolor sit""consectetur""esp""tempor"];
var graphData = [805022106];
var colorData = ["#FD2F56""#F1E7DE""#343F3A""#5ED1D4""#FFC001"];
var pie = d3.pie();
var arc = d3.arc().innerRadius(70).outerRadius(200);
 
var svg = d3.select(".one-graph")
    .append("svg")
    .attr("width", w)
    .attr("height", h)
    .attr("id""graphWrap");
 
var g = svg.selectAll(".pie")
    .data(pie(graphData))
    .enter()
    .append("g")
    .attr("class""pie")
    .attr("transform","translate("+w/2+","+h/2+")");
 
g.append("path")
    .style("fill"function(d, i) {
        return colorData[i];
    }) 
    .transition()
    .duration(400)
    .delay(function(d, i) { 
        return i * 400;
    })
    .attrTween("d"function(d, i) { 
        var interpolate = d3.interpolate(
            {startAngle : d.startAngle, endAngle : d.startAngle}, 
            {startAngle : d.startAngle, endAngle : d.endAngle} 
        );
        return function(t){
            return arc(interpolate(t)); 
        }
    });
 
g.append("text")
    .attr("transform"function(d) { return "translate(" + arc.centroid(d) + ")"; })
    .attr("dy"".35em")
    .style("text-anchor""middle")
    .text(function(d, i) {
        return  d.endAngle-d.startAngle > 0.2 ?
                dataName[i] + " (" + Math.round(1000*(d.endAngle-d.startAngle)/(Math.PI*2))/10 + "%)" : ""
    });
 
svg.append("text")
    .attr("class""total")
    .attr("transform""translate("+(w/2-35)+", "+(h/2+5)+")")
    .text(" Total:" + d3.sum(graphData));
 
 
</script>









댓글