일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 시큐어코딩
- D3.js
- Token ) was not valid
- 로트관리
- D3차트
- 생산SCM
- mms.cfg
- SEcure coding
- 유효재고
- rmate chart
- flash에러
- adobe flash 보안정책
- chart flash
- SQL0104
- rmate chart flash
- Today
- Total
티스토리 뷰
이번에는 D3의 차트를 만들면서 정리한다.
먼저, 라인 차트의 구현 방법을 알기 위해
이전에 작성한 바 차트 중
하나를 골라서 마지막에 다음 코드를 추가한다.
|
위 코드를 추가하고 실행해보면
그림과 같이 라인 차트가 생성되는 것을 볼 수 있다.
즉, D3에서는 바 차트나 라인차트를
간단하고 유사하게 생성하고 관리할 수 있다.
그림을 보면 선의 시작 (A값) 부분이 축을 벗어나 있다.
바 차트는 Y축에서 일정 부분 떨어져서 시작하고
라인 차트는 Y축에 딱 붙어서 시작한다.
선의 위치(x)에 bandwidth를 추가해도 되지만
라인 차트 고유의 처리법으로 해결한다.
따라서 라인차트 X값이 0 부터 시작하기에 +55의 수치를 주어 조정하였다. [line 2].
append를 작성해서
선 채우기(fill), [line 7].
선의 색(stroke), [line 8].
선의 굵기(stroke-width), [line 9].
형태 [line 10].
를 설정했다.
만약 채우기 값에 색상을 지정하게되면 아래그림 처럼 영역차트가 된다.
.attr("fill", "royalblue") |
바 차트는 각각의 데이터에 대하여
각각의 도형(rect)를 생성하지만,
라인 차트는 각각의 선(데이터)이 이어져 하나의 선(Path)이 그려진다.
따라서 path를 생성하였다 (append). [line 5].
그리고 각각의 선은 line 함수(인스턴스- d3.line())를 호출해서 그리게 된다.
라인 함수는 하나의 선 전체(path)에 대한 속성을 지정하는 부분과
각각의 선(line)에 대한 속성을 지정하는 부분으로 구성된다. [line 10].
데이터(data) 사용법에도 차이가 있다.
바 차트에서는 data 지정시 배열을 그냥 넘겼다.
라인 차트에서는 data 지정시
배열(dataset)을 다시 배열([dataset])로 지정했다.
즉, 2차원 배열로 변환 한 것이다.
1차원 배열 하나가 하나의 선(path)이 되기 때문이고,
2차원으로 여러 개의 값을 지정하면 여러 개의 선이 생성된다.
라인은 여러 개의 선이 있다고 전제하고 있다.
2차원 배열을 기본으로 사용한다.
지금까지는 바 차트에 라인을 추가해서
간단하게 라인 차트와의 차이점을 살펴 봤다.
이제부터 그림과 같이 라인 차트에 맞는 예제를 구현해봤다.
SVG 크기도 조절하고 년도에 맞게 데이터도 조정해봤다.
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 | <!DOCTYPE html> <meta charset="utf-8"> <style> .grid line { stroke: lightgrey; stroke-opacity: 0.7; } .lineChart { fill: none; stroke: steelblue; stroke-width: 1.5px; } .lineChart:hover { stroke: black; stroke-width: 3px; } </style> <svg width="700" height="320"></svg> <script src="https://d3js.org/d3.v4.min.js"></script> <script> var series = ["2017", "2018"]; var dataset = [ {'1':17, '2':27, '3':37, '4':27, '5':17, '6':7, '7':9, '8':19, '9':29, '10':19, '11':9, '12':0}, {'1': 9, '2':19, '3':29, '4':39, '5':29, '6':19, '7':9, '8':7, '9':17, '10':27, '11':17, '12':7}]; var data = []; dataset.forEach(function(d, i) { data[i] = keys.map(function(key) { return {x: key, y: d[key]}; }) }); var margin = {left: 20, top: 10, right: 10, bottom: 20}; 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 svgG = svg.append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var xScale = d3.scalePoint()//scaleBand() scaleOrdinal .domain(keys) .rangeRound([0, width]); var yScale = d3.scaleLinear() .domain([0, d3.max(dataset, function(d) { return d3.max(keys, function(key) { return d[key];});})]) .nice() .range([height, 0]); var colors = d3.scaleOrdinal(d3.schemeCategory20); 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(5) .tickSize(-width) ); var line = d3.line() .curve(d3.curveBasis) .x(function(d) { return xScale(d.x); }) .y(function(d) { return yScale(d.y); }); var lineG = svgG.append("g") .selectAll("g") .data(data) .enter().append("g"); lineG.append("path") .attr("class", "lineChart") .style("stroke", function(d, i) { return colors( series[i]); }) .attr("d", line); var legend = svgG.append("g") .attr("text-anchor", "end") .selectAll("g") .data(series) .enter().append("g") .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; }); legend.append("rect") .attr("x", width - 20) .attr("width", 19) .attr("height", 19) .attr("fill", colors); legend.append("text") .attr("x", width - 30) .attr("y", 9.5) .attr("dy", "0.32em") .text(function(d) { return d; }); </script> |
배열은 앞서 사용했던 Json(x,y)으로 구성된 1차원 배열을 사용했다.
(1차원 배열이 2차원보다 이해하기 쉽고 구현하기 쉽기 때문에...)
2차원배열
var dataset = [ [ {x: 1, y: 17}, {x: 2, y: 27}, {x: 3, y: 37}, {x: 4, y: 27}, {x: 5y: 17}, {x: 6, y: 7}, {x: 7, y: 9}, {x: 8, y: 19}, {x: 9, y: 29}, {x: 10, y: 19},{x: 11, y: 9}, {x: 12, y: 0} ], [ {x: 1, y: 9}, {x: 2, y: 19}, {x: 3, y: 29}, {x: 4, y: 39}, {x: 5y: 29}, {x: 6, y: 19}, {x: 7, y: 9}, {x: 8, y: 7}, {x: 9, y: 17}, {x: 10, y: 27},{x: 11, y: 17}, {x: 12, y: 7} ] ]; |
[line 25].
하나의 행이 하나의 데이터(선-path)가 되게 하기 위해
1:17, 2:17 와 같이
각 값을 Json으로 지정해서 1차원배열로 구현했다
전 시간에 설명했듯이 원소 하나가 큰 Json으로 이룬다.
표를 다시 확인해 보면 데이터 행이 2개이다.
즉, 두 개의 선이 생성되는 예제이다.
dataset 변수에 값만 추가해 주면
여러 개의 선이 계속 생성된다.
이렇게 여러 개의 선(행) 각각을 시리즈(series)라고 한다.
여기에서는 2016년과 2017년 두 개의 데이터(행),
두 개의 시리즈를 사용한다.
[line 23].
인터넷에서 구할 수 있는 예제들은
다음과 아래와 같이 시리즈를 데이터와 같이 구성하는데,
var dataset = [ {'series': '2017', 'A': 10, 'B':20}, {'series': '2018', 'A':15, 'B':25} ]; |
[line 23]과 같이 별도의 배열 변수(series)를 사용하는 것도 가능했다.
앞서 정리한 바 차트에 라인을 추가한 예제에서
라인은 2차원 배열을 기본으로 한다고 정리했다.
쉬운 이해와 개발을 위해
1차원 배열로 구현했기 때문에
이것을 2차원 배열로 변환하는 작업이 필요하다. [line 29 ~ 34].
(처음부터 2차원으로 구현하는 것이 더 좋을 수도 있다.)
1차원의 dataset을 2차원의 data 변수로
변환하기에 앞서해 d3의 keys 함수를 이용하여
json key값들을 추출한다. [line 29].
여기서는 각 시리즈의 데이터 개수가 같다는 전제로
첫 번째 (dataset[0]) 행의 json key값들을 추출하여
keys 변수에 저장한다.
[line 29 ~ 34].
해당 코드는 1차원으로 지정된 데이터(dataset)를
2차원의 데이터(data)로 변환하는 코드이다.
변환을 위해
그 안에서 각 배열의 원소인
Json의 개수만큼 반복(keys.map) 해서.
반환된 배열을 다시 배열 (data[i])에 넣으면서
2차원 배열이 만들어진다.
첫 배열이 만들어질 때,
바 차트 데이터와 같이
{A: 9}가 {x: ‘A’, y: 9}로 변환되어 저장된다 ({x: key, y: d[key]}).
바 차트 예제 데이터와 같아 진 것이다.
x축에 1, 2, 3, .... 가 출력되도록
지금까지의 앞의 bar 예제와 다르게
xScale에 scaleBand가 아닌 scalePoint가 사용되었다.
실행 결과로 짐작할 수 있겠지만,
이 둘은 고정된 값을 처리하는 공통점이 있지만,
scalePoint는 0부터 시작하는 차이가 있다.
즉, y축에 딱 붙어서 시작된다.
앞의 예에서 문제가 된 라인의 시작 위치가 해결된 것을 볼 수 있다.
Y축은 다소 복잡해 보이는데
첫 max에 dataset을 지정하면 2개의 json 데이터가 반환되고
다시 각각에 대하여 max를 keys 개수만큼 반복한다.
keys 개수만큼 반복하는 이유는
각 json 데이터 개수를 의미 하기 때문이다.
이렇게 반복해서 키에 해당하는 값(d[key])을 반환하고
이중에 가장 큰 값(max)이 반환되고,
반환된 2개의 큰 값 중 가장 큰 값이 반환되어
척도 구성을 위한 domain의 값으로 지정한다.
그리고 이 domain의 소수점이 너무 많으면
즉 데이터내의 최대값 찾기가
처음부터 2차원 데이터를 사용하지 않고
1차원 데이터를 사용한 이유 중의 하나.
이해하기 어렵다면 전 예제의 bar 차트에서
변수 yScale의 scaleLinear()에서
domain과 range부분을 비교해서 보면 도움이 됨.
여기서는 각 라인별로 다른 색을 사용했다
여기서 사용한 코드와 같이
D3에서 지정한 컬러 값 집합(schemeCategory20)을 사용해도 되고,
컬러를 지정해도 된다.
https://github.com/d3/d3-scale
D3에서 지정한 컬러 집합(schemeCategory20)을 사용하지 않고
원하는 색상인, 뚜렷한 컬러지정 값을 넣고 싶을 경우엔 다음 아래와 같이 내용을 추가한다.
var colors = d3.scaleOrdinal(d3.schemeCategory20); var colors = d3.scaleOrdinal().range(["IndianRed", "RoyalBlue"]); |
(...전 예제부터 보면 눈치챘겠지만, 개인적으로 그냥 red나 blue보다 약간 indianred나 royalblue처럼 이런 색상이 좋다 ㅎㅎ)
그리고 CSS를 이용해서 각 선에 마우스를 올리면(over)
선을 강조하기 위해 더 두껍게 (3px)
보이도록 작성했다 [line 14].
[line 36 ~ 42].
SVG내에서 차트를 중앙에 놓기 위한 코드이다.
즉, 상하좌우에 여백(margin)을 주는 코드이다.
왼쪽(left)과 상단(top)은 주어진 값만큼
전체 구조를 이동(transform)시키면 된다.
우측(right)과 바닥의 여백(bottom)은
차트를 생성할 때 빼고 계산하면 된다.
계산하는 것이 척도이니
X축 계산에 사용되는 width 값을
여백 만큼 (-margin.left - margin.right) 줄여주고
Y축 계산에 사용되는 height 값을
여백 만큼(-margin.top - margin.bottom) 줄여주면 된다.
[line 84].
마지막으로 범례(legend)를 구현한다
범례는 차트 내에서 적당한 위치(우측 상단)에
각 라인이 나타내는 의미를 보여 준다.
(위치는 지정하기 나름이다.)
[line 91 ~ 101].
각 라인은 색으로 구분하기 때문에
도형(rect)을 생성하고,
어떤 데이터 인지(series)를
문자로(text)로 출력해 주면 된다.
https://github.com/zziuni/d3/wiki/API-Reference
https://github.com/d3/d3-scale
the next plan. 라인차트II
원형차트
'D3.js' 카테고리의 다른 글
07 원형 차트(Pie & Doughnut Chart) (1) | 2018.06.15 |
---|---|
06 라인 차트(Line Chart) - 툴팁(Tooltip), 꺾은선(Curve Line) (0) | 2018.06.11 |
04 툴팁(Tooltip) (2) | 2018.06.04 |
03 그리드(Grid) (0) | 2018.06.01 |
02 척도(X / Y축) - Axis와 Scale의 이해 (0) | 2018.05.30 |