0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【D3.js】9調査の生成AI導入率17.3%〜64.4%を横断比較できるダッシュボードを作ってみた

0
Posted at

genai-adoption-dashboard.jpg

この記事でできること

  • 複数の公開調査データをD3.jsで横断比較できるバーチャートに整形・表示する手順を理解する
  • WordPress(SWELLテーマ)でD3.jsのスクリプトが動かない原因と解決方法を把握する
  • 「定義が異なるデータを同一チャートに表示する」設計の判断基準を学ぶ

実際に動作するツールは生成AI企業導入率ダッシュボード(AI Japan Index)で確認できる。


環境・バージョン

  • D3.js: v7.9.0(CDN: https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js
  • WordPress: 6.7系
  • SWELLテーマ: 2.10系
  • ブラウザ確認: Chrome 133 / Safari 17 / Firefox 124
  • 動作OS: Windows 11 / macOS Sequoia 15

完成形

9機関の調査結果(17.3%〜64.4%)を1画面に表示し、ユーザーが数値の差の理由を確認できるダッシュボードを作る。

実物: 生成AI企業導入率ダッシュボード(AI Japan Index)

実装するチャート:

  • 9調査横断比較バーチャート(大手中心vs全規模を色分け)
  • 企業規模別導入率バーチャート
  • 国際比較バーチャート(中国95.8%・米国90.6%・ドイツ90.3%・日本55.2%)
  • NRI時系列折れ線チャート(2023〜2025年度)
  • 導入障壁横棒グラフ

Step 1: データの整形

9調査の数値をそのまま使うのではなく、「定義・対象規模・調査時期」をセットでオブジェクトに持たせる。これをしないと後でUI上で「なぜ数値が違うのか」を表示できなくなる。

// data.js(HTMLにインライン展開)
const surveyData = [
  {
    org: "JUAS",
    date: "2026年2月",
    rate: 33.9,
    n: 957,
    definition: "「導入済み」のみ(試験導入除く)",
    scale: "large"   // large=大手中心 / mixed=全規模
  },
  {
    org: "NRI",
    date: "2025年11月",
    rate: 57.7,
    n: 517,
    definition: "「導入済み」(試験利用含む可能性あり)",
    scale: "large"
  },
  {
    org: "日経BP",
    date: "2025年9月",
    rate: 64.4,
    n: 1450,
    definition: "IT・ビジネス関心層への調査(最高値)",
    scale: "large"
  },
  {
    org: "総務省",
    date: "2025年7月",
    rate: 55.2,
    n: null,
    definition: "企業の生成AI「業務で使用中」",
    scale: "mixed"
  },
  {
    org: "TSR",
    date: "2025年8月",
    rate: 25.2,
    n: 6645,
    definition: "「会社として推進」(個人利用22.3%を除く)",
    scale: "mixed"
  },
  {
    org: "TDB",
    date: "2024年8月",
    rate: 17.3,
    n: 4705,
    definition: "「活用している」(最も厳格な定義・最小値)",
    scale: "mixed"
  }
];

Step 2: D3.jsをWordPressで読み込む

<script src="CDN">はWordPressで機能しない。 SWELLテーマが投稿コンテンツ内の外部スクリプトタグを無視するため、動的ロードが必要になる。

// NG: SWELLが無視するパターン
// <script src="https://cdn.jsdelivr.net/npm/d3@7/..."></script>

// OK: JS動的ロード
function loadD3(callback) {
  if (typeof d3 !== 'undefined') {
    callback();
    return;
  }
  const s = document.createElement('script');
  s.src = 'https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js';
  s.onload = callback;
  s.onerror = function() {
    const w = document.getElementById('aji-genai-dashboard-wrap');
    if (w) { w.innerHTML = '<p>グラフの読み込みに失敗しました</p>'; }
  };
  document.head.appendChild(s);
}

document.addEventListener('DOMContentLoaded', function() {
  loadD3(initAllCharts);
});

Step 3: 横断比較バーチャートの実装

9調査を並べる際、「大手中心(high bias)」と「全規模(realistic)」をユーザーが区別できるよう色分けする。

function renderSurveyChart(data) {
  const wrap = document.getElementById('survey-chart');
  if (!wrap) return;

  const mobile = (window.innerWidth <= 767);
  const margin = { top: 16, right: mobile ? 8 : 80, bottom: 50, left: mobile ? 15 : 120 };
  const W = wrap.clientWidth || 700;
  const H = data.length * (mobile ? 32 : 40) + margin.top + margin.bottom;
  const innerW = W - margin.left - margin.right;
  const innerH = H - margin.top - margin.bottom;

  const svg = d3.select(wrap).append('svg').attr('width', W).attr('height', H);
  const g = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

  const yScale = d3.scaleBand()
    .domain(data.map(d => d.org))
    .range([0, innerH])
    .padding(0.25);

  const xScale = d3.scaleLinear().domain([0, 100]).range([0, innerW]);

  // バー(大手中心=オレンジ、全規模=グレー)
  g.selectAll('.bar')
    .data(data)
    .enter()
    .append('rect')
    .attr('y', d => yScale(d.org))
    .attr('x', 0)
    .attr('height', yScale.bandwidth())
    .attr('width', d => xScale(d.rate))
    .attr('fill', d => (d.scale === 'large') ? '#ff8906' : '#a7a9be')
    .attr('rx', 2);

  // PC: 数値ラベル
  if (!mobile) {
    g.selectAll('.val')
      .data(data)
      .enter()
      .append('text')
      .attr('y', d => yScale(d.org) + yScale.bandwidth() / 2 + 4)
      .attr('x', d => xScale(d.rate) + 5)
      .attr('fill', '#e0e0e0')
      .attr('font-size', '12px')
      .text(d => d.rate + '%');

    g.append('g').call(d3.axisLeft(yScale).tickSize(0)).select('.domain').remove();
    g.selectAll('.tick text').attr('fill', '#e0e0e0');
  }

  // X軸(スマホは3本に間引く)
  const xAxisG = g.append('g')
    .attr('transform', 'translate(0,' + innerH + ')')
    .call(d3.axisBottom(xScale).tickFormat(d => d + '%'));
  xAxisG.select('.domain').remove();
  xAxisG.selectAll('text').attr('fill', '#a7a9be');

  if (mobile) {
    const ticks = xAxisG.selectAll('.tick');
    const n = ticks.size();
    const keep = [0, Math.floor((n - 1) / 2), n - 1];
    ticks.each(function(d, i) {
      if (keep.indexOf(i) === -1) { d3.select(this).style('display', 'none'); }
    });
  }

  // スマホ: タッチで定義をパネル表示
  if (mobile) {
    const panel = document.getElementById('mobile-panel');
    g.selectAll('.bar')
      .on('touchstart', function(e, d) {
        e.preventDefault();
        e.stopPropagation();
        if (panel) {
          panel.innerHTML = '<strong>' + d.org + ' ' + d.date + '</strong>: ' +
            d.rate + '% — ' + d.definition + '(n=' + (d.n || '非公開') + '';
          panel.style.display = 'block';
        }
      });
  }
}

Step 4: && 問題を解消してWordPressでデバッグする

WordPressの投稿コンテンツ内のJSスクリプトが動かない原因の多くは && 変換問題だ。

// NG: WordPressが &#038;&#038; に変換して構文エラー
function checkData(arr) {
  if (arr !== null && arr.length > 0) {
    render(arr);
  }
}

// OK: ネストifで回避
function checkData(arr) {
  if (arr !== null) {
    if (arr.length > 0) {
      render(arr);
    }
  }
}

デバッグ手順:

  1. WP管理画面でHTMLブロックのソースを「テキスト表示」で確認する
  2. &&&#038;&#038; に変換されていないかチェックする
  3. 変換されていたら全ての && をネストifまたは三項演算子に修正する
  4. <!-- wp:html -->ブロックで保存し直す

Step 5: スマホ対応と下部パネル

スマホではY軸の日本語ラベルが必ず見切れるため、ラベルを非表示にしてタッチで下部パネルに切り替える。

<!-- 下部固定パネル(HTML構造) -->
<div id="mobile-panel"
     style="display:none; position:fixed; bottom:0; left:0; width:100%;
            background:#1a1a2e; border-top:2px solid #ff8906;
            padding:12px 16px; z-index:10000;
            max-height:40vh; overflow-y:auto;">
  <button onclick="document.getElementById('mobile-panel').style.display='none'"
          style="float:right; background:transparent; border:none; color:#ff8906; font-size:18px;">×</button>
  <div id="mobile-panel-content"></div>
</div>

つまずきポイントまとめ

  • <script src="CDN">がWordPressで無視される → JS動的ロードに変更
  • &&演算子が&#038;&#038;に変換されてJS構文エラー → ネストifまたは三項演算子
  • Chart.jsのcanvasがSWELLで描画されない → D3.jsのSVG描画に変更
  • scaleBandのドメインと.attr('y')のアクセサが不一致でバーがずれる → 同じ値で統一
  • スマホで日本語バー名(組織名8〜10文字)がX軸に重なる → モバイル時に3本に間引く
  • 異なる定義の数値を同一チャートに並べると「精度の差」と誤解される → scaleフィールドで色分け + 定義ポップアップ
  • d3.eventがv7で廃止 → function(event, d) として第一引数で受け取る

まとめ(今回学んだこと)

  • WordPressでD3.jsを動かす3つの必須ルール: 動的ロード・&&禁止・wp:htmlブロック
  • 複数定義の調査データは「定義フィールド」を設けて色分けし、タッチ/ホバーで定義を表示する
  • スマホ対応はY軸非表示 + 下部固定パネルのセットで設計する
  • 推定値はレンジ(min〜max)で表示する方が「単一点」より誤解が少ない

関連ツールはAI Japan Indexで全て無料公開している。

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?