D3.js 막대 그래프(bar chart)

예제 프로그램

간단한 막대 그래프를 만든다.

코드 확인

예제 코드

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>D3 bar chart basic</title>
  <script src="https://d3js.org/d3.v7.min.js"></script>
</head>

<body>
  <script>
    // 1. 데이터 준비
    var dataset = [
      { "name": "A", "value": 5 },
      { "name": "B", "value": 6 },
      { "name": "C", "value": 8 },
      { "name": "D", "value": 1 },
      { "name": "E", "value": 2 },
      { "name": "F", "value": 6 },
      { "name": "G", "value": 8 },
      { "name": "H", "value": 6 },
      { "name": "I", "value": 10 },
      { "name": "J", "value": 9 }
    ]
    var width = 400; // 그래프 넓이
    var height = 300; // 그래프 높이
    var padding = 30; // 스케일 표시용 여백

    // 2. SVG 영역 설정
    var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);

    //  3. 축 스케일(눈금) 설정
    var xScale = d3.scaleBand()
      .rangeRound([padding, width - padding])
      .padding(0.1)
      .domain(dataset.map(function (d) { return d.name; }));

    var yScale = d3.scaleLinear()
      .domain([0, d3.max(dataset, function (d) { return d.value; })])
      .range([height - padding, padding]);

    // 4. 축 표시
    svg.append("g")
      .attr("transform", "translate(" + 0 + "," + (height - padding) + ")")
      .call(d3.axisBottom(xScale));

    svg.append("g")
      .attr("transform", "translate(" + padding + "," + 0 + ")")
      .call(d3.axisLeft(yScale));

    // 5. 막대 표시
    svg.append("g")
      .selectAll("rect")
      .data(dataset)
      .enter()
      .append("rect")
      .attr("x", function (d) { return xScale(d.name); })
      .attr("y", function (d) { return yScale(d.value); })
      .attr("width", xScale.bandwidth())
      .attr("height", function (d) { return height - padding - yScale(d.value); })
      .attr("fill", "steelblue");
  </script>
</body>

</html>

코드 설정

1. 데이터 준비

축을 표시하는 폭을 확보하기 위해 padding을 변수로 생성한다. 상하좌우에 padding으로 설정한 폭의 여백을 설정한다.

2. SVG 영역 설정

body 안에 svg 태그를 설정하여 그릴 영역을 준비한다.

3. 축 스케일(눈금) 설정

축의 너비를 화면의 너비에 맞추는 스케일 변환과 축 표시를 위한 함수를 준비한다.

x축은 D3의 함수 d3.scaleBand()를 사용하여 설정한다.

var xScale = d3.scaleBand()
    .rangeRound([padding, width - padding])
    .padding(0.1)
    .domain(dataset.map(function(d) { return d.name; }));

먼저, .rangeRound(..) 함수로 스케일 표시 범위를 반올림으로 설정한다.

.rangeRound([padding, width - padding])

여기서는 그래프 양 끝에 padding 만큼의 간격을 두기 때문에 x축을 padding ~ width-padding의 범위로 설정한다.

.padding(..)에서 막대 그래프의 막대와 막대 사이의 간격을 0.0 ~ 1.0의 비율로 설정합니다.

.padding(0.1)

.domain(..)의 부분에서 가로축의 레이블(name)과 수를 설정한다.

.domain(dataset.map(function(d) { return d.name; }));

인수를 map으로 변환하여 설정한다. 여기에 설정된 레이블의 문자열은 x축에 표시된다.

y축은 D3의 함수 d3.scaleLinear()를 사용하여 설정한다.

  var yScale = d3.scaleLinear()
    .domain([0, d3.max(dataset, function(d) { return d.value; })])
    .range([height - padding, padding]);

d3.scaleLinear()domain(..)으로 설정한 스케일을 range로 설정한 스케일로 등배로 변환하는 함수이다. domain에 그래프 내 좌표값으로 0부터 숫자(value)의 최대값(d3.max(...)로 가져옴)를 설정하고, range에 표시상의 거리를 고려하여 범위를 한다. padding을 고려한 범위를 설정한다. y축은 화면 아래로 갈수록 값이 커지므로, y=0에 화면 하측의 큰 값(height - padding)을 설정한다.

4. 축 표시

x, y축을 설정한다.

  svg.append("g")
    .attr("transform", "translate(" + 0 + "," + (height - padding) + ")")
    .call(d3.axisBottom(xScale));
 
  svg.append("g")
    .attr("transform", "translate(" + padding + "," + 0 + ")")
    .call(d3.axisLeft(yScale));

x축은 그래프의 높이만큼, y축은 x축의 표시 여백만큼 띄워서 설정한다.

d3.axisBottom(xScale)
d3.axisLeft(yScale)

위에 함수는 축의 svg 요소 표시용의 함수로, call(..)로 호출하면 svg 요소의 line 등을 사용해 축의 각도를 표시한다.

5. 막대 표시

  svg.append("g")
    .selectAll("rect")
    .data(dataset)
    .enter()
    .append("rect")
    .attr("x", function(d) { return xScale(d.name); })
    .attr("y", function(d) { return yScale(d.value); })
    .attr("width", xScale.bandwidth())
    .attr("height", function(d) { return height - padding - yScale(d.value); })
    .attr("fill", "steelblue");

막대를 표시한다. xScale에는 d3.scaleBand()로 호출한 함수가 설정되어 있지만, map한 축 라벨(d.name)을 인수로 하면 각 좌표를 호출할 수 있게 되어 있다. 그리고, .bandwidth()로 설정한 padding()을 고려한 폭을 가져온다. "rect"는 위에서 오른쪽 아래로 사각형을 표시하므로 "y" 속성이 막대의 위쪽 좌표가 된다.




최종 수정 : 2024-01-18