2015 Feb 02

d3 infoviz

d3js: chapter 7. scale

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

척도(scale)는 입력되는 정의역(domain)과 출력되는 치역(range)을 매핑한 함수를 말한다.- Mike Bostock

  • 척도는 함수다.
  • 이 장에서는 Linear Scale만 다룬다.

7.5 산포도에 척도

var w = 500;
var h = 100;

var dataset = [
    [5, 20],
    [480, 90],
    [250, 50],
    [100, 33],
    [330, 95],
    [410, 12],
    [475, 44],
    [25, 67],
    [85, 21],
    [220, 88]
];

var svg = d3.select('#phr-scatterplot-01').append('svg')
    .attr('width', w)
    .attr('height', h);

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

circle.enter()
    .append('circle')
    .attr('cx', (d)=> d[0])
    .attr('cy', (d)=> d[1])
    .attr('r', (d)=> Math.sqrt(h-d[1]))

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

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

7.5.2 동적인 척도 만들기.

var w = 500;
var h = 100;

var dataset = [
    [5, 20],
    [480, 90],
    [250, 50],
    [100, 33],
    [330, 95],
    [410, 12],
    [475, 44],
    [25, 67],
    [85, 21],
    [220, 88]
];

//정적 스케일
var xScale = d3.scale.linear()
    .domain([0, 500])
    .range([0,w]);

var yScale = d3.scale.linear()
    .domain([0, 100])
    .range([0,h]);

//동적 스케일
var xScale = d3.scale.linear()
    .domain([0, d3.max(dataset, (d)=> d[0])]) // d = [5, 20], ...
    .range([0,w]);

var yScale = d3.scale.linear()
    .domain([0, d3.max(dataset, (d)=> d[1])]) // d = [5, 20], ...
    .range([0,h]);

var svg = d3.select('#phr-scatterplot-02').append('svg')
    .attr('width', w)
    .attr('height', h);

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

circle.enter()
    .append('circle')
    .attr('cx', (d)=> xScale(d[0]))
    .attr('cy', (d)=> yScale(d[1]))
    .attr('r', (d)=> Math.sqrt(h-d[1]));

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');

7.5.4 가공하기

y축 바꾸기:

var w = 500;
var h = 100;

var dataset = [
    [5, 20],
    [480, 90],
    [250, 50],
    [100, 33],
    [330, 95],
    [410, 12],
    [475, 44],
    [25, 67],
    [85, 21],
    [220, 88]
];

//동적 스케일
var xScale = d3.scale.linear()
    .domain([0, d3.max(dataset, (d)=> d[0])]) // d = [5, 20], ...
    .range([0,w]);

var yScale = d3.scale.linear()
    .domain([0, d3.max(dataset, (d)=> d[1])]) // d = [5, 20], ...
    .range([h,0]); //요걸 바꿔 본 것임

var svg = d3.select('#phr-scatterplot-03').append('svg')
    .attr('width', w)
    .attr('height', h);

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

circle.enter()
    .append('circle')
    .attr('cx', (d)=> xScale(d[0]))
    .attr('cy', (d)=> yScale(d[1]))
    .attr('r', (d)=> Math.sqrt(h-d[1]));

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');

padding 넣기:

var w = 500;
var h = 100;

var dataset = [
    [5, 20],
    [480, 90],
    [250, 50],
    [100, 33],
    [330, 95],
    [410, 12],
    [475, 44],
    [25, 67],
    [85, 21],
    [220, 88]
];

//////요기
var padding = 20;

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

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

var svg = d3.select('#phr-scatterplot-04').append('svg')
    .attr('width', w)
    .attr('height', h);

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

circle.enter()
    .append('circle')
    .attr('cx', (d)=> xScale(d[0]))
    .attr('cy', (d)=> yScale(d[1]))
    .attr('r', (d)=> Math.sqrt(h-d[1]));

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');

Mike Bostock’s Margin Convention을 보면 margin 코딩 패턴을 소개하고 있다.

반지름도 스케일 적용:

var w = 500;
var h = 100;

var dataset = [
    [5, 20],
    [480, 90],
    [250, 50],
    [100, 33],
    [330, 95],
    [410, 12],
    [475, 44],
    [25, 67],
    [85, 21],
    [220, 88]
];

var padding = 20;

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

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

//////반지름용 scale
var rScale = d3.scale.linear()
    .domain([0, d3.max(dataset, (d)=> d[1])])
    .range([2, 5]);

var svg = d3.select('#phr-scatterplot-05').append('svg')
    .attr('width', w)
    .attr('height', h);

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

circle.enter()
    .append('circle')
    .attr('cx', (d)=> xScale(d[0]))
    .attr('cy', (d)=> yScale(d[1]))
    .attr('r', (d)=> rScale(d[1])); //요기

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 w = 500;
var h = 100;

var dataset = [
    [5, 20],
    [480, 90],
    [250, 50],
    [100, 33],
    [330, 95],
    [410, 12],
    [475, 44],
    [25, 67],
    [85, 21],
    [220, 88],
    [600, 150] //요거 추가.
];

var padding = 20;

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

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

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

var svg = d3.select('#phr-scatterplot-06').append('svg')
    .attr('width', w)
    .attr('height', h);

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

circle.enter()
    .append('circle')
    .attr('cx', (d)=> xScale(d[0]))
    .attr('cy', (d)=> yScale(d[1]))
    .attr('r', (d)=> rScale(d[1]));

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');

h를 100에서 300으로:

var w = 500;
var h = 300;

var dataset = [
    [5, 20],
    [480, 90],
    [250, 50],
    [100, 33],
    [330, 95],
    [410, 12],
    [475, 44],
    [25, 67],
    [85, 21],
    [220, 88],
    [600, 150] //요거 추가.
];

var padding = 20;

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

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

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

var svg = d3.select('#phr-scatterplot-07').append('svg')
    .attr('width', w)
    .attr('height', h);

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

circle.enter()
    .append('circle')
    .attr('cx', (d)=> xScale(d[0]))
    .attr('cy', (d)=> yScale(d[1]))
    .attr('r', (d)=> rScale(d[1]));

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');

기타 척도 메소드

Linear

nice()

정의역 반올림.

scale(0):

scale(0.123):

scale(4.567):

var scale = d3.scale.linear()
    .domain([0.123, 4.567])
    .range([0, 500])
    .nice();

$('#nice-01').text(scale(0));
$('#nice-02').text(scale(0.123));
$('#nice-03').text(scale(4.567));
rangeRound()

안티에일리어싱(antialiasing)으로 발생할 수 있는 뿌연 경계선이 없는, 정확한 픽셀 값을 갖는 도형을 그릴 때 유용하다. - p151

치역 반올림.

scale(0):

scale(0.123):

scale(4.567):

var scale = d3.scale.linear()
    .domain([0.123, 4.567])
    .rangeRound([0, 500])
    .nice();

$('#range-round-01').text(scale(0));
$('#range-round-02').text(scale(0.123));
$('#range-round-03').text(scale(4.567));
clamp()

기본적으로 d3는 치역 밖에 값도 반환해주지만, 정의한 치역 범위를 벗어나는 경우 치역의 최소값과 최대값 중 하나를 반환한다.

scale(10):

var scale = d3.scale.linear()
    .domain([0.123, 4.567])
    .range([0, 500]);

$('#clamp-01').text(scale(10));

scale(10):

var scale = d3.scale.linear()
    .domain([0.123, 4.567])
    .range([0, 500])
    .clamp(true);

$('#clamp-02').text(scale(10));

7.8 다른 종류의 척도

다른 척도로 이런게 있는데, 언젠가 업데이트를….

sqrt

pow

log

quantize

quantile

ordinal

d4.scale.category10
d4.scale.category20
d4.scale.category20b
d4.scale.category20c

d3.time.scale()