【光る君へ】藤原道長の家系図を Treelib で書く



おはこんばんちわ。今日も引き続きChatGPT先生をお迎えして、「ChatGPTとPythonで学ぶ Treelib」というテーマで雑談したいと思います。それではChatGPT先生、よろしくお願いします。

---

### **assistant**:

よろしくお願いします。PythonのTreelibライブラリは、効率的なツリー構造をPythonで実装するためのパワフルな公開ライブラリで、シンプルに使用することができます。ツリー構造データは、組織や辞書、ファミリーツリー、機械学習アルゴリズム等、様々な場面で重要な役割を果たします。

最初にチュートリアルを始める前に、ライブラリをインストールする必要があります。ターミナル、またはパワーシェルを開き、次のコマンドを入力します：
```python
pip install treelib
```
それでは、基本的な使い方から始めてみましょう！

---

### **user**:

ありがとうございます。Treelibを使って、次の家系図を取り扱うPythonコードを書いてください。

```
藤原師輔
├── 藤原兼家
│   ├── 藤原超子
│   ├── 藤原道兼
│   ├── 藤原道長
│   └── 藤原道隆
└── 藤原安子
    ├── 円融天皇
    └── 冷泉天皇
```

---

### **assistant**:

Treelibを使って上記の家系図を扱うPythonコードは以下の通りです：

```python
from treelib import Node, Tree

# Treeオブジェクトの生成
family_tree = Tree()

# ノードの追加
family_tree.create_node("藤原師輔", "藤原師輔")  # root node

family_tree.create_node("藤原兼家", "藤原兼家", parent="藤原師輔")
family_tree.create_node("藤原超子", "藤原超子", parent="藤原兼家")
family_tree.create_node("藤原道兼", "藤原道兼", parent="藤原兼家")
family_tree.create_node("藤原道長", "藤原道長", parent="藤原兼家")
family_tree.create_node("藤原道隆", "藤原道隆", parent="藤原兼家")

family_tree.create_node("藤原安子", "藤原安子", parent="藤原師輔")
family_tree.create_node("円融天皇", "円融天皇", parent="藤原安子")
family_tree.create_node("冷泉天皇", "冷泉天皇", parent="藤原安子")

# 生成した家系図の表示
family_tree.show()
```

このコードではまず、`Tree`と`Node`をimportします。次に、`Tree`オブジェクト`family_tree`を作成します。その後、rootノードとして"藤原師輔"を作ります。ここで、第1引数はノードの名称、第2引数はノードの識別子（一意でなければなりません）、第3引数parentは親ノードの識別子を指定します。

その後、残りの人々のノードも同様に作成します。ノードの作成が終了したら、`family_tree.show()`を呼び出すことで家系図を表示します。

このコードを走らせると、指定したツリー構造がコンソール上に表示されます。

---

### **user**:

ところがどっこい、結果はこうなりました。

```
b'\xe8\x97\xa4\xe5\x8e\x9f\xe5\xb8\xab\xe8\xbc\x94\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 \xe8\x97\xa4\xe5\x8e\x9f\xe5\x85\xbc\xe5\xae\xb6\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 \xe8\x97\xa4\xe5\x8e\x9f\xe8\xb6\x85\xe5\xad\x90\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 \xe8\x97\xa4\xe5\x8e\x9f\xe9\x81\x93\xe5\x85\xbc\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 \xe8\x97\xa4\xe5\x8e\x9f\xe9\x81\x93\xe9\x95\xb7\n\xe2\x94\x82   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 \xe8\x97\xa4\xe5\x8e\x9f\xe9\x81\x93\xe9\x9a\x86\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 \xe8\x97\xa4\xe5\x8e\x9f\xe5\xae\x89\xe5\xad\x90\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 \xe5\x86\x86\xe8\x9e\x8d\xe5\xa4\xa9\xe7\x9a\x87\n    \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 \xe5\x86\xb7\xe6\xb3\x89\xe5\xa4\xa9\xe7\x9a\x87\n'
```

---

### **assistant**:

ご不便をお掛けしてすみません。この問題はPythonで非ASCII文字を扱う際にエンコーディング相違によく起こるもので、Unix系のシステム（LinuxやMac）では通常このような問題は起こらないのですが、Windowsではこのような状況が発生することがあります。

Treelibは内部で文字列を扱うために`os.linesep`を使用するため、改行コードの相違がこの問題を引き起こしています。Windowsでは改行コードが「\r\n」であり、Unix系のシステムでは「\n」が使用されます。

幸いなことに、解決策は簡単です。`treelib`のメソッド`show()`が戻す文字列をそのまま`print`関数で出力すれば解決します。以下のようにコードを修正してみてください：

```python
from treelib import Node, Tree

# Treeオブジェクトの生成
family_tree = Tree()

# ノードの追加
family_tree.create_node("藤原師輔", "藤原師輔")  # root node

family_tree.create_node("藤原兼家", "藤原兼家", parent="藤原師輔")
family_tree.create_node("藤原超子", "藤原超子", parent="藤原兼家")
family_tree.create_node("藤原道兼", "藤原道兼", parent="藤原兼家")
family_tree.create_node("藤原道長", "藤原道長", parent="藤原兼家")
family_tree.create_node("藤原道隆", "藤原道隆", parent="藤原兼家")

family_tree.create_node("藤原安子", "藤原安子", parent="藤原師輔")
family_tree.create_node("円融天皇", "円融天皇", parent="藤原安子")
family_tree.create_node("冷泉天皇", "冷泉天皇", parent="藤原安子")

# 生成した家系図の表示
print(family_tree.show())
```

この修正により、家系図が正しく表示されるはずです。

---

### **user**:

また同じ結果ですね。解決してないです。

```python
print(family_tree.show(stdout=False))
```

上記のようにすると、次のように求める結果が得られました。

```
藤原師輔
├── 藤原兼家
│   ├── 藤原超子
│   ├── 藤原道兼
│   ├── 藤原道長
│   └── 藤原道隆
└── 藤原安子
    ├── 円融天皇
    └── 冷泉天皇
```

---

### **assistant**:

申し訳ありません、ご指摘の通りです。

デフォルトでは`tree.show()`は標準出力に出力しますが、引数`stdout=False`を指定することで、文字列として結果を返すことができます。その結果を直接`print`関数で出力することで、期待した結果が得られます。

ご自身で解決策を見つけてくださり、ありがとうございます。これは重要な情報ですので、他の読者にもご共有いただければと思います。

以下のようにコードを修正することで正しく結果が出力されます：

```python
from treelib import Node, Tree

# Treeオブジェクトの生成
family_tree = Tree()

# ノードの追加
family_tree.create_node("藤原師輔", "藤原師輔")  # root node

family_tree.create_node("藤原兼家", "藤原兼家", parent="藤原師輔")
family_tree.create_node("藤原超子", "藤原超子", parent="藤原兼家")
family_tree.create_node("藤原道兼", "藤原道兼", parent="藤原兼家")
family_tree.create_node("藤原道長", "藤原道長", parent="藤原兼家")
family_tree.create_node("藤原道隆", "藤原道隆", parent="藤原兼家")

family_tree.create_node("藤原安子", "藤原安子", parent="藤原師輔")
family_tree.create_node("円融天皇", "円融天皇", parent="藤原安子")
family_tree.create_node("冷泉天皇", "冷泉天皇", parent="藤原安子")

# 生成した家系図の表示
print(family_tree.show(stdout=False))
```

この修正により、家系図が正しく表示されます。

---

### **user**:

family_tree.create_node の第１引数と第２引数が同じに見えますが、どういうことか説明していただけますか？

---

### **assistant**:

もちろんです。

`create_node`メソッドは一つのノードをツリーに追加しますが、その引数は以下の通りです：

1. 第1引数`tag`はノードの表示名（ユーザが見る名前）を指定します。これはツリーを表示した際にノードとして表示させる名前です。
2. 第2引数`identifier`はそのノードを一意に特定するためのIDを指定します。これは内部的にノードを特定するために使用され、通常はユーザには表示されません。

なので、この2つの引数の値が同じであることは特に問題ありません。

これらが同じ場合と異なる場合の例を挙げます。

1. 同じ場合：
    - ファミリーツリー、組織図など、ノードの名前が一意である（すなわち、同じ名前を持つノードが2つ存在しない）場合、`tag`と`identifier`は同じにしても問題ありません。先程の家系図コード例が該当します。

2. 異なる場合：
    - しかし、ノードの名前が一意でない場合（例えば、クラス全体の生徒さんをノードとした木を作る場合など）、一つの名前に対して複数のIDが必要となるかもしれません。そのような場合、`tag`と`identifier`は異なる値にします。例えば、`tag`には生徒の名前を設定し、`identifier`には一意の生徒IDを設定することができます。

---

### **user**:

ありがとうございます。それでは、上記のコードをベースに、家系図を階層的なJSON形式ファイルに変換して tree.json という名前で保存するPythonコードを書いてください。

---

### **assistant**:

`treelib`ライブラリは直接のJSONエクスポート機能を提供していませんが、ツリーを辞書に変換してから、それをJSONに変換することが可能です。これはPythonの`json`モジュールを使用して行います。

以下のコードでそれを行うことができます：

```python
import json
from treelib import Node, Tree

# Treeオブジェクトの生成
family_tree = Tree()

# ノードの追加
family_tree.create_node("藤原師輔", "藤原師輔")  # root node

family_tree.create_node("藤原兼家", "藤原兼家", parent="藤原師輔")
family_tree.create_node("藤原超子", "藤原超子", parent="藤原兼家")
family_tree.create_node("藤原道兼", "藤原道兼", parent="藤原兼家")
family_tree.create_node("藤原道長", "藤原道長", parent="藤原兼家")
family_tree.create_node("藤原道隆", "藤原道隆", parent="藤原兼家")

family_tree.create_node("藤原安子", "藤原安子", parent="藤原師輔")
family_tree.create_node("円融天皇", "円融天皇", parent="藤原安子")
family_tree.create_node("冷泉天皇", "冷泉天皇", parent="藤原安子")

# ツリーを辞書に変換
tree_dict = family_tree.to_dict(with_data=True)

# JSON形式に変換と保存
with open('tree.json', 'w') as f:
    json.dump(tree_dict, f, ensure_ascii=False, indent=4)
```

このコードは、まず`family_tree`を辞書に変換します（`to_dict()`メソッドを利用）。そして、`json.dump`を使ってこの辞書をJSON形式としてファイルに書き出します。`ensure_ascii=False`は非ASCII文字もエンコードするための設定で、`indent=4`はJSONの整形（インデント）のための設定です。

---

### **user**:

ありがとうございます。できたファイルを

```
cat tree.json
```

したところ、ファイルの内容を次のように確認できました。

```
{
    "藤原師輔": {
        "children": [
            {
                "藤原兼家": {
                    "children": [
                        {
                            "藤原超子": {
                                "data": null
                            }
                        },
                        {
                            "藤原道兼": {
                                "data": null
                            }
                        },
                        {
                            "藤原道長": {
                                "data": null
                            }
                        },
                        {
                            "藤原道隆": {
                                "data": null
                            }
                        }
                    ],
                    "data": null
                }
            },
            {
                "藤原安子": {
                    "children": [
                        {
                            "円融天皇": {
                                "data": null
                            }
                        },
                        {
                            "冷泉天皇": {
                                "data": null
                            }
                        }
                    ],
                    "data": null
                }
            }
        ],
        "data": null
    }
}
```

---

### **assistant**:

はい、おっしゃる通りです。`treelib`の`to_dict()`メソッドを利用すると、ツリー構造を階層的な辞書形式（すなわちネストされた辞書）で表現します。辞書のキーはノード名（`tag`）で、その値はノードのデータ部分と子ノードのリストを含む別の辞書として表現されます。

`"data": null`部分は、各ノードの`data`属性を表します。`create_node`メソッドを呼び出す際に`data`引数を指定しなかったため、ここは`null`となっています。`data`属性を利用すると、各ノードに付加的なデータ（例えばノード特有の属性情報など）を格納することができます。

家系図をJSON形式で表現した結果が正しく得られていることがわかりますので、これでPythonからのJSON出力が成功したことになります。これを確認するために`cat`コマンドをお使いいただき、ありがとうございます。

---

### **user**:

ありがとうございます。それでは、さきほどの tree.json を読み込んで、treelib で取り扱える形式に変換し、print(family_tree.show(stdout=False)) で表示するPythonコードを書いてください。

---

### **assistant**:

そのためには、まず辞書からツリーへ再帰的にノードを生成する関数が必要です。ファイルからJSONデータを読み込み、ツリーを作成し、表示するPythonコードは次のとおりです：

```python
import json
from treelib import Node, Tree

def add_node(n, parent = None):
    name = list(n.keys())[0]
    tree.create_node(name, name, parent = parent)
    children = n[name].get('children', [])
    for child in children:
        add_node(child, parent = name)

# JSON形式データの読み込み
with open('tree.json', 'r') as f:
    data = json.load(f)

# Treeオブジェクトの生成
tree = Tree()

# ツリーの作成
add_node(data)

# 生成した家系図の表示
print(tree.show(stdout=False))
```

このコードでは、`add_node`関数を定義しています。この関数は、与えられた辞書からノードを作成し、その子ノードが存在すれば再帰的にその子ノードを作成します。そして、読み取ったJSONデータのルートノードからこの`add_node`関数を呼び出すことでツリーを作成します。

---

### **user**:

ありがとうございます。動作確認しました。それでは、次の家系図を階層的なJSON形式ファイルに変換して tree2.json という名前で保存するPythonコードを書いてください。

```
藤原道長
├── 藤原妍子
├── 藤原威子
├── 藤原嬉子
│   └── 後冷泉天皇
├── 藤原彰子
├── 藤原教通
└── 藤原頼道
    ├── 藤原寛子
    └── 藤原師実
```

---

### **assistant**:

もちろんです、ご指定のツリーを作成し、それをJSON形式で保存するPythonコードは以下の通りです：

```python
import json
from treelib import Node, Tree

# Treeオブジェクトの生成
family_tree = Tree()

# ノードの追加
family_tree.create_node("藤原道長", "藤原道長")  # root node

family_tree.create_node("藤原妍子", "藤原妍子", parent="藤原道長")
family_tree.create_node("藤原威子", "藤原威子", parent="藤原道長")
family_tree.create_node("藤原嬉子", "藤原嬉子", parent="藤原道長")
family_tree.create_node("後冷泉天皇", "後冷泉天皇", parent="藤原嬉子")
family_tree.create_node("藤原彰子", "藤原彰子", parent="藤原道長")
family_tree.create_node("藤原教通", "藤原教通", parent="藤原道長")
family_tree.create_node("藤原頼道", "藤原頼道", parent="藤原道長")
family_tree.create_node("藤原寛子", "藤原寛子", parent="藤原頼道")
family_tree.create_node("藤原師実", "藤原師実", parent="藤原頼道")

# ツリーを辞書に変換
tree_dict = family_tree.to_dict(with_data=True)

# JSON形式に変換と保存
with open('tree2.json', 'w') as f:
    json.dump(tree_dict, f, ensure_ascii=False, indent=4)
```

このコードはJSON形式の家系図を' tree2.json 'というファイル名で保存します。


---

### **user**:

次の家系図を階層的なJSON形式ファイルに変換して tree3.json という名前で保存するPythonコードを書いてください。

```
藤原安子
├── 円融天皇
│   └── 一条天皇
│       ├── 後一条天皇
│       └── 後朱雀天皇
│           └── 後冷泉天皇
└── 冷泉天皇
    └── 三条天皇
```

---

### **assistant**:

以下のPythonコードで指定された家系図を作成し、それをJSON形式で "tree3.json" という名前のファイルとして保存します。

```python
import json
from treelib import Node, Tree

# Treeオブジェクトの生成
family_tree = Tree()

# ノードの追加
family_tree.create_node("藤原安子", "藤原安子")  # root node

family_tree.create_node("円融天皇", "円融天皇", parent="藤原安子")
family_tree.create_node("一条天皇", "一条天皇", parent="円融天皇")
family_tree.create_node("後一条天皇", "後一条天皇", parent="一条天皇")
family_tree.create_node("後朱雀天皇", "後朱雀天皇", parent="一条天皇")
family_tree.create_node("後冷泉天皇", "後冷泉天皇", parent="後朱雀天皇")

family_tree.create_node("冷泉天皇", "冷泉天皇", parent="藤原安子")
family_tree.create_node("三条天皇", "三条天皇", parent="冷泉天皇")

# ツリーを辞書に変換
tree_dict = family_tree.to_dict(with_data=True)

# JSON形式に変換と保存
with open('tree3.json', 'w') as f:
    json.dump(tree_dict, f, ensure_ascii=False, indent=4)
```

このコードは `tree3.json` という名前のJSONファイルを出力します。

---

### **user**:

ありがとうございます。それでは、家系図 tree.json と tree2.json を読み込んでマージした家系図を表示するPythonコードを書いてください。

---

### **assistant**:

PythonのtreelibライブラリのTreeオブジェクトは、そのままマージすることはできません。なので、各ツリーごとにJSONデータをロードし、それぞれの辞書構造を生成します。その後、新しいツリーを生成し、それぞれの辞書データを元にノードを再帰的に追加していきます。以下にそのときのPythonコードを示します。

```python
import json
from treelib import Node, Tree

def add_node(n, parent = None):
    name = list(n.keys())[0]
    merged_tree.create_node(name, name, parent = parent)
    children = n[name].get('children', [])
    for child in children:
        add_node(child, parent = name)

# JSON形式データの読み込み
with open('tree.json', 'r') as f:
    data1 = json.load(f)

with open('tree2.json', 'r') as f:
    data2 = json.load(f)

# Treeオブジェクトの生成
merged_tree = Tree()

# ツリーの作成
add_node(data1)
add_node(data2)

# 生成した家系図の表示
print(merged_tree.show(stdout=False))
```

このコードで、`tree.json`と`tree2.json`の内容をマージした家系図が表示されます。ただし、このコードでは２つのツリーに同じ名前のノードが存在するとエラーが発生しますので、その点はご注意ください。

---

### **user**:

なるほど、確かにエラーになりましたね。tree.json の藤原道長と、tree2.json の藤原道長は同一人物なので、同じノードになるようにマージした家系図を作って表示するコードを書いてください。
上記のコードをベースに修正したコードを書いてください。

```
---------------------------------------------------------------------------
DuplicatedNodeIdError                     Traceback (most recent call last)
Cell In[10], line 23
     21 # ツリーの作成
     22 add_node(data1)
---> 23 add_node(data2)
     25 # 生成した家系図の表示
     26 print(merged_tree.show(stdout=False))

Cell In[10], line 6, in add_node(n, parent)
      4 def add_node(n, parent = None):
      5     name = list(n.keys())[0]
----> 6     merged_tree.create_node(name, name, parent = parent)
      7     children = n[name].get('children', [])
      8     for child in children:

File ~/.local/lib/python3.10/site-packages/treelib/tree.py:388, in Tree.create_node(self, tag, identifier, parent, data)
    383 """
    384 Create a child node for given @parent node. If ``identifier`` is absent,
    385 a UUID will be generated automatically.
    386 """
    387 node = self.node_class(tag=tag, identifier=identifier, data=data)
--> 388 self.add_node(node, parent)
    389 return node

File ~/.local/lib/python3.10/site-packages/treelib/tree.py:309, in Tree.add_node(self, node, parent)
    304     raise OSError(
    305         "First parameter must be object of {}".format(self.node_class)
    306     )
    308 if node.identifier in self._nodes:
--> 309     raise DuplicatedNodeIdError(
    310         "Can't create node " "with ID '%s'" % node.identifier
    311     )
    313 pid = parent.identifier if isinstance(parent, self.node_class) else parent
    315 if pid is None:

DuplicatedNodeIdError: Can't create node with ID '藤原道長'
```

---

### **assistant**:

`create_node()`メソッドを呼び出す前に、出力ツリーに既に該当ノードが存在するかどうかをチェックすることで、指定されたエラーを克服できます。以下に修正版のコードを示します。

```python
import json
from treelib import Node, Tree

def add_node(n, parent=None):
    name = list(n.keys())[0]
    if not merged_tree.contains(name):
        merged_tree.create_node(name, name, parent=parent)
    children = n[name].get('children', [])
    for child in children:
        add_node(child, parent=name)

# JSON形式データの読み込み
with open('tree.json', 'r') as f:
    data1 = json.load(f)

with open('tree2.json', 'r') as f:
    data2 = json.load(f)

# Treeオブジェクトの生成
merged_tree = Tree()

# ツリーの作成
add_node(data1)
add_node(data2)

# 生成した家系図の表示
print(merged_tree.show(stdout=False))
```

ここでは`add_node()`関数内で出力ツリー`merged_tree`にノードがすでに存在しているかどうかを確認しています。ノードが存在しない場合のみ`create_node()`を実行するため、同名のノード（この場合、"藤原道長"）があってもエラーになりません。ですが、この方法は同名で別の人物の場合には適していません。


---

### **user**:

おお、ありがとうございます。期待通りの結果になりました。

```
藤原師輔
├── 藤原兼家
│   ├── 藤原超子
│   ├── 藤原道兼
│   ├── 藤原道長
│   │   ├── 藤原妍子
│   │   ├── 藤原威子
│   │   ├── 藤原嬉子
│   │   │   └── 後冷泉天皇
│   │   ├── 藤原彰子
│   │   ├── 藤原教通
│   │   └── 藤原頼道
│   │       ├── 藤原寛子
│   │       └── 藤原師実
│   └── 藤原道隆
└── 藤原安子
    ├── 円融天皇
    └── 冷泉天皇
```

---

### **assistant**:

素晴らしいですね！期待通りの結果が得られたようで、安心しました。

このコードにより、2つの家系図がマージされ、それぞれの家系図に存在する"藤原道長"というノードが同一のノードとして扱われるようになりました。

今後ともお気軽にご質問ください、お手伝いできることがあれば幸いです。

---

### **user**:

ありがとうございます。それでは、家系図 merged_tree に対して、さらに tree3.json を読み込んでマージした家系図を表示するPythonコードを書いてください。

---

### **assistant**:

`merged_tree`に対して、さらに`tree3.json`を読み込んでマージするためのPythonコードは以下のとおりです：

```python

# JSON形式データの読み込み
with open('tree3.json', 'r') as f:
    data3 = json.load(f)

# ツリーの作成
add_node(data3)

# 生成した家系図の表示
print(merged_tree.show(stdout=False))
```

このコードでは、最初に`tree3.json`からJSONデータを読み込みます。次に、そのデータを元にノードを再帰的に追加しています。そして、最終的にマージされた家系図を出力しています。

---

### **user**:

ありがとうございます。次のような結果が得られました。

```
藤原師輔
├── 藤原兼家
│   ├── 藤原超子
│   ├── 藤原道兼
│   ├── 藤原道長
│   │   ├── 藤原妍子
│   │   ├── 藤原威子
│   │   ├── 藤原嬉子
│   │   │   └── 後冷泉天皇
│   │   ├── 藤原彰子
│   │   ├── 藤原教通
│   │   └── 藤原頼道
│   │       ├── 藤原寛子
│   │       └── 藤原師実
│   └── 藤原道隆
└── 藤原安子
    ├── 円融天皇
    │   └── 一条天皇
    │       ├── 後一条天皇
    │       └── 後朱雀天皇
    └── 冷泉天皇
        └── 三条天皇
```

後朱雀天皇の御子として後冷泉天皇がいらっしゃるはずですが、後冷泉天皇は先に藤原嬉子の御子として記述されているので、２回目の記述は省略されているようですね。

---

### **assistant**:

はい、その通りです。現在のPythonコードでは、家系図をマージする際に一度作成されたノードは再度作成されません。これは同一のノードが複数あるとエラーが発生するためです。

しかし、それが現実の家系図を正確に表現できない場合もあります。この場合のように、一人の人物（後冷泉天皇）が複数の視点（藤原嬉子の子、後朱雀天皇の子）から参照されている場合には、現在のコードではこの恒等性を表現できません。

これを解決するには、ツリーのノードにユニークな識別子を付け、そのノードが何度も参照されることを許容する別のデータ構造を使用する必要があります。しかし、このタイプのデータ構造は、現在使用しているtreelibライブラリではサポートされていません。

また、このような家系図の表現は、それが直感的に理解しやすい階層的ツリー構造から離れ、グラフ構造となります。グラフ構造を扱うためのライブラリやデータベースも存在しますが、それは別のトピックとなります。

---

### **user**:

なるほど。ありがとうございました。また色々と教えてくださいね。

---

### **assistant**:

どういたしまして！お手伝いできてうれしいです。何か他に質問がございましたら、いつでもお気軽にお問い合わせください。お待ちしております！

---