「Streamlitでデータアプリを作ったけど、ウィジェットを操作するたびに動作が遅くてストレス…」
そんな経験はありませんか? Streamlitはインタラクティブな操作のたびにスクリプト全体を再実行するのが特徴ですが、それが原因で重い処理(データの読み込みや複雑な計算)が何度も走り、アプリが遅くなることがあります。この問題を解決する魔法のような機能が、キャッシュデコレータです。今回は、特に重要な@st.cache_dataに焦点を当て、その効果的な使い方と、よく似た@st.cache_resourceとの違いを分かりやすく解説します!
@st.cache_dataとは? 関数の結果を記憶するメモ帳
@st.cache_dataは、関数の「実行結果」を一時的に保存(キャッシュ)しておくためのデコレータです。
@st.cache_data
def my_slow_function(arg1, arg2):
# 時間のかかる処理
...
return resultこのように関数の直前に@st.cache_dataを付けるだけで、Streamlitは以下の動作をします。
- 初回実行時: my_slow_functionを実行し、その返り値をキャッシュに保存します。このとき、関数の中身や引数(arg1, arg2)も一緒に記憶します。
- 2回目以降: 同じ関数が同じ引数で呼び出された場合、関数を再実行するのではなく、キャッシュから保存しておいた返り値を直接返します。
これにより、重い処理をスキップして、アプリの応答を劇的に速くすることができるのです。
@st.cache_dataの使いどころ
具体的にどのような場面で@st.cache_dataは使用するのでしょうか?主な使いどころは、**「データのロード」や「時間のかかる計算」**です。
1. データの読み込み
Pandasで大きなCSVファイルを読み込む処理は、代表的な使いどころです。悪い例 ❌(キャッシュなし) スライダーを動かすたびに、毎回3秒かけてCSVを読み込んでしまいます。
import streamlit as st
import pandas as pd
import time
def load_data():
time.sleep(3) # 重い処理をシミュレート
return pd.read_csv("my_large_data.csv")
df = load_data()
st.dataframe(df)
number = st.slider("Select a number", 0, 10)
st.write(f"Selected number: {number}")良い例 ⭕️(キャッシュあり) @st.cache_dataを付けるだけで、初回ロード以降はスライダーを動かしても一瞬で表示されます。
import streamlit as st
import pandas as pd
import time
@st.cache_data # これを追加!
def load_data():
time.sleep(3) # この処理は初回しか実行されない
return pd.read_csv("my_large_data.csv")
df = load_data()
st.dataframe(df)
number = st.slider("Select a number", 0, 10)
st.write(f"Selected number: {number}")2. APIからのデータ取得
外部APIからデータを取得する処理も、通信時間がかかるためキャッシュに最適です。何度もAPIを叩くことを防ぎ、相手サーバーへの負荷も減らせます。
import streamlit as st
import requests
@st.cache_data
def get_api_data(api_url):
response = requests.get(api_url)
return response.json()
# この関数は同じapi_urlに対しては一度しか実行されない
data = get_api_data("https://api.example.com/data")
st.write(data)
3. 時間のかかるデータ加工や計算
データの前処理や、機械学習モデルの予測など、CPUに負荷のかかる計算結果をキャッシュするのも非常に有効です。
@st.cache_data vs. @st.cache_resource ⚖️ どっちを使う?
Streamlitにはもう一つ、@st.cache_resourceというキャッシュデコレータがあります。この2つは似ていますが、明確な使い分けがあります。
一言で言うと、その名の通りです。
- @st.cache_data: **データ(Data)**をキャッシュする
- @st.cache_resource: **リソース(Resource)**をキャッシュする
もう少し詳しく見てみましょう。
| 特徴 | @st.cache_data | @st.cache_resource |
| 主な用途 | データフレーム、辞書、リストなど | DB接続、機械学習モデルなど |
| キャッシュするもの | シリアライズ可能な「データ」 ※ファイルに保存できるもの | 一度だけ初期化したい「リソース」 |
| 仕組み | 返り値のコピーを保存 | オブジェクトへの参照を共有 |
@st.cache_dataの例(データ)
データフレームやAPIのJSONレスポンスなど、中身が変わらない「値」として扱えるものに使います。Streamlitは返り値のコピーを保存するため、元のキャッシュデータが意図せず変更されてしまう心配がありません。
@st.cache_data
def get_dataframe():
return pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]})@st.cache_dataを使うときのポイント
@st.cache_dataは、データの中身が常に固定であれば、使用することによる弊害はないと思いますが、
データの中身が変わってしまう場合は少し工夫が必要です。更新頻度によって使いわけが必要です。
- リアルタイムが必要なデータ→キャッシュを使わない
- 数分~数時間の遅延が許容出来るデータ→@st.cache_data(ttl=…)で短い期間を設定
- 更新頻度が低い(1日)など→長い有効期間を設定するまたは、@st.cache_dataのみを設定する。
@st.cache_resourceの例(リソース)
データベース接続のコネクションや、TensorFlow/PyTorchで読み込んだ学習済みモデルなど、**何度も生成したくない「モノ」**に使います。これらはグローバルなシングルトン(アプリ全体で一つだけの存在)として扱いたいオブジェクトです。
import streamlit as st
from my_ml_library import load_model
@st.cache_resource
def load_my_model():
# このモデル読み込み処理は一度しか実行されない
model = load_model("./my_model.pkl")
return model
model = load_my_model()
prediction = model.predict(some_input)もしモデルの読み込みに@st.cache_dataを使うと、Streamlitがモデルをハッシュ化しようとしてエラーになる可能性があります。 このような「モノ」には@st.cache_resourceを使いましょう。
まとめ
@st.cache_dataは、Streamlitアプリのパフォーマンスを向上させるための重要なデコレーターです。
- データの読み込みや重い計算でデータの入力内容と出力内容が変わらない場合は
@st.cache_dataを使う - DB接続やMLモデルなど、一度だけ初期化したいものには@st.cache_resourceを使う
この2つのデコレータを正しく使い分けることで、ユーザーにとって快適でサクサク動く素晴らしいStreamlitアプリを開発できます。ぜひ、あなたのアプリにもキャッシュ機能を取り入れてみてください!


コメント