Slide 1

Slide 1 text

Streamlit 総合解説 ~ Pythonista のためのWeb アプリ開発 ~ 1

Slide 2

Slide 2 text

1. Streamlit とは? PythonだけでインタラクティブなWebアプリを作成できるオープンソースフレーム ワーク データサイエンス、機械学習プロジェクトの可視化・共有に最適 HTML/CSS/JavaScriptの知識は不要 2

Slide 3

Slide 3 text

2. Streamlit の特徴 シンプル: 直感的なAPIで数行のコードから始められる インタラクティブ: ウィジェット操作でリアルタイムに結果が反映 Pythonic: Pythonの知識だけで開発可能 共有が容易: 作成したアプリを簡単にデプロイ・共有 3

Slide 4

Slide 4 text

3. Streamlit 基本操作 インストール: pip install streamlit 基本的なアプリ ( app.py ): import streamlit as st st.title("My First Streamlit App") st.write("Hello, Streamlit!") 実行: streamlit run app.py 4

Slide 5

Slide 5 text

Streamlit の実行モデル: リラン (Rerun) Streamlitアプリは、ユーザーがウィジェットを操作するたびに スクリプト全体が上 から下へ再実行(リラン) されます。 各リランは基本的に独立しており、前のリランの状態は保持されません Session Stateを使わない場合。 5

Slide 6

Slide 6 text

Magic コマンド Streamlitでは、 st.write() を使わずに変数やMarkdown文字列を直接書くと表示 できます。 import streamlit as st import pandas as pd "# タイトル" # st.title("タイトル") と同様 df = pd.DataFrame({'col1':[1,2], 'col2':[3,4]}) df # st.write(df) と同様 6

Slide 7

Slide 7 text

4. 表示系ウィジェット: テキスト import streamlit as st st.title("タイトル") st.header("ヘッダー") st.subheader("サブヘッダー") st.write("通常のテキストや変数を表示") st.markdown("Markdownも使えます: **太字**, *イタリック*") st.caption("これはキャプション(注釈)です") st.code("print('Hello, World!')", language='python') st.latex(r''' e^{i\pi} + 1 = 0 ''') 7

Slide 8

Slide 8 text

表示系ウィジェット: データ # DataFrame df = pd.DataFrame(np.random.randn(10, 5), columns=('col %d' % i for i in range(5))) st.dataframe(df) # インタラクティブ st.table(df.head()) # 静的 # JSON st.json({'foo': 'bar', 'baz': 'qux'}) # Metric st.metric(label="気温", value="25 °C", delta="1.5 °C") 8

Slide 9

Slide 9 text

表示系ウィジェット: メディア # 画像 (ローカルファイル or URL) try: st.image("https://streamlit.io/images/brand/streamlit-logo-secondary-colormark-darktext.png", caption="Streamlit Logo", width=300) except: st.warning("画像の読み込みに失敗しました。") # 音声 (ローカルファイル or URL) # st.audio("audio.mp3") # 動画 (ローカルファイル or URL) # st.video("video.mp4") 注: 音声・動画ファイルは別途用意する必要があります。 9

Slide 10

Slide 10 text

5. 入力系ウィジェット: ボタン # 通常ボタン if st.button("クリックしてください"): st.write("ボタンがクリックされました!") # ダウンロードボタン data = "これはダウンロードされるテキストデータです。" st.download_button( label="テキストファイルをダウンロード", data=data, file_name="sample.txt", mime="text/plain" ) # リンクボタン st.link_button("Streamlit公式サイトへ", "https://streamlit.io") 10

Slide 11

Slide 11 text

入力系ウィジェット: 選択 ( 単一) # チェックボックス agree = st.checkbox("利用規約に同意します") if agree: st.write("同意ありがとうございます!") # ラジオボタン genre = st.radio( "好きな映画のジャンルは?", ('コメディ', 'ドラマ', 'ドキュメンタリー'), index=None # デフォルト未選択 ) if genre: st.write(f"{genre}を選びましたね。") 11

Slide 12

Slide 12 text

入力系ウィジェット: 選択 ( 複数) # セレクトボックス (ドロップダウン) option = st.selectbox( "連絡方法は?", ('メール', '電話', 'Slack'), placeholder="選択してください..." ) st.write(f"選択: {option}") # マルチセレクト options = st.multiselect( "好きな色を教えてください", ['緑', '黄', '赤', '青'], ['黄', '赤'] # デフォルト選択 ) st.write("選択:", options) 12

Slide 13

Slide 13 text

入力系ウィジェット: テキスト入力 # 1行テキスト入力 name = st.text_input("名前を入力してください", placeholder="山田 太郎") if name: st.write(f"こんにちは、{name}さん!") # パスワード入力 password = st.text_input("パスワード", type="password") # 複数行テキスト入力 feedback = st.text_area("フィードバックをお願いします", height=150) 13

Slide 14

Slide 14 text

入力系ウィジェット: 数値・日時入力 import datetime # 数値入力 age = st.number_input("年齢を入力してください", min_value=0, max_value=120, value=25, step=1) st.write(f"年齢: {age}歳") # 日付入力 d = st.date_input("誕生日を選択してください", datetime.date(2000, 1, 1)) st.write('誕生日:', d) # 時間入力 t = st.time_input('会議の時間を設定してください', datetime.time(9, 00)) st.write('会議時間:', t) 14

Slide 15

Slide 15 text

入力系ウィジェット: スライダー # 数値スライダー level = st.slider("レベルを選択", 0, 100, 50) st.write(f"選択したレベル: {level}") # 範囲スライダー price_range = st.slider( "価格帯を選択", min_value=0, max_value=10000, value=(1000, 5000), step=100 ) st.write("選択した価格帯:", price_range) # セレクトスライダー (離散値) color = st.select_slider( '好きな色を選択', options=['赤', 'オレンジ', '黄', '緑', '青', '紫'] ) t it ('好きな色 ' l ) 15

Slide 16

Slide 16 text

入力系ウィジェット: その他 import pandas as pd # カラーピッカー color = st.color_picker('テーマカラーを選択', '#00f900') st.write('選択した色:', color) # ファイルアップローダー uploaded_file = st.file_uploader("CSVファイルをアップロード", type="csv") if uploaded_file is not None: df = pd.read_csv(uploaded_file) st.write("アップロードされたデータ:") st.dataframe(df.head()) 16

Slide 17

Slide 17 text

6. レイアウト: サイドバー # サイドバーに要素を追加 add_selectbox = st.sidebar.selectbox( "連絡方法は?", ("メール", "自宅電話", "携帯電話") ) add_slider = st.sidebar.slider( "範囲を選択してください", 0.0, 100.0, (25.0, 75.0) ) st.write("メインコンテンツエリア") st.write(f"サイドバーの選択: {add_selectbox}") st.write(f"サイドバーのスライダー: {add_slider}") st.sidebar.* でサイドバー内にウィジェットを配置。 17

Slide 18

Slide 18 text

レイアウト: カラム ( 横並び) import numpy as np st.header("カラムレイアウト") col1, col2, col3 = st.columns(3) # 3つの均等なカラム with col1: st.write("ここは最初のカラムです。") st.image("https://static.streamlit.io/examples/cat.jpg") with col2: st.write("ここは中央のカラムです。") st.line_chart(np.random.randn(10, 1)) with col3: st.write("ここは最後のカラムです。") t b tt ("カラム3のボタン") st.columns() でカラムを作成し、 with 構文で各カラムに要素を追加。 18

Slide 19

Slide 19 text

レイアウト: タブ import numpy as np st.header("タブレイアウト") tab1, tab2, tab3 = st.tabs([" チャート", " データ", " 説明"]) with tab1: st.header("チャートタブ") st.line_chart(np.random.randn(20, 3)) with tab2: st.header("データタブ") st.dataframe(np.random.randn(20, 3)) with tab3: st.header("説明タブ") i (" れはタブ機能 デ トレ シ す ") st.tabs() でタブを作成し、 with 構文で各タブに要素を追加。 19

Slide 20

Slide 20 text

レイアウト: エキスパンダー ( 折りたたみ) import streamlit as st st.header("エキスパンダー") st.bar_chart({"data":[1,5,2,6,7,8,8}) with st.expander("詳細を見る"): st.write(""" このチャートはランダムなデータを表示しています。 エキスパンダーを使うことで、詳細情報を隠しておき、 ユーザーが必要な時に展開できるようにします。 """) st.image("https://static.streamlit.io/examples/dice.jpg") st.expander() で折りたたみ可能なセクションを作成。 20

Slide 21

Slide 21 text

レイアウト: 空の要素 ( st.empty ) import streamlit as st import time st.header("st.empty: 動的更新") placeholder = st.empty() # 空のプレースホルダーを作成 # プレースホルダーに要素を追加 placeholder.text("処理を開始します...") time.sleep(2) # プレースホルダーの内容を上書き placeholder.text("処理中です... 50%") time.sleep(2) # さらに上書き l h ld ("処理が完了しました!") st.empty() で作成したプレースホルダーの内容を後から書き換えられる。プログレ ス表示などに便利。 21

Slide 22

Slide 22 text

レイアウト: コンテナ(複数を格納できる) import time st.header("コンテナ") st.write("1行目") st.write("---") container = st.container() st.write("---") st.write("2行目") time.sleep(5) container.write("3行目") container.write("4行目") st.container() で要素をグループ化。表示順序の制御などに使う。 22

Slide 23

Slide 23 text

7. グラフ・チャート: 基本チャート import streamlit as st import pandas as pd import numpy as np st.header("Streamlit組み込みチャート") chart_data = pd.DataFrame( np.random.randn(20, 3), columns=['a', 'b', 'c']) # ラインチャート st.subheader("ラインチャート") st.line_chart(chart_data) # エリアチャート st.subheader("エリアチャート") st.area_chart(chart_data) 23

Slide 24

Slide 24 text

グラフ・チャート: Matplotlib import streamlit as st import matplotlib.pyplot as plt import numpy as np st.header("Matplotlib") arr = np.random.normal(1, 1, size=100) fig, ax = plt.subplots() ax.hist(arr, bins=20) ax.set_title("Matplotlib Histogram") st.pyplot(fig) st.pyplot() で Matplotlib の Figure オブジェクトを表示。 24

Slide 25

Slide 25 text

グラフ・チャート: Plotly import streamlit as st import plotly.express as px import pandas as pd st.header("Plotly") df = px.data.iris() # Plotly組み込みデータセット fig = px.scatter(df, x="sepal_width", y="sepal_length", color="species", title="Plotly Scatter Plot") st.plotly_chart(fig, use_container_width=True) st.plotly_chart() で Plotly の Figure オブジェクトを表示。インタラクティブな 操作が可能。 25

Slide 26

Slide 26 text

グラフ・チャート: Altair import streamlit as st import altair as alt import pandas as pd import numpy as np st.header("Altair") chart_data = pd.DataFrame( np.random.randn(20, 3), columns=['a', 'b', 'c']) c = alt.Chart(chart_data).mark_circle().encode( x='a', y='b', size='c', color='c', tooltip=['a', 'b', 'c']) st.altair_chart(c, use_container_width=True) st.altair_chart() で Altair の Chart オブジェクトを表示。 26

Slide 27

Slide 27 text

グラフ・チャート: 地図 (Pydeck) import streamlit as st import pandas as pd import numpy as np import pydeck as pdk st.header("地図 (Pydeck)") # 東京駅周辺のランダムな点を生成 chart_data = pd.DataFrame( np.random.randn(100, 2) /[100,100] + [35.68, 139.76], columns=['lat', 'lon']) st.pydeck_chart(pdk.Deck( map_style=None, # or 'mapbox://styles/mapbox/light-v9' など initial_view_state=pdk.ViewState( latitude=35.68, longitude=139.76, zoom=11, pitch=50, ), layers=[ pdk.Layer( 'HexagonLayer', data=chart_data, get_position='[lon, lat]', radius=100, st.pydeck_chart() で Pydeck を使った高度な地図可視化。 27

Slide 28

Slide 28 text

8. 状態管理: Session State Streamlitアプリのリラン間で 変数の値を保持するための仕組み。 各ユーザーセッション(ブラウザタブごと)に独立した状態を持つ。 Pythonの辞書によく似たインターフェースで操作できる。 28

Slide 29

Slide 29 text

なぜSession State が必要か? ( 復習) # counter_bad.py st.title('カウンター (問題あり)') count = 0 # リランのたびに0に初期化される increment = st.button('増加') if increment: count += 1 st.write('カウント = ', count) # クリックしても常に1になる ボタンを押すとリランが発生し、 count が毎回0に戻ってしまう。 29

Slide 30

Slide 30 text

Session State の初期化 値を保持したい変数がSession State内に存在するか確認し、なければ初期値を設定 する。 if 'key' not in st.session_state: の形式が一般的。 # 'count' が session_state になければ 0 で初期化 if 'count' not in st.session_state: st.session_state['count'] = 0 # または st.session_state.count = 0 st.write("初期化後のSession State:", st.session_state) 30

Slide 31

Slide 31 text

Session State の値の読み書き 辞書のようにキーを指定するか、属性アクセスで値を読み書きできる。 if 'my_value' not in st.session_state: st.session_state['my_value'] = "Initial Value" # 値の読み取り current_value = st.session_state['my_value'] st.write(f"現在の値: {current_value}") # 値の書き込み (更新) if st.button("値を更新"): st.session_state.my_value = "Updated Value!" # または st.session_state['my_value'] = "Updated Value!" 31

Slide 32

Slide 32 text

Session State を使ったカウンターアプリ ( 改善版) # counter_good.py st.title('カウンター (Session State使用)') # 'count' が session_state になければ 0 で初期化 if 'count' not in st.session_state: st.session_state.count = 0 increment = st.button('増加') if increment: st.session_state.count += 1 # Session Stateの値を更新 st.write('カウント = ', st.session_state.count) # Session Stateの値を表示 これでボタンをクリックするたびにカウントが増加する。 32

Slide 33

Slide 33 text

Session State とウィジェットの状態連携 ウィジェットに key 引数を指定すると、そのウィジェットの状態が Session State に自動的に保存・同期される。 st.title("ウィジェットとSession Stateの連携") # スライダーに key を設定 temp_c = st.slider( "温度 (°C)", min_value=-50.0, max_value=50.0, value=25.0, step=0.1, key="celsius" # このキーで Session State に保存される ) # Session State を使ってスライダーの値を取得・表示 st.write(f"Session Stateから取得した温度: {st.session_state.celsius} °C") 33

Slide 34

Slide 34 text

Session State とウィジェット連携の注意点 ウィジェットの状態は、Session State API を使って 直接設定(書き込み)すること はできない。 ウィジェットの状態を読み取ることは可能。 # これはエラーになる例 # if 'my_button_state' not in st.session_state: # st.session_state.my_button_state = True # ボタンの状態は設定できない # st.button('エラーになるボタン', key='my_button_state') # -> StreamlitAPIException # 読み取りは可能 if st.button("クリックして状態確認", key="confirm_button"): st.write(f"ボタンの状態 (押されたか): {st.session_state.confirm_button}") 34

Slide 35

Slide 35 text

Session State の応用例: ユーザー入力の保持 st.title("ユーザー入力の保持") # Session State の初期化 if 'user_name' not in st.session_state: st.session_state.user_name = "" name = st.text_input("名前を入力してください", key="user_name") # Session State に入力値を保存 # この例では、次のリランで text_input が st.session_state.user_name を使う st.session_state.user_name = name if st.session_state.user_name: st.write(f"こんにちは、{st.session_state.user_name} さん!") else: st.write("まだ名前が入力されていません。") 後述のコールバックを使うと、入力と同時にSession Stateを更新できる。 35

Slide 36

Slide 36 text

Session State の中身を確認 Session State は辞書ライクなオブジェクトなので、そのまま表示できる。 if 'counter' not in st.session_state: st.session_state.counter = 0 if 'text' not in st.session_state: st.session_state.text = "Hello" st.session_state.counter += 1 st.text_input("テキスト", key="text") st.write("現在のSession State:") st.write(st.session_state) # そのまま表示 # またはマジックコマンドで: # st.session_state 36

Slide 37

Slide 37 text

Session State のキーの削除 Pythonの辞書と同様に del を使って削除できる。 if 'temp_data' not in st.session_state: st.session_state.temp_data = "一時的なデータ" st.write("削除前のSession State:", st.session_state) if st.button("一時データを削除"): if 'temp_data' in st.session_state: del st.session_state['temp_data'] st.success("一時データを削除しました。") else: st.warning("一時データは既に存在しません。") st.write("削除後のSession State:", st.session_state) 37

Slide 38

Slide 38 text

コールバック関数とSession State ウィジェットの on_change や on_click 引数にコールバック関数を指定すると、ウ ィジェットの値が変更された/クリックされた 直後にその関数が実行される。 コールバック内で Session State を更新するのが一般的な使い方 38

Slide 39

Slide 39 text

コールバック関数とSession State ( 詳細) # コールバック関数: テキスト入力が変更されたら呼ばれる def update_text(): st.session_state.text_value =st.session_state.my_text_input.upper() #text_value状態の初期化 if "text_value" not in st.session_state: st.session_state.text_value = "" # テキスト入力ウィジェットに key と on_change を設定 st.text_input( "テキストを入力 (入力後にEnterかフォーカスを外すとコールバック実行):", key="my_text_input", # Session Stateに値を保存するキー on_change=update_text # 値が変更されたときに呼び出す関数 ) st.write(f"Session State に保存されたテキスト: {st.session_state.text_value}") 39

Slide 40

Slide 40 text

コールバック引数 ( args , kwargs ) コールバック関数に追加の引数を渡すことができる。 if 'count' not in st.session_state: st.session_state.count = 0 def increment_counter(increment_value): st.session_state.count += increment_value st.info(f"{increment_value} だけ増加しました!") def set_value(key, value): st.session_state[key] = value st.info(f"キー '{key}' に値 '{value}' を設定しました。") # args を使用 st.button("+1", on_click=increment_counter, args=(1,)) st.button("+5", on_click=increment_counter, args=(5,)) # kwargs を使用 st.button("カウンターを0にリセット", on_click=set_value, kwargs={'key': 'count', 'value': 0}) 40

Slide 41

Slide 41 text

Session State のベストプラクティスと注意点 初期化: アプリの最初に、使用するキーが存在するか確認し、なければ初期化する ( if key not in st.session_state: )。 キー名の衝突: ウィジェットの key と手動で設定するSession Stateのキー名が衝突 しないように注意する。 複雑な状態: 状態が非常に複雑になる場合は、クラスやデータクラスを使って状態を 構造化することを検討する。 メモリ使用量: Session State に巨大なデータを保存するとメモリを消費するので注 意。大きなデータはキャッシュ ( st.cache_data ) を検討する。 スコープ: Session Stateはセッション固有。異なるユーザーやタブ間で状態は共有さ れない。 41

Slide 42

Slide 42 text

9. キャッシュ機構: なぜ必要か? Streamlitアプリはユーザーインタラクションのたびにスクリプト全体が再実行され る。 時間のかかる処理(大規模データのロード、重い計算、外部APIアクセスなど)が毎 回実行されると、アプリの応答性が著しく低下する。 キャッシュは、これらの処理の結果をメモリ(またはディスク)に保存し、 同じ入力 に対しては再計算せずに保存した結果を即座に返す仕組み。 42

Slide 43

Slide 43 text

st.cache_data : データ計算結果のキャッシュ(1) 主な用途: DataFrameの処理、Numpy配列の計算、APIからのデータ取得など、 シリ アライズ可能(pickle 化可能)なデータオブジェクトを返す関数の結果をキャッシュ する。 デコレータ @st.cache_data を関数に付与する。 @st.cache_data # データ処理関数にデコレータを付与 def fetch_and_clean_data(url): st.write(f"データを取得中: {url}") # このメッセージは初回のみ表示されるはず time.sleep(3) # 時間のかかる処理をシミュレート df = pd.DataFrame(np.random.randn(20, 3), columns=['A', 'B', 'C']) # (実際にはここで pd.read_csv(url) や前処理を行う) return df st.title("st.cache_data のデモ") data_url_1 = "http://example.com/data1.csv" data_url_2 = "http://example.com/data2.csv" 43

Slide 44

Slide 44 text

st.cache_data : データ計算結果のキャッシュ(2) st.subheader("データセット1") if st.button("データ1をロード"): df1 = fetch_and_clean_data(data_url_1) # 同じ引数ならキャッシュが返る st.dataframe(df1) st.subheader("データセット2") if st.button("データ2をロード"): df2 = fetch_and_clean_data(data_url_2) # 引数が違うので再計算される st.dataframe(df2) st.subheader("再度データセット1") if st.button("データ1を再度ロード"): df1_again = fetch_and_clean_data(data_url_1) # キャッシュが使われるはず st.dataframe(df1_again) st.success("キャッシュからロードされました! (取得中のメッセージは表示されない)") 44

Slide 45

Slide 45 text

st.cache_resource : リソースのキャッシュ 主な用途: 機械学習モデル、データベース接続、TensorFlowセッションなど、 シリア ライズできない、またはグローバルに共有したいリソースをキャッシュする。 デコレータ @st.cache_resource を関数に付与する。 @st.cache_resource # リソース読み込み関数にデコレータを付与 def load_heavy_model(model_path): st.write(f"モデルをロード中: {model_path}") # このメッセージは初回のみ表示されるはず time.sleep(5) # 重いモデルのロードをシミュレート # model = load_model(model_path) # 実際のモデルロード処理 model = f"LOADED::{model_path}" # ダミーのモデルオブジェクト return model st.title("st.cache_resource のデモ") model_path = "path/to/my/model.h5" st.subheader("機械学習モデル") if st.button("モデルをロード/使用"): model = load_heavy_model(model_path) # 同じ引数ならキャッシュが返る st.write(f"使用中のモデル: {model}") # st.write(model.summary()) # 実際のモデル操作など 45

Slide 46

Slide 46 text

キャッシュの仕組み 1. キャッシュデコレータ ( @st.cache_data , @st.cache_resource ) が付いた関数が呼 び出される。 2. Streamlitは以下の要素から キャッシュキーを生成する: 関数のバイトコード 関数が依存する他のコード(関数内でimportしているモジュールなど) 関数に渡された 入力引数の値 キャッシュデコレータの引数( ttl (time to live) など) 3. 生成されたキャッシュキーがキャッシュストレージ内に存在するか確認。 4. 存在する場合: 保存されている結果を返し、関数本体は実行しない。 5. 存在しない場合: 関数本体を実行し、その結果をキャッシュキーと共にキャッシュス トレージに保存してから結果を返す。 46

Slide 47

Slide 47 text

キャッシュのクリア キャッシュされた結果を意図的に削除したい場合がある (例: 元データが更新され た)。 @st.cache_data def get_current_time_cached(): st.write("関数実行: 現在時刻を取得中...") time.sleep(1) return time.time() st.write(f"キャッシュされた時刻: {time.ctime(get_current_time_cached())}") if st.button("キャッシュをクリア (st.cache_data)"): st.cache_data.clear() # st.cache_data でキャッシュされた全関数をクリア st.success("st.cache_data のキャッシュをクリアしました!") st.rerun() # ページを再読み込みして効果を確認 st.cache_data.clear() : @st.cache_data でキャッシュされたものすべて。 st.cache_resource.clear() : @st.cache_resource でキャッシュされたものすべ て。 47

Slide 48

Slide 48 text

キャッシュのパラメータ(1) デコレータに引数を渡してキャッシュの挙動を制御できる。 # TTL (Time To Live): 60秒間だけキャッシュを有効にする @st.cache_data(ttl=60) def get_data_with_ttl(): st.write("TTL付き関数実行中...") return random.randint(1, 1000) # max_entries: 最新5件の結果のみキャッシュ @st.cache_data(max_entries=5) def get_data_with_max_entries(param): st.write(f"max_entries付き関数実行中 (param={param})...") return f"Data for {param} is {random.random()}" # show_spinner: キャッシュミス時に関数実行中にスピナーを表示 @st.cache_data(show_spinner="重いデータを計算中...") def expensive_computation(): return "計算結果" 48

Slide 49

Slide 49 text

キャッシュのパラメータ(2) st.title("キャッシュパラメータ") st.subheader("TTL (Time To Live)") st.write(f"データ (60秒有効): {get_data_with_ttl()}") st.subheader("Max Entries") entry_param = st.number_input("パラメータ (1-10)", 1, 10, 1) st.write(f"データ (最新5件): {get_data_with_max_entries(entry_param)}") st.subheader("Show Spinner") if st.button("重い計算を実行"): result = expensive_computation() st.write(f"結果: {result}") ttl : キャッシュの有効期限(秒数 or timedelta )。 max_entries : キャッシュする最大エントリ数。LRU方式で古いものから削除。 show_spinner : キャッシュ実行中に表示するメッセージ(文字列)または False 。 49

Slide 50

Slide 50 text

st.cache_data vs st.cache_resource の使い分けまとめ 特徴 st.cache_data st.cache_resource 主な用 途 データ、計算結果 (シリアライ ズ可能) リソース、共有オブジェクト (シリアライズ 不可) 返すオ ブジェ クト DataFrame, Numpy配列, dict, list, 文字列など MLモデル, DB接続, Tensorflow Sessionなど コピー の挙動 結果の コピーを返す (変更して もキャッシュに影響なし) オブジェクトへの 参照を返す (変更するとキ ャッシュも変更される可能性あり) 共有 主に計算結果の再利用 アプリ全体でのリソース共有 例 データロード・前処理、API コール結果 load_model() , create_db_connection() 50

Slide 51

Slide 51 text

使い分けのヒント: 返り値が「データ」そのものか? → st.cache_data 返り値が「接続」や「ロードされたモデル」のような、状態を持つオブジェクトか? → st.cache_resource 返り値を変更しても他の場所で影響がないようにしたいか? → st.cache_data 51

Slide 52

Slide 52 text

10. フォーム ( st.form ) 目的: 複数の入力ウィジェットの値を、ユーザーが「送信」ボタンを押すまで まとめ て扱いたい場合に使う。 背景: 通常、Streamlitでは各ウィジェットを操作するたびにリランが発生する。フォ ームを使うと、フォーム内のウィジェット操作ではリランせず、送信ボタンが押され たときに初めてリランし、フォーム内の全ウィジェットの値にアクセスできる。 52

Slide 53

Slide 53 text

フォームの基本的な使い方(1) # 'my_form' というキーでフォームを作成 with st.form(key='my_form'): st.write("以下の項目を入力してください。") name = st.text_input("名前") age = st.number_input("年齢", min_value=0, max_value=120, step=1) fav_color = st.selectbox("好きな色", ["赤", "青", "緑"]) feedback = st.text_area("ご意見・ご感想") # フォーム専用の送信ボタン submitted = st.form_submit_button("送信") 53

Slide 54

Slide 54 text

フォームの基本的な使い方(2) # 送信ボタンが押された後の処理 if submitted: st.success("フォームが送信されました!") st.write("--- 入力内容 ---") st.write(f"名前: {name}") st.write(f"年齢: {age}") st.write(f"好きな色: {fav_color}") st.write(f"フィードバック: {feedback}") # ここで入力データを使った処理(DB保存、メール送信など)を行う else: st.info("フォームを入力して送信ボタンを押してください。") with st.form(key=...) ブロック内にウィジェットを配置する。 ブロックの最後に st.form_submit_button() を置く。 送信ボタン ( submitted ) が True になったときに、フォーム内のウィジェットの値 を使って処理を行う。 54

Slide 55

Slide 55 text

フォーム外のウィジェットとの違い フォーム 内のウィジェット: 操作してもすぐにはリランしない。 値は送信ボタンが押されるまで保持される。 送信ボタンが押されると、その時点でのすべての値が確定し、リランがトリガー される。 フォーム 外のウィジェット: 操作すると即座にリランがトリガーされる。 値はリランのたびにスクリプトに基づいて再評価される(Session Stateを使わ ない場合)。 55

Slide 56

Slide 56 text

フォーム利用時の注意点 key は必須: st.form() には一意の key 引数が必要。 st.form_submit_button : フォーム内には必ず1つの st.form_submit_button が必 要。複数置くことはできない。 フォームのネスト: フォームの中に別のフォームを作ることはできない。 Session State との連携: フォーム内のウィジェットに key を指定すれば、送信時に その値が Session State にも反映される。ただし、フォームが送信されるまでは Session State は更新されない。 条件分岐: フォームを if 文の中に入れることは推奨されない。常にレンダリングさ れる場所に置くのが基本。 56

Slide 57

Slide 57 text

11. マルチページアプリ 目的: 複雑なアプリを機能ごとに複数のページに分割し、ナビゲーションしやすくす る。 仕組み: Streamlit 1.10.0 以降、特別なディレクトリ構造とファイル命名規則で簡単 に実現できるようになった。 57

Slide 58

Slide 58 text

マルチページアプリの基本的な構造 1. メインのアプリファイル (例: main_app.py ) を作成する。 2. メインファイルと同じディレクトリに pages という名前の サブディレクトリを作成 する。 3. pages ディレクトリ内に、各ページに対応する .py ファイルを作成する (例: pages/01_Data_Upload.py , pages/02_Visualization.py )。 your_project/ ├── main_app.py # メインページ (最初に表示される) ├── pages/ # このディレクトリ名が重要 │ ├── 01_ _Data_Upload.py │ ├── 02_ _Visualization.py │ └── About.py └── requirements.txt main_app.py を実行 ( streamlit run main_app.py ) すると、サイドバーに自動的に ページナビゲーションが表示される[12]。 58

Slide 59

Slide 59 text

ページの順序と表示名 ページの順序は、 pages ディレクトリ内のファイル名の 辞書順で決まる。 数字やアンダースコアで始まるプレフィックスを使うと順序を制御しやすい (例: 01_ , 02_ )[12]。 プレフィックスを除いたファイル名(アンダースコアはスペースに置換)が、サイド バーでの表示名になる[12]。 01_Data_Upload.py → "Data Upload" About.py → "About" ファイル名に絵文字を入れると、それがアイコンとして表示される (例: )。 59

Slide 60

Slide 60 text

各ページのコード例 main_app.py ( ホームページ) import streamlit as st st.set_page_config(page_title="マルチページアプリ Demo", layout="wide") st.title("ようこそ!") st.sidebar.success("上のメニューからページを選択してください。") st.markdown( """ これはStreamlitのマルチページ機能のデモンストレーションです。 サイドバーから他のページに移動できます。 """ ) 60

Slide 61

Slide 61 text

pages/01_ _Data_Upload.py ( データアップロードページ) import streamlit as st import pandas as pd st.set_page_config(page_title="データアップロード", page_icon=" ") # ページ固有設定 st.title(" データアップロード") st.write("ここにCSVファイルをアップロードしてください。") uploaded_file = st.file_uploader("CSVを選択", type="csv", key="uploader") if uploaded_file is not None: # Session State を使ってDataFrameを他ページと共有 try: df = pd.read_csv(uploaded_file) st.session_state['uploaded_df'] = df # Session Stateに保存 st.success("ファイルがアップロードされ、データが保存されました!") st.dataframe(df.head()) except Exception as e: st.error(f"エラーが発生しました: {e}") 61

Slide 62

Slide 62 text

pages/02_ _Visualization.py ( 可視化ページ) import streamlit as st import pandas as pd st.set_page_config(page_title="データ可視化", page_icon=" ") st.title(" データ可視化") # Session State からデータを取得 if 'uploaded_df' in st.session_state and isinstance(st.session_state['uploaded_df'], pd.DataFrame): df = st.session_state['uploaded_df'] st.write("アップロードされたデータの可視化:") cols = df.columns.tolist() if len(cols) >= 2: x_axis = st.selectbox("X軸を選択", cols, index=0) y_axis = st.selectbox("Y軸を選択", cols, index=1 if len(cols)>1 else 0) if x_axis and y_axis: try: st.scatter_chart(df, x=x_axis, y=y_axis) st.line_chart(df, x=x_axis, y=y_axis) except Exception as e: st.error(f"チャート描画エラー: {e}") else: 62

Slide 63

Slide 63 text

st.set_page_config 各ページの最初に呼び出すことで、そのページのタブタイトル、アイコン、レイアウ トなどを設定できる[12]。 メインファイル ( main_app.py ) と各ページファイル ( pages/*.py ) の 両方で呼び出 すのが一般的。 よく使う引数: page_title : ブラウザタブに表示されるタイトル。 page_icon : タブアイコン (絵文字、URL、ローカルパス)。 layout : "centered" (デフォルト) or "wide" 。 initial_sidebar_state : "auto" , "expanded" , "collapsed" 。 63

Slide 64

Slide 64 text

ページ間でのデータ共有 マルチページアプリの各ページは独立したスクリプトとして実行されるため、通常の 変数共有はできない。 Session State ( st.session_state ) を使うことで、ページ間でデータを共有でき る。 前のページの例のように、あるページで処理した結果(例: アップロードされた DataFrame)を Session State に保存し、別のページでそれを読み出して使う。 64

Slide 65

Slide 65 text

ページ遷移 ( st.page_link ) Streamlit 1.33 で導入された、プログラム的にページ遷移を行うための新しいAPI。 ボタンのようなUIで他のページへのリンクを表示できる。 # 基本的な使い方 (ファイルパスを指定) st.page_link("pages/01_ _Data_Upload.py", label="データアップロードページへ", icon=" ") # Aboutページへのリンク (pages ディレクトリ内) st.page_link("pages/About.py", label="Aboutページ") # ホームページ (メインアプリファイル) へのリンク st.page_link("main_app.py", label="ホームページに戻る", icon=" ") label : リンクの表示テキスト。 icon : 表示アイコン。 disabled : True にするとリンクが無効(クリック不可)になる。 65

Slide 66

Slide 66 text

12. カスタマイズ & デプロイ アプリの外観や機能をさらにカスタマイズし、他者と共有するための方法。 66

Slide 67

Slide 67 text

テーマのカスタマイズ ( config.toml ) (1) アプリの配色、フォントなどを変更できる。 方法1: 設定メニュー ( 一時的) アプリ右上の「 」メニュー → "Settings" → "Edit active theme"。 GUIで色などを変更でき、リアルタイムで反映される。 方法2: config.toml ( 永続的) アプリのルートディレクトリに .streamlit というフォルダを作成し、その中 に config.toml ファイルを作成する。 config.toml にテーマ設定を記述する。 67

Slide 68

Slide 68 text

テーマのカスタマイズ ( config.toml ) (2) .streamlit/config.toml の例: [theme] primaryColor="#F63366" # アクセントカラー (ウィジェットなど) backgroundColor="#FFFFFF" # 全体の背景色 secondaryBackgroundColor="#F0F2F6" # サイドバーやエキスパンダーなどの背景色 textColor="#262730" # メインテキストの色 font="sans serif" # フォント (sans serif, serif, monospace) # サーバー設定などもここに記述可能 # [server] # port = 8501 # headless = true # ヘッドレスモード (ブラウザを自動で開かない) # [logger] # level = "info" 設定可能な項目は公式ドキュメント参照。 68

Slide 69

Slide 69 text

コンポーネントの自作 (Streamlit Components API) Streamlitに組み込まれていない独自のUI要素やインタラクションを追加したい場 合、ReactやVueなどのフロントエンド技術を使ってカスタムコンポーネントを作成 できる。 Pythonから呼び出し、双方向のデータ通信が可能。 開発にはフロントエンドの知識が必要。 多くのサードパーティ製コンポーネントが公開されている (例: streamlit-aggrid , streamlit-echarts )。 69

Slide 70

Slide 70 text

デプロイオプション 作成したStreamlitアプリを他者がアクセスできるように公開する方法: Streamlit Community Cloud: (推奨、無料) Streamlit社が提供するホスティングサービス。 GitHubリポジトリから直接デプロイできる。 無料枠があり、公開・限定共有が可能。 Hugging Face Spaces: (無料オプションあり) 機械学習デモのホスティングに強い。Streamlitアプリも簡単にデプロイ可能。 AWS, GCP, Azure: (IaaS/PaaS) EC2, App Engine, App Service などで柔軟にデプロイ可能。インフラ管理が必 要。 社内サーバー: 企業のイントラネット内などで実行。 70

Slide 71

Slide 71 text

デプロイ準備: requirements.txt アプリが依存するPythonパッケージをリストアップしたファイル。 デプロイ先の環境でこれらのパッケージをインストールするために必要。 pip freeze > requirements.txt コマンドで現在の環境のパッケージリストを生成 できるが、不要なものも含まれるため、 手動で必要なものだけ記述するのが推奨され る。 requirements.txt の例: streamlit>=1.30.0 pandas numpy plotly matplotlib # scikit-learn==1.2.0 # バージョン指定も可能 71

Slide 72

Slide 72 text

Secrets Management ( 機密情報の管理)(1) APIキー、データベースパスワードなどの機密情報は、コードに直接書き込まず、安 全に管理する必要がある。 Streamlit Community Cloud: アプリの設定画面から Secrets を登録できる。 登録した Secrets は st.secrets 辞書ライクオブジェクト経由でアクセスでき る。 # .streamlit/secrets.toml (ローカル開発用) # API_KEY = "your_local_api_key" # DB_PASSWORD = "your_local_db_password" # アプリコード内 import streamlit as st api_key = st.secrets["API_KEY"] db_pw = st.secrets["database"]["password"] # ネストも可能 72

Slide 73

Slide 73 text

Secrets Management ( 機密情報の管理)(2) その他の環境: 環境変数 ( os.environ.get("API_KEY") ) を使うのが一般的。 .env ファイルと python-dotenv ライブラリを使う。 各クラウドプロバイダーのシークレット管理サービス (AWS Secrets Manager, Google Secret Manager など) を利用する。 73

Slide 74

Slide 74 text

Streamlit Community Cloud へのデプロイ手順概略(1) 1. GitHub リポジトリ準備: Streamlitアプリのコード ( app.py または main_app.py ) requirements.txt ファイル 必要なら .streamlit/config.toml や pages/ ディレクトリ これらをGitHubの 公開または 非公開リポジトリに push する。 2. Streamlit Community Cloud にサインアップ/ ログイン: share.streamlit.io にアクセスし、GitHubアカウントで連携。 3. "New app" ボタンをクリック: デプロイしたいGitHubリポジトリを選択。 ブランチ名 (通常 main または master ) を指定。 メインのPythonファイル名を指定。 (オプション) 詳細設定 (Pythonバージョンなど)。 74

Slide 75

Slide 75 text

Streamlit Community Cloud へのデプロイ手順概略(2) 4. Secrets を設定 ( 必要な場合): デプロイ前に、"Advanced settings..." から Secrets を入力する。 5. "Deploy!" ボタンをクリック: Streamlitが必要なパッケージをインストールし、アプリをビルド・起動する。 完了すると、固有のURL ( your-app-name.streamlit.app ) が発行され、アプリ にアクセスできる。 75

Slide 76

Slide 76 text

13. まとめ & 次のステップ StreamlitはPythonだけでインタラクティブなWebアプリを驚くほど簡単に作成でき る。 データ表示、ウィジェット、レイアウト、状態管理 (Session State)、キャッシュな ど、豊富な機能を提供。 マルチページアプリ機能で複雑なアプリも構築可能。 Community Cloud を使えばデプロイも容易。 76

Slide 77

Slide 77 text

学習リソース 公式ドキュメント: docs.streamlit.io - 最も正確で網羅的な情報源。APIリファレン ス、チュートリアル、コンセプト解説が充実。 チートシート: docs.streamlit.io/library/cheatsheet - よく使うコマンドの早見表。 ギャラリー: streamlit.io/gallery - 様々なStreamlitアプリの例とソースコード。イン スピレーションを得られる。 コミュニティフォーラム: discuss.streamlit.io - 質問したり、他のユーザーと交流し たりできる場。 77

Slide 78

Slide 78 text

さらに学ぶべきこと カスタムコンポーネント開発: 独自のUI/UXを実現。 パフォーマンスチューニング: 大規模データや複雑な処理への対応。キャッシュの高 度な利用。 テスト: Streamlitアプリのテスト手法。 FastAPI との連携: Streamlitをフロントエンド、FastAPIをバックエンドAPIとして組 み合わせる構成。より複雑なバックエンドロジックやデータベース操作に対応。 認証・認可: よりセキュアなアプリ構築 (例: streamlit-authenticator コンポーネ ント)。 Streamlit for Teams / Snowflake SiS: エンタープライズ向けの機能とデプロイ。 78

Slide 79

Slide 79 text

ご清聴ありがとうございました! Happy Streamlit-ing! 79