【Flask】クライアントサイドにpandas.Series、pandas.DataFrameを渡す

前提条件

CHECK.1
ディレクトリ
ディレクトリ
flask/
app.py

┗ templates/
index.html

ターミナル(コマンドプロンプト)のカレントディレクトリは、 flaskフォルダにあるものとします。

CHECK.2
app.py
app.py
# -*- coding: utf-8 -*-
from flask import Flask, render_template

app = Flask(__name__)

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

if __name__ == '__main__':
	app.run()
CHECK.3
index.html
index.html
<!doctype html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Hello Jinja2</title>
  </head>
  <body>
    <h1>Hello Jinja2</h1>
    <p>Jinja2でHTMLファイルを読み込みことに成功しました。</p>
  </body>
</html>
CHECK.4
動作確認
ターミナル
$ python app.py

http://127.0.0.1:5000/

END

pandas.Seriesを渡す

結論から言うと、Jinja2テンプレートエンジンでpandas.Seriesを直接送ることはできません!

では、どうするか

pandas.SeriesをPythonのリストに変換させる必要があります。

Python
import pandas as pd # => pip install pandas

index = ['2019-12-29', '2019-12-30', '2019-12-31', '2020-01-01']
data = [12.4, 15.2, 7.9, 9.8]

s = pd.Series(data=data, index=index)

pandas.Seriesの値が変数sに格納されているとすると、以下のテーブルの書式に従い、シリーズのコンテンツやインデックスをPythonのリストとして取得することができます。

書式 戻り値の型 説明
s.values.tolist() 1次元配列 シリーズのコンテンツ
s.index.tolist() 1次元配列 シリーズのインデックス

サンプルプログラム

app.py
# 中略
@app.route('/')
def index():
	s_values = s.values.tolist() # 1次元配列(中身)
	s_index = s.index.tolist()   # 1次元配列(インデックス)

	return render_template('index.html', \
		s_values = s_values, \
		s_index = s_index)
index.html
<!-- 中略 -->
<body>
	<h1>{{ title }}</h1>
	<ul>
	{%- for i in s_index %}
		<li>{{ i|e }}, {{ s_values[loop.index0]|e }}</li>
	{%- endfor %}
	</ul>
</body>

Jinja2テンプレートエンジンのforループでloop.index0を使って、サーバーサイドから受け取った配列s_valuesの番号を指定しているところがポイントです。

forループの中で使えるloop変数についてもっと詳しく知りたい人はこの記事からどうぞ

pandas.DataFrameを渡す

結論から言うと、Jinja2テンプレートエンジンでpandas.DataFrameを直接送ることはできません!

pandas.Seriesのときと同様に、pandas.DataFrameをPythonのリストに変換させる必要があります。

Python
import pandas as pd # => pip install pandas

index = ['2019-12-29', '2019-12-30', '2019-12-31', '2020-01-01']
columns = ['最高気温', '最低気温', '天候']
data = [[12.4, 5.1, '曇り'], [15.2, 12.0, '曇り'], [7.9, 7.2, '晴れ'], [9.8, 1.6, '晴れ']]

df = pd.DataFrame(data=data, columns=columns, index=index)

pandas.DataFrameの値が変数dfに格納されているとすると、以下のテーブルの書式に従い、データフレームのコンテンツやヘッダー、インデックスをPythonのリストとして取得することができます。

書式 戻り値の型 説明
df.values.tolist() 2次元配列 データフレームのコンテンツ
df.columns.tolist() 1次元配列 データフレームのヘッダー
df.index.tolist() 1次元配列 データフレームのインデックス

テーブルのCSSは見やすさを考慮して、Jupyter Notebookと同じデザインを起用しています。

CSS
table {
	margin-left: 2em;
	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;
}

中身(コンテンツ)のみ渡す

サンプルプログラム

app.py
# 中略
@app.route('/')
def index():
	df_values = df.values.tolist() # 2次元配列(中身)

	return render_template('index.html', \
		df_values = df_values)
index.html
<!-- 中略 -->
<body>
	<h1>中身のみ渡す</h1>
	<table>
		<tbody>
			{%- for i in df_values %}
			<tr>{% for j in i %}<td>{{ j|e }}</td>{% endfor %}</tr>
			{%- endfor %}
		</tbody>
	</table>
</body>

中身とヘッダー(columns)を渡す

サンプルプログラム

app.py
# 中略
@app.route('/')
def index():
	df_values = df.values.tolist()   # 2次元配列(中身)
	df_columns = df.columns.tolist() # 1次元配列(ヘッダー)

	return render_template('index.html', \
		df_values = df_values, \
		df_columns = df_columns)
index.html
<!-- 中略 -->
<body>
	<h1>ヘッダー(columns)も渡す</h1>
	<table>
		<thead>
			<tr>{%- for i in df_columns %}<th>{{ i|e }}</th>{%- endfor %}</tr>
		</thead> 
		<tbody>
			{%- for i in df_values %}
			<tr>{% for j in i %}<td>{{ j|e }}</td>{% endfor %}</tr>
			{%- endfor %}
		</tbody>
	</table>
</body>

中身とインデックス(index)を渡す

サンプルプログラム

app.py
# 中略
@app.route('/')
def index():
	df_values = df.values.tolist() # 2次元配列(中身)
	df_index = df.index.tolist()   # 1次元配列(インデックス)

	return render_template('index.html', \
		df_values = df_values, \
		df_index = df_index)
index.html
<!-- 中略 -->
<body>
	<h1>インデックス(index)も渡す</h1>
	<table>
		<tbody>
			{%- for i in df_values %}
			<tr><th>{{ df_index[loop.index0]|e }}</th>{% for j in i %}<td>{{ j|e }}</td>{% endfor %}</tr>
			{%- endfor %}
		</tbody>
	</table>
</body>

中身とヘッダーとインデックスを渡す

サンプルプログラム

app.py
# 中略
@app.route('/')
def index():
	df_values = df.values.tolist()   # 2次元配列(中身)
	df_columns = df.columns.tolist() # 1次元配列(ヘッダー)
	df_index = df.index.tolist()     # 1次元配列(インデックス)

	return render_template('index.html', \
		df_values = df_values, \
		df_columns = df_columns, \
		df_index = df_index)
index.html
<!-- 中略 -->
<body>
	<h1>ヘッダーとインデックスも渡す</h1>
	<table>
		<thead>
			<tr>{%- for i in df_columns %}<th>{{ i|e }}</th>{%- endfor %}</tr>
		</thead>
		<tbody>
			{%- for i in df_values %}
			<tr><th>{{ df_index[loop.index0]|e }}</th>{% for j in i %}<td>{{ j|e }}</td>{% endfor %}</tr>
			{%- endfor %}
		</tbody>
	</table>
</body>

連載目次:FlaskでWebアプリケーションを開発するためのロードマップ

入門編:10記事

入門編の10記事を順に読んでいけば、FlaskでWebアプリケーションを開発する必要最小限のことが学べます。

簡単なアプリケーションであれば、セキュリティ上の観点を考慮しなかった場合公開できるでしょう。

実践編

実践編の記事では、FlaskでWebアプリケーションを公開するために欠かせないセキュリティのことや実践的なテクニックを紹介しています。

ここまで読み込めば、あとはアイディア次第でいろんなWebアプリケーションを公開できるでしょう!

  1. セッション
  2. Cookie
  3. メソッドベース・ディスパッチ
  4. ベーシック認証・Digest認証
  5. ログイン機能
  6. サーバーサイドからクライアントサイドにpandas.Series、pandas.DataFrameを渡す
  7. クライアントサイド(form)からPOSTされた1、2次元配列を受け取る
  8. matplotlibを使う
  9. Bootstrap4を使う
  10. フォームの非同期通信(Ajax, jQuery)