RDKitのPandasToolで画像が表示されなくなる問題

# RDKitのPandasToolで画像が表示されなくなる問題

最初から解決策を知りたい方は[こちら](#解決策)

## 問題

### RDKitのPandasToolsで画像が表示されなくなる時がある

RDKitのPandasTools、便利ですよね。でもデータフレームを操作していくうちにROMolカラムの画像が出力されなくなる時ありませんか？

例えばこんな感じ↓

```python
# とりあえずいつものインポート
import pandas as pd
from rdkit import Chem
from rdkit.Chem import PandasTools, Descriptors
```

```python
# PandasToolsのLoadSDFでsdfファイルをロードしてデータフレームを作成
data = PandasTools.LoadSDF('./data/example_data.sdf')
data　# とりあえず、データフレームを表示してみる
```

![スクリーンショット 2024-01-24 14.01.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676860/dc175edb-c55c-7c33-4372-71926dbf6f47.png)


この時点ではきちんと化合物の画像が表示される
でも`.head()`で表示すると…

```python
data.head()
```
![スクリーンショット 2024-01-24 14.04.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676860/761ec10d-952f-9464-154f-169c7b8732fb.png)


このように画像が文字表現になってしまっている。

他のケースとして、例えば記述子での絞り込みをしたい時↓

```python
data['MolWt'] = data.ROMol.map(Descriptors.MolWt) # 例として分子量をカラムに追加
data # 表示
```
![スクリーンショット 2024-01-24 14.08.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676860/16f1f1ef-8abf-d0b2-900d-d3e97bf538f0.png)

このままだと、普通に画像込みで表示されるが、

```python
data[data.MolWt >= 500] # 分子量500以上を表示
```
![スクリーンショット 2024-01-24 14.10.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676860/db095309-6a89-cc47-1681-4e8676cf0b38.png)


`.head()` と同じように画像が文字表現で出力されてしまう

## 原因と解決方法
先に解決策が知りたい方は[こちら](#解決策)


### 原因

正直、文字表現でもmolオブジェクトとしては普通に扱えるので、気にならなければこのままでも良い。

しかし、慣れないうちは色々と確認しながら作業したいし、操作してる化合物がどんなものなのか知りたいと思うことは多い。

そもそも、なぜこんなことが起こるのか簡単に説明すると

**最初にPandasToolsで作成されたデータフレームインスタンス：`data_0`
と
`.head()`などで作成されたデータフレームインスタンス：`data_1`
は似て非なるもの**

だからなのです。

どういことか、コードで説明しますと…

```python
data = PandasTools.LoadSDF('./data/example_data.sdf') #再ロード

print(id(data), id(data.head()) # idを確認してみる
```

```bash
4393217136 5616518672
```

この通り、`data`と`data.head()`で違うidが振り当てられています。

記述子の絞り込みも確認

```python
pre_id = id(data) # 一応MolWtカラムを追加する前にidを変数に代入
data['MolWt'] = data.ROMol.map(Descriptors.MolWt) # MolWt

print(f'MolWt追加前：{pre_id}, 追加後：{id(data)}, 絞り込み：{id(data[data.MolWt >= 500])}') # idを確認してみると...
```

```
MolWt追加前：4393217136, 追加後：4393217136, 絞り込み：5616584976
```

追加前と追加後（つまり`data`）は同じid(`4393217136`)ですが、
絞り込みのデータフレーム（`data[data.MolWt >= 500]`）は別のid(`5616584976`)が振り当てられているのがわかります。

これが画像の表示にどう関係してくるかとういうと、

PandasToolsで生成されたデータフレームインスタンス`data`では化合物が表示されるような特殊な設定（レンダリング）が使用されるので化合物が表示されますが、`data.head()`で生成されたインスタンスのように、一旦別のデータフレームになってしまうと通常のpandasの設定が使用されてしまい、画像が表示されなくなってしまうということです。

![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676860/fe610e94-3660-5f6d-8e64-ccfbb17261d3.png)


## 解決策

### PandasTools.ChangeMoleculeRendering(frame)

[https://www.rdkit.org/docs/source/rdkit.Chem.PandasTools.html#rdkit.Chem.PandasTools.ChangeMoleculeRendering](https://www.rdkit.org/docs/source/rdkit.Chem.PandasTools.html#rdkit.Chem.PandasTools.ChangeMoleculeRendering)

画像表示させたいデータフレームをPandasToolsのChangeMoleculeRenderingに入れると、画像表示する設定がデータフレームに付与され、画像が表示されるようになります。

```python
data = PandasTools.LoadSDF('./data/example_data.sdf') # 再ロード
data['MolWt'] = data.ROMol.map(Descriptors.MolWt) # MolWt
pre_id = id(data) #一応id
```

```python
data_mw_500 = data[data.MolWt >= 500] # 絞り込み
mw500_id = id(data_mw_500) # 一応idも
data_mw_500 #一応表示
```

![スクリーンショット 2024-01-24 14.10.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676860/1422e217-fe1e-5ddd-3be4-79cbea60f95f.png)


もちろん画像はなし

```python
PandasTools.ChangeMoleculeRendering(data_mw_500) # ChangeMoleculeRenderingにdata_mw_500
data_mw_500　# 表示
```

![スクリーンショット 2024-01-24 15.45.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676860/da8d606c-4a4a-2815-0705-7c290e45d7f6.png)


画像が表示された！！

```python
print(pre_id, mw500_id, id(data_mw_500))
```

```python
4400685360 5965143824 5965143824
```

`data_mw_500`のidを確認すると`data`とは違うインスタンスになっている。つまり、`data`別のインスタンス`data_mw_500`に対して画像表示の設定が適用されるようになったということ。

ちなみに、画像表示しているデータフレームを表示させないようにすることもできる

```python
data = PandasTools.LoadSDF('./data/example_data.sdf') # ロード
#このままだと画像を表示してしまうので
PandasTools.ChangeMoleculeRendering(data) # ChangeMoleculeRenderingにdata
data # 表示
```

### PandasTools.RenderImagesInAllDataFrames(bool)

[https://www.rdkit.org/docs/source/rdkit.Chem.PandasTools.html#rdkit.Chem.PandasTools.RenderImagesInAllDataFrames](https://www.rdkit.org/docs/source/rdkit.Chem.PandasTools.html#rdkit.Chem.PandasTools.RenderImagesInAllDataFrames)

PandasTools.ChangeMoleculeRendering()は任意のデータフレームのみに対して文字表現or画像表現の設定を変更するものでした。

でも、
「新しいインスタンスを生成する度にこれを実行するのは面倒臭い。」「最初から全部のデータフレームで画像が表示されるようになって欲しい！」と思いますよね。

`PandasTools.RenderImagesInAllDataFrames()`がおすすめ

`PandasTools.RenderImagesInAllDataFrames(True)`にすればノートブック内の全データフレームで画像が表示されるようになり

逆に画像が煩わしければ`PandasTools.RenderImagesInAllDataFrames(False)`にすればノートブック内の全データフレームで画像が表示されなくなる。

```python
data = PandasTools.LoadSDF('./data/example_data.sdf')
PandasTools.RenderImagesInAllDataFrames(True)
data.head()
```

![スクリーンショット 2024-01-24 16.09.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676860/12b9bc10-1c48-d6ab-0ee5-d2988c9cf6f1.png)


`data.head()`でも表示された！！

:::note warn
注意
`RenderImagesInAllDataFrames`はグローバルな変更になるので、他のpythonのセッションに影響を及ぼすことがあります。その場合は`ChangeMoleculeRendering`を使いましょう。
:::