Chart.jsでヒストグラム内に指標を表示

教育系のサービスのダッシュボードの設計を行う際に、記事タイトルのような事がそもそも実装可能かどうか疑問に思い、実装可否の確認のため実際にコードを書いてみたので備忘録も兼ねてメモ

実装概要

実装内容としては、棒グラフと折れ線グラフの複合グラフの中に指標を表示するというもの。

指標出力の際にややこしい点として、
スコアの0-20のx軸中央地点が081-100のx軸中央地点が100
つまりインデックス換算すると、スコアの0地点(左端)が-0.5100地点(右端)が4.5となる。

指標出力時のx座標はインデックス換算の値となるため、計算が凄く大変そう(数学苦手民)

chart.jsのバージョンは4.5.0

実際に書いたコードが下記(先に出しておく)

See the Pen combo chart – 1 by inouee (@inouee) on CodePen.

指標の位置を計算

サンプルとして、スコア43を指標として設定する

ステップ1:線形補間の形を作る

0%の値を \( y_0 = -0.5 \)、100%の値を \( y_{100} = 4.5 \) とします。
線形関係なので、一般式は次のように表されます。

\[ y = m \cdot x + b \]

ここで \( x \) はパーセント、\( y \) は対応する値です。


ステップ2:傾きを求める

傾き \( m \) は次のように計算されます。

\[
m = \frac{y_{100} – y_0}{100 – 0}
= \frac{4.5 – (-0.5)}{100}
= \frac{5}{100}
= 0.05
\]


ステップ3:切片 \( b \) を求める

0% のとき \( y = -0.5 \) なので、

\[
-0.5 = 0.05 \cdot 0 + b
\]

これを解くと、

\[
b = -0.5
\]

となります。


ステップ4:43% の値を求める

上で求めた傾きと切片を使い、43% のときの値を計算します。

\[
y = 0.05 \cdot 43 – 0.5
= 2.15 – 0.5
= 1.65
\]

したがって、43% に対応する値は 1.65 となります。

指標を出力

上記で求めた指標を出力します。
ざっくりこんな感じ

options: {
  responsive: true,
  plugins: {
    legend: {
      display: true,
      position: 'bottom'
    },
    annotation: {
      annotations: {
        yourScore: {
          type: 'line',
          // 43点 → ラベル位置をインデックス換算(0%を-0.5 100%を4.5)
          // 線形補間を用いる
          xMin: 1.65,
          xMax: 1.65,
          borderColor: 'rgba(0,68,255,0.5)',
          borderWidth: 4,
          label: {
            display: true,
            content: 'あなた',
            position: 'start',
            backgroundColor: 'rgba(0,0,255,0.2)',
            color: 'rgba(0,0,255,0.5)',
            font: {
              weight: 'bold'
            }
          }
        }
      }
    }
  },
  scales: {
    x: {
      title: {
        display: true,
        text: 'スコア'
      },
    },
    y: {
      title: {
        display: true,
        text: '人数'
      },
      beginAtZero: true,
      ticks: {
        stepSize: 100
      }
    }
  }
}

下記の2箇所に先ほど求めた指標の位置を入力する感じです。

xMin: xxx,
xMax: xxx,

まとめ

結構というかだいぶ大変だった。
実装を外部に依頼するのはちょっと躊躇するかも