10장 d3를 이용한 다이나믹 그래픽스

10.1 d3.js란?

9장에서 <svg>나 <canvas>를 이용하여 브라우저에 여러 가지 기본 도형을 그리는 방법을 살펴보았다. 하지만 브라우저에 <svg>나 <canvas>를 이용하여 동적으로 움직이는 그래픽을 그리는 것은 쉽지 않다. 브라우저에 다양하고 동적인 그래픽을 쉽게 그리기 위해 여러 자바스크립트 클래스와 메소드를 만들어 모아 놓은 프로그램을 자바스크립트 그래프 라이브러리라 부른다.

동적 그래프 라이브러리에는 d3.js, jqplot, Google Charts 등 여러 종류가 있다. 이중에서 d3.js(d3는 Data-Driven Documents의 첫 글자 D가 3개임을 의미)는 <그림 10.1.1>과 같이 막대그래프, 파이차트, 선그래프와 같은 단순한 그래프와 지리정보, 네트워크, 워드 클라우드 등 다채로운 그래픽을 동적으로 그릴 수 있는 데이터시각화의 차원을 한 단계 높인 도구로 각광받고 있다(https://d3js.org 참조)

<그림 10.1.1> 웹사이트 https://d3js.org에 있는 d3.js 예제

d3.js를 잘 활용하면 다양한 형태의 그래픽을 역동적으로 그릴 수 있는 장점이 있으며 PC 뿐만 아니라 스마트폰이나 태블릿에서도 잘 작동한다. 다이나믹한 그래픽은 단순한 2차원 그래프보다 시각화 결과의 가독성이 커지고 훨씬 더 흥미롭다. 하지만 d3.js로 그래픽을 그리기 위해서는 사용자가 일일이 브라우저 위에 좌표와 속성을 지정하는 몇 단계의 과정을 거치기 때문에 번거롭고 시간이 걸릴 수 있다. 단순한 그래픽은 jQplot이나 Google Charts를 이용하는 것이 시간이나 비용 면에서 유리하다.

d3는 마이크 보스톡(Mike Bostock)이 2011년에 발표한 버전 2.0.0부터 다이나믹 대화형 그래프 라이브러리로 주목을 받기 시작했다. d3는 기본적으로 SVG, HTML5, CSS의 표준을 따르기 때문에 크롬에서는 잘 작동되나 인터넷 익스플로러 및 엣지에서는 일부 기능이 제한될 수 있다. 대신 플래시나 자바 애플릿을 사용하지 않고도 역동적인 정교한 그래픽을 만들 수 있는 장점이 있다.

10.1.1 d3.js의 특징

d3.js는 9장에서 연구한 SVG를 이용하여 벡터기반 그래픽을 그린다. d3.js는  HTML 문서의 자바스크립트 코드가 위치하는 <script> … </script> 태그 안에 삽입하여 이용한다. d3.js는 HTML 요소를 선택하거나, SVG 객체를 생성하고 스타일을 주며 변환 및 동적인 효과를 줄 수 있는 많은 자바스크립트 메소드를 가지고 있다. 이러한 d3 객체는 CSS를 이용하여 스타일을 줄 수도 있다. 대용량 데이터를 처리할 수 있는 함수도 제공하는데 JSON, CSV, geoJSON 형식 등의 데이터를 지원한다.

d3.js는 여러 메소드를 동시에 사용할 때 ‘.’을 삽입한 자바스크립트의 메소드 체인을 이용한다.

객체명.메소드명().메소드명(), …

메소드 체인이 길어지면 읽기가 힘들어 다음과 같이 여러 줄에 걸쳐서 적는 방법을 많이 이용한다.

d3.select(“#myGraph”)

.selectAll(“rect”)

.data(dataSet)

.enter()

한 명령어가 끝났을 때 세미콜론 ‘;’을 명령어 끝에 넣기도 하는데 반드시 필요하지는 않다.

d3.js의 핵심 디자인 특징은 셀렉션(selection), 트랜지션(transition), 데이터 바인딩(binding), 데이터를 이용한 노드 붙이기(appending node)로 요약 할 수 있다.

가. 셀렉션(Selection)

d3.js의 핵심 디자인은 DOM 노드들을 선택할 수 있는 CSS 스타일의 선택자(selector)를 사용 가능하게 한 것이다. 예를 들면 d3.js에서는 HTML 문서의 <p> … </p> 태그로 구성된 모든 문단의 색을 다음과 같이 지정할 수 있다. 태그 p를 선택자로 이용하였으며, CSS 클래스를 이용하고, 속성(attribute)의 값(value)을 지정하였다.

d3.selectAll(“p”)             // 모든 <p> 요소 문단 선택

.style(“color”, “blue”)     // 문단의 텍스트 색을 파랑색으로 지정

.attr(“class”, “pfont”)     // 문단의 텍스트 속성을 “pfont” 클래스로 지정

.attr(“x”, 50);             // 문단의 시작 위치 “x” 좌표 속성을 50픽셀로 지정

위 코드에서 첫 번째 명령문 d3.selectAll()은 셀렉션의 핵심이다. 이와 유사한 select() 메소드도 있는데 selectAll() 은 모든 요소를 선택하고 select() 메소드는 첫 번째 요소만 선택한다. 위의 예는 데이터와 연결되어 있지 않지만 셀렉션은 데이터에 연결되어 있을 수도 있는 하나 이상의 HTML 요소 그룹이다.

HTML의 각 요소는 스타일(style), 속성(attribute), 프로퍼티(property)의 3대 정보로 특징을 가질 수 있다. d3.js에서는 위 코드의 두 번째 줄과 같이 style() 메소드로 CSS와 같은 스타일을 주며, 세 번째와 네 번째 줄과 같이 attr() 메소드로 속성을 지정할 수 있다. 다음과 같이 property()를 이용해 요소의 상태(예를 들어 체크박스가 체크되어 있는지 여부)를 지정할 수도 있다.

d3.select(“checkbox”).property(“checked”, true)

이와 같이 d3 객체가 HTML 요소를 선택하거나 생성 또는 제거하고, 요소의 속성을 지정하거나 바꾸며, 데이터에 따라 수정 및 변경이 가능하도록 한 것이 d3.js의 기본 개념이며 원칙이다.

나. 트랜지션(Transition)

d3.js는 transition()을 이용하여 생성된 객체가 시간에 따라 속성 값이 부드럽게 변화하며 다이나믹한 그래픽을 그릴 수 있게 하여준다. 예를 들어 다음 코드는 HTML의 태그 <p>…</p>로 지정된 문단의 색이 서서히 빨강색으로 바뀌게 하여 준다.

d3.selectAll(“p”)           // 모든 <p> 요소 문단 선택

.transition(“trans”)      // “trans로 트랜지션을 지정

.delay(0)                 // 0ms 이후에 트랜지션 시작

.duration(500)            // 500ms 동안 트랜지션 진행

.ease(“linear”)           // 트랜지션 진행을 선형(linear)으로 진행

.style(“color”, “red”);   // 색을 빨강색으로 지정

다. 데이터 바인딩(Data-binding)

d3.js에서는 각 데이터의 값과 SVG 객체의 속성을 묶어서(binding) 연결해 줄 수 있다. 예를 들어 다음 코드에서는 countriesData의 income, life, pop, color를 원의 속성(cx, cy, r, colors, values)과 바인딩하였다.

// 데이터

var countriesData = [

{ name:”Ireland”,  income:53000, life: 78, pop:6378, color: “black”},

{ name:”Norway”,   income:73000, life: 87, pop:5084, color: “blue” },

{ name:”Tanzania”, income:27000, life: 50, pop:3407, color: “grey” }

];

// SVG 캔버스 생성

var svgCanvas = d3.select(“#hook”).append(“svg”)

.attr(“width”, 120)

.attr(“height”, 120)

.style(“background-color”, “#D0D0D0”);

// 데이터로부터 SVG 요소의 속성 지정

svgCanvas.selectAll(“circle”)      // 원으로 이루어지는 가상 템플레이트 생성

.data(countriesData)            // 데이터 바인딩

.enter()                        // 데이터의 각 행에 대하여 반복한 for문

.append(“circle”)               // 원과 각 데이터를 바인딩(bind circle & data row)

.attr(“id”, function(d) { return d.name })        // 원의 id를 country name으로 지정

.attr(“cx”, function(d) { return d.income/1000 }) // 원 중심 x좌표를 income 이용

.attr(“cy”, function(d) { return d.life })        // 원 중심 y좌표를 life 이용

.attr(“r”,  function(d) { return d.pop/1000 *2 }) // 원의 반지름을 population 이용

.attr(“fill”, function(d) { return d.color });    // 원의 색을 나라의 color 이용

데이터와 바인딩하지 않는 셀렉션도 있지만 위의 예처럼 바인딩하면 d3의 강력한 시각화 기법을 이용할 수 있다. 그리고 원과 같은 도형이 움직이는 행동(transitions, events)도 데이터에 종속적으로 묶어서 지정할 수 있다.

라. 데이터를 이용한 노드 붙이기(Appending node)

이미 위의 예에서 보았듯이 d3.js는 SVG 요소에 데이터 바인딩을 한 후에는 enter() 메소드를 이용하여 각 데이터를 하나씩 “업데이트”하고 “나오며(exit)” DOM 트리에 노드 붙이기(appending node)를 반복한다. 이 부분은 d3가 일반 프로그래밍 언어의 for 루프와 유사한 기능을 하는 다른 개념의 프로그램이다. enter() 뒤에 체인으로 연결된 모든 메소드는 역시 각 데이터마다 실행된다.

10.1.2 d3.js의 API 구조

d3.js의 모든 기능은 d3라는 객체에 들어 있는데 이 객체는 다양한 메소드를 가지고 있다. 이밖에도 d3.js는 d3.layout, d3.scale, d3.geo, d3.svg, d3.geom, d3.tim, d3.behavior 등의 많은 객체를 가지고 있다([표 10.1.1] 참조). 각 객체에 어떠한 메소드가 있고 기능이 무엇인지 살펴보려면 다음 API 사이트를 참조하면 된다.

https://github.com/d3/d3/blob/master/API.md

d3.js가 가지고 있는 수백 개의 메소드는 기능별로 다음과 같이 그룹으로 묶을 수 있는데 주요 메소드를 살펴보면 다음과 같다.

가. 수학(Math)관련 메소드

정규분포, 로그정규분포 등의 확률 난수 생성

2차원 변환: 이동(translation), 회전(rotation), 기울임(skew), 축척(scaling) 등

나. 배열(Array) 관련 메소드

d3.js는 기존 자바스크립트의 배열 관련 메소드(sort, reverse, splice, shift, unshift, concat, join, slice, indexOf, lastIndexOf, filter, every, forEach, map, some, reduce and reduceRight 등)와 이를 보완하는 메소드를 제공한다.

d3.js는 최소, 최대, 합계, 평균, 중앙값, 백분위수를 계산하는 통계 관련 메소드를 배열에서 제공한다. 아울러 순서(ordering), 셔플(shuffling), 순열(permuting) 등의 메소드와 맵(map) 및 집합(set) 관련 메소드를 제공한다.

다. 기하(Geometry) 관련 메소드

d3.js는 점들의 집합에 대한 컨벡스헐(convex hull)을 계산하는 메소드와 다각형(polygon)의 기본 연산에 관한 메소드를 제공한다.

계층적 형식의 데이터 구조를 지원하는 메소드를 제공한다.

라. 색(Color) 관련 메소드

d3.js는 RGB, HSL, HCL 등의 컬러 표현 방식을 지원한다.

색을 밝게 하거나(brightening), 진하게(darkening) 그리고 색상을 보간(interpolation)하는 메소드를 제공한다.

d3가 제공하는 색은 다음과 같이 이용한다.

var colors = d3.scaleOrdinal(d3.schemeCategory10);

여기서 schemeCategory10과 그밖에 많이 이용되는 색은 다음과 같다.

다음은 d3를 이용하여 작성한 간단한 프로그램 예이다.

[예제 10.1.1]은 단순한 2차원 도형을 d3를 이용하여 구현하였다. 10.2절에서는 d3를 이용하여 간단한 막대그래픽을 그려본다. 10.3절에서는 d3 도형의 그룹화 및 변환에 대해서 살펴보고 이를 이용한 막대그래프 축 설정을 한다. 10.4절에서는 d3 도형의 애니메이션을 살펴보고, 10.5절에서는 다양한 데이터 불러오기 등 여러 가지 기능을 하나씩 추가하면서 전반적인 d3 프로그램 구조를 살펴본다.

10.2 가로형 막대그래프 예

[예제 10.1.1]과 같이 단순한 도형을 그리기 위해서는 d3가 반드시 필요하지는 않을 수 있다. 하지만 d3는 다이나믹한 그래픽을 그리려고 할 때 매우 효과적이다. 이 절에서는 먼저 d3.js를 이용하여 간단한 가로형 막대그래픽을 그려본다. 단순한 그래프에서 시작하여 색 넣기를 하고, 동적인 그래픽을 만들어 본다.

10.2.1 가로형 막대그래프

배열에 간단한 데이터를 입력한 후 SVG의 직사각형 요소를 이용하여 가로형 막대그래픽을 그리는 다음 예제를 살펴보자.

10.2.2 여러 개의 데이터 처리를 위한 셀렉션 메소드

[예제 10.2.1]에서는 데이터의 개수가 세 개이어서 svg로 직사각형을 그리기위해 세 개의 d3 객체와 append(), attr() 메소드를 이용하였다. 하지만 데이터의 개수가 많아지면 직사각형을 그리는 비슷한 작업을 수행하는 d3 객체와 메소드를 데이터의 수만큼 필요로 하게 되어 매우 귀찮은 작업이 된다.

이와 같은 귀찮은 작업을 해결하는 전통적인 방법은 자바스크립트의 for 문을 다음과 같이 사용하는 것이다.

for (var i=0; i < dataSet.length; i++) {

svg.append(“rect”)

.attr(“x”,0)

.attr(“y”,20 + i*50)      // y좌표가 20, 70, 120 으로 변함

.attr(“width”,dataSet[i]) // width가 dataSet[0], dataSet[1], dataSet[2]로 변함

.attr(“height”,”20px”)

}

d3.js에서는 데이터가 많을 때 자바스크립트의 for와 같은 반복문 대신에 셀렉션 메소드 selectAll(), data(), enter()를 이용한다. 단순한 2차원 그래프의 경우에는 for를 사용하여도 되지만 다이나믹한 그래픽을 쉽게 그리기위해서는 d3의 셀렉션 메소드를 사용하여야 한다. d3 메소드를 사용하면 공통된 속성을 한 번에 지정하고, 데이터 수에 따라 여러 개의 rect 요소를 다음과 같이 지정할 수 있다.

svg.selectAll(“rect”).data(dataSet).enter().append(“rect”)

.attr(“x”,0).attr(“y”,function(d,i) {return 20 + i*50;})

.attr(“width”,function(d,i) {return d+”px”;})

.attr(“height”,”20px”)

 

selectAll() 메소드는 교체하거나 추가할 요소를 선택하는데 위의 예제에서는 직사각형 요소가 계속 추가되므로 rect 요소를 선택한다.

data() 메소드는 dataSet 배열에 있는 데이터를 d3객체와 바인딩하여 내부에 저장해주는 메소드로서 저장된 데이터는 ‘d’라는 고유 배열명을 갖는다.

enter() 메소드는 d에 저장한 데이터를 append()로 추가하는 요소와 하나씩 연결시켜준다. 즉 이 예제에서는 rect 요소가 하나씩 데이터와 연결된다.

attr() 메소드는 .attr(“x”,0)과 같이 SVG 직사각형 요소의 파라미터를 지정하는데  y좌표와 width는 각 데이터마다 달리 지정되어야 한다. 이와 같은 유사한 작업의 수행은 두 번째 파라미터 y에 다음과 같은 function(d,i) 함수를 이용한다.

.attr(“y”,function(d,i) {return 20 + i*50;})

function(d,i) 함수는 for(i=0; i<d.length; i++) {20 + i*50} 라는 코드와 같은 역할을 한다. 데이터가 d[0]인 경우 i=0 이므로 y 좌표를 20으로 계산하고, d[1]인 경우 i=1 이므로 y 좌표를 20 + 1*50 = 70으로 계산하고, d[2]인 경우 i=2 이므로 y 좌표를 20 + 2*50 = 120 으로 계산해 준다. 여기서 사용된 함수를 콜백(callback) 함수라고 부른다.

유사한 방법으로 width의 지정은 다음과 같은 function(d,i)함수를 이용한다. 즉, 이 함수는 i=0일 때 d[0]px을 계산하고, i=1일 때 d[1]px, I=2일 때 d[2]px를 계산한다.

.attr(“width”,function(d,i) {return d+”px”;})

10.2.3 막대그래프에 스타일 적용

9.3절에서 SVG 도형의 스타일을 적용하는 방법을 연구하였다. d3.js는 기본적으로 SVG를 이용하기 때문에 유사한 스타일을 지정하여 그래픽을 예쁘게 만들 수 있다.

앞의 예제의 막대그래픽은 막대에 대한 설명 ‘좋음’, ‘보통’, ‘나쁨’등의 표시가 없었는데 이것을 막대 옆에 표시하고, 각 막대에 대해서는 [표 9.2.2]에 있는 fill, stroke, stroke-width, opacity 등을 이용하여 예쁘게 만들어 보자.

막대에 대한 설명을 위해 다음과 같이 vname 이라는 배열을 만들면 편리하다.

var vname = [“좋음”,”보통”,”나쁨”];

svg라는 d3 객체를 만들었다면 다음과 같이 append(“text”)를 이용하여 막대 설명 텍스트 넣을 위치를 계산하여 넣는다.

svg.enter().append(“text”).attr(“class”,”barname”)

.data(vname)

.attr(“x”,3)

.attr(“y”,function(d,i) {return 35 + i*50;})

.text(function(d,i) {return d;})

막대그래프의 사각형 색을 바꾸려면 프로그램에서 처리할 수도 있으나 간단하게 CSS 스타일로 구현할 수 있다. 일반적인 HTML 태그는 배경색을 background-color로 지정하는데 d3에서는 fill로 지정한다. 즉 막대그래프 사각형(rect)에 대한 CSS 클래스 bar를 만들어 fill 속성 값을 다음과 같이 지정해 주면 된다. 마우스를 올려 놓은 사각형의 색을 바꾸는 것은 hover 선택자를 이용한다.

<style>
.bar {stroke : red; stroke-width : 2px; fill : green; opacity : 0.7;}
.bar: hover {fill: skyblue;}
</style>

막대그래프의 사각형 위의 데이터 값(dataSet)을 적어주면 사용자의 이해를 도울 수 있다. 스타일에서 stroke는 white로 지정하고 막대의 끝과 글자를 맞추기 위해 text-anchor는 end로 지정한다. 데이터 값은 다음과 같이 append(“text”)를 이용하여 텍스트 넣을 x좌표 위치를 function(d,i) {return 20 + d})로 계산하여 넣는다. y축 좌표는 막대 설명 좌표와 동일하게 하면 된다.

<style> .barfreq{stroke: white; font-size: 9pt; text-anchor: end;} </style>

svg.append(“text”).attr(“class”,”barfreq”).data(dataSet)

.attr(“x”,function(d,i) {return 20 + d})

.attr(“y”,function(d,i) {return 35 + i*50;})

.text(function(d,i) {return d;})

10.3 도형의 변환과 그룹화

이 절에서는 d3를 이용한 도형의 변환과 그룹화에 대하여 살펴본다. 이를 이용하여 앞 절에서 그려본 가로형 막대그래프에 축을 표시할 수 있다

10.3.1 도형의 변환

d3에서 기본 도형과 텍스트는 그룹화하여 transform 속성을 이용하여 모양을 바꾸거나 이동할 수 있다. SVG 객체에서 사용할 수 있는 transform 속성은 [표 10.3.1]과 같다.

10.3.2 도형의 그룹화

여러 개의 기본 도형을 그룹으로 묶을 수 있다. 그룹화를 한 후에 변환을 적용함으로써 도형의 집합을 한 번에 이동할 수 있어 편리하게 다룰 수 있다.

10.3.3 막대그래프의 축 설정

앞 절에서 그린 막대그래프에는 가로축 또는 세로축에 그래픽을 설명할 수 있는 눈금 같은 것이 없어서 실용적이지 못하다. 그래프에서 축은 눈금선, 눈금값, 선스타일 등 여러 가지 속성을 가지고 있는데 가능하면 스타일을 분리해 나중에 쉽게 고칠 수 있도록 하는 것이 좋다.

축의 눈금은 데이터의 종류가 범주형인지, 연속형인지, 문자형인지에 따라 여러 가지 경우가 있을 수 있어 d3에서도 많은 척도(scale)를 가지고 있다. 많이 이용되는 척도에는 선형 척도 d3.scaleLinear(), 범주형 척도 d3.scaleBand(), 이산형 척도d3.scalePoint() 등이 있다. 막대그래프의 데이터 축은 일정한 간격의 선형눈금 scaleLinear를 대개 이용하고, 기본 축은 이산형 또는 범주형 데이터가 많아 scaleBand 또는 scalePoint가 이용된다. 11.4절의 선그래프에서 scalePoint의 예를 살펴본다.

모든 척도는 domain() 메소드로 데이터 값의 범위를 지정하고, range() 메소드로 SVG 캔버스에 그래프가 출력되는 픽셀 범위를 지정한다. 예를 들어 한 데이터의 최솟값이 0최댓값이 30이고 x축에 이 데이터를 그리는 공간이 0픽셀에서 300픽셀이라면 domain([0,30]).range([0,300])으로 지정한다. 즉 데이터 값의 10배를 해서 그래프가 그려지는 공간의 길이로 하자는 의미이다.

var xScale = d3.scaleLinear().domain([0,30]).range([0,300])

만일 콘솔에서 xScale(10)을 입력하면 결과 값은 100이 될 것이다.

눈금을 표시하려면 SVG 요소를 그룹화하는 기능을 사용하여 눈금의 선, 숫자와 그래픽을 하나의 그룹으로 지정하는데 SVG의 g 요소를 이용한다. d3.js에서 SVG의 g 요소는 append(“g”)를 이용하면 된다. 그룹화된 g 요소는 스타일을 지정하는데 axis 라는 스타일을 이용하려면 attr(“class”,”axis”)라고 지정한다. 눈금을 표시하려면 call() 메소드에서 그래프의 아래쪽에 xScale을 이용해서 눈금을 표시해 달라는 d3.axisBottom(xScale) 메소드를 호출한다.

d3.select(“#BarGraph”).append(“g”)

.attr(“transform”,”translate(40,”+((1+dataSet.length)*40+10)+”)”)

.call(d3.axisBottom(xScale))   // 아래쪽 눈금을 표시할 함수 호출, 스케일 적용

d3.js에는 눈금의 속성을 지정하기 위해 axis 클래스를 제공하는데 이 클래스의 text, path, line 등의 속성명을 이용하는 예는 다음과 같다.

.axis text { font-family: sans-serif; font-size: 11px; }

.axis path,

.axis line { stroke: black; }

[예제 10.3.3]은 데이터가 dataSet = [350, 150, 200] 일 때 scaleLinear()를 이용하여 domain([0,350])과 range([0,350])로 선형 변환하여 그래프의 x축을 설정하였다. y축에 표시한 “좋음”,”보통”,”나쁨”과 같은 문자열이나 나열된 숫자 등은 scaleBand()를 사용한다. 이 경우 다음과 같이 척도를 지정한다.

yScale = d3.scaleBand().domain([“좋음”,”보통”,”나쁨”]).range([0,300])

이 경우 domain([“좋음”,”보통”,”나쁨”]) 각각의 값은 0픽셀, 100픽셀, 200픽셀 등의 위치값을 계산해서 반환한다. 막대와 막대 사이의 간격을 주기위해서는 paddingInner() 메소드를 사용한다. paddingInner(0.3)은 막대 높이의 30%로 간격을 준다는 의미이다.

d3.scaleBand().domain([“좋음”,”보통”,”나쁨”]).range([0,200]).paddingInner(0.3);

10.4 그래프 애니메이션

d3.js의 장점은 그래프에 애니메이션 효과를 쉽게 줄 수 있다는 것이다. 애니매이션을 위해 d3에는 transition(), 대기시간을 설정하기 위한 delay(), 전체 애니메이션 시간을 설정하는 duration() 메소드 등을 제공한다.

애니메이션 효과는 간단하게 transition()을 추가함으로써 얻을 수 있다. 예를 들어, 다음과 같은 코드를 생각해보자.

var circle = svg.append(“circle”)

.attr(“cx”, 50)

.attr(“cy”, 50)

.attr(“r”, 20)

circle.attr(“cx”, 250);

처음 명령에서는 (50, 50) 좌표를 중심으로 하는 반지름이 20인 원을 그리고 두번째 명령에서는 가로축의 위치를 250으로 바꾼다. 하지만 이것이 즉시 적용되므로 화면에서 중심 (50, 50)에 위치한 원을 볼 수 없으며 중심 (250, 50)에 반지름이 20인 원이 그려진다. 그러나 마지막 명령문을 circle.transition().attr(“cx”, 25)로 하고 웹브라우저에서 새로 고침을 하면 중심이 (20, 20)인 원이 (250, 50)으로 이동하는 애니메이션을 볼 수 있다.

하지만 트랜지션이 진행되는 지속 시간을 적당히 조절하지 않으면 애니매이션이 너무 빨리 진행될 수 있다. duration() 메소드를 이용하여 밀리초 단위로 숫자를 지정하면 된다. 기본값은 250밀리초로 매우 빠르게 진행된다. 2초 동안 트랜지션이 진행되게 하고 싶다면 duration(2000)으로 지정하면 된다.

circle.transition()

.duration(2000)

.attr(“cx”, 250);

기본 트랜지션 애니메이션 효과가 일어날 때 속도가 일정하지 않고 처음에는 천천히 움직이다가 점점 빨리지고 다시 느려지면서 멈춘다. 이러한 시간에 따른 움직임도 조절할 수 있는데 ease() 메소드를 이용하여 다음과 같은 움직임 함수를 선택하면 된다.

linear  : 일정한 속도로 움직임

exp     : 지수적으로 가속

elastic : 최종 지점을 통과하여 한번 튕긴 후 멈춤

bounce  : 최종 지점에 부딪히고 튕기다 멈춤

다음은 ease(“elastic”)을 이용한 예이다.

circle.transition()

.duration(10000)

.ease(“elastic”)

.attr(“cx”, 250);

한 트랜지션의 시작을 지연시킬 수도 있는데 delay() 메소드를 사용한다. 다음 예는 트랜지션이 일어나기 전에 1초간 대기한 후 2초간 애니메이션이 실행된다.

.transition()

.delay(1000)

.duration(2000)

다음 예는 앞 절에서 그려본 막대그래프에 애니메이션 효과를 준 것이다.

10.5 이벤트 처리

d3.js에서는 SVG 캔버스 요소에 이벤트를 설정할 수 있는데 다음과 같은 on() 메소드를 이용한다.

on(이벤트 이름, 이벤트 발생 시 호출 함수)

d3에서는 웹브라우저에서 지원하는 모든 DOM 이벤트 유형을 사용할 수 있다. 자주 이용되는 마우스 관련 이벤트와 터치 이벤트에 대해서 살펴보자.

10.5.1 마우스 클릭 이벤트

마우스 관련 이벤트로 자주 사용되는 것은 다음과 같은 것들이 있다.

click     : 클릭

dblclick  : 더블클릭

mouseover : 마우스가 대상 위에 올라감

mouseout  : 마우스가 대상에서 벗어남

이벤트가 발생하면 d3.event에 저장된다. 마우스를 클릭했을 때 어떤 일을 수행하기 위해서는 다음과 같은 형태로 작성한다.

.on(“click”, function() {

});

즉 마우스 클릭 이벤트는 첫 번째 파라미터가 “click”이고 두 번째 파라미터는 대개 무명 함수가 많이 사용된다,

현재 이벤트에서 마우스의 상대 좌표를 알아내기 위하여 mouse() 메소드를 이용할 수 있다.다음 예제에서는 SVG 캔버스 위에 클릭을 하면 점이 찍힌다.

[예제 10.2.3]의 막대그래프에서 마우스로 막대를 클릭하였을 때 막대의 색을 하늘색으로 바꾸는 프로그램은 다음과 같다.

.on(“click”,function() {

d3.select(this)

.style(“fill”,”cyan”)

})

여기서 d3.select(this)는 마우스로 클릭한 요소를 지정하는 것을 의미한다. 클릭하였다면 하늘색으로 스타일을 바꾸어준다.

HTML 문서에 버튼을 만들어 이 버튼을 마우스로 클릭하였을 때 발생하는 이벤트 처리 도 on() 메소드를 사용한다. [예제 10.2.3]에 버튼를 만들고 이를 마우스로 클릭하였을 때 다른 데이터로 막대그래픽을 그리는 다음 예제를 살펴보자.

10.5.2 마우스 오버 이벤트

다음 예제에서는 원에 마우스를 가져다 대면 움직인다. 인터랙티브 그래픽을 반드시 transition()과 함께 사용 필요는 없으나 함께 사용하면 효과적일 때가 많다.

10.5.3 터치 이벤트

d3에서는 모바일 기기를 위한 터치 인터페이스도 제공한다. 터치 이벤트가 발생했을 때 d3.touch()를 이용하여 상대 좌표를 알아낼 수 있다. iOS의 멀티터치 좌표는 d3.touches()를 이용하여 알 수 있다. 터치 이벤트에는 기본적으로 다음과 같은 것들이 있다.

touchstart : 사용자가 터치 화면에 터치 포인트를 대는 이벤트

touchend   : 사용자가 터치 화면에서 터치 포인트를 떼는 이벤트

touchmove  : 사용자가 터치 포인트를 움직이는 이벤트

다음 예제에서는 SVG 캔버스 위에 터치를 하면 점이 찍힌다.

10.6 외부 데이터 불러오기

10.6.1 데이터 구분

데이터는 그 형태나 속성, 특징 등에 따라 구분할 수 있고 이 구분에 따라 시각화 방법이 차이가 날 수 있다.

가. 정형 데이터와 비정형 데이터

데이터가 한 관찰대상에 대해 여러 가지 항목(item)(이를 변수(variable) 또는 변량이라고도 부름)를 측정하여 구해질 때 그 형태는 대개 다음과 같이 행렬 형태로 정리한다. 이와 같은 데이터를 정형 데이터(structured data)라 부른다.

관찰대상   변수1    변수2  …  변수m

—————————————–

관찰대상1       …      …         …

관찰대상2       …      …         …

 

관찰대상n       …      …         …

정형 데이터는 행과 열의 셀(cell)들로 구성된 엑셀에서 편집하기 편리하고, 엑셀 파일은 csv, tsv 형식으로 저장할 수 있어 d3에서 쉽게 불러올 수 있다.

이와는 달리 신문의 기사나 SNS에서 주고받는 문자들로 이루어진 데이터들은 형태가 일정하지 않아 비정형 데이터(unstructured data)라고 부른다. 문자로 이루어진 비정형 데이터는 단어의 빈도수 등을 조사해(즉 정형화 하여) 워드클라우드(word cloud)와 같은 그래픽으로 시각화 하기도 한다.

나. 범주형 데이터와 연속형 데이터

변수의 측정값은 문자 또는 숫자가 될 수 있는데 측정값의 속성에 따라 구분하기도 한다. 통계학에서는 크게 각 변수값의 종류를 셀 수 있는(countable) 이산형 변수(discrete variable)와 변수값의 종류가 셀 수 없을 정도로 많은 연속형 변수(continuous variable)로 나눈다. 하지만 일반적으로 그래픽을 그릴 때 많이 사용하는 구분은 이산형 변수 중에서 성별(남,여)이나 직업(회사원,학생,교사,의사 등) 등 값의 종류가 범주 형태인 범주형 변수(categorical variable)와 기타 변수를 통털어 부르는 연속형 변수로 나눈다. 범주형 변수의 측정된 값들을 범주형 데이터(discrete data), 연속형 변수의 측정된 값들을 연속형 데이터(continuous data)라 부른다.

범주형 변수는 대개 변수값에 대한 도수분포표(예: 남 13명, 여 15명)를 구해 막대그래프, 원그래프, 띠그래프 등을 그려 분석한다. 범주형 데이터는 아래 예와 같이 도수분포를 구하기전의 원시 데이터(raw data)와 도수분포를 조사해 정리해 놓은 요약 데이터(summary data)로 구분한다.

– 성별 변수의 원시 데이터 :   남, 여, 남, 여, 여, 여, 남, 여, 남, 여

– 성별 변수의 요약 데이터 :   남  4,  여  6

연속형 변수는 체중, 신장, 온도, 수출액 등과 같이 변수의 값이 실수의 형태로 되어 있어 평균, 분산, 최댓값, 최솟값 등의 통계량을 구하고 점그래프, 히스토그램, 줄기와 잎 그림 등을 그려 분석한다.

월, 연도 등 시간에 따라 측정된 연속형 변수는 꺽은선그래픽을 그려 분석한다. 두 개의 연속형 변수가 있을 때는 산점도를 그려 상관성 등을 분석한다. 연속형 변수의 데이터는 구간으로 그룹화하여, 즉 범주형 형태로 만들어 도수분포표를 구한 후 막대, 원, 띠그래픽으로 분석하기도 한다.

11장에서는 범주형 변수의 요약형 데이터에 대한 그래프인 막대그래프, 원그래프, 띠그래프와 시간에 따라 변화를 살펴보는 꺽은선그래픽을 살펴본다. 12장에서는 연속형 변수의 점그래프, 히스토그램, 줄기와 잎 그림, 산점도를 살펴본다.

다. 계층적 데이터

인구 조사 데이터의 경우 행정구역에 따라 각 시도의 인구와, 그 밑의 행정구역인 시군구 인구, 그리고 다시 읍면동 인구로 되어 있다. 이와 같은 데이터를 계층적 데이터(hierarchical data)라 부른다. 계층적 데이터는 대개 json 형식의 파일로 저장하고 트리맵, 팩그래프 등으로 시각화하여 분석한다.

라. 위상 데이터

요사이 SNS가 활성화 되면서 한 사람이 다른 사람과 얼마나 문자를 주고받는지를 조사해 친소관계를 표시하는데 이를 위상 데이터(topological data)라 한다. 지도에 보이는 한 지점과 다른 지점간의 거리를 모아 놓은 것도 위상 데이터라 볼 수 있다. 이와 같은 위상 데이터는 대개 네트워크 그래픽으로 시각화하여 분석한다.

마. 지리 데이터

국가의 국경선이나, 도시, 강, 도로, 지역명 등이 표시되어있는 지도 등을 지리 데이터(geographical data)라 부른다. 이러한 지리 데이터는 SVG 형태의 벡터 폴리곤(polygon), 텍스트, 도형의 크기 등의 데이터 조합으로 볼 수 있다. 지리 데이터는 각 지역의 인구, 기후 등 다른 데이터와 결합하여 지리정보시스템(geographical information system)을 구축하여 서비스 되기도 한다.

바. 날짜/시간 데이터

년/월/일/시로 이루어지는 시간 데이터는 여러 가지 형식으로 표현이 가능하여 복잡한 계산을 가능하게 할 수 있다. 자바스크립트는 Date 객체에 여러 메소드를 제공하는데 d3에서도 제공하는 함수가 있어 시간 데이터를 쉽게 처리할 수 있다.

10.6.2 데이터 형식

지금까지는 프로그램안의 배열에 데이터를 넣고 막대그래픽을 그려보았다. 하지만 실제 응용에서는 대개 데이터를 외부로부터 불러와서 처리를 하게 된다. d3.js는 txt, csv, tsv, json, html, xml 등의 데이터 형식을 불러올 수 있도록 지원하는데 여기서는 많이 이용하는 csv, tsv, json 파일 불러오기에 대해 살펴본다.

가. csv 파일

csv(comma separated value) 파일이란 아래 예와 같이 모든 데이터가 콤마(‘,’)로 구분된 텍스트 파일(확장자가 csv)이며 첫 행은 변수명이 콤마로 구별되어 적혀있다.

EX100601.csv

엑셀과 같이 행과 열로 구분된 시트에서 대개 행은 관찰대상, 콤마로 구분된 열은 변수를 의미한다. 11장과 12장에서 다루는 통계 그래프의 데이터는 이와 같이 정형화된 데이터이다. 모든 엑셀 파일은 csv 형식으로 저장할 수 있다.

csv 형식의 파일을 불러오려면 다음과 같은 d3.csv() 메소드를 사용한다.

d3.csv(url[,accessor]. callback])

첫 번째 매개변수는 csv 파일 디렉토리로서 같은 도메인(서버)의 파일만 불러올 수 있다. 두 번째 매개변수는 파일 불러오기가 완료되었을 때 호출할 함수를 지정한다. 다음은 csv 파일을 불러오는 예이다. csv 파일을 읽어오면 data 배열 객체에 저장되게 된다.

d3.csv(“EX100601.csv”,function(error,data){

for(var i=0; i<data.length;i++){

data1.push(data[i].x);

data2.push(data[i].y);

data3.push(data[i].z);

}

})

만일 csv 파일의 첫 행 변수명이 우유, 사과, 토마토와 같이 한글명이라면 다음과 같이 위의 명령어를 수정해 주어야 한다.

d3.csv(“EX100601.csv”,function(error,data){

for(var i=0; i<data.length;i++){

data1.push(data[i][“우유”]);

data2.push(data[i][“사과”]);

data3.push(data[i][“토마토”]);

}

})

로컬 컴퓨터에서 csv 파일을 읽으려면 웹브라우저에서 csv 파일은 접근 제한을 한다. 자신의 컴퓨터에 서버를 구축해서 크롬 브라우저로 htm 파일을 실행하여야 한다.

주의할 것은 d3에서는 파일이 모든 로드되기 전에 메소드를 반환한다는 것이다. 따라서 큰 파일의 경우는 파일이 모두 로드되기 전에 메소드가 반환되어 그 데이터를 필요로 하는 코드를 실행할 때 오류가 발생할 수 있다. 이와 같은 문제는 데이터 로딩 함수에 데이터 처리하여 해결하는 방법과, 파일 로딩이 완료되었을 때 이벤트를 발생시켜 해결하는 방법이 있는데 이 책의 수준을 넘으므로 자세한 것은 관련 문헌을 참고하기 바란다.

다음 예제는 csv 파일을 읽어 막대그래픽을 그리는 예이다.

<그림 10.6.1> csv 파일을 읽어 막대그래픽을 그림

 

나. tsv 파일

tsv(tab separated value) 파일이란 아래 예와 같이 모든 데이터가 탭(tab)으로 구분된 텍스트 파일(확장자가 tsv)이며 첫 행은 변수명이 탭으로 구별되어 적혀있다. 모든 엑셀 파일은 역시 tsv 형식으로도 저장할 수 있다.

EX100601.tsv

tsv(tab separated value) 파일을 불러오는 방법은 csv 파일과 유사한 d3.tsv() 메소드를 사용한다. csv 파일 불러오는 경우와 유사하므로 예는 생략한다.

d3.tsv(url[,accessor]. callback])

다. JSON 파일

JSON은 JavaScript Object Notation의 머리글자를 딴 데이터 형식인데 2006년에 결정된 것으로 웹에서 많이 사용된다. JSON 형식의 데이터는 csv 파일이 제공할 수 없는 다양한 형태의 데이터를 만들 수 있다. JSON 형식으로 파일을 로딩하면 여러 변수명:변수값 쌍을 담고 있는 객체를 만든다. 아래 예와 같이 JSON 데이터는 대괄호 []안에 중괄호 {}를 필요한 데이터 개수만큼 만들고 콤마로 구분하며, 그 안에 ‘변수명:변수값’의 쌍을 넣는다. csv나 tsv 형식과 달리 첫 행의 변수명 데이터는 없다.

EX100601.json

json 파일은 csv나 tsv와 같은 헤더 부분이 없다. ‘이름:값’의 쌍이 여러 개 있을 뿐이다. json 파일에서는 다음과 같이 ‘이름:값’순서가 바뀌어 나타나거나 불필요한 다른 변수가 있어도 상관이 없어 형식이 자유롭다.

다음은 json 파일을 읽는 예이다.

d3.json(“EX100601.json”,function(error,data){

for(var i=0; i<data.length;i++){

data1.push(data[i].x[0]);       // 데이터의 첫째 데이터만 명시

}