Streamlitで長時間処理も安心！ユーザーにやさしい途中経過表示術

# **user**:

Streamlitで、長い計算を行うときがあります。結果が返ってくるまで何も応答がないのは辛いので、途中報告を一時的に出力する良い方法はありませんか？

# **assistant**:

Streamlitで長い計算を行う際に、**途中経過（進捗）をユーザーに伝える方法**はいくつかあります。以下におすすめの方法を紹介します：

---

## ✅ 方法1: `st.status` を使って進捗ログを表示（おすすめ）

Streamlit 1.25以降では `st.status` を使うことで、**処理の途中経過ログや状態の表示**ができます。

```python
import streamlit as st
import time

with st.status("計算を開始します...", expanded=True) as status:
    for i in range(5):
        st.write(f"{i+1} 秒目の処理中...")
        time.sleep(1)
    status.update(label="計算が完了しました ✅", state="complete")
```

### 特徴

* 見た目が整っていて、ログが残る
* 状態の更新 (`status.update()`) が可能
* 非同期には対応していない（長い処理が止まっている間はStreamlit側はブロックされる）

---

## ✅ 方法2: `st.progress` + `st.text` の組み合わせでプログレスバー表示

```python
import streamlit as st
import time

progress = st.progress(0)
status_text = st.empty()

for i in range(100):
    status_text.text(f"処理中... {i+1}%")
    progress.progress(i + 1)
    time.sleep(0.05)
```

### 特徴

* ビジュアルで進捗が分かりやすい
* `%`ベースで進捗を出すのに適している

---

## ✅ 方法3: `st.empty()` を使って任意の情報を更新

`st.empty()` を使うと、表示内容を**後から上書き・更新**できます。

```python
import streamlit as st
import time

placeholder = st.empty()

for i in range(5):
    placeholder.text(f"ステップ {i+1} 実行中...")
    time.sleep(1)

placeholder.text("すべての処理が完了しました ✅")
```

### 特徴

* 自由度が高く、任意の表示に切り替え可能
* コンパクトに情報を伝えられる

---

## ✅ 方法4: `st.spinner` で処理中のアイコン表示

簡易な「読み込み中」表示だけでいい場合はこちら。

```python
import streamlit as st
import time

with st.spinner("計算中です...しばらくお待ちください"):
    time.sleep(5)

st.success("計算が完了しました！")
```

---

## 🔁 注意点

Streamlitは**同期的にコードが実行される**ため、処理の途中でUIに何度もフィードバックを返したい場合は、上記のようにループ内で明示的に表示を更新し、`time.sleep()`などで進捗を可視化する必要があります。

---

### 💡まとめ

| 方法            | 向いているケース            |
| ------------- | ------------------- |
| `st.status`   | ログを含む進捗表示（最もおすすめ）   |
| `st.progress` | %で進捗を見せたい場合         |
| `st.empty()`  | カスタムな進捗メッセージを出したい場合 |
| `st.spinner`  | 簡易な「処理中」表示だけでよい場合   |

---

必要であれば、**バックグラウンド処理や非同期処理**（例：`threading`や`concurrent.futures`）も応用可能ですが、Streamlitとの相性を考えると、上記の方法を工夫するのが最も安定します。

さらに進んだ実装（例：長時間の処理とUI分離、マルチページ構成など）にも興味があれば、お知らせください。

# **user**:

ありがとうございます。計算途中に、途中結果の図を見せることは可能ですか？

# **assistant**:

はい、**Streamlitでは計算途中に図（プロットなど）を表示・更新することも可能**です！
以下のように `st.empty()` を使えば、**図の「差し替え」や「更新」**ができます。

---

## ✅ 例：途中経過として図を段階的に更新

```python
import streamlit as st
import matplotlib.pyplot as plt
import numpy as np
import time

# 空の表示枠（図の更新用）
placeholder = st.empty()

# 長い計算の代わりにループで処理
for i in range(1, 6):
    # ダミーデータ生成
    x = np.linspace(0, 10, 100)
    y = np.sin(x + i)

    # プロット作成
    fig, ax = plt.subplots()
    ax.plot(x, y)
    ax.set_title(f"Step {i} の途中結果")

    # 図を更新表示
    placeholder.pyplot(fig)

    # ちょっと待つ（本来はここで計算）
    time.sleep(1)

# 最終結果を別に表示してもよい
st.success("計算完了！")
```

---

## ✅ 応用ポイント

* `st.empty()` を使うことで **何度でも同じ場所に上書き表示** できます。
* Matplotlibの他に **Plotly, Altair, Bokeh** なども使えます（例：`placeholder.plotly_chart(fig)`）。
* グラフだけでなく `st.table()` や `st.image()` などの他のコンテンツにも応用可能です。

---

## 💡注意点

* **Streamlitは1つのスクリプトを逐次的に実行**するため、リアルタイムで更新したい場合はループ内で明示的に描画・待機（`time.sleep()`など）を行う必要があります。
* 複数の途中結果を残したい場合は、`st.container()` や `st.columns()` を使うと柔軟なレイアウトが可能です。

---

### 📌まとめ

| やりたいこと      | 方法                              |
| ----------- | ------------------------------- |
| 図をリアルタイムに更新 | `st.empty().pyplot(fig)`        |
| 図を段階的に追加表示  | `st.container()` や `st.write()` |
| インタラクティブな図  | PlotlyやAltairの使用                |

---

必要であれば、PlotlyやAltairを使った例や、複数の図を保存して並べて見せる方法もご紹介できます。気軽にどうぞ！

# **user**:

ありがとうございます。st.container()を使う例を教えていただけますか？

# **assistant**:

もちろんです！
`st.container()` は、Streamlitで**複数のコンポーネントをまとめて配置したいとき**や、**途中結果を「追記する」ように表示したいとき**に便利です。

---

## ✅ `st.container()` の基本的な使い方

```python
import streamlit as st
import matplotlib.pyplot as plt
import numpy as np
import time

# コンテナを用意
container = st.container()

# 繰り返し途中結果を追記表示
for i in range(1, 6):
    with container:
        # ダミーデータ作成
        x = np.linspace(0, 10, 100)
        y = np.sin(x + i)

        # グラフ作成
        fig, ax = plt.subplots()
        ax.plot(x, y)
        ax.set_title(f"Step {i} の途中結果")

        # グラフ表示（追記）
        st.pyplot(fig)
        st.write(f"Step {i} のメモ: sin(x + {i}) の計算結果")
    
    time.sleep(1)

st.success("すべてのステップが完了しました！")
```

---

## 🔍 実行結果のイメージ

* 毎回のループで **新しい図が下に追加される**
* ログのように「蓄積表示」される（`st.empty()` とは逆に「上書き」しない）

---

## ✅ `st.container()` vs `st.empty()` の違い

| 機能    | `st.container()` | `st.empty()`   |
| ----- | ---------------- | -------------- |
| 表示の追加 | 追記されていく          | 上書きされる         |
| 用途    | ログや履歴の蓄積         | 状態のリアルタイム更新    |
| 例     | 処理ごとの図を全部残したい    | 1つの図を常に最新に保ちたい |

---

## 💡補足：複数コンテナで構造を整えることも可能

例えば、以下のように `st.columns()` と組み合わせて、「途中結果を横に並べる」ようなレイアウトも可能です：

```python
col1, col2 = st.columns(2)

with col1:
    st.write("左側の進捗")
    # グラフやテキスト表示

with col2:
    st.write("右側の進捗")
    # 別の情報を表示
```

---

## ✅ まとめ

* `st.container()` は **「ログ風の出力」や「逐次結果の蓄積表示」** に最適。
* `st.empty()` は **「ひとつの表示を上書き更新」** したい場合に向いています。
* 両者を組み合わせれば、**進捗バー + ログ + 図の更新**といった構成も可能です。

---

ご希望があれば、`st.container()` を使って **表・画像・テキストを同時に追加する複雑な例** もご紹介できます！

# **user**:

すみません、追記されていくのであれば、通常の表示と何が違うんでしょうか？

# **assistant**:

とても良い質問です！

実は、`st.container()` の**振る舞い自体は「通常の表示」とほとんど同じ**で、**その場所（コンテナ）にまとめて出力できるだけ**です。

---

## 🔍 結論から言うと：

> **`st.container()` の特徴は「明示的に表示の“まとまり”を作れる」こと**で、通常の `st.write()` などとは「機能的な違い」よりも「構造化・レイアウト面の意図を明確に示せる」点にあります。

---

## ✅ `st.container()` と「通常表示」の違いを整理

| 項目       | 通常の `st.write()` など | `st.container()`                  |
| -------- | ------------------- | --------------------------------- |
| 表示位置     | コードの実行順にそのまま表示される   | 特定のブロック（コンテナ）内にまとめて表示             |
| 明示的な領域分け | ❌ できない              | ✅ `with container:` によるブロック構造     |
| 再利用      | ❌ 不可                | ✅ `container.empty()` で上書きや差し替え可能 |
| レイアウト制御  | やや難しい               | 他の要素（columnsやexpander等）と組み合わせやすい  |

---

## ✅ 例：`st.container()` が便利なケース

### 1. **繰り返し出力を「まとまり」として管理したい**

```python
for i in range(3):
    with st.container():
        st.write(f"Step {i+1}")
        st.line_chart(np.random.randn(10))
```

上記では、各ループで **1つのまとまった表示ブロック** が作られ、他と混ざりません。

---

### 2. **後から表示内容を更新したい（更新用ハンドルとして）**

```python
container = st.container()

for i in range(5):
    with container:
        st.write(f"現在のステップ: {i}")
    time.sleep(1)
```

これは `st.empty()` に似た使い方で、**コンテナ内を上書きして使う**こともできます。

---

## ✅ 逆に「通常表示」でいいケース

* 単に上から順に表示していけばよい
* 特定のブロックとして管理する必要がない
* レイアウトや後からの更新の必要がない

---

## 💡補足：名前付きで管理したい場合も便利

```python
my_container = st.container()
my_container.write("ここにまとめて表示")
```

という形で、「この部分にだけ図を出す」や「ログを出す」という設計がしやすくなります。

---

## ✅ まとめ

* `st.container()` は、\*\*「通常表示と同じ表示ができるが、表示のまとまりを構造的に分けられる」\*\*のが利点。
* 特に複数の部分表示を整理したいときや、後から更新・再利用したいときに有効。
* 単に「出力を追記する」だけなら `st.write()` で十分なことも多い。

---

「なんとなく機能が被ってない？」と感じるのは自然です。
ただ、UIを整理したり、動的に操作したいときには強力な味方になります！

必要なら、「複数の`st.container()`を使ってログと図を分ける例」もお見せできます。

# **user**:

ありがとうございます。では、st.empty() を使ってリアルタイム更新した後、計算終了後に表示を消すことはできますか？

# **assistant**:

はい、**`st.empty()` を使えば、表示をリアルタイム更新した後に「消す」こともできます！**

---

## ✅ 基本の考え方

`st.empty()` で作ったプレースホルダ（表示枠）は、

* `.write()` や `.pyplot()` などで**内容を表示**
* `.empty()` メソッドで\*\*中身をクリア（＝非表示に）\*\*できます。

---

## ✅ 例：計算中に進捗メッセージを表示 → 計算後に非表示

```python
import streamlit as st
import time

# プレースホルダ作成
status = st.empty()

# リアルタイム進捗表示
for i in range(5):
    status.write(f"ステップ {i+1}/5 を計算中...")
    time.sleep(1)

# 計算完了 → 表示を消す
status.empty()

# 別の完了メッセージを表示
st.success("計算が完了しました！")
```

---

## ✅ 図やチャートでも同様に使える

```python
import matplotlib.pyplot as plt
import numpy as np

chart_area = st.empty()

# プロット表示（途中経過）
x = np.linspace(0, 10, 100)
for i in range(3):
    y = np.sin(x + i)
    fig, ax = plt.subplots()
    ax.plot(x, y)
    ax.set_title(f"ステップ {i+1}")
    chart_area.pyplot(fig)
    time.sleep(1)

# 最後に表示をクリア
chart_area.empty()
```

---

## ✅ 注意点

* `.empty()` で消せるのは `st.empty()` で作った表示領域だけです。
* 直接 `st.write()` などで表示したものは削除できません。

---

## ✅ 応用：一時的にメッセージを出して自動で消す

```python
message = st.empty()
message.warning("しばらくお待ちください...")
time.sleep(2)
message.empty()
```

---

## ✅ まとめ

| やりたいこと      | 方法                           |
| ----------- | ---------------------------- |
| 表示をリアルタイム更新 | `st.empty().write()` など      |
| 最後に非表示にする   | `.empty()` を呼び出す             |
| 表示枠を再利用する   | 同じ `st.empty()` インスタンスに書き換える |

---

もし「最後に別のメッセージや図に差し替えたい」という場合も、`.empty()` を使えば柔軟にコントロールできますよ！必要ならその例もご紹介します。

# **user**:

ありがとうございました。今日の会話をブログに残したいので、タイトル案をいくつか考えていただけませんか？

# **assistant**:

もちろんです！
Streamlitの「計算中の進捗表示」や「途中結果のグラフ表示」「`st.empty()`／`st.container()`の使い分け」に関する内容から、以下のようなブログタイトル案をご提案します。

---

## ✨ タイトル案（汎用・初心者向け）

1. **「Streamlitで重い計算中に進捗表示をする方法まとめ」**
2. **「`st.empty()` vs `st.container()` 使い分け完全ガイド」**
3. **「Streamlitで計算途中のグラフやログを表示するテクニック」**
4. **「止まって見えるStreamlitアプリを救う！進捗と途中出力のベストプラクティス」**
5. **「Streamlitで長時間処理も安心！ユーザーにやさしい途中経過表示術」**

---

## 🔧 技術者・実装重視の方向け

6. **「Streamlitアプリで非同期っぽく見せるための`st.empty()`活用術」**
7. **「Streamlitでリアルタイム更新＆ログ表示を両立するには？」**
8. **「Streamlitの途中出力：`st.empty()`と`st.container()`の実戦活用」**
9. **「計算途中のプロットもOK！Streamlitでリアルタイム表示する方法」**

---

## 💡少しキャッチー・読みやすさ重視

10. **「そのStreamlitアプリ、フリーズしてませんか？ユーザーの不安を減らす工夫」**
11. **「進捗を見せるだけで好印象！Streamlitでの“途中経過表示”のコツ」**
12. **「ただの`st.write()`じゃもったいない！Streamlitの表示をもっと自在に」**

---

## 📌 サブタイトルの付け方例

* メインタイトル + `:` or `–` で説明を加えると読みやすくなります。

例：

* **「Streamlitで重い処理中に進捗を見せる方法：`st.empty()`と`st.container()`の使い方」**
* **「リアルタイム更新するStreamlitアプリの作り方 – 図・ログ・進捗を丁寧に見せる」**

---

ご希望の文体（カジュアル／まじめ／読者層など）に合わせて、さらに絞り込むこともできます！お気軽にどうぞ。

# **user**:

「Streamlitで長時間処理も安心！ユーザーにやさしい途中経過表示術」で行きたいと思います。最後に、今回のまとめを書いてください。

# **assistant**:

素敵なタイトルですね！
それでは、\*\*「Streamlitで長時間処理も安心！ユーザーにやさしい途中経過表示術」\*\*というブログ用に、今回の会話内容をもとにしたまとめ文をご用意します。

---

## 📝 まとめ：Streamlitで長時間処理も安心！ユーザーにやさしい途中経過表示術

Streamlitはとても手軽にインタラクティブなWebアプリを作れますが、**計算に時間がかかる処理**では「画面が固まったように見えてしまう」ことがユーザー体験を損なう原因になります。
今回ご紹介したのは、**長時間処理の間に途中経過や進捗、図などをユーザーに丁寧に見せる方法**です。

---

### ✅ `st.empty()` を使ったリアルタイム更新

* `st.empty()` は一時的な表示領域を作るための**プレースホルダ**。
* `.write()` や `.pyplot()` などで中身を表示し、`.empty()` で消すことができます。
* **進捗メッセージや途中のグラフ表示に最適**です。

```python
progress = st.empty()
for i in range(5):
    progress.write(f"処理中: ステップ {i+1}/5")
    time.sleep(1)
progress.empty()
st.success("処理が完了しました！")
```

---

### ✅ `st.container()` で表示ブロックをまとめる

* `st.container()` は、複数の表示要素を**ひとまとまりのブロック**として管理できる仕組み。
* ループ内で複数の図やログを出力したいときに便利です。
* `with container:` のブロック内に複数の表示をまとめられます。

```python
for i in range(3):
    with st.container():
        st.write(f"ステップ {i+1}")
        st.line_chart(np.random.randn(10))
```

---

### ✅ 表示をユーザーにやさしく

* **リアルタイムに状況がわかるだけで、ユーザーの安心感は段違い**です。
* 特に、以下のような情報を見せるのが効果的：

  * 現在の進捗状況
  * 処理の対象（例：ファイル名、行数など）
  * 途中結果のグラフや統計量
  * 最後に「完了」メッセージや処理時間

---

### ✅ 表示をクリアする・差し替える

* `st.empty()` を使えば、一時的に表示 → 消す、あるいは差し替えることも自由自在。
* 「途中メッセージを消して、最後に結果だけ表示したい」などの場面で活躍します。

---

### 🔚 おわりに

Streamlitでの長時間処理も、**ちょっとした工夫でぐっと使いやすいアプリに変身します**。
`st.empty()` と `st.container()` を使いこなして、**ユーザーにやさしい、気配りのあるアプリ**を目指しましょう！

---

ご希望に応じて、このまとめ文をMarkdown形式にしたり、コードをより整えて見やすくすることもできます。お気軽にどうぞ！

# **user**:

ありがとうございました。これで大丈夫です。また今後もよろしくお願いします！

# **assistant**:

こちらこそ、ありがとうございました！
とても丁寧にご質問いただき、やり取りも楽しかったです 😊

ブログ執筆やStreamlitの開発でまた何かあれば、いつでも気軽にお声がけください。
今後もどうぞよろしくお願いします！✨