食品安全文化を評価するWebアプリを簡単作成②(アプリ作成)

食品安全文化を評価するWebアプリを簡単作成②(アプリ作成)

前回の記事で、VS CodeでPythonを使用する準備ができました。

ここからはVS Codeにコードを入力していきます。

ウェブアプリの完成形はこちらになります。

目次

streamlitをインストールする

今回ウェブアプリに使用する「streamlit」はPythonに最初からインストールされているパッケージではありません。そのため、新たにインストールする必要があります。

新たにパッケージをインストールするにはpipを使います。ターミナルにcmdが起動していることを確認し、ターミナルにpip install streamlitと入力しEnterキーで決定してください。するとstreamlitのインストールが始まります。

streamlitをインストール

また、今回のグラフを描くのに使う「plotly」も新たにインストールする必要があります。同様にターミナルにpip install plotlyと入力しEnterキーで決定してください。

streamlitとは

ウェブアプリを作る際、通常ウェブフレームワークを使います。フレームワークは「枠組み」のことです。家を建てる際にも、まず枠組みを作った後に、個別の床や壁、窓などの部品を作った方がと効率的に作業が行えます。ウェブアプリも同じで、ウェブフレームワークの「枠組み」を使うことで効率的にアプリを作ることができます。

Pythonでよく使われるフレームワークには「Django」、「Flask」、「streamlit」などがあります。streamlitはこれらの中でも、コードが直感的で学習コストが低いため、初学者にも取り組みやすいフレームワークです。ただし、簡単に作れる一方、複雑なウェブアプリには向きません。

streamlitを使ってみる

streamlitとplotlyのインストールが終わったら、app.pyファイルのprint('Hello World!')は削除し、新たに以下のコードを入力します。

import streamlit as st
import pandas as pd
import plotly.express as px

st.title('食品安全文化の計測アプリ')

実際の画面は下のようになります。最初の3行で、必要なライブラリを呼び出しています。

streamlitのst.title

CtrlSでファイルを保存します。

そしてターミナルに、streamlit run app.pyと入力しEnterキーで決定します。

すると以下のように表示され、ウェブブラウザに新しいタブが作られます。

localhostでstreamlitを起動

以下のような画面が開いていれば、成功です。誤ってブラウザの画面を消してしまったとしても、ターミナルのhttp://localhost:8501のリンクをキーボードのCtrlを押しながらクリックすれば、再びページを開くことができます。

ウェブブラウザでstreamlitが起動

なんとこれだけのコードで、ブラウザ上にウェブアプリが起動します。

5行目に書いたst.title('食品安全文化の計測アプリ')の内容が表示されています。stは「streamlit」の略で、titleは「タイトル」のことです。

つまり先程のコードは、タイトルのようなフォントで「食品安全文化の計測アプリ」と表示してくださいという命令です。

streamlitのコードは、このように直感的でわかりやすいです。

下に今回使用するstreamlitの部品(ウィジットとも言います。)を紹介します。

ウィジット意味
st.title( )タイトルのフォントで表示
st.write( )通常フォントで文字を表示
文字の修飾もできる。例:アスタリスクで文字を囲めば(**文字**)、文字が太字に表示される。
st.radio( )ラジオボタンを表示
複数の中から選択してもらう時に使用する。
st.button( )ボタンを表示
起動装置のように、ボタンを押すと別のアクションが起きるようにできる。

この他にもいろいろなウィジットがあります。英語ですが、公式HPのAPI Referenceで解説されています。また、インターネットで「streamlit ウィジット」と検索すると、日本語の記事も多く出てきます。

ウィジットの使い方に迷ったら、他の記事も参考にしてみてください。

ラジオボタンを表示する

それではコードを追記していきます。

import streamlit as st
import pandas as pd
import numpy as np

answer = ["そう思わない", 
          "あまりそう思わない", 
          "どちらとも言えない", 
          "ややそう思う", 
          "そう思う"]

st.title('食品安全文化の計測アプリ')

q1 = st.radio("1.誰も見ていない時でも、従業員は食品安全のルールを守っている。", 
              [1,2,3,4,5], 
              captions = answer, index=2, horizontal=True)

5行目について。回答の選択肢(そう思わない/あまりそう思わない/どちらとも言えない/ややそう思う/そう思う)は今後何度も同じ内容を使うため、あらかじめ準備し、answerに入れておきます。

13行目はst.radioを使ってラジオボタンを作っています。実際の画面を見たほうが何をしているか理解しやすいので、見てみましょう。

app.pyを保存してから、ブラウザの再読み込みを行ってください。

ブラウザの再読み込み

すると先ほど追加した部分が、ブラウザでも追加されたのが分かります。

比較すると、コードのどの部分が、画面でどのように表示されているかがわかります。streamlitの強みとして、コードを変更した結果をすぐに確認できる点があります。

少しわかりにくいコードを解説します。

index=2は選択されるボタンの初期配置です。index=2を削除すると一番左があらかじめ選択されます。2なのに3番目が選択されている理由は、ご存じのようにPythonは0から数えるからです。

horizontal=Trueは回答を横並びにします。英語のhorizontalが「水平の、横並びの」という意味ですね。これを削除すると回答が縦に並びます。

q1 = st.radio( )としています。これは、ラジオボタンで選択された結果をq1に格納するということです。例えば「1 そう思わない」を選択すると、数値の1がq1に格納されます。この数値は後で回答を集計する際に使用します。


このように、まず1つ質問を作り、うまくいくことを確認してから他の質問に拡大した方が、いきなりすべてを作るより作業が効率的に行えます。

他の質問項目も同じように作っていってみてください。

なお、streamlitを停止したい場合は、VS Codeのターミナル上で、キーボードのCtrlCを押します。

streamlitを停止する

またstreamlitを起動したい場合は、ターミナルにstreamlit run app.pyと入力しEnterキーを押します。

完成形のコード

1つの質問で問題なく動くことが確認できました。

みなさんも、完成形を見ながら、どのようなコードを書けばいいか試してみてください。

ここでコードの完成形をお見せします。

import streamlit as st
import pandas as pd
import plotly.express as px

answer = ["そう思わない", 
          "あまりそう思わない", 
          "どちらとも言えない", 
          "ややそう思う", 
          "そう思う"]

#ここからアプリ開始
st.title('食品安全文化の計測アプリ')

st.write('この施設における食品安全対策について、意見を聞かせてください。')
st.write('この施設で経験したことのうち、もっとも当てはまる欄にチェックしてください。匿名のため、名前は記入しないでください。')

st.write('**この飲食店では...**')

#4つのエリアの質問を作る
    # 従業員のコミットメント
q1 = st.radio("1.誰も見ていない時でも、従業員は食品安全のルールを守っている。", 
              [1,2,3,4,5], 
              captions = answer, index=2, horizontal=True)
q2 = st.radio("2.従業員同士が食品安全のルールを守るよう励まし合っている。", 
              [1,2,3,4,5], 
              captions = answer, index=2, horizontal=True)
q3 = st.radio("3.従業員はそれぞれの役割における食品安全に責任を持っている。", 
              [1,2,3,4,5], 
              captions = answer, index=2, horizontal=True)
q4 = st.radio("4.従業員はルールに従い手を洗っている。", 
              [1,2,3,4,5], 
              captions = answer, index=2, horizontal=True)

    # 資源
q5 = st.radio("5.素手で食品に触れないように、手袋や器具が十分に用意されている。", 
              [1,2,3,4,5], 
              captions = answer, index=2, horizontal=True)
q6 = st.radio("6.近くに手洗いシンクがあり、手洗いを行いやすい。", 
              [1,2,3,4,5], 
              captions = answer, index=2, horizontal=True)
q7 = st.radio("7.手洗いシンクは、お湯が出て、石鹸、ペーパータオルなどがある。", 
              [1,2,3,4,5], 
              captions = answer, index=2, horizontal=True)

    # 責任者のコミットメント
q8 = st.radio("8.忙しい時、責任者は食品安全のルールを守ることよりも、料理を出すことを優先する。", 
              [1,2,3,4,5], 
              captions = answer, index=2, horizontal=True)
q9 = st.radio("9.仕事が多すぎるため、従業員は手を抜かざるを得ない。", 
              [1,2,3,4,5], 
              captions = answer, index=2, horizontal=True)
q10 = st.radio("10.従業員が食品安全のルールを守っていない時、責任者は見て見ぬふりをする。", 
              [1,2,3,4,5], 
              captions = answer, index=2, horizontal=True)

    # リーダーシップ
q11 = st.radio("11. 私が仕事をする上で十分な食品安全に関するトレーニングを提供してくれる。", 
              [1,2,3,4,5], 
              captions = answer, index=2, horizontal=True)
q12 = st.radio("12.責任者は従業員からフィードバックを得て、食品安全を改善している。", 
              [1,2,3,4,5], 
              captions = answer, index=2, horizontal=True)
q13 = st.radio("13.食品安全は、掲示物、ポスター、シフトミーティングなどで強調されている。", 
              [1,2,3,4,5], 
              captions = answer, index=2, horizontal=True)
q14 = st.radio("14.従業員が食品安全のルールを守ることが積極的に評価されている。", 
              [1,2,3,4,5], 
              captions = answer, index=2, horizontal=True)
q15 = st.radio("15.責任者は私の役割、責任を説明してくれる。", 
              [1,2,3,4,5], 
              captions = answer, index=2, horizontal=True)
q16 = st.radio("16.従業員は、この施設で従業員に求められる食品安全のレベルを理解している。", 
              [1,2,3,4,5], 
              captions = answer, index=2, horizontal=True)

#質問8~10は逆転項目のため、変換する
q8t = 6 - q8
q9t = 6 - q9
q10t = 6 - q10

#それぞれのエリアの平均を出す
m1 = round((q1 + q2 + q3 + q4) / 4 ,2)
m2 = round((q5 + q6 + q7) / 3, 2)
m3 = round((q8t + q9t + q10t) / 3, 2)
m4 = round((q11 + q12 + q13 + q14 + q15 + q16) / 6, 2)

#ボタンが押されたら、グラフを描画+それぞれのエリアの平均値を表示する
if st.button('提出'):
    df = pd.DataFrame(dict(
        r=[m1, m2, m3, m4],
        theta=['従業員のコミットメント','資源','責任者のコミットメント', 'リーダーシップ']))
    fig = px.line_polar(df, r='r', theta='theta', line_close=True)
    st.plotly_chart(fig)
    st.write('従業員のコミットメント:', m1)
    st.write('資源:', m2)
    st.write('責任者のコミットメント:', m3)
    st.write('リーダーシップ:', m4)

私自身も何度もトライ&エラーを繰り返し、この最終コードができました。

一見すると長いコードに見えますが、書いてあることは繰り返しが多いです。

21~74行目は質問1~16になります。先ほどのコードをコピーし、一部修正しただけです。

77~79行目は、質問8~10が「逆転項目」のため、他と合わせるために変換しています。

逆転項目とは

測定の向きが逆になる質問のことです。他の質問は「5 そう思う」が良い意味なのに対し、質問8~10は「5 そう思う」が悪い意味になります。回答者がきちんと考えて回答してくれることを期待してアンケートに忍び込ませておきます。

82~85行目は4つのエリアの平均値を出しています。「round関数」を使い、引数を2とすることで、小数点第二位以下で四捨五入しています。

88行目のif st.button('提出'):では、提出というボタンを作り、もし提出ボタンが押されたら:より後の処理を行うという命令です。

89行目からは、ボタンが押された後の処理です。グラフの描写と、それぞれのエリアの点数を表示しています。グラフ描画のコードはこちらの記事を参考に書きました。

実際に完成したアプリをいろいろ試して、不具合がないかを確認してみてください。


100行のコードと聞くととてもボリュームがあるように感じます。

しかし、重複部分を考慮すると、実際は30~40行くらいの内容です。

streamlitを使って、たった30~40行のコードで作りたいウェブアプリを開発することができました。

今はアプリが自分のブラウザだけで表示されています。

次回の記事では、アプリを外部に公開し、誰でもインターネットからアクセスできるようにします。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次