以前にExcelのピボットテーブルを使ってパレート図を作成した記事を書きましたが、今回はその時のデータを流用して、複数のグラフを一つのグラフで表示させたり、目盛線のカスタマイズをやってみます。

Photo by Isaac Smith on Unsplash

 

前回の記事の続きになります。

この記事では、Chart.js を使う際に役立つ以下の事柄を説明しています。

  • 2つのY軸を利用する方法
  • 組み合わせグラフの作成方法
  • 目盛のカスタマイズ
  • ツールチップのカスタマイズ
  • スケールラベル(軸ラベル)の表示方法

 

使用するデータ

Excelでピボットテーブルを作成した記事で利用したデータを流用して、手打ちでデータを作成します。元にしたのは次のデータです。

実際には、AJax 等で受け取ったデータをパレート図として出力するイメージだと思います。ただ、今回はこのピボットテーブルの数値を元に、手でデータを起こしていきます。

  var salesData = [
    19630770, 6202180, 5398800, 5397370,
    4419560, 2533000, 1804000, 1541280,
    1272240, 1220070, 1156800, 842100,
    516670, 479320, 452200, 395390,
    323440, 183680, 170430, 161100,
    122430, 75720, 58520, 56630,
    46860, 17520
  ];
  var percentage = [
    0.3603, 0.4742, 0.5733, 0.6724,
    0.7535, 0.8, 0.8331, 0.8614,
    0.8847, 0.9071, 0.9284, 0.9438,
    0.9533, 0.9621, 0.9704, 0.9777,
    0.9836, 0.9870, 0.9901, 0.9931,
    0.9953, 0.9967, 0.9978, 0.9988,
    0.9997, 1
  ];

先のデータに紐付けるラベルは次の通り。これらのラベル、売上データに比率のデータはすべて並び順が一致している必要があります。

  var labels = [
    "ショートソード", "薬草+1", "薬草", "ロングソード",
    "革の鎧", "ロングソード+1", "まよけ", "回復の巻物",
    "メイス", "グレートソード", "ブレストプレート+1", "鎧帷子",
    "解毒剤", "気付け薬", "解毒の巻物", "光の盾",
    "ドラゴンスレイヤー", "魚の干物", "フレイル", "食事セット",
    "干し肉", "光の鎧", "ミルク", "光の剣",
    "伝説の鎧", "パン"
  ];

 

Y軸を2軸にするには

今回は、パレート図なので、各データの値の単位は2種類必要になります。

一つは、salesData に対応する「売上高(円)」と、もう一つは、percentage に対応する「比率(%)」になります。

コレを実現するには、optionsscalesyAxes に配列でそれぞれのY軸を定義します。この場合、データ側とY軸側に、それぞれ紐付け合うための yAxisID を割り振ります。

今回の場合は、次の表のようにします。

ラベル 軸ID データ 位置
売上高(円) y-axis-sales salesData
比率 y-axis-percentage percentage

位置というのは、左右どちらのY軸に yAxisID を割り当てるのかを示しています。

以下が、そのコードです。

options: {
  scales: {
    yAxes: [{
      id: 'y-axis-sales',
      type: 'linear',
      display: true,
      position: 'left',
      ticks: {
        beginAtZero: true
      }
    }, {
      id: 'y-axis-percentage',
      type: 'linear',
      display: true,
      position: 'right',
      ticks: {
        beginAtZero: true
      }
    }]
  }
}

 

折線グラフと棒グラフの混合グラフを描くには

また、このパレート図は棒グラフと折れ線グラフの混合されたグラフになります。

これは、各 dataSettype プロパティに bar あるいは、line を割り当てることで実現できます。

ただし、Chart の設定のトップレベルにある type プロパティを削除すると、グラフの描画に失敗するので気をつけてください。また、このtype には bar を割り当てておく必要があるみたいです。

    type: 'bar', // 常に ’bar’ で指定
    data: {
      labels: labels,
      datasets: [{
        label: '売上',
        type: 'bar',  // ’bar’ もしくは 'line'
        data: salesData,
        backgroundColor: 'rgba(255, 99, 132, 0.2)',
        borderColor: 'rgba(255, 99, 132, 1)',
        borderWidth: 1,
        yAxisID: 'y-axis-sales'
      }, {
        label: '比率の累計',
        type: 'line',  // ’bar’ もしくは 'line'
        data: percentage,
        backgroundColor: 'rgba(99, 132, 255, 0.2)',
        borderColor: 'rgba(99, 132, 255, 1)',
        borderWidth: 1,
        yAxisID: 'y-axis-percentage'
      }]
    },

結果、コード全体としては次のようになります。

<canvas id="myChart" width="400" height="400"></canvas>
<script>
  var ctx = document.getElementById('myChart').getContext('2d');
  var salesData = [
    19630770, 6202180, 5398800, 5397370,
    4419560, 2533000, 1804000, 1541280,
    1272240, 1220070, 1156800, 842100,
    516670, 479320, 452200, 395390,
    323440, 183680, 170430, 161100,
    122430, 75720, 58520, 56630,
    46860, 17520
  ];
  var percentage = [
    0.3603, 0.4742, 0.5733, 0.6724,
    0.7535, 0.8, 0.8331, 0.8614,
    0.8847, 0.9071, 0.9284, 0.9438,
    0.9533, 0.9621, 0.9704, 0.9777,
    0.9836, 0.9870, 0.9901, 0.9931,
    0.9953, 0.9967, 0.9978, 0.9988,
    0.9997, 1
  ];
  var labels = [
    "ショートソード", "薬草+1", "薬草", "ロングソード",
    "革の鎧", "ロングソード+1", "まよけ", "回復の巻物",
    "メイス", "グレートソード", "ブレストプレート+1", "鎧帷子",
    "解毒剤", "気付け薬", "解毒の巻物", "光の盾",
    "ドラゴンスレイヤー", "魚の干物", "フレイル", "食事セット",
    "干し肉", "光の鎧", "ミルク", "光の剣",
    "伝説の鎧", "パン"
  ];
  var myChart = new Chart(ctx, {
    type: 'bar',
    data: {
      labels: labels,
      datasets: [{
        label: '売上',
        data: salesData,
        backgroundColor: 'rgba(255, 99, 132, 0.2)',
        borderColor: 'rgba(255, 99, 132, 1)',
        borderWidth: 1,
        yAxisID: 'y-axis-sales'
      }, {
        label: '比率の累計',
        data: percentage,
        backgroundColor: 'rgba(99, 132, 255, 0.2)',
        borderColor: 'rgba(99, 132, 255, 1)',
        borderWidth: 1,
        type: 'line',
        yAxisID: 'y-axis-percentage'
      }]
    },
    options: {
      scales: {
        yAxes: [{
            id: 'y-axis-sales',
            type: 'linear',
          display: true,
          position: 'left',
          ticks: {
            beginAtZero: true
          }
        }, {
            id: 'y-axis-percentage',
          type: 'linear',
          display: true,
          position: 'right',
          ticks: {
            beginAtZero: true
          }
        }]
      }
    }
  });

</script>

それでは、早速表示させてみましょう。

 

目盛り (ticks) のカスタマイズ

ここで表示された票は、Y軸の数字に違和感がありますよね。売上高はやたら滅多に 0 が多いし、比率は割合で表示されているし。

そこで、この目盛りの表示をカスタマイズしてみます。

ラベル 軸ID データ 位置
売上高(万円) y-axis-sales salesData
比率(%) y-axis-percentage percentage

ここでは、売上高を10000円をベースにして表示し、比率はパーセンテージで表示させます。

この目盛表示のカスタマイズには、tickscallback にコールバックを定義します。

 

売上高のコールバック

options: {
  yAxes: [{
    id: 'y-axis-sales',
      :
    ticks: {
      beginAtZero: true,
      callback: function(label, index, labels) {
        return label / 10000;
      }
    }
  }]
}

callback の引数は次のような値が渡されます。

  • label: 目盛りに表示される値
  • inxex: labels に対するインデックス
  • labels: 目盛りに表示される値の配列

この売上高の例では、売上高÷10000 で軸の目盛りを表示します。

比率のコールバック

比率のコールバックは以下のようになります。

options: {
  yAxes: [{
    id: 'y-axis-sales',
      :
    ticks: {
        :
      }
    }
  }, {
    id: 'y-axis-percentage',
      :
    ticks: {
      beginAtZero: true,
      stepSize: 0.1,
      min: 0,
      max: 1,
      callback: function(label, index, labels) {
        return label * 100;
      }
    }
  }]
}

min は 目盛りの最小値で 0 としており、max は目盛りの最大値で 1 になります。stepSize は目盛りを刻む間隔で 0.1 としています。要するに、0から1まで0.1間隔で目盛りを振るということになります。

要するにこの例では、Y軸の目盛に0から100までの数字が、10間隔で表示されるようになります。

 

ツールチップのカスタマイズ

このままグラフを表示させると、目盛りの数字は意図したとおりに表示されますが、マウスをグラフ上にホバーした時に表示されるツールチップは元のままの値が表示されます。

そこでツールチップもコールバックを用いてカスタムしておきます。

options: {
  :
  tooltips: {
    title: function(tooltipItem, data) {
      return tooltipItem[0].xLabel;
    },
    label: function(tooltipItem, data) {
      var datasetIndex = tooltipItem.datasetIndex;
      var dataset = data.datasets[datasetIndex];
      if (datasetIndex == 0) {
        return dataset.label + ': ' + tooltipItem.value + "円";
      } else if (datasetIndex == 1) {
        return dataset.label + ': ' + Number(tooltipItem.value * 100).toFixed(2) + "%";
      }
    }
  }
}

それぞれ、titlelabel に定義した関数には、tooltipItemdata の2つの引数が渡されます。

data には Chart に渡している labels と、datasets のオブジェクトが内包されています。

tooltipItem はオブジェクトの配列で、オブジェクトの内容は、例えば次のようになっています。

{
  datasetIndex: 1,
  index: 6,
  label: "まよけ",
  value: "0.8331",
  xLabel: "まよけ",
  yLabel: 0.8331
    :
}

datasetIndex は先程の data に含まれているデータセットの配列のインデックスで、index はそのデータセット内のデータ、あるいはラベルの配列に対するインデックスになっています。

datasetIndex で売上データと比率データ、いずれのデータセット化を判別できますし、index でそのデータセット内のデータやラベルを取得できます。

 

スケールラベルの設定

ここまで来ると、後はY軸のラベルを割り振るだけです。

売上高の軸には、「売上高(万円)」を、比率の累計の軸には「比率(%)」を表示させます。

それには以下のようにします。

options: {
  scales: {
    yAxes: [{
      :
      scaleLabel: {
      display: true,
        labelString: '売上高(万円)'
      }
    }, {
      :
      scaleLabel: {
        display: true,
        labelString: '比率(%)'
      }
    }]
  }
}

これで、ソースは次のようになるはずです。

<canvas id="myChart" width="400" height="400"></canvas>
<script>
  var ctx = document.getElementById('myChart').getContext('2d');
  var salesData = [
    19630770, 6202180, 5398800, 5397370,
    4419560, 2533000, 1804000, 1541280,
    1272240, 1220070, 1156800, 842100,
    516670, 479320, 452200, 395390,
    323440, 183680, 170430, 161100,
    122430, 75720, 58520, 56630,
    46860, 17520
  ];
  var percentage = [
    0.3603, 0.4742, 0.5733, 0.6724,
    0.7535, 0.8, 0.8331, 0.8614,
    0.8847, 0.9071, 0.9284, 0.9438,
    0.9533, 0.9621, 0.9704, 0.9777,
    0.9836, 0.9870, 0.9901, 0.9931,
    0.9953, 0.9967, 0.9978, 0.9988,
    0.9997, 1
  ];
  var labels = [
    "ショートソード", "薬草+1", "薬草", "ロングソード",
    "革の鎧", "ロングソード+1", "まよけ", "回復の巻物",
    "メイス", "グレートソード", "ブレストプレート+1", "鎧帷子",
    "解毒剤", "気付け薬", "解毒の巻物", "光の盾",
    "ドラゴンスレイヤー", "魚の干物", "フレイル", "食事セット",
    "干し肉", "光の鎧", "ミルク", "光の剣",
    "伝説の鎧", "パン"
  ];
  var myChart = new Chart(ctx, {
    type: 'bar',
    data: {
      labels: labels,
      datasets: [{
        label: '売上',
        type: 'bar',
        data: salesData,
        backgroundColor: 'rgba(255, 99, 132, 0.2)',
        borderColor: 'rgba(255, 99, 132, 1)',
        borderWidth: 1,
        yAxisID: 'y-axis-sales'
      }, {
        label: '比率の累計',
        type: 'line',
        data: percentage,
        backgroundColor: 'rgba(99, 132, 255, 0.2)',
        borderColor: 'rgba(99, 132, 255, 1)',
        borderWidth: 1,
        yAxisID: 'y-axis-percentage'
      }]
    },
    options: {
      scales: {
        yAxes: [{
          id: 'y-axis-sales',
          type: 'linear',
          display: true,
          position: 'left',
          ticks: {
            beginAtZero: true,
            callback: function(label, index, labels) {
                return label / 10000;
            }
          },
          scaleLabel: {
            display: true,
            labelString: '売上高(万円)'
          }
        }, {
          id: 'y-axis-percentage',
          type: 'linear',
          display: true,
          position: 'right',
          ticks: {
            beginAtZero: true,
            stepSize: 0.1,
            min: 0,
            max: 1,
            callback: function(label, index, labels) {
              return label * 100;
            }
          },
          scaleLabel: {
            display: true,
            labelString: '比率(%)'
          }
        }]
      },
      tooltips: {
        callbacks: {
          title: function(tooltipItem, data) {
            return tooltipItem[0].xLabel;
          },
          label: function(tooltipItem, data) {
            var datasetIndex = tooltipItem.datasetIndex;
            var dataset = data.datasets[datasetIndex];
            if (datasetIndex == 0) {
              return dataset.label + ': ' + tooltipItem.value + "円";
            } else if (datasetIndex == 1) {
              return dataset.label + ': ' + Number(tooltipItem.value * 100).toFixed(2) + "%";
            }
          }
        }
      }
    }
  });

</script>

 

グラフの表示

それではここまでの内容をまとめて表示してみましょう。

 

まとめ

Chart.js では軸やグラフにかなり柔軟に設定をすることができるみたいですね。

これで比較的いろいろなグラフを表示できるかと思うのですが、まだ後何点か調べていることがあるので、また後日記事にしたいと思います。

カテゴリー: javascriptweb

0件のコメント

コメントを残す

メールアドレスが公開されることはありません。