Why not login to Qiita and try out its useful features?

We'll deliver articles that match you.

You can read useful information later.

4
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[備忘録] StreamlitでAgGridを使った高機能テーブルの実装方法

Last updated at Posted at 2025-04-03

Streamlitで高機能テーブルを実装したい方へ📊 AgGridを使うとExcel風の編集機能やフィルター、ソート機能などが簡単に実装できます。初心者向けに基本から行の追加・削除までをまとめてみました。🙏

はじめに

Streamlitでデータ分析アプリを作る際、標準のテーブル表示では機能が不足しがちです。特にExcelのような編集機能やフィルタリング、ソート機能などが欲しい場合には「AgGrid」というライブラリが非常に便利です。この備忘録では、AgGridの基本的な使い方から、行の追加・削除などの操作を含めた実装方法をまとめています。

image.png

インストール方法

pip install streamlit-aggrid

基本的な使い方

AgGridを使ったExcel風の編集可能なグリッドを実装する完全なコード例を以下に示します。このコードには行の追加・削除といった基本的な操作も含まれています。

image.png

image.png

import streamlit as st
import pandas as pd
from st_aggrid import AgGrid, GridOptionsBuilder

# --------------------------------
# 1) セッションの初期化
# --------------------------------
if 'data' not in st.session_state:
    st.session_state.data = pd.DataFrame({
        "名前": ["佐藤", "鈴木", "田中"],
        "業種": ["Technology", "Finance", "Healthcare"],
        "評価": ["Hot", "Warm", "Cold"]
    })

st.title("🧮 Excel風 入力グリッド(AgGrid)")

# --------------------------------
# 2) GridOptions の設定
# --------------------------------
gb = GridOptionsBuilder.from_dataframe(st.session_state.data)
gb.configure_default_column(
    editable=True,
    sorteable=True,
    filterable=True,
    resizable=True
)

# 行選択(複数行選択)を有効化
gb.configure_selection('multiple')
grid_options = gb.build()

# --------------------------------
# 3) AgGridの表示
# --------------------------------
grid_response = AgGrid(
    st.session_state.data,
    gridOptions=grid_options,
    update_mode="MODEL_CHANGED",
    fit_columns_on_grid_load=True,
    allow_unsafe_jscode=True
)

# AgGrid から返されるデータをセッションに反映
if grid_response['data'] is not None:
    st.session_state.data = grid_response['data']

# 選択された行をDataFrameとして取得
selected_rows_df = grid_response['selected_rows']  # ここが DataFrame 形式の場合の想定

# --------------------------------
# 4) ボタンによる行操作
# --------------------------------
col1, col2, col3 = st.columns(3)

# ------ (A) 行の追加 ------
with col1:
    if st.button("➕ 行を追加"):
        new_row = pd.DataFrame([{"名前": "", "業種": "", "評価": ""}])
        st.session_state.data = pd.concat([st.session_state.data, new_row], ignore_index=True)
        st.rerun()

# ------ (B) 選択行の削除 ------
with col2:
    if st.button("🗑️ 選択行を削除"):
        # ★ DataFrame判定で空かどうかを確認
        if selected_rows_df is None or selected_rows_df.empty:
            st.warning("削除する行が選択されていません")
        else:
            current_data = st.session_state.data.copy()
            # 選択された行を 1 行ずつループして削除
            # (DataFrameなので .iterrows() で回す)
            for idx, row in selected_rows_df.iterrows():
                # "名前","業種","評価" が一致する行を削除する例
                mask = (
                    (current_data["名前"] == row["名前"]) &
                    (current_data["業種"] == row["業種"]) &
                    (current_data["評価"] == row["評価"])
                )
                current_data = current_data[~mask]
            # 削除結果をセッションへ反映
            st.session_state.data = current_data.reset_index(drop=True)
            st.rerun()

# ------ (C) 保存ボタン ------
with col3:
    if st.button("✅ 保存"):
        st.success("データが保存されました")
        st.write(st.session_state.data)

# ------ CSVエクスポート ------
csv_data = st.session_state.data.to_csv(index=False).encode("utf-8-sig")
st.download_button(
    "📥 CSVエクスポート",
    csv_data,
    "aggrid_data.csv",
    "text/csv",
    key='download-csv'
)

コードの詳細解説

1. セッションの初期化

Streamlitはステートレスなフレームワークなので、ページがリロードされるたびに変数は初期化されます。そのため、データを保持するにはst.session_stateを使います。

if 'data' not in st.session_state:
    st.session_state.data = pd.DataFrame({
        "名前": ["佐藤", "鈴木", "田中"],
        "業種": ["Technology", "Finance", "Healthcare"],
        "評価": ["Hot", "Warm", "Cold"]
    })

2. GridOptionsの設定

AgGridの挙動はGridOptionsで細かく設定できます。

gb = GridOptionsBuilder.from_dataframe(st.session_state.data)
gb.configure_default_column(
    editable=True,    # 編集可能にする
    sorteable=True,   # ソート可能にする
    filterable=True,  # フィルタリング可能にする
    resizable=True    # 列幅のリサイズを可能にする
)

# 行選択(複数行選択)を有効化
gb.configure_selection('multiple')
grid_options = gb.build()
  • editable=True: セルを直接編集できるようにします
  • sorteable=True: 列ヘッダーをクリックしてソートできるようにします
  • filterable=True: 列ヘッダーのメニューからフィルタリングできるようにします
  • resizable=True: 列の幅を変更できるようにします
  • configure_selection('multiple'): 複数行の選択を可能にします

3. AgGridの表示

grid_response = AgGrid(
    st.session_state.data,
    gridOptions=grid_options,
    update_mode="MODEL_CHANGED",
    fit_columns_on_grid_load=True,
    allow_unsafe_jscode=True
)
  • update_mode="MODEL_CHANGED": データモデルが変更されたときに更新します
  • fit_columns_on_grid_load=True: 初期表示時に列幅を自動調整します
  • allow_unsafe_jscode=True: JavaScript関数の実行を許可します(カスタムレンダラーなどに必要)

グリッド操作後のデータを取得して、セッションに保存します:

if grid_response['data'] is not None:
    st.session_state.data = grid_response['data']

# 選択された行をDataFrameとして取得
selected_rows_df = grid_response['selected_rows']

4. ボタンによる行操作

(A) 行の追加

if st.button("➕ 行を追加"):
    new_row = pd.DataFrame([{"名前": "", "業種": "", "評価": ""}])
    st.session_state.data = pd.concat([st.session_state.data, new_row], ignore_index=True)
    st.rerun()

空の行を作成し、現在のデータフレームに結合してから、ページを再読み込みします。

(B) 選択行の削除

if st.button("🗑️ 選択行を削除"):
    # DataFrame判定で空かどうかを確認
    if selected_rows_df is None or selected_rows_df.empty:
        st.warning("削除する行が選択されていません")
    else:
        current_data = st.session_state.data.copy()
        # 選択された行を1行ずつループして削除
        for idx, row in selected_rows_df.iterrows():
            mask = (
                (current_data["名前"] == row["名前"]) &
                (current_data["業種"] == row["業種"]) &
                (current_data["評価"] == row["評価"])
            )
            current_data = current_data[~mask]
        # 削除結果をセッションへ反映
        st.session_state.data = current_data.reset_index(drop=True)
        st.rerun()

選択された行の特定は、複数の列の値が一致する行を探すことで行っています。Pandasの条件式で一致する行を特定し、逆マスク(~mask)で一致しない行だけを残します。

(C) 保存とエクスポート

if st.button("✅ 保存"):
    st.success("データが保存されました")
    st.write(st.session_state.data)

# CSVエクスポート
csv_data = st.session_state.data.to_csv(index=False).encode("utf-8-sig")
st.download_button(
    "📥 CSVエクスポート",
    csv_data,
    "aggrid_data.csv",
    "text/csv",
    key='download-csv'
)

保存ボタンは現在の状態を表示するだけですが、実際のアプリケーションではデータベースやCSVファイルなどに保存する処理を追加します。

CSVエクスポート機能は、データフレームをCSV形式に変換してダウンロードボタンを表示します。

応用例

カスタム列設定

特定の列だけに異なる設定を適用したい場合:

# 特定の列設定
gb.configure_column("名前", editable=False)  # 名前列は編集不可
gb.configure_column("評価", 
                   editable=True,
                   cellEditor='agSelectCellEditor',
                   cellEditorParams={
                       'values': ['Hot', 'Warm', 'Cold']
                   })  # 評価列はドロップダウンリストから選択

条件付き書式

値に応じてセルの色を変更したい場合:

from st_aggrid import JsCode

# 条件付き書式
cellsytle_jscode = JsCode("""
function(params) {
    if (params.value == 'Cold') {
        return {
            'color': 'white',
            'backgroundColor': 'blue'
        }
    }
    if (params.value == 'Hot') {
        return {
            'color': 'white',
            'backgroundColor': 'red'
        }
    }
    return {
        'color': 'black',
        'backgroundColor': 'yellow'
    }
};
""")
gb.configure_column('評価', cellStyle=cellsytle_jscode)

ページネーション

大量のデータを扱う場合にはページネーションが便利です:

# ページネーション設定
gb.configure_grid_options(pagination=True, paginationAutoPageSize=True)
# または固定ページサイズ
# gb.configure_grid_options(pagination=True, paginationPageSize=10)

学習メモ・注意点

  1. Streamlitのステートレス性:

    • Streamlitはステートレスなので、ボタンをクリックするたびにスクリプト全体が再実行されます
    • データを保持するには必ずst.session_stateを使いましょう
  2. 型の扱い:

    • Pandas DataFrameを条件式で直接評価すると「The truth value of a DataFrame is ambiguous」エラーになります
    • 正しくはif df is None or df.empty:のように明示的にチェックします
  3. 行の削除:

    • 複数の列を使って行を一意に特定することが重要です
    • インデックスが予想と異なる場合があるので、値ベースでの比較が安全です
  4. パフォーマンス:

    • 大きなデータセットを扱う場合は、ページネーションを有効にしましょう
    • フィルタリングやソートはクライアント側で行われるので、非常に大きなデータセットでは注意が必要です

参考リンク

4
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

Qiita Conference 2025 will be held!: 4/23(wed) - 4/25(Fri)

Qiita Conference is the largest tech conference in Qiita!

Keynote Speaker

ymrl、Masanobu Naruse, Takeshi Kano, Junichi Ito, uhyo, Hiroshi Tokumaru, MinoDriven, Minorun, Hiroyuki Sakuraba, tenntenn, drken, konifar

View event details
4
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?