【Streamlit】階層構造データを扱うには 

## 導入

Streamlitって便利ですよね。

基本的なwidgetを利用するだけで、必要要件を満たしたアプリに仕上げることができ、個人利用で重宝しています。

:::note info
初めての方は[こちらのページ](https://qiita.com/papasim824/items/1804bc1bd8d4c195d8a8)を参照すると、streamlitで何ができるかわかると思います。
:::

色々触っていく中で、躓いたポイントがありました。
**...これ、階層構造の制御が上手く出来なくないか？**

時系列データを扱う際には特に問題なかったのですが、階層構造のデータを扱う場面でん？ってなりまして。

何かいい方法がないかなと探していたところ、便利なカスタムコンポーネントがありましたので、共有します。

## 紹介
### ➀streamlit_tree_select

〈Github〉

https://github.com/Schluca/streamlit_tree_select

〈使用イメージ〉
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3310860/960e469b-8870-3ae5-e31c-bb68efc9ed3f.png)

**求めていたものだ！！** ってなりました。

簡単に言えば
- Folder Cの子に➀Sub-folder D ②Sub-folder E ③Sub-folder Fの3種が存在
- 上記②Sub-folder Eの子に、④Sub-sub-folder A ⑤Sub-sub-folder Bの2種が存在
- これらを**纏めて表示することが可能**である

といったところでしょうか。

:::note info
階層構造のデータを扱うだけなら、これだけで十分だと思います。
:::

### ②StreamlitAntdComponents

streamlit_tree_selectだけでも十分に魅力的なのですが、実際に利用するとやや気になる点があります。

個人的には下記が気になりました。

- どの階層が子を持つかがやや分かりにくい (>で表示されてはいますが...)
- UIが簡素で少しダサい

※あくまで個人的な意見なので、別に気にならないよって人は飛ばしてもらって結構です。

どうにか改善できないものかと色々探していたところ、見つかったのが**StreamlitAntdComponents**でした。

〈Github〉

https://github.com/nicedouble/StreamlitAntdComponents

〈Copilotからの回答〉
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3310860/42af90ee-f480-8391-a274-c828d6308d66.png)

CopilotとGithubから得られる情報だけでも、**多岐にわたり利用できるコンポーネント**だと分かります。

今回はスルーしてしまいますが、checkboxのグループとか便利そうですね...
本題に戻り、treeで何ができるのかを調査してみようと思います。

## 実用例

### ➀利用までの流れ
はじめに、ライブラリのインストールを行いましょう。
```python
pip install streamlit-antd-components
```
続けて、ライブラリをimportします。
```python
import streamlit as st
import streamlit_antd_components as sac
```

### ②パラメータ理解
一つ一つ説明したかったのですが、パラメータ20個もあったので直貼りしてしまいます。
Chat GPT4oから帰ってきた和訳回答がこちらです。

:::note info
- items: ツリーのデータ
- index: デフォルトで選択されるツリー項目のインデックス
- format_func: ラベルフォーマット関数。文字列を受け取り、文字列を返します
- label: ツリーのラベル。文字列およびマークダウン形式の文字列に対応
- description: ツリーの説明。文字列およびマークダウン形式の文字列に対応
- icon: ツリー項目のアイコン
- align: ツリーの配置
- size: ツリーのサイズ。Mantineサイズおよびピクセル単位の整数に対応
- color: ツリーの色。デフォルトはStreamlitのプライマリカラー。Mantineカラー、16進数、およびRGBカラーに対応
- width: ツリーの幅
- height: ツリーの高さ
- open_index: デフォルトで開かれるインデックス。指定がない場合、ツリーはデフォルトインデックスの親ノードを開きます
- open_all: すべての項目を開くかどうか。優先順位は[open_all > open_index]
- checkbox: 複数選択を許可するチェックボックスを表示するかどうか
- checkbox_strict: 親項目と子項目が関連付けられないかどうか
- show_line: 線を表示するかどうか
- return_index: Trueの場合、ツリー項目のインデックスを返します。デフォルトはラベルを返します
- on_change: 項目が変更されたときのコールバック関数
- args: コールバック関数への引数
- kwargs: コールバック関数へのキーワード引数
- key: コンポーネントの一意の識別子
:::

on_changeやargs、key等は他のwidgetでも設定されているパラメータなので、何となく意味合いが分かりますね。他の項目は基本的にwidgetの装飾といったところでしょうか。

気になったパラメータについて、③サンプル例で実践してみようと思います。

### ③サンプル例

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3310860/fc4ea5d2-972b-846d-8325-3f52eaf2aff7.png)

```python
# サンプルデータ
category_dict = {
    "文系科目": ["英語", "国語", "社会"],
    "理系科目": ["数学", "理科"],
}

# ツリー構造のウィジェット
with st.container():
    selected_category = sac.tree(
        items=[
            sac.TreeItem(
                category,
                children=[
                    sac.TreeItem(sub_category, disabled=False)
                    for sub_category in sub_category_list
                ],
                disabled=False
            )
            for category, sub_category_list in category_dict.items()
        ],
        label="科目を選択してください",
        description="文系科目と理系科目を選択することができます。複数選択が可能です",
        format_func="title",
        icon="bell",  # Ant Design（AntD）のアイコンセットから
        width=500,
        height=1500,
        open_all=True,
        checkbox=True,
        show_line=True,
        key="sample",
    )
```

実際に書いてみたものがこちらになります。
sac.treeのitemsにsac.TreeItemを利用することで、図表のような親ノード子ノード表示が可能です。

複数選択のウィジェットはmultiselectが主流ではありますが、項目数が多くなるとなかなか使いにくいですよね。そういう時にsacはかなり強力だと思います。