프로필사진
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

티스토리 뷰


소스코드에 배열이나 데이터설정값에

데이터를 입력하지 않고, 

외부파일에서 데이터를 읽어

차트를 띄워보기로 한다.


많은 방법이 있는데

그중 .tsv나 .csv와 같은 파일형식이

가장 대표적이다.






TSV파일이란?


TSV 파일 형식, "탭(Tab)으로 구분 된 값"을 의미하고,

이 탭으로 구분 된 값 파일이 많은 스프레드 시트 응용 프로그램에 의해 만들어지고 사용된다. 


예를 들어, "[data.tsv]" 의 파일형식은

메모장에서 ","(콤마) 대신에 Tab키로 간격을 맞춰줘야 한다.

이는 ","가 많이 사용이 되는 주로 화폐나 수치단위 등과 같은 숫자데이터에 사용하기 적절하다.


","(콤마)로 간격 맞추는건 CSV파일이다.

CSV파일은 보통 스프레드 시트에서 응용되어 생성된다.

CSV파일은 이번 장에서 다루지 않고,

먼저 TSV파일 구현부터 해본다.






[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
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
http://codelib.tistory.com d3-bar chart.tsv
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.grid line {
    stroke: lightgrey;
    stroke-opacity: 0.4;
    shape-rendering: crispEdges;
}
.bar {
    fill: orange;
}
.bar:hover {
    fill: orangered ;
}
.toolTip {
    position: absolute;
    border-radius: 4px 4px 4px 4px;
    font-weight: bold;
    padding: 12px;
    background: rgba(0, 0, 0, 0.8);
    color: #fff;
}
.toolTip:after {
    box-sizing: border-box;
    display: inline;
    font-size: 10px;
    width: 100%;
    color: rgba(0, 0, 0, 0.8);
    content: "\25BC";
    position: absolute;
    left:-4px;
    top:36px;
    text-align: center;
}
.toolTip.n:after {
    margin: -1px 0 0 0;
    top: 100%;
    left: 0;
}

</style>
<body>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
 
var margin   = {top: 40, right: 20, bottom: 30, left: 40};
var formatPercent = d3.format(".0%");
var svg = d3.select("svg");
var width     = parseInt(svg.style("width"), 10- margin.left - margin.right;
var height    = parseInt(svg.style("height"), 10- margin.top - margin.bottom;
var xScale = d3.scaleBand()
    .rangeRound([0, width], .1).padding(0.2);
var yScale = d3.scaleLinear()
    .rangeRound([height, 0]);
var svgG = svg.append("g")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .attr("transform""translate(" + margin.left + "," + margin.top + ")");
var tooltip = d3.select("body").append("div")
    .attr("class""toolTip")
    .style("display""none");
 
 
d3.tsv("./js/data.tsv", type, function(error, data) {
    xScale.domain(data.map(function(d) { return d.letter; }));
    yScale.domain([0, d3.max(data, function(d) { return d.frequency; })]);
 
      svgG.append("g")//+
          .attr("class""grid")
              .attr("transform""translate(0," + height + ")")
              .call(d3.axisBottom(xScale)
                  .tickSize(-height)
                  );
 
      svgG.append("g")
          .attr("class""grid")
          .call(d3.axisLeft(yScale)
              .ticks(10)
              .tickSize(-width)
             .tickFormat(formatPercent)
              );
 
    var barG = svgG.append("g");
 
     barG.selectAll("rect")
         .data(data)
         .enter().append("rect")
         .attr("class""bar")
         .attr("height"function(d, i) { return height - yScale(d.frequency)})
         .attr("width", xScale.bandwidth())
         .attr("x"function(d, i) { return xScale(d.letter); })
         .attr("y"function(d, i) { return yScale(d.frequency); })
         .on("mouseover"function() { tooltip.style("display""block"); })
         .on("mouseout",  function() { tooltip.style("display""none"); })
        .on("mousemove"function(d) {
             tooltip.style("left", (xScale(d.letter) + xScale.bandwidth()/2 - 10+ "px");
             tooltip.style("top", (yScale(d.frequency) + 10+ "px");
             tooltip.html("<strong>Frequency:</strong> <span style='color:red'>" + d.frequency + "</span>");
         });
 
     });
 
function type(d) {
    d.frequency = d.frequency;
 
      return d;
}
 
</script>


[data.tsv]

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
letter    frequency
A    .06167
B    .01492
C    .02780
D    .04253
E    .12702
F    .02288
G    .02022
H    .06094
I    .06973
J    .00153
K    .00747
L    .04025
M    .02517
N    .06749
O    .07507
P    .01929
Q    .00098
R    .05987
S    .06333
T    .09056
U    .02758
V    .01037
W    .02465
X    .00150
Y    .01971
Z    .00074
cs


[line 52]

d3.format 함수는 인자로 숫자를 받고 포맷팅된 숫자형 문자열로 반환해준다.

'%'로 정의하여 '%단위'의 문자로 출력된다.

format 관련해서는 아래에서 다루도록 하겠다.


[line 69 ~ 71]

.tsv 파일을 불러오기위한 작업이다.

"./js/data.tsv" 주소의 tsv파일을 불러오며 임의의 인자값(data)을 넣어준다.

이 인자값은 xScale과 yScale에 데이터 맵핑할때 사용되며 

[line 91]에서 차트 도형(Bar)을 생성할때도 사용한다.




그 외에는 보는 것과 같이 지금까지

구현방식과 별다를 것이 없고

비교적 간단해보인다.


하지만 Scale에서 보면

지금까지 Scale구현 방식과 다른 것을 알 수 있다.

domain부분이 없어지고,

tsv파일을 불러오는 함수쪽에

생성된 것을 알 수 있다. [line 70 ~ 71].

그 이유는 키(Key)값 주입때문이다.

data.tsv 파일의 내용을 자세히보면

첫번째 라인에 컬럼명이 있다. 

키값 역할인 컬럼을 입력하여 

각각의 해당 컬럼을 x축과 y축에 주입한 것이다.


[line 108 ~ 111]

숫자(정수)를 문자로 변환하기 위해 사용했다.

사용하지 않아도 상관은 없다.

[line 69]에서도 type을 지워주면 된다.

다만, 문자로 변환하지 않으면

".00150"같은 데이터는 ".0015"로 출력되어 나온다.

반대로 ".0015"처럼 출력을 원하고자 한다면

다음 아래와 같이 정의해준다.


function type(d) {

    d.frequency = +d.frequency;


+연산자를 사용하면 숫자로 출력된다.


의도치않는 데이터변형을 막을 수 있다.











다음은 결과 차트에서 

부족한 부분을 채워보고

잘못 출력된 부분은 수정해보면서 마무리한다.




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
119
120
121
122
123
124
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
   font: 10px sans-serif;
}
/*
.grid line {
    stroke: lightgrey;
    stroke-opacity: 0.4;
      shape-rendering: crispEdges;
}
 */
.bar {
     fill: #7C96C9;
}
.bar:hover {
     fill: #1F50B5 ;
}
.toolTip {
    position: absolute;
    border-radius: 4px 4px 4px 4px;
    font-weight: bold;
    padding: 12px;
    background: rgba(0, 0, 0, 0.8);
    color: #fff;
    min-width: 100px;
}
.toolTip:after {
    box-sizing: border-box;
    display: inline;
    font-size: 10px;
    width: 100%;
    color: rgba(0, 0, 0, 0.8);
    content: "\25BC";
    position: absolute;
    left: 0px;
    top: 35px;
    text-align: center;
}
.toolTip.n:after {
    margin: -1px 0 0 0;
    top: 100%;
    left: 0;
}
</style>
<body>
<svg style="width:960px; height:550px; border:0px solid;"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
 
var margin     = {top: 60, right: 20, bottom: 30, left: 40};
var formatPercent = d3.format(".0%");
var svg = d3.select("svg");
var width     = parseInt(svg.style("width"), 10- margin.left - margin.right;
var height     = parseInt(svg.style("height"), 10- margin.top - margin.bottom;
var xScale = d3.scaleBand()
    .rangeRound([0, width], .1).padding(0.2);
var yScale = d3.scaleLinear()
    .rangeRound([height, 0]);
var svgG = svg.append("g")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .attr("transform""translate(" + margin.left + "," + margin.top + ")");
var tooltip = d3.select("body").append("div")
    .attr("class""toolTip")
    .style("display""none");
 
 
 
d3.tsv("./js/data.tsv", type, function(error, data) {
    xScale.domain(data.map(function(d) { return d.letter; }));
    yScale.domain([0, d3.max(data, function(d) { return d.frequency; })]);
 
      svgG.append("g")
              .attr("transform""translate(0," + height + ")")
              .call(d3.axisBottom(xScale)
                      .tickSize(5)//
                      .tickPadding(5)//
                      );
 
      svgG.append("g")
          .call(d3.axisLeft(yScale)
              .ticks(10)
              .tickSize(10)//
             .tickPadding(5)//
             .tickFormat(formatPercent)
              );
 
      svgG.append("text")//
          .attr("transform""rotate(-90)")
          .attr("yScale"10)
          .attr("dy""1.3em")
          .style("text-anchor""end")
          .text("Frequency" + "    ");
 
    var barG = svgG.append("g");
 
     barG.selectAll("rect")
          .data(data)
         .enter().append("rect")
          .attr("class""bar")
          .attr("height"function(d, i) { return height - yScale(d.frequency)})
          .attr("width", xScale.bandwidth())
          .attr("x"function(d, i) { return xScale(d.letter); })
          .attr("y"function(d, i) { return yScale(d.frequency); })
          .on("mouseover"function() { tooltip.style("display""block"); })
         .on("mouseout",  function() { tooltip.style("display""none"); })
         .on("mousemove"function(d) {
             tooltip.style("left", (xScale(d.letter) + xScale.bandwidth()/2 - 15 ) + "px");
             tooltip.style("top", (yScale(d.frequency) +  15+ "px");
             tooltip.html("<strong>Frequency:</strong> <span style='color:#3162C7; font-size: 1.2em;'>" 
+ d.frequency + "</span>");
         });
 
     });
 
function type(d) {
    d.frequency = d.frequency;
 
      return d;
}
 
</script>



Grid를 없애보고,

CSS관련설정과 tick설정에 따라서

SVG크기와 margin을 보기좋게 재설정해줬다.


이번에는 tick설정을 다양하게 해봤다.

[line 84 ~ 87] 처럼 

tick함수 부분을

차트에서는 자세하게 알아볼 필요가 있다.

이전에 말하고자했던 format함수 지정형식도

tick함수 영역에서 지정한다. 


tick함수 API

https://github.com/zziuni/d3/wiki/SVG-Axes#tickSize


 

              .ticks(10)                               // 눈금 개수

              .tickSize(10)                           // 눈금 '-' 크기

              .tickPadding(5)                       // 눈금과 눈금 사이간격

              .tickFormat(formatPercent)      // tick값 설정 format을 설정하거나 가져온다. [line 53] 

                                                               tickFormat경우 형식을 지정하고, 

                                                               지정된 함수의 형식을 설정하고 축으로 나타낸다. 

                                                               형식을 지정하지 않으면 기본값은 null.



format형식 API

https://github.com/zziuni/d3/wiki/Formatting#d3_format





    


y축 text범례 설정부분. [line 90 ~ 95].

      tick눈금 설정부분. [line 84 ~ 87].






  


차트를 살펴보다가

일정영역에서는 text가 길거나 

텍스트가 다음 단으로 내려가

원치않게 텍스트박스가 변형됐다..

따라서 [line 27]처럼 최소 고정영역 값을 준다.(min-width: 100px;)

그럼 텍스트 내용이 아무리 작아도 width: 100px는 유지한다.













댓글