【Flask】PandasのDataFrameをJupyter Notebook風にWebページ上で表示させる方法

こんにちは、データサイエンティストのたぬ(@tanuhack)です!

今回は、Flaskを使った簡単なWebアプリケーションで、PandasのDataFrameを最小のHTMLとjinja2テンプレートエンジンの変数を使って、Webページ上に表示させる方法を紹介します。

サーバーサイド側の処理によってDataFrameのサイズが動的に変化しても、レイアウトが崩れないように設計しているので、よかったら参考にしてみてください。

[aside]補足

HTMLのテンプレートエンジンはjinja2を使用しています。jinja2の使い方があやしい人は過去記事にめっちゃ詳しくまとめたのであわせてどうぞ

【随時更新】Flaskのjinja2テンプレートエンジンのチートシート [/aside]

app.py(Python)の設定

df-to-web/
app.py
├ templates/
│ └ index.html
└ static/
  └ style.css

サーバーサイドのDataFrameをクライアントサイドのHTMLに渡すには、『カラム名の1次元配列のリスト』と『インデックスを含まない全レコードの2次元配列のリスト』をそれぞれ変数に代入し、Flaskのrender_templateメソッドでそれぞれを引数にして渡します。

説明
df.columns カラム名の1次元配列のリストを取得
df.values.tolist() インデックスを含まない全レコードの2次元配列のリストを取得

当記事では、機械学習の勉強で良く使用されるアヤメ(iris)のデータセットを変数dfに格納するものとして進めます。(※DataFrameの中身は何でも大丈夫です。

from flask import Flask, render_template
import pandas as pd

app = Flask(__name__)

df = pd.read_csv('static/csv/iris.csv')
header = df.columns # DataFrameのカラム名の1次元配列のリスト
record = df.values.tolist() # DataFrameのインデックスを含まない全レコードの2次元配列のリスト

@app.route('/')
def index():
  return render_template('index.html', header=header, record=record)

if __name__ == '__main__':
  app.run()

index.html(HTML)の設定

df-to-web/
├ app.py
├ templates/
│ └ index.html
└ static/
  └ style.css

HTMLのtableタグとjinja2のforループを使って最小限のHTMLの記述だけで、DataFrameをブラウザ上に表示させます。

  • DataFrameのカラム名をthタグに格納
  • {% for i in header: %}
    <th scope="col">{{ i }}</th>
    {% endfor %}
  • DataFrameのレコードをtdタグに格納し、最後のレコードまで繰り返す
  • {% for i in record: %}
    <tr>
    	{% for j in i: %}
    	<td>{{ j }}</td>
    	{% endfor %}
    </tr>
    {% endfor %}
  • index.html
  • <!DOCTYPE html>
    <html lang="ja">
    <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>DataFrameをブラウザ上に表示させる</title>
    <link rel="stylesheet" href="/static/style.css">
    </head>
    <body>
    	<table>
    		<thead>
    			<tr>
    				{% for i in header: %}
    				<th scope="col">{{ i }}</th>
    				{% endfor %}
    			</tr>
    		</thead>
    		<tbody>
    			{% for i in record: %}
    			<tr>
    				{% for j in i: %}
    				<td>{{ j }}</td>
    				{% endfor %}
    			</tr>
    			{% endfor %}
    		</tbody>
    	</table>
    </body>
    </html>

    style.css(CSS)の設定

    df-to-web/
    ├ app.py
    ├ templates/
    │ └ index.html
    └ static/
      └ style.css

    CSSに関しては、特に言うことはありません。HTML側のclass名にあわせて、適宜修正してください。

    table {
      margin-top: 1em;
      margin-left: auto;
      margin-right: auto;
      border: none;
      border-collapse: collapse;
      border-spacing: 0;
      color: black;
      font-size: 12px;
      table-layout: fixed;
      text-align: right;
      padding: 0.4em;
    }
    thead {
      border-bottom: 1px solid black;
      vertical-align: bottom;
    }
    tbody tr:nth-child(odd) {
      background: #f5f5f5;
    }
    tr, th, td {
      text-align: right;
      vertical-align: middle;
      padding: 0.5em 0.5em;
      line-height: normal;
      white-space: normal;
      max-width: none;
      border: none;
    }
    th {
      font-weight: bold;
    }