こんにちは、ミナピピン(@python_mllover)です!
今回はPythonのwebアプリ用フレームワークであるFlaskを使ってhtml上にMatplotlibで作成したグラフを表示させる方法について紹介したいと思います。
これまでの記事
FlaskによるWebアプリ開発①~簡単なWebページを表示させてみる
FlaskによるWebアプリ開発②~テンプレートエンジン(Jinja2)による動的Webページの生成
FlaskによるWebアプリ開発③~formタグでのデータのGET/POST送信
FlaskによるWebアプリ開発④~SQLAlchemyを使ってデータベースを操作する
webアプリケーション上でのグラフのプロット
webアプリケーションを作るうえで知っておきたいのがデータをプロットするテクニックですね。サイトに円グラフとかがあるとオシャレに見えます。
自分はReactやNode.jsは専門外なのでよく知りませんが、多分 JavaScript のライブラリでかっこいいグラフとかをひょいひょい作ってくれたりするのではないかなと思います。
ですが、Pythonでwebアプリーケーションを作るなら、やはりそのまま使い慣れた matplotlibで作ったグラフをwebアプリ上に表示させたいですよね。
そう思って今回はFlaskで作った簡単なhtml上にサーバー上でMatplotlibで作成したグラフを表示させる方法について色々模索したので、その結果を自分用に保存しておきます。
前提となるPythonの軽量Webアプリケーションフレームワークである「Flask」の使い方については以下の記事で解説しているのでそちらを参照してください。
FlaskとMatplotlibでwebアプリ上にプロットしたグラフを表示させる
サーバー上でMatplotlibを使って作ったグラフをhtmlで表示させる方法は大きく分けて2通りあります。
①生成した画像を画像データとしてレスポンスに返す
②内部ディレクトリにプロットした画像ファイル保存して、相対パスで呼び出す
画像ファイルを一時ファイルとして保存する方法はやってみたけどよく分からなかったというかpngをレスポンスにして返したら一発でできたので、また時間があるときに追記します。
画像データをレスポンスで返してhtml上にグラフを表示させたい場合は以下のように記述するとできます。インデントがおかしい場合はgitにコードを上げているのでそちらをダウンロードしてコピペしてください。
# ライブラリの読み込み from flask import Flask, render_template, make_response, jsonify from io import BytesIO import urllib from matplotlib.backends.backend_agg import FigureCanvasAgg from matplotlib.figure import Figure import matplotlib.pyplot as plt # インスタンスの生成 app = Flask(__name__) @app.route('/') def index(): return('<html><h1>実行結果</h1><p><p><img src="/graph1.png" ></img></html>') @app.route('/graph1.png') def graph1(): # データからグラフをプロットする x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] y = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100] fig = plt.figure() ax = fig.add_subplot(111) plt.title('サンプル') plt.grid(which='both') plt.legend() plt.plot(x, y) # canvasにプロットした画像を出力 canvas = FigureCanvasAgg(fig) png_output = BytesIO() canvas.print_png(png_output) data = png_output.getvalue() # HTML側に渡すレスポンスを生成する response = make_response(data) response.headers['Content-Type'] = 'image/png' response.headers['Content-Length'] = len(data) return response if __name__ == "__main__": app.run(debug=True)
参照:https://github.com/beginerSE/flask_plot_img/blob/master/app.py
<実行結果>
plt.grid()
や plt.legend()
はplt.plotでグラフをプロットするときによく使う関数です。Matplotlibの基本については以下の記事で解説しています。
‘/graph1.png’というルーティングで行っているサーバ内部の処理の流れとしては以下のような感じです。
- グラフをプロットする
- データをBytesIOでバッファに書き込む
- バッファに書き込んだバイト文字列に置き換えた画像データをレスポンスとして返す
BytesIOとは、Pythonで作ったバイナリデータをPCのバッファという一時的な保管庫に格納できるようにする関数で、標準ライブラリの io に中に含まれています。バイナリデータとは主に画像や音声などのデータのことです。
今のPCだと画像や音声はペイントやWindowsメディアプレーヤーなんかで、音声や画像として自動で表示されますが、内部的にはコンピューターで扱うデータは全てバイナリデータなのです。
バイナリデータでも文字として認識できるテキストデータと違って、音声や画像は内部(バイナリ)と外部見え方が余りにも違うため、テキストデータと対比して用いられ、一般的にバイナリデータ=画像・音声のバイナリデータを指します。例えば画像データなんかをバイナリとして開くと↓みたいな感じです
引用元:https://java2005.cis.k.hosei.ac.jp/materials/lecture22/binstream.html
PythonだとStringIOというライブラリでバッファに書き込んでいた人もいたのですが、自分の環境ではStringIOなりライブラリがなくpipでも見つからなかったので、BytesIOを使いました。
あとで知ったのですが。thinkerとThinkerみたいにPython2と3で名前が変わり呼び出し方が変わっていたみたいです。
関連記事:【Python】Pandasのデータフレームをテーブルに高速INSERTする
後はこの画像データをresponse.headers['Content-Type']
で、リクエストが来た際に画像データ(png)だと宣言しておき、レスポンスが返されるURL(/graph1.png)をindex.html上で<img src=’/graph1.png’></img>を使って埋め込むとFlaskで動かしているwebアプリ上にMatplotlibで作成したグラフを表示させることができます。
<htmlに画像のURLを埋め込む場合の例>
<img src="https:/ドメイン//graph1.png" alt=""></img>
終わり
Pythonでやった機械学習の結果をグラフなりで見やすく返すみたいなFlaskで作る個人レベルの簡単webアプリならこの処理で行けると思います。
ただMatplotlibのグラフはあまりオシャレではないので、見栄えなどを重視するのであれば、javascriptのChartJSなんかを使う方が一般的だと思います。
関連記事:Chartjsとcolorschemesでオシャレなグラフをプロットする
今回のプロットに使ったx,yのデータをデータベースから引っ張ってくる処理に変えると仕組み的にはそれっぽくなりますね。PythonのFlaskならsqlite3という簡易データベースが標準で用意されているので、それと連携させるとミニデータベースサイトが作れます
関連記事:【Python】スクレイピングした価格データをデータベース(DB)に保存する
関連記事:【Python】Flask+SQLAlchemyを使って、「ひとこと掲示板」を作る
コメント
[…] 関連記事:【Python】FlaskでMatplotlibでプロットしたグラフを表示させる […]