Flaskを使ってみる

Chart.jsを使ったグラフ表示


最近、Chart.jsというライブラリの存在を知りました。
matplotlib、seaborn、bokehはpythonのライブラリですが、Chart.jsはJavaScriptのライブラリで、 HTMLファイルに埋め込んで使います。

インストール方法がこ ちらに公開されていますが、cdnjsサーバー(JSライブラ リ配信サイト)に登録されているライブラリを使用することができるので、特別なインストールは不要です。
ローカルサーバー上にライブラリをインストールして、ローカルのライブラリを使うこともできます。

Flaskと組み合わせることで、グラフ表示用のJavaスクリプトを含んでいるHTMLファイルを作成する事ができます。
グラフ表示はJavaスクリプト(クライアント側)で行われます。



最初にこちらに公開されているサンプ ルを実行してみます。
pythonコードは以下の様になります。
chart1.htmlを表示しているだけのコードです。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from flask import Flask, render_template, redirect
import datetime
app = Flask(__name__)

@app.route("/")
def index():
    return render_template('chart1.html')

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=8080, debug=True)

テンプレートファイル(chart1.html)は以下の様になります。
今回はRelease2.9.3のChart.min.js(タブや改行を取り除いた圧縮版)を組み込んでいますが、Chart.jsでも結果は 同じで す。
MyChartのcanvasにグラフを埋め込んで表示します。
<!DOCTYPE html>
<head>
<title>chart.Js</title>
<link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"/>
</head>

<body>

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<!--
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.js"></script>
-->

<canvas id="myChart" width="400" height="200"></canvas>
<script>
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
    type: 'bar',
    data: {
        labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
        datasets: [{
            label: '# of Votes',
            data: [12, 19, 3, 5, 2, 3],
            backgroundColor: [
                'rgba(255, 99, 132, 0.2)',
                'rgba(54, 162, 235, 0.2)',
                'rgba(255, 206, 86, 0.2)',
                'rgba(75, 192, 192, 0.2)',
                'rgba(153, 102, 255, 0.2)',
                'rgba(255, 159, 64, 0.2)'
            ],
            borderColor: [
                'rgba(255, 99, 132, 1)',
                'rgba(54, 162, 235, 1)',
                'rgba(255, 206, 86, 1)',
                'rgba(75, 192, 192, 1)',
                'rgba(153, 102, 255, 1)',
                'rgba(255, 159, 64, 1)'
            ],
            borderWidth: 1
        }]
    },
    options: {
        scales: {
            yAxes: [{
                ticks: {
                    beginAtZero: true
                }
            }]
        }
    }
});
</script>
</body>
</html>


実行すると以下のグラフを表示します。
htmlファイルに定義されているラベルとデータを表示します。




次にpythonコードを少し変更し、表示する値、ラベル、背景色のリストを追加し、テンプレートに渡します。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from flask import Flask, render_template, redirect
import datetime
app = Flask(__name__)

@app.route("/")
def index():
    labelList = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri']
    valueList = [1,2,3,4,5]
    backgroundColorList= [
                'rgba(255, 99, 132, 0.2)',
                'rgba(54, 162, 235, 0.2)',
                'rgba(255, 206, 86, 0.2)',
                'rgba(75, 192, 192, 0.2)',
                'rgba(153, 102, 255, 0.2)',
                'rgba(255, 159, 64, 0.2)'
    ]

    valueIndex = len(valueList)
    templateData = {
       'chartTitle' : 'myChart',
       'valueIndex': valueIndex,
       'valueList': valueList,
       'labelList': labelList,
       'backgroundColorList': backgroundColorList
       }
    #./templates/sample.html
    return render_template('chart3.html', **templateData)

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=8080, debug=True)

テンプレートファイル(chart3.html)にラベルと値の表示を追加します。
<!DOCTYPE html>
<head>
<title>chart.Js</title>
<link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"/>
</head>

<body>
<ul>
{% for no in range(valueIndex) %}
    <li>{{ labelList[no] }}-{{ valueList[no] }}</li>
{% endfor %}
</ul>

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<!--
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.js"></script>
-->

<canvas id="myChart" width="200" height="100"></canvas>

<script>
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
    type: 'bar',
    data: {
        labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
        datasets: [{
            label: '# of Votes',
            data: [12, 19, 3, 5, 2, 3],
            backgroundColor: [
                'rgba(255, 99, 132, 0.2)',
                'rgba(54, 162, 235, 0.2)',
                'rgba(255, 206, 86, 0.2)',
                'rgba(75, 192, 192, 0.2)',
                'rgba(153, 102, 255, 0.2)',
                'rgba(255, 159, 64, 0.2)'
            ],
            borderColor: [
                'rgba(255, 99, 132, 1)',
                'rgba(54, 162, 235, 1)',
                'rgba(255, 206, 86, 1)',
                'rgba(75, 192, 192, 1)',
                'rgba(153, 102, 255, 1)',
                'rgba(255, 159, 64, 1)'
            ],
            borderWidth: 1
        }]
    },
    options: {
        scales: {
            yAxes: [{
                ticks: {
                    beginAtZero: true
                }
            }]
        }
    }
});
</script>
</body>
</html>

実行するとpython側で指定したラベルと値を表示しますが、グラフは何も変わりません。




Chart.Js側に変数を渡すやり方はこ ちらに公開されていました。
テンプレート内のスクリプトでchartDataという変数を定義し、その変数に表示したい値を詰め込んでいきます。
<!DOCTYPE html>
<head>
<title>chart.Js</title>
<link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"/>
</head>

<body>
<ul>
{% for no in range(valueIndex) %}
    <li>{{ labelList[no] }}{{ valueList[no] }}</li>
{% endfor %}
</ul>

<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script>

<canvas id="myChart" width="200" height="100"></canvas>

<script>
// define the chart data
// https://www.patricksoftwareblog.com/creating-charts-with-chart-js-in-a-flask-application/
var chartData = {
        labels: [
            {% for item in labelList %}
            "{{item}}",
            {% endfor %}],
        datasets: [{
            label: '{{ chartTitle }}',
            data: [
                {% for item in valueList %}
                {{item}},
                {% endfor %}],
            backgroundColor: [
                {% for item in backgroundColorList %}
                "{{item}}",
                {% endfor %}],
            borderColor: [
                {% for item in backgroundColorList %}
                "{{item}}",
                {% endfor %}],
            borderWidth: 1
  }]
}

var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
    type: 'bar',
    data: chartData,
    options: {
        scales: {
            yAxes: [{
                ticks: {
                    beginAtZero: true
                }
            }]
        }
    }
});

/*
var myChart = new Chart(ctx, {
    type: 'bar',
    data: {
        labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
        datasets: [{
            label: '# of Votes',
            data: [12, 19, 3, 5, 2, 3],
            backgroundColor: [
                'rgba(255, 99, 132, 0.2)',
                'rgba(54, 162, 235, 0.2)',
                'rgba(255, 206, 86, 0.2)',
                'rgba(75, 192, 192, 0.2)',
                'rgba(153, 102, 255, 0.2)',
                'rgba(255, 159, 64, 0.2)'
            ],
            borderColor: [
                'rgba(255, 99, 132, 1)',
                'rgba(54, 162, 235, 1)',
                'rgba(255, 206, 86, 1)',
                'rgba(75, 192, 192, 1)',
                'rgba(153, 102, 255, 1)',
                'rgba(255, 159, 64, 1)'
            ],
            borderWidth: 1
        }]
    },
    options: {
        scales: {
            yAxes: [{
                ticks: {
                    beginAtZero: true
                }
            }]
        }
    }
});
*/
</script>
</body>
</html>

今まで静的な値を定義していた以下の部分が、そっくり変数に置き換わります。
    data: {
        labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
        datasets: [{
            label: '# of Votes',
            data: [12, 19, 3, 5, 2, 3],
            backgroundColor: [
                'rgba(255, 99, 132, 0.2)',
                'rgba(54, 162, 235, 0.2)',
                'rgba(255, 206, 86, 0.2)',
                'rgba(75, 192, 192, 0.2)',
                'rgba(153, 102, 255, 0.2)',
                'rgba(255, 159, 64, 0.2)'
            ],
            borderColor: [
                'rgba(255, 99, 132, 1)',
                'rgba(54, 162, 235, 1)',
                'rgba(255, 206, 86, 1)',
                'rgba(75, 192, 192, 1)',
                'rgba(153, 102, 255, 1)',
                'rgba(255, 159, 64, 1)'
            ],
            borderWidth: 1
        }]
    },

これでグラフタイトル、値、ラベルが変わります。




seabornやbokehはpythonコード側でグラフを作ります。
それに対して、Chart.Jsはpython側で表示させたい値のリストだけを作り、見た目は全てテンプレート側に任せます。
テンプレートのhtmlファイルを変えるだけでグラフの見た目を以下の様に変えることができます。
 



Chart.jsと同様なJavaScriptのグラフチャートライブラリは沢山公開されてます。
こ ちらに代表的なライブラリがまとまって紹介されています。

また、Canvas要素に直接図形を表示するチュートリアルがこ ちらに公開されています。
簡単な図形や座標軸が特殊な図形は、Canvas要素に直接図形を描画してもそれほど難しくは有りません。

続 く...