프로필사진
owgno6
CODELIB
Recent Posts
Recent Comments
«   2025/01   »
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
Archives
Today
Total

티스토리 뷰



전 시간 예제에서는 

외부파일인 TSV(Tab-Separated Values) 파일형식으로 

탭(Tab)으로 데이터를 구분해 차트를 구현해봤다.




이번 장에서는 비슷한 포맷인 

CSV(Comma-Separated Values) 파일형식으로 

차트를 구현하려고한다.


CSV는 몇 가지 필드를 쉼표(,)로 구분한 텍스트 데이터 및 텍스트 파일이다. 

확장자는 .csv이며 MIME 형식은 text/csv이다.


CSV는 지금까지도 대중적으로 많이 사용한다.

보통 스프레드시트(Excel)나, 데이터베이스 소프트웨어 (SQL)등에서 문서화된다.

세부적인 구현식은 소프트웨어에 따라 서로 다르지만

공통점이 있다.

문서를 기록하고 저장에 안전하며 수정하기도 쉽고 

원하는 데이터만 출력하여, 사용이 간편하고 편리하다.


따라서 많은 데이터를 다룰때

CSV파일을 사용하면 편리하다.


서론은 여기까지만하고 바로 본론으로 돌아가서

csv파일을 불어와 차트를 구현해본다.


[HTML]

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
<!DOCTYPE html>
<meta charset="utf-8">
<style> 

.line {
  fill: none;
  stroke: steelblue;
  stroke-width: 2px;
}
 
</style>
<body>
 
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;
var parseTime = d3.timeParse("%d-%m-%y");
var x = d3.scaleTime()
    .range([0, width]);
var y = d3.scaleLinear()
    .range([height, 0]);
var valueline = d3.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.close); });
var svg = d3.select("body").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 + ")");
 
 
d3.csv("./js/data3.csv"function(error, data) {
    if (error) throw error;
 
    data.forEach(function(d) {
     d.date = parseTime(d.date);
         d.close = +d.close;
           });
 
      x.domain(d3.extent(data, function(d) { return d.date; }));
      y.domain([0, d3.max(data, function(d) { return d.close; })]);
 
      svg.append("path")
          .data([data])
          .attr("class""line")
          .attr("d", valueline);
 
      svg.append("g")
          .attr("transform""translate(0," + height + ")")
          .call(d3.axisBottom(x));
 
      svg.append("g")
          .call(d3.axisLeft(y))
          .ticks(10);
 
      });
 
</script>
</body>



[data3.csv]



먼저 그래프의 svg크기와 여백을 설정하고 [line 16 ~ 18].

d3, 시간 파싱 함수로 날짜와 시간을 파싱한다. [line 19] .

포맷의 형식 지정 표기법은 저번에도 언급했으므로

더이상 설명은 생략한다.

아래 API링크 참조(d3.timeParse, format(%))

https://github.com/d3/d3-time-format


Scale의 range를 설정하고 [line 20 ~ 23].

행을 d3.line(Line Chart)으로 정의해준다. [line 24 ~ 26].


[line 27 ~ 32]

페이지 본문에 SVG 객체를 추가해주고

'svg'에 'group(g)'요소를 추가한다.

그리고 그룹(g)을 왼쪽 상단 여백으로 이동시킨다.




사실 여기까지는 그동안 해왔던 내용이고

이해하는데 큰 어려운 부분은 없다.

다음 장부터는 가급적 위 내용의 설명은 생략하여 진행하고

요점만 설명하기로 한다.



[line 35 ~ 41]

CSV파일의 데이터설정(dataSet) 방법은 

데이터에 따라 다르게 정의된다.

하지만 첫번째부터 두번째행까지는 모두 같다.

생성된 모든 오류를 catch하고 배열 'data'로 처리되는 데이터를 로드한다.

중요한 부분은 그 다음 행에 있다.

어떻게 데이터를 포맷팅하고 파싱하는지가 주 요건이다.

다음 아래는 데이터의 따른

다양한 포맷팅방법이다.


//#1

 d3.csv("data2.csv", function(error, data) {

        if (error) throw error;

        data.forEach(function(d) {

         if (d.y == "null") { d.y = null};

         });


 //#2

 d3.csv("data2.csv", function(error, data) {

        if (error) throw error;

        xData = d3.stratify()(data);

        xValue(xData);

        });


 //#3

 d3.csv("data2.csv", function(error, data) {

        if (error) throw error;

        data.forEach(function(d) {

        d.x = xValue(d.x);

        d.y = +d.y;

        });




[line 43 ~ 44]

각 축의 Scale의 domain 데이터 설정한다.

(키 값주입(=.tsv와 동일))


[line 46 ~ 49]

valueline 경로 추가하고

X축과 [line 51~ 53].

Y축을 추가해준다. [line 55~ 57].




csv파일에 데이터를 가정하여

외부파일로 차트를 구현해봤다.



그런데 전 장에서 다뤘던 tsv와

'탭(Tap)'과 '쉼표(,)'가 바뀐 것 외에는 크게 차이가 없었다.

X축에는 dataName,

Y축에는 dataValue가 

변함이 없는 이유다.





하지만 보통의 스프레드시트는 많은 컬럼이 있다.

꼭 두개의 열만 있는건 아니라는 얘기다.











그렇다면 아래와 같이 데이터가 있다면 어떻게할까?


[data2.csv]




이번에는 하나의 JSP파일에서도 구현이 당연 가능하지만

편의와 유틸에 맞게 css와 script부분의 영역을 나눠서 구현해본다.

나눠서 구현하면 스크립트부분에서 구현할때 

좀 더 편리함과 가독성을 높힐 수 있다.

컬러기능과 같은 함수끼리는 태그기능이 있기 때문.


[HTML]

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>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>codelib sample</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
svg {
    width: 680px;
    height: 460px;
    border: 1px solid black;
}
.bar {
    fill: LightPink;
}
.barNum {
    font-size: 9pt;
    text-anchor: middle;
}
.axis text {
    font-famaily: sans-serif;
    font-size: 11px;
}
.axis path, .axis line {
    fill: none;
    stroke: black;
}
.axis_x line {
    fill: none;
    stroke: black;
}
.barName {
    font-size: 9pt;
    text-anchor: middle;
}
</style>
</head>
<body>
    <svg id="myGraph">
        </svg>
    <script src="i_ani_BarChart02_1.js"></script>
</body>
</html>



[JS] i_ani_BarChart02_1.js

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//http://codelib.tistory.com/14
//데이터셋은 CSV 파일
d3.csv("./js/data2.csv"function(error, data) {
    var dataSet = []; //데이터를 저장할 배열 변수
    var labelName = []; //레이블을 넣을 배열 변수
    for ( var i in data[0]) { //최초의 데이터만 처리
        dataSet.push(data[0][i]); //가로 한 줄 모두를 한꺼번에 넣음
        labelName.push(i); //레이블 넣음
    }
 
    //SVG요소의 넓이와 높이를 구함
    var svgEle = document.getElementById("myGraph");
    var svgWidth = window.getComputedStyle(svgEle, null).
    getPropertyValue("width");
    var svgHeight = window.getComputedStyle(svgEle, null).
    getPropertyValue("height");
    //https://developer.mozilla.org/ko/docs/Web/API/Window/getComputedStyle
    svgWidth = parseFloat(svgWidth); //값은 단위가 있으므로 단위를 삭제
    svgHeight = parseFloat(svgHeight); //값은 단위가 있으므로 단위를 삭제
 
    //그래프에 사용할 변수
    var offsetX = 40//X 좌표의 오프셋(어긋난 정도)
    var offsetY = 30//Y 좌표의 오프셋(어긋난 정도)
    var barElements; //막대그래프의 막대 요소를 저장할 변수
    var dataMax = 400//데이터의 최댓값
    var barWidth = 40//막대의 넓이
    var barMargin = 15//막대의 옆 간격
 
    //그래프 그리기(그래프 옵션)
    barElements = d3.select("#myGraph")
    .selectAll("rect"//rect 요소를 지정
    .data(dataSet) //데이터를 요소에 연결
 
    //데이터가 추가될 때
    barElements.enter() //데이터 개수 만큼 반복
    .append("rect"//데이터 개수만큼 rect 요소가 추가됨
    .attr("class""bar"//CSS 클래스를 지정
    .attr("height"0//초깃값 0으로 설정
    .attr("width", barWidth) //넓이 지정
    .attr("x"function(d, i) { //X 좌표를 지정함
        //X좌표를 표시 순서
        return i * (barWidth + barMargin) + offsetX + barMargin; //처음 Y축과 간격
    })
    .attr("y", svgHeight - offsetY) //그래프 가장 아래에 좌표를 설정
 
    //이벤트 추가
    .on("mouseover"function() {
        d3.select(this)
        .style("fill""LightCoral"//마우스오버시 색상
    })
    .on("mouseout"function() {
        d3.select(this)
        .style("fill""LightPink"//마우스아웃시 색상
    })
 
    //애니메이션 처리
    .transition()
    .duration(1000//1초동안 애니메이션 처리
    .delay(function(d, i) {
        return i * 400//0.4초 대기
    })
    .attr("y"function(d, i) { //Y 좌표를 지정
        return svgHeight - d - offsetY; //Y 좌표를 계산
    })
    .attr("height"function(d, i) { //넓이설정.2번째의 파라미터에 함수지정
        return d; //데이터 값을 그대로 높이로 지정
    })
 
    //text 요소 지정
    barElements.enter()
    .append("text"//text 요소 추가
    .attr("class""barNum"//CSS 클래스를 지정
    .attr("x"function(d, i) { //X 좌표를 지정함
        //막대그래프의 표시 간격을 맞춤
        return i * (barWidth + barMargin) + offsetX + (barWidth/2 + barMargin);
    })
    .attr("y", svgHeight - 10 - offsetY) //Y 좌표 출력 위치 지정
    .text(function(d, i) { //데이터 표시
        return d;
    })
 
    //눈금을 표시하기 위한 스케일 설정
    var yScale = d3.scaleLinear() //스케일 설정
    .domain([ 0, dataMax ]) //원래크기
    .range([ dataMax, 0 ]) //실제 출력 크기
 
    //세로(Y축) 방향의 눈금을 설정하고 표시
    d3.select("#myGraph")
    .append("g")
    .attr("class""axis")
    .attr("transform",
            "translate(" + offsetX + "," + ((svgHeight - dataMax) - offsetY) + ")")
    .call(d3.axisLeft(yScale)//스케일 적 용
            .ticks(10)//눈금의 표시 위치를 왼쪽으로 지정
    )
 
    //가로(X축) 방향의 선을 표시
    d3.select("#myGraph")
    .append("rect")
    .attr("class""axis_x")
    .attr("width", svgWidth)
    .attr("height""1")
    .attr("transform",
            "translate(" + offsetX + ", " + (svgHeight - offsetY) + ")")
 
    //막대의 레이블을 표시
    barElements.enter()
    .append("text")
    .attr("class""barName")
    .attr("x"function(d, i) { //X좌표 지정
        return i * (barWidth + barMargin) + offsetX + (barWidth/2 + barMargin); //막대그래프의 표시 간격을 맞춤
    })
    .attr("y", svgHeight - offsetY + barMargin)
    .text(function(d, i) {
        return labelName[i]; //레이블 이름을 반환
    })
 
});



내용이 생각보다 많고 설명이 길어질 것 같아서

코딩 옆에 주석으로 달아봤다.

[line 3 ~ 9]는 csv파일 설정부분이다.

데이터의 내용이나 형식이 달라서

전 예제와 위 예제코딩이 서로 다른 구현방식임을 알 수 있다.

[line 22 ~ 27]은 그래프에 사용되는 수치를 변수화 시켰다.

지금까지는 그래프 옵션을 변경할때마다 

각 영역별로 요소에 해당되는 자리에 직접 일정수치와 설정을 변경해줘야했지만,

변수화를 시키면 그래프 옵션만 바꿔주면된다. 














댓글