2015 Feb 02

d3 infoviz

d3js: chapter 8. axis

이 글은 http://infovis.kr 스터디 하면서 작성했고 d3.js 8장의 내용이다.

Axis

이 페이지 내내 사용하는 공통 코드(7장의 내용):

//global로 선언하는 것은 안좋은 습관이지만,
//아래 예제에서 계속 사용하기 위해 global로 선언한다.
var g = window;

g.w = 500;
g.h = 300;
g.padding = 20;
g.dataset = [
    [5, 20],
    [480, 90],
    [250, 50],
    [100, 33],
    [330, 95],
    [410, 12],
    [475, 44],
    [25, 67],
    [85, 21],
    [220, 88],
    [600, 150]
];

g.xScale = d3.scale.linear()
    .domain([0, d3.max(dataset, (d)=> d[0])])
    .range([padding, w - padding*2]);

g.yScale = d3.scale.linear()
    .domain([0, d3.max(dataset, (d)=> d[1])])
    .range([h-padding, padding]);

g.rScale = d3.scale.linear()
    .domain([0, d3.max(dataset, (d)=> d[1])])
    .range([2, 5]);

//global
window.scatterplot = function(selector){
    var svg = d3.select(selector).append('svg')
        .attr('width', w)
        .attr('height', h);

    var circle = svg.selectAll('circle')
        .data(dataset);

    circle.enter()
        .append('circle')
        .attr('cx', function(d){
            return xScale(d[0]);
        })
        .attr('cy', function(d){
            return yScale(d[1]);
        })
        .attr('r', function(d){
            return rScale(d[1]);
        });

    return svg;
};

산포도 그리기:

//svg.append('g')
scatterplot('#phr-scatterplot-01').append('g')
    .call(d3.svg.axis()
        .scale(xScale)
        .orient('bottom'));

Axis그릴려면 많은 SVG 엘리먼트가 필요한데 그 엘리먼트를 담는 Container 역활엔 Group을 의미하는 g가 적합하다:

bottom에 그렸는데 위에 나온다.ㅎㅎ

8.3 보기 좋게 다듬기.

var xAxis = d3.svg.axis()
    .scale(xScale)
    .orient('bottom');

var svg = scatterplot('#phr-scatterplot-02')
svg.append('g')
    .attr("class", "axis")
    .call(xAxis);

var text = svg.selectAll('text')
    .data(dataset);

text.enter()
    .append('text')
    .text((d)=> d.join(', '))
    .attr('x', (d)=> xScale(d[0]))
    .attr('y', (d)=> yScale(d[1]))
    .attr('font-family', 'sans-serif')
    .attr('font-size', '11px')
    .attr('fill', 'red');
.axis path,
.axis line {
    fill: none;
    stroke: black;
    shape-rendering: scrispEdges;
}
.axis text {
    font-family: sans-serif;
    font-size: 11px;
    fill: olive;
}

transform

위치를 이동시키자:

var xAxis = d3.svg.axis()
    .scale(xScale)
    .orient('bottom');

var svg = scatterplot('#phr-scatterplot-03')
svg.append('g')
    .attr('class', 'axis')
    .attr('transform', `translate(0, ${h-padding})`) //translate(0, 280)
    .call(xAxis);

var text = svg.selectAll('text')
    .data(dataset);

text.enter()
    .append('text')
    .text((d)=> d.join(', '))
    .attr('x', (d)=> xScale(d[0]))
    .attr('y', (d)=> yScale(d[1]))
    .attr('font-family', 'sans-serif')
    .attr('font-size', '11px')
    .attr('fill', 'red');

8.4 눈금 생성 시 확인 사항

7개나 5개 d3가 생성해주는 눈금 개 수는 같다. d3는 ticks()로 설정한 눈금 근처로 해서 데이터에 맞는 눈금 개수를 알아서 생성해준다:

var xAxis = d3.svg.axis()
    .scale(xScale)
    .orient('bottom')
    .ticks(7);

var svg = scatterplot('#phr-scatterplot-04')
svg.append('g')
    .attr('class', 'axis')
    .attr('transform', `translate(0, ${h - padding})`)
    .call(xAxis);

var text = svg.selectAll('text')
    .data(dataset);

text.enter()
    .append('text')
    .text((d)=> d.join(', '))
    .attr('x', (d)=> xScale(d[0]))
    .attr('y', (d)=> yScale(d[1]))
    .attr('font-family', 'sans-serif')
    .attr('font-size', '11px')
    .attr('fill', 'red');

var xAxis = d3.svg.axis()
    .scale(xScale)
    .orient('bottom')
    .ticks(5);

var svg = scatterplot('#phr-scatterplot-05')
svg.append('g')
    .attr('class', 'axis')
    .attr('transform', `translate(0, ${h - padding})`)
    .call(xAxis);

var text = svg.selectAll('text')
    .data(dataset);

text.enter()
    .append('text')
    .text((d)=> d.join(', '))
    .attr('x', (d)=> xScale(d[0]))
    .attr('y', (d)=> yScale(d[1]))
    .attr('font-family', 'sans-serif')
    .attr('font-size', '11px')
    .attr('fill', 'red');

Y축도 만들자

var padding = 30;

//아래 예제에서 재정의 하기 귀찮으니 글로벌로 선언한다.
var g = window
g.xAxis = d3.svg.axis()
    .scale(xScale)
    .orient('bottom')
    .ticks(5);

g.yAxis = d3.svg.axis()
    .scale(yScale)
    .orient('left')
    .ticks(5);

var svg = scatterplot('#phr-scatterplot-06');
svg.append('g')
    .attr('class', 'axis')
    .attr('transform', `translate(0, ${h - padding})`)
    .call(xAxis);

svg.append('g')
    .attr('class', 'axis')
    .attr('transform', `translate(${padding}, 0)`)
    .call(yAxis);

var text = svg.selectAll('text')
    .data(dataset);

text.enter()
    .append('text')
    .text((d)=> d.join(', '))
    .attr('x', (d)=> xScale(d[0]))
    .attr('y', (d)=> yScale(d[1]))
    .attr('font-family', 'sans-serif')
    .attr('font-size', '11px')
    .attr('fill', 'red');

8.6 마지막 손질

var g = window
g.dataset = [];
var numDataPoints = 50;
var xRange = Math.random() * 1000;
var yRange = Math.random() * 1000;
for( var i = 0; i < numDataPoints; i++){
    var newNumber1 = Math.floor(Math.random() * xRange);
    var newNumber2 = Math.floor(Math.random() * xRange);
    dataset.push([newNumber1, newNumber2]);
}

//데이터가 달라졌으니 Scale도 다시(계속 위 예제의 것을 재사용 함) 만들고
g.xScale = d3.scale.linear()
    .domain([0, d3.max(dataset, (d)=> d[0])])
    .range([padding, w - padding*2]);

g.yScale = d3.scale.linear()
    .domain([0, d3.max(dataset, (d)=> d[1])])
    .range([h-padding, padding]);

g.rScale = d3.scale.linear()
    .domain([0, d3.max(dataset, (d)=> d[1])])
    .range([2, 5]);

//Scale이 바뀌었으니 Axis도
g.xAxis = d3.svg.axis()
    .scale(xScale)
    .orient('bottom')
    .ticks(5);

g.yAxis = d3.svg.axis()
    .scale(yScale)
    .orient('left')
    .ticks(5);

var svg = scatterplot('#phr-scatterplot-07');
svg.append('g')
    .attr('class', 'axis')
    .attr('transform', `translate(0, ${h - padding})`)
    .call(xAxis);

svg.append('g')
    .attr('class', 'axis')
    .attr('transform', `translate(${padding}, 0)`)
    .call(yAxis);

var text = svg.selectAll('text')
    .data(dataset);

text.enter()
    .append('text')
    .text((d)=> d.join(', '))
    .attr('x', (d)=> xScale(d[0]))
    .attr('y', (d)=> yScale(d[1]))
    .attr('font-family', 'sans-serif')
    .attr('font-size', '11px')
    .attr('fill', 'red');

text 제거

var svg = scatterplot('#phr-scatterplot-08');
svg.append('g')
    .attr('class', 'axis')
    .attr('transform', `translate(0, ${h - padding})`)
    .call(xAxis);

svg.append('g')
    .attr('class', 'axis')
    .attr('transform', `translate(${padding}, 0)`)
    .call(yAxis);

8.7 눈금의 라벨 포매팅

d3.format

var xAxis = d3.svg.axis()
    .scale(xScale)
    .orient('bottom')
    .ticks(5)
    .tickFormat(d3.format('.1%'));

var yAxis = d3.svg.axis()
    .scale(yScale)
    .orient('left')
    .ticks(5)
    .tickFormat(d3.format('03d'));

var svg = scatterplot('#phr-scatterplot-09');
svg.append('g')
    .attr('class', 'axis')
    .attr('transform', `translate(0, ${h - padding})`)
    .call(xAxis);

svg.append('g')
    .attr('class', 'axis')
    .attr('transform', `translate(${padding}, 0)`)
    .call(yAxis);