본문 바로가기

Javascript/Node.js

D3.js 사용법을 알기 위한 튜토리얼 예제 분석(D3공식홈페이지 example과 실시간 Line 그래프 예제 분석)

반응형

D3.js 기본 문법 정리

D3.js는 Data-Driven Documents의 약어로 데이터를 렌더링할 수 있는(그릴 수 있는, 다룰수 있는) 웹 기반 문서 라이브러리다.

좀 더 간단 명료하게 설명하면 데이터 시각화 라이브러리다. (실시간 데이터 차트 및 다양한 그래프)

D3 공식홈페이지에 가보면 엄청나게 다양한 그래프들이 있고 거기서 원하는 것을 골라서 소스를 얻어갈 수 있다.

라이센스도 BSD로 영리든 비영리든 마음대로 사용, 수정해도 된다.

D3.js의 기본 문법(사용법)을 숙지하고 예제코드를 분석할 것이다.


- D3 작업 순서

1. 데이터를 불러온다.

2. 작업할 공간을 선택한다.

3. 공간에 불러온 데이터를 연결(바인딩)한다.

4. 각각 연결한 것을 원하는대로 그린다.

1
2
3
4
5
6
7
var dataset = [ 155322021 ];
d3.select("body")          // 1
  .selectAll("p")          // 2
  .data(dataset)           // 3
  .enter()                 // 4
  .append("p")             // 5
  .text("hi jeongpro!");   // 6


위 코드는 아주 간단한 D3 코드다. (d3를 이용하려면 d3.js 파일이나 cdn으로 한 줄만 가져오면 됨 위에선 생략)

첫 번째줄에서는 데이터 배열을 불러왔다. (데이터 불러오기)

csv, tsv, json등의 파일에서 데이터를 불러올 수도 있고 서버에서 데이터를 response 받을 수도 있다.

어떤 방법으로든 데이터를 불러온다.

(1) d3.select("body") 로 <body>태그를 선택했다. (jquery selector랑 비슷하네? 하지만 차이점은 아래에서 설명..)

(2) .selectAll("p") 로 위에서 선택한 <body>태그 자식 노드로 있는 모든 <p>태그를 선택했다. (공간 선택하기)

jquery에서 사용하던 선택자와 비슷해보여서 익숙하지만 아주 명확한 차이점이 있다.

jquery에서는 이미 존재하는 태그를 선택해야하지만 d3에서는 존재하지 않는 태그를 선택해야한다.

무슨말인지는 아래에서 바인딩을 보면 알 수 있다.

(3) .data(dataset) 으로 데이터를 불러와 바인딩한다.

(4) .enter() 를 통해 바인드가 되지않은 <p>요소에 데이터를 넣어 새로운 selection을 반환한다.

아까 선택하기에서 존재하지 않는 태그를 선택해야된다고 한 이유가 여기에 있다.

만약 <body>태그 안에 2개의 <p>태그가 존재했다면 그 2개의 p태그에는 데이터 바인딩이 적용되지 않고 3개의 p태그를 새로 만들어 데이터를 바인딩했을 것이다. (3개는 위에서 데이터배열의 수가 5개이기 때문 5-2=3)

<출처 : https://www.slideshare.net/aliceinwoon/d3js-2 >

(5) .append("p") 를 통해 해당 데이터 공간 수 만큼 문서요소를 만든다.

(6) .text("hi jeongpro!"); 로 해당 p 태그들에 text를 삽입한다.

<body에 p태그가 2개가 있을 때 결과>

1
2
3
4
5
p태그 내용1
p태그 내용2
hi jeongpro!
hi jeongpro!
hi jeongpro!


<body에 p태그가 없을 때 결과>

1
2
3
4
5
hi jeongpro!
hi jeongpro!
hi jeongpro!
hi jeongpro!
hi jeongpro!



* 위에서 의문이 들었을 수 있다. 없는 공간을 선택한다고 하면 .selectAll("div")로 해도 되고 .selectAll("jeongpro")라고 해도 되지않을까? 맞다. 이렇게 해도 동일하게 빈 selection들을 반환한다. (div, jeongpro 태그가 없다면)

D3 공식홈페이지 예제 소스 분석

기본 문법을 알았보았으니 쉬운 예제소스를 분석해보고 실행해본다.

아까 말했듯이 d3 공식홈페이지에서 이미 충분한 그래프 유형과 소스를 제공하고 있다. 열어서 분석하고 사용하면 끝이다. (basic chart인 line chart를 선택했다.)

data.tsv

- 참고로 tsv는 csv가 콤마로 데이터를 구분하는 것처럼 tab으로 데이터를 구분하는 데이터형식이다.

이런 데이터가 엄청나게 존재한다. 링크(https://bl.ocks.org/mbostock/3883245)

[index.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
<!DOCTYPE html>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
 
var svg = d3.select("svg"), // svg태그 선택
    margin = {top: 20, right: 20, bottom: 30, left: 50},// 그래프에서 margin을 정함
    width = +svg.attr("width"- margin.left - margin.right, // 전체 width에서 양쪽 마진 제외
    height = +svg.attr("height"- margin.top - margin.bottom,// 전체 heigth에서 위아래로 마진 제외
    g = svg.append("g").attr("transform""translate(" + margin.left + "," + margin.top + ")");
    //svg에 <g>태그를 넣고 속성을 넣음 <g transform="translate(50,20)">
var parseTime = d3.timeParse("%d-%b-%y");
//시간 파싱 함수
var x = d3.scaleTime()
    .rangeRound([0, width]);
//x축은 시간값 범위 0~width
var y = d3.scaleLinear()
    .rangeRound([height, 0]);
//y축은 linear 순차적인 값, 범위 height~0
//y축은 좌상단이 (0,0)이고 y값이 증가할수록 밑으로 내려가도록 그리기때문
var line = d3.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.close); });
//d3.line()으로 선을 그리는데 x값은 콜백으로 x(데이터.date값), y값은 y(데이터.close값)
//d3.tsv로 tsv파일을 읽을 수 있음
d3.tsv("data.tsv"function(d) {
  d.date = parseTime(d.date);
  d.close = +d.close;
  return d;
}, function(error, data) {
  if (error) throw error;
//d3.extent는 [최소,최대] 시작과 끝 직선 어디만큼 그릴지를 리턴
//domain은 입력 값의 구간, range는 출력 값의 범위
  x.domain(d3.extent(data, function(d) { return d.date; }));
  y.domain(d3.extent(data, function(d) { return d.close; }));
 
//aixs는 x축선과 y축선을 그리기위한 용도다.
//x축
  g.append("g")
      .attr("transform""translate(0," + height + ")")
      .call(d3.axisBottom(x))
    .select(".domain")
      .remove();
//y축
  g.append("g")
      .call(d3.axisLeft(y))
    .append("text")
      .attr("fill""#000")
      .attr("transform""rotate(-90)")
      .attr("y"6)
      .attr("dy""0.71em")
      .attr("text-anchor""end")
      .text("Price ($)");
//데이터선
  g.append("path")
      .datum(data)
      .attr("fill""none")
      .attr("stroke""steelblue")
      .attr("stroke-linejoin""round")
      .attr("stroke-linecap""round")
      .attr("stroke-width"1.5)
      .attr("d", line);
});
 
</script>
cs

주석을 달아보았으나 명쾌하게 해설하지 못했다. 이부분을 좀 더 공부해서 수정할 예정이다.


D3 실시간 그래프(Realtime streaming)

실시간 그래프를 그리는 간단한 예제도 넣어놓고 분석해보면 좋을듯 하다.

[index.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
<!DOCTYPE html>
<html lang="en" >
<head>
  <meta charset="UTF-8">
  <title>D3 REaltime chart </title>
<style>
    svg {
      font: 10px sans-serif;
    }
    .line {
      fill: none;
      stroke: #000;
      stroke-width: 1.5px;
    }
    .axis path,
    .axis line {
      fill: none;
      stroke: #000;
      shape-rendering: crispEdges;
    }
</style>
</head>
<body>
    <script src='https://d3js.org/d3.v3.min.js'></script>
    <script  src="js/index.js"></script>
</body>
</html>
 


[js/index.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
var n = 40,
    random = d3.random.normal(0, .2),
    data = d3.range(n).map(random);
 
var margin = {top: 20, right: 20, bottom: 20, left: 40},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;
 
var x = d3.scale.linear()
    .domain([0, n - 1])
    .range([0width]);
 
var y = d3.scale.linear()
    .domain([-11])
    .range([height0]);
 
var line = d3.svg.line()
    .x(function(d, i) { return x(i); })
    .y(function(d, i) { return y(d); });
 
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 + ")");
 
svg.append("defs").append("clipPath")
    .attr("id""clip")
  .append("rect")
    .attr("width"width)
    .attr("height"height);
 
svg.append("g")
    .attr("class""x axis")
    .attr("transform""translate(0," + y(0+ ")")
    .call(d3.svg.axis().scale(x).orient("bottom"));
 
svg.append("g")
    .attr("class""y axis")
    .call(d3.svg.axis().scale(y).orient("left"));
 
var path = svg.append("g")
    .attr("clip-path""url(#clip)")
  .append("path")
    .datum(data)
    .attr("class""line")
    .attr("d", line);
 
tick();
 
function tick() {
 
  // push a new data point onto the back
  data.push(random());
 
  // redraw the line, and slide it to the left
  path
      .attr("d", line)
      .attr("transform"null)
    .transition()
      .duration(500)
      .ease("linear")
      .attr("transform""translate(" + x(-1+ ",0)")
      .each("end", tick);
 
  // pop the old data point off the front
  data.shift();
 
}
cs

참고 사이트

https://d3js.org/

http://blog.nacyot.com/articles/2015-02-02-d3-selection/

반응형