3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめてのアドベントカレンダーAdvent Calendar 2024

Day 5

【D3】散布図 (Scatter Chart) を実装する

Last updated at Posted at 2024-11-13

はじめに

この記事では、JavaScript 向けのデータ可視化ライブラリである D3 を使って、散布図 (Scatter Chart) を実装する手順を記載します。

開発環境

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

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

実装

要素ごとに実装を進めていきます。

固定値

これから作成する散布図では、X軸に count、Y軸に score を表示します。

data.ts
export const data = [
  {
    score: 1,
    count: 2,
  },
  {
    score: 2,
    count: 2,
  },
  {
    score: 3,
    count: 4,
  },
  {
    score: 4,
    count: 5,
  },
  {
    score: 5,
    count: 5,
  },
];
page.tsx
  const width = 600;
  const height = 400;
  const margin = { top: 20, right: 20, bottom: 40, left: 40 };

データの出力範囲の指定

以下の関数を利用して、X方向・Y方向のデータの出力範囲を指定します。

X方向(水平方向)では、count の0から最大値までの値を margin を除いた width に表示します。

page.tsx
  const xScale = d3
    .scaleLinear()
    .domain([0, d3.max(data, (d) => d.count) || 0]) // `count` values mapped to x-axis
    .range([margin.left, width - margin.right]);

Y方向(垂直方向)では、score の0から最大値までの値を margin を除いた height に表示します。

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

散布図のレンダリング

データの出力範囲が指定できたので、画面上に散布図をレンダリングします。

まず、<svg> タグを利用して散布図全体のサイズを指定します。

page.tsx
  return (
    <svg width={width} height={height}></svg>
  );

image.png

次は <svg> タグ内に散布図のドットを表示します。各ドットは、<circle> タグを使って表示します。

<circle> タグの属性(props)には以下の値を渡します。

  • cx: X軸座標の位置
  • cy: Y軸座標の位置
  • r: 円の半径
  • fill: 円の色
page.tsx
  return (
    <svg width={width} height={height}>
      {data.map((d, i) => (
        <circle
          key={i}
          cx={xScale(d.count)}
          cy={yScale(d.score)}
          r={5}
          fill="steelblue"
        />
      ))}
    </svg>
  );

image.png

X軸 / Y軸の作成

次は X軸 / Y軸を作成して、それぞれどのような値を表示するかわかるようにします。
SVG の <g> (group) タグ内に軸を作成します。<g> タグを使うことで、SVGキャンバス上に表示する軸の位置指定を簡単にできます。

page.tsx
  // Create refs for axes
  const xAxisRef = useRef<SVGGElement>(null);
  const yAxisRef = useRef<SVGGElement>(null);

  // Set up x-axis and y-axis with useEffect to handle D3 rendering
  useEffect(() => {
    if (xAxisRef.current) {
      const xAxis = d3.axisBottom(xScale);
      d3.select(xAxisRef.current).call(xAxis);
    }

    if (yAxisRef.current) {
      const yAxis = d3.axisLeft(yScale);
      d3.select(yAxisRef.current).call(yAxis);
    }
  }, [xScale, yScale]);

完成

最終的なコードを画面は以下の通りです。

page.tsx
"use client";

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

const width = 600;
const height = 400;
const margin = { top: 20, right: 20, bottom: 40, left: 40 };

export default function Page() {
  const xScale = d3
    .scaleLinear()
    .domain([0, d3.max(data, (d) => d.count) || 0]) // `count` values mapped to x-axis
    .range([margin.left, width - margin.right]);

  const yScale = d3
    .scaleLinear()
    .domain([0, d3.max(data, (d) => d.score) || 0]) // `score` values mapped to y-axis
    .range([height - margin.bottom, margin.top]);

  // Create refs for axes
  const xAxisRef = useRef<SVGGElement>(null);
  const yAxisRef = useRef<SVGGElement>(null);

  // Set up x-axis and y-axis with useEffect to handle D3 rendering
  useEffect(() => {
    if (xAxisRef.current) {
      const xAxis = d3.axisBottom(xScale);
      d3.select(xAxisRef.current).call(xAxis);
    }

    if (yAxisRef.current) {
      const yAxis = d3.axisLeft(yScale);
      d3.select(yAxisRef.current).call(yAxis);
    }
  }, [xScale, yScale]);

  return (
    <svg width={width} height={height}>
      {data.map((d, i) => (
        <circle
          key={i}
          cx={xScale(d.count)}
          cy={yScale(d.score)}
          r={5}
          fill="steelblue"
        />
      ))}

      {/* X-axis */}
      <g ref={xAxisRef} transform={`translate(0, ${height - margin.bottom})`} />

      {/* Y-axis */}
      <g ref={yAxisRef} transform={`translate(${margin.left}, 0)`} />
    </svg>
  );
}

image.png

参考

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?