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

便利ページ:グラフ表示

Last updated at Posted at 2025-01-13

chart.jsを使って、CSVやTSVのデータをグラフ表示するページを作成しました。
大したことはやってはいないのですが、備忘録として残しておきます。

便利ページ:Javascriptでちょっとした便利な機能を作ってみた」のシリーズものです。

ソースコードもろもろは、以下に上げておきました。

poruruba/utilities

すぐに使いたい場合は以下から参照し、ユーティリティタブからグラフを選択してください。

CSV/TSVファイルの読み出し

CSV/TSVファイルの読み出しには、以下の「CSV Parser for Node.js」を使いました。

まず、index.htmlに以下を含めます。

<script src="https://cdn.jsdelivr.net/npm/csv@6.3.11/dist/umd/sync.min.js" async></script>

CSVの場合

        var rows =  csv_sync.parse(text);
        console.log(rows);

TSVの場合

        var rows =  csv_sync.parse(text, { delimiter: "\t" });
        console.log(rows);

chart.jsによるグラフ表示

グラフ表示にchart.jsを使います。以下のサイトにサンプル等有用な情報がたくさんあります。

まず、以下をindex.htmlに含めます。

<script src="https://cdn.jsdelivr.net/npm/chart.js" async></script>

基本的には、以下のように呼び出すだけです。

        graph_chartjs = new Chart(document.getElementById('graph_chart'), {
            type: line,
            data: {
        		labels: [],
        		datasets: [{
			     label: label,
			     data: []
    		    }]
            },
            options: {}
        });

もし内容を変えたかれば、dataset等を変更したのち、以下を呼び出せばよいです。

graph_chartjs.update();

ソースコード

まとめるとこんな感じです。

js/comp/comp_graph.js
let scatter_chartjs;
let graph_chartjs;

export default {
  mixins: [mixins_bootstrap],
  template: `
<div>
  <h2 class="modal-header">グラフ</h2>

  <ul class="nav nav-tabs">
    <li class="nav-item"><a class="nav-link active" href="#graph_graph" data-bs-toggle="tab">Graph</a></li>
    <li class="nab-item"><a href="#graph_scatter" class="nav-link" data-bs-toggle="tab">Scatter</a></li>
  </ul>
  <br>

  <div class="tab-content">
    <div class="tab-pane active" id="graph_graph">

      <collapse-panel id="panel_graph_options" title="options" collapse="true">
        <span slot="content">
          <div class="card-body">
            <button class="btn btn-primary btn-sm" v-on:click="update_options">update</button>
            <button class="btn btn-primary btn-sm" v-on:click="clear_options">clear</button>
            <br>
            <span class="row">
              <label class="title"><input type="checkbox" v-model="chartjs_base0"> base0</label><br>
              <label class="title"><input type="checkbox" v-model="chartjs_stacked"> stacked</label><br>
              <label class="title col-auto">max <input type="text" class="form-control" v-model="chartjs_max"></label>
              <label class="title col-auto">min <input type="text" class="form-control" v-model="chartjs_min"></label>
            </span>
          </div>
        </span>
      </collapse-panel>

      <collapse-panel id="panel_graph_data" title="data" collapse="true">
        <span slot="content">
          <div class="card-body">
            <button class="btn btn-primary btn-sm" v-on:click="clear_data">clear</button>
            <br>
            <label class="title"><input type="checkbox" v-model="chk_header"> header</label> <label class="title"><input type="checkbox" v-model="chk_stepped"> stepped</label>
            <br>
            <label class="title">csv/tsv</label> <comp_file v-bind:callback="graph_callback" accept=".csv,.tsv"></comp_file>
            <br>
            <ol>
              <li v-for="(item, index) in graph_data.datasets"><button class="btn btn-secondary btn-xs" v-on:click="graph_delete_row(index)">del</button> {{item.label}}</li>
            </ol>
          </div>
        </span>
      </collapse-panel>

      <span class="row">
        <label class="title col-auto">type</label>
        <span class="col-auto">
            <select class="form-select" v-model="chartjs_type" v-on:change="change_type">
              <option value="line">line</option>
              <option value="bar">bar</option>
              <option value="pie">pie</option>
            </select>
        </span>
      </span>

      <div>
        <canvas id="graph_chart"></canvas>
      </div>
    </div>

    <div class="tab-pane" id="graph_scatter">

      <collapse-panel id="panel_scatter_options" title="options" collapse="true">
        <span slot="content">
          <div class="card-body">
            <button class="btn btn-primary btn-sm" v-on:click="update_scatter_options">update</button>
            <button class="btn btn-primary btn-sm" v-on:click="clear_scatter_options">clear</button>
            <br>
            <span class="row">
              <label class="title"><input type="checkbox" v-model="chartjs_scatter_base0"> base0</label><br>
              <label class="title col-auto">max <input type="text" class="form-control" v-model="chartjs_scatter_max"></label>
              <label class="title col-auto">min <input type="text" class="form-control" v-model="chartjs_scatter_min"></label>
            </span>
          </div>
        </span>
      </collapse-panel>

      <collapse-panel id="panel_scatter_data" title="data" collapse="true">
        <span slot="content">
          <div class="card-body">
            <button class="btn btn-primary btn-sm" v-on:click="clear_scatter_data">clear</button>
            <br>

            <label class="title"><input type="checkbox" v-model="chk_header"> header</label> <label class="title"><input type="checkbox" v-model="chk_stepped"> stepped</label>
            <br>
            <label class="title">tsv</label> <comp_file v-bind:callback="tsv_scatter_callback" accept=".tsv"></comp_file>
            <br>
            <ol>
              <li v-for="(item, index) in graph_data.datasets"><button class="btn btn-secondary btn-xs" v-on:click="graph_delete_row(index)">del</button> {{item.label}}</li>
            </ol>

            </li>
          </div>
        </span>
      </collapse-panel>

      <div>
        <canvas id="scatter_chart"></canvas>
      </div>
    </div>
  </div>
  <br>
</div>`,
  data: function () {
    return {
      chartjs_type: "line",
      graph_data: {
          labels: [],
          datasets: []
      },
      graph_options: {
          scales : {
              x: {},
              y: {}
          },
          plugins: {
              colors: {
                forceOverride: true
              }
          }
      },
      scatter_data: {
          labels: [],
          datasets: []
      },
      scatter_options: {
          scales : {
              x: {},
              y: {}
          },
          plugins: {
              colors: {
                forceOverride: true
              }
          }
      },        
      chk_header: false,
      chk_stepped: false,
      chartjs_stacked: false,
      chartjs_base0: false,
      chartjs_max: "",
      chartjs_min: "",
      chartjs_scatter_base0: false,
      chartjs_scatter_max: "",
      chartjs_scatter_min: "",
    }
  },
  methods: {
    /* グラフ */
    update_scatter_options: async function(){
      console.log("update_scatter_options called");
      this.scatter_options = {
          scales : {
              x: {},
              y: {}
          },
          plugins: {
              colors: {
                forceOverride: true
              }
          }
      };
      if( this.chartjs_scatter_base0 )
          this.scatter_options.scales.y.beginAtZero = true;
      if( this.chartjs_scatter_max !== undefined && this.chartjs_scatter_max != ""  )
          this.scatter_options.scales.y.suggestedMax = parseInt(this.chartjs_scatter_max, 10);
      if( this.chartjs_scatter_min !== undefined && this.chartjs_scatter_min != ""  )
          this.scatter_options.scales.y.suggestedMin = parseInt(this.chartjs_scatter_min, 10);
      if( scatter_chartjs ){
          scatter_chartjs.options = this.scatter_options;
          scatter_chartjs.update();
      }
    },
    graph_delete_row: async function(index){
        console.log("graph_delete_row called");
        this.graph_data.datasets.splice(index, 1);
        if( graph_chartjs ){
            graph_chartjs.data = this.graph_data;
            graph_chartjs.update();
        }
    },
    clear_scatter_data: async function(){
        this.scatter_data = {
            labels: [],
            datasets: [],
        };
        scatter_chartjs.data = this.scatter_data;
        scatter_chartjs.update();
    },
    clear_scatter_options: async function(){
        this.scatter_options = {
            scales : {
                x: {},
                y: {}
            }
        };
        scatter_chartjs.options = this.scatter_options;
        scatter_chartjs.update();
    },        

    update_options: async function(){
        console.log("update_options called");
        this.graph_options = {
            scales : {
                x: {},
                y: {}
            },
            plugins: {
                colors: {
                  forceOverride: true
                }
            }
        };
        if( this.chartjs_base0 )
            this.graph_options.scales.y.beginAtZero = true;
        if( this.chartjs_stacked ){
            this.graph_options.scales.x.stacked = true;
            this.graph_options.scales.y.stacked = true;
        }
        if( this.chartjs_max !== undefined && this.chartjs_max != ""  )
            this.graph_options.scales.y.suggestedMax = parseInt(this.chartjs_max, 10);
        if( this.chartjs_min !== undefined && this.chartjs_min != ""  )
            this.graph_options.scales.y.suggestedMin = parseInt(this.chartjs_min, 10);
        if( graph_chartjs ){
            graph_chartjs.options = this.graph_options;
            graph_chartjs.update();
        }
    },
    clear_options: async function(){
        this.graph_options = {
            scales : {
                x: {},
                y: {}
            }
        };
        if( graph_chartjs ){
            graph_chartjs.options = this.graph_options;
            graph_chartjs.update();
        }
    },
    clear_data: async function(){
        this.graph_data = {
            labels: [],
            datasets: [],
        };
        if( graph_chartjs ){
            graph_chartjs.data = this.graph_data;
            graph_chartjs.update();
        }
    },
    change_type: async function(){
        console.log("change_type called");
        if( graph_chartjs )
            graph_chartjs.destroy();

        graph_chartjs = new Chart(document.getElementById('graph_chart'), {
            type: this.chartjs_type,
            data: this.graph_data,
            options: this.graph_options
        });
    },
    graph_callback: async function(files){
        console.log("graph_callback called");
        if( files.length <= 0 )
            return;

        if( files[0].name.toLowerCase().endsWith(".csv") )
            this.csv_callback(files[0]);
        else if( files[0].name.toLowerCase().endsWith(".tsv") )
            this.tsv_callback(files[0]);
        else
            throw new Error("unknown file ext");
    },
    csv_callback: async function(file){
        console.log("csv_callback called");

        var text = await read_file(file, "text");
        console.log(text);

        var rows =  csv_sync.parse(text);
        console.log(rows);
        for( var i = 0 ; i < rows.length ; i++ ){
            if( this.chk_header && i == 0 )
                continue;
            this.graph_data.datasets.push({
                label: this.graph_data.datasets.length + 1,
                data: rows[i],
                stepped: this.chk_stepped
            });
        }
        for( var i = 0 ; i < rows[0].length ; i++ ){
            if( this.graph_data.labels.length <= i )
                this.graph_data.labels.push(this.chk_header ? rows[0][i] : i);
        }
        if( graph_chartjs ){
            graph_chartjs.data = this.graph_data;
            graph_chartjs.update();
        }
    },
    tsv_callback: async function(file){
        console.log("tsv_callback called");

        var text = await read_file(file, "text");
        console.log(text);

        var rows =  csv_sync.parse(text, { delimiter: "\t" });
        console.log(rows);

        var datum = [];
        for( var i = 0 ; i < rows[0].length ; i++ )
            datum[i] = [];
        for( var i = 0 ; i < rows.length ; i++ ){
            if( this.chk_header && i == 0 )
                continue;
            for( var j = 0 ; j < rows[i].length ; j++ ){
                datum[j].push(rows[i][j]);
            }
        }
        for( var i = 0 ; i < rows[0].length ; i++ ){
            this.graph_data.datasets.push({
                label: this.chk_header ? rows[0][i] : this.graph_data.datasets.length + 1,
                data: datum[i],
                stepped: this.chk_stepped
            });
        }
        for( var i = 0 ; i < datum[0].length ; i++ ){
            if( this.graph_data.labels.length <= i)
                this.graph_data.labels[i] = i;
        }
        if( graph_chartjs ){
            graph_chartjs.data = this.graph_data;
            graph_chartjs.update();
        }
    },
    tsv_scatter_callback: async function(files){
        console.log("tsv_scatter_callback called");
        if( files.length <= 0 )
            return;

        var text = await read_file(files[0], "text");
        console.log(text);

        var rows =  csv_sync.parse(text, { delimiter: "\t" });
        console.log(rows);

        var datum = [];
        for( var i = 1 ; i < rows[0].length ; i++ )
            datum[i - 1] = [];
        for( var i = 0 ; i < rows.length ; i++ ){
            if( this.chk_header && i == 0 )
                continue;
            for( var j = 1 ; j < rows[i].length ; j++ ){
                datum[j - 1].push({
                    x: rows[i][0],
                    y: rows[i][j]
                });
            }
        }
        for( var i = 1 ; i < rows[0].length ; i++ ){
            this.scatter_data.datasets.push({
                label: this.chk_header ? rows[0][i] : this.scatter_data.datasets.length + 1,
                data: datum[i - 1],
            });
        }
        scatter_chartjs.data = this.scatter_data;
        scatter_chartjs.update();
    },
  },
  mounted: function(){
    graph_chartjs = new Chart(document.getElementById('graph_chart'), {
      type: this.chartjs_type,
      data: this.graph_data,
      options: this.graph_options
    });
    scatter_chartjs = new Chart(document.getElementById('scatter_chart'), {
      type: "scatter",
      data: this.scatter_data,
      options: this.scatter_options
    });
  }
};

以上

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