【D3】scaleBand、scaleLiner、scaleSqrt の使い分け

# はじめに
この記事では、JavaScript 向けのデータ可視化ライブラリである D3 のスケール（入力値をもとにして、チャート上に表示する範囲を出力する）に関する以下の関数の使い分けについて、具体例を交えながら記載します。

- [scaleBand](https://d3js.org/d3-scale/band#scaleBand)
- [scaleLinear](https://d3js.org/d3-scale/linear#scaleLinear)
- [scaleSqrt](https://d3js.org/d3-scale/pow#scaleSqrt)

https://d3js.org/

# 開発環境
開発環境は以下の通りです。

- Windows 11
- Next.js 14.2.4
- React 18.3.1
- TypeScript 5.5.2
- D3 7.9.0
- @types/d3 7.4.3

# ユースケース
以下のコードでは、Next.js でバブルチャートを実装しています。
X軸に `element`、Y軸に `score` を表示します。また、バブルの大きさは `count` で表現します。

```data.ts
export const data = [
  { element: "A", score: 1, count: 2 },
  { element: "A", score: 2, count: 2 },
  { element: "A", score: 3, count: 4 },
  { element: "B", score: 1, count: 0 },
  { element: "B", score: 2, count: 5 },
  { element: "B", score: 3, count: 3 },
  { element: "C", score: 1, count: 7 },
  { element: "C", score: 2, count: 7 },
  { element: "C", score: 3, count: 1 },
];
```

X軸は `scaleBand`、Y軸は `scaleLiner`、バブルの大きさは `scaleSqrt` を使っています。


```page.tsx
"use client";

import { useEffect, useRef } from "react";
import * as d3 from "d3";
import { data } from "../data";

export default function Page() {
  const svgRef = useRef<SVGSVGElement | null>(null);
  const width = 600;
  const height = 400;

  const renderChart = () => {
    // Define scales
    const xScale = d3
      .scaleBand()
      .domain(data.map((d) => d.element))
      .range([0, width]);

    const yScale = d3
      .scaleLinear()
      .domain([0, d3.max(data, (d) => d.score) || 0])
      .range([height, 0]);

    const sizeScale = d3
      .scaleSqrt()
      .domain([0, d3.max(data, (d) => d.count) || 0])
      .range([0, 30]);

    // Enable D3 API manipulate svg
    const svg = d3.select(svgRef.current);
    // Clear everything before appending new elements
    svg.selectAll("*").remove();
    // Bind data to bubbles
    const bubbles = svg.selectAll(".bubble").data(data);

    // Enter
    bubbles
      .enter()
      .append("circle")
      .attr("class", "bubble")
      .attr("fill", "teal")
      .attr("cx", (d) => xScale(d.element)! + xScale.bandwidth() / 2)
      .attr("cy", (d) => yScale(d.score))
      .attr("r", (d) => sizeScale(d.count));
  };

  useEffect(() => {
    renderChart();
  }, []);

  return <svg ref={svgRef} width={width} height={height} />;
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/b64aba17-f856-d172-8e26-9ca958988a57.png)

# `scaleBand`
`scaleBand` は離散値（カテゴリなど）を等間隔に表示します。
今回のユースケースでは、element（A、B、C）を 0 から width (600) の間に表示しています。

```page.tsx
    const xScale = d3
      .scaleBand()
      .domain(data.map((d) => d.element))
      .range([0, width]);
```

# `scaleLiner`
`scaleLiner` は連続値を等間隔に表示します。
今回のユースケースでは、score（1、2、3）を 0 から height (400) の間に表示しています。

```page.tsx
    const yScale = d3
      .scaleLinear()
      .domain([0, d3.max(data, (d) => d.score) || 0])
      .range([height, 0]);
```

:::note
上記のサンプルコードの range の引数の配列を反対にする（`range[0, hegiht]`）と、チャートの上下も反対になります。
:::

# `scaleSqrt`
`scaleSqrt` も `scaleLiner` と同じく連続値を入力値として受け取ります。ただ、表示の仕方が異なります。`scaleLiner` が入力値の大小を直線の長短で表現するのに対し、`scaleSqrt` では、点の大小で表現します。
今回のユースケースでは、count（0、...7）を 0 から 30 の大きさで表示しています。

```page.tsx
    const sizeScale = d3
      .scaleSqrt()
      .domain([0, d3.max(data, (d) => d.count) || 0])
      .range([0, 30]);
```

# まとめ
まとめると以下の通りです。

|Scale |入力 |出力 |ユースケース |
|---|---|---|---|
|`scaleBand` |離散値、カテゴリ |連続値 |カテゴリの軸方向の表示位置 |
|`scaleLiner` |連続値 |連続値 |数値の軸方向の表示位置、線の表示位置 |
|`scaleSqrt` |連続値 |連続値 |バブルなどの大きさ |

# 参考
- [Band scales](https://d3js.org/d3-scale/band)
- [Linear scales](https://d3js.org/d3-scale/linear)
- [Power scales](https://d3js.org/d3-scale/pow)