ケモインフォマティクスで学ぶPythonのデータ構造

# はじめに

[ケモインフォマティクスで学ぶPythonの変数とデータ型](https://qiita.com/yukiya285/items/2e800179fcf06aab0fb9)に引き続き、リピドミクス（脂質の網羅解析）を題材として「データ構造」について解説していきます。
ケモインフォマティクスの実践例を中心に説明していきますので、基本を確認したいという人は以下の記事を読んでからこの記事を読んでみてください。

[製薬企業研究者がPythonのデータ構造についてまとめてみた](https://qiita.com/yukiya285/items/207b0096b01dc8cc81a9)


# リスト

## リストの作成、要素の値の参照、更新

リスト（list）は、複数の要素を格納したデータ型で、`リスト名 = [要素1, 要素2, ・・・]`で作ることができます。
以下の例は文字列のみを格納したリストですが、数値や真偽値を入れることもできますし、同じ値の要素を複数入れたり、複数のデータ型を混在させたりすることもできます。
リストの中の要素は、`リスト名[インデックス番号]`で値を参照することができます。
インデックス番号は`1`からではなく、`0`から始まることに注意する必要があります。

```python
fatty_acids = ['FA 16:0', 'FA 18:0', 'FA 18:1', 'FA 18:2', 'FA 18:3']

print(fatty_acids[0]) # 1番目（最初）の要素
print(fatty_acids[1]) # 2番目の要素
print(fatty_acids[-1]) # 後ろから1番目（最後）の要素
print(fatty_acids[2:4]) # 3番目から4番目の要素
print(fatty_acids[3:]) # 4番目以降の要素
print(fatty_acids[:3]) # 4番目までの要素
print(fatty_acids[:-2]) # 後ろから2番目までの要素
```

`リスト名[インデックス番号] = 値`とすることで、指定したインデックス番号の要素の値を更新することができます。

```python
fatty_acids[3] = 'FA 18:2 (6Z, 9Z)'
print(fatty_acids)
```

ちなみに、`(6Z, 9Z)`は、二重結合の位置と様式を表しています。`6`と`9`はカルボン酸とは反対側の炭素原子から数えて何番目の炭素原子が二重結合を形成しているかを示し、`Z`というのは二重結合が*cis*であることを示しています。`E`だと*trans*ということになります。
リノール酸の構造など詳しくは、以下のリンク先を見てください。
[Linoleic acid (FA 18:2)](http://www.lipidmaps.org/data/LMSDRecord.php?LMID=LMFA01030120)
二重結合がある場合は、本来であれば上記のように二重結合の位置や結合様式を明示する必要があるわけですが、少し長くなってしまうので、以降では省略することとします。

## リストの要素の数の取得

リストに含まれる要素の数は`len`で確認することができます。
ちなみに、`len`は「length」の略です。

```python
print(len(fatty_acids))
```

## リストの演算

リストの演算でよく使うのは、`+`と`*`です。
`+`でリスト同士を結合することができ、`*`で同じ要素を指定個数持つリストを作成することができます。

```python
saturated_fatty_acids = ['FA 16:0', 'FA 18:0'] # 飽和脂肪酸（二重結合がない脂肪酸）
unsaturated_fatty_acids = ['FA 18:1', 'FA 18:2', 'FA 18:3'] # 不飽和脂肪酸（二重結合がある脂肪酸）
fatty_acids = saturated_fatty_acids + unsaturated_fatty_acids # リストの結合
print(fatty_acids)

number_carbons = [16] + [18]*4 # リストの結合と複製
print(number_carbons)
```

`number_carbons`は、リスト`fatty_acids`の炭素原子数だけ取り出したものになっています。
`fatty_acids`には、炭素原子数18の分子種が4つ含まれているので、`*`で複製しています。


## リストのメソッド

リストでよく使うメソッドを以下で紹介します。

```python
fatty_acids = ['FA 16:0', 'FA 18:0', 'FA 18:1', 'FA 18:2', 'FA 18:3']

fatty_acids_copy = fatty_acids.copy() # コピーを作成
print(fatty_acids_copy)

fatty_acids.append('FA 20:4') # 末尾に要素を追加
print(fatty_acids)

fatty_acids.extend(['FA 20:5', 'FA 22:6']) # 末尾に要素（複数）を追加
print(fatty_acids)

fatty_acids.insert(1, 'FA 16:1') # 指定したインデックス番号に要素を追加
print(fatty_acids)

fatty_acids.remove('FA 18:3') # 指定した要素を削除
print(fatty_acids)

print(fatty_acids.pop()) # 最後の要素を削除し、削除した要素を出力

print(fatty_acids.pop(2)) # 3番目の要素を削除し、削除した要素を出力

fatty_acids.sort(key=None, reverse=True) # 要素を降順に並び替え
print(fatty_acids)

fatty_acids.sort(key=None, reverse=False) # 要素を昇順に並び替え
print(fatty_acids)

print(fatty_acids.index('FA 18:2')) # 指定した要素のインデックス番号
print(fatty_acids.count('FA 18:2')) # 指定した要素の個数
```

`.extend(['FA 20:5', 'FA 22:6')`は、`.append(['FA 20:5', 'FA 22:6'])`と書いても良さそうですが、実行結果が変わってきます。
`extend`を使った場合は、`'FA 20:5'`と`'FA 22:6'`という2つの要素が追加されますが、`append`を使った場合は、`['FA 20:5', 'FA 22:6']`というリストが1つの要素として追加されます。つまり、`append`を使った場合は、リストの中にリストが含まれるという状態になるわけです。
`append`と`extend`の使い分けには注意が必要です。


## 文字列の扱い

文字列もリストと同じように扱うことができます。文字列を単一文字のリストと考えて、前から5文字目を参照する、といったことができます。

```python
palmitic_acid = fatty_acids[0] # リスト「fatty_acid」の1番目の要素
print(palmitic_acid) # FA 16:0
print(palmitic_acid[0]) # 「FA 16:0」という文字列の1文字目、すなわち「F」
print(len(palmitic_acid)) # 文字数

lipid_class = palmitic_acid[0:2]
print(lipid_class) # FA
Cn = int(palmitic_acid[3:5])
print(Cn) # 16（数値）
Un = int(palmitic_acid[6])
print(Un) # 0（数値）
```

## 応用：SMILES記法

応用編として、SMILES記法で脂肪酸の炭素原子や二重結合の数を数えるということを考えます。

```python
smiles_la = 'OC(CCCCCCC/C=C\C/C=C\CCCCC)=O' # リノール酸のSMILES

Cn = smiles_la.count('C') # 炭素原子の数
Un = smiles_la.count('=') - 1 # 炭素鎖の中の二重結合の数

linoleic_acid = f'FA {Cn}:{Un}' # f-string
print(linoleic_acid)
```


# タプル

タプル（tupple）はリストに似たデータ型で、`タプル名 = (要素1, 要素2, ・・・)`で作ることができます。
リストと同様に、`タプル名[インデックス番号]`で値を参照することはできますが、値の更新をすることはできません。
なので、値を書き換えたくないデータを配列にしたい場合は、タプルを使うと良いです。

```python
fatty_acids = ('FA 16:0', 'FA 18:0', 'FA 18:1', 'FA 18:2', 'FA 18:3')

print(fatty_acids[0]) # 1番目の要素
print(fatty_acids[1]) # 2番目の要素
print(fatty_acids[-1]) # 後ろから1番目の要素
print(fatty_acids[2:4]) # 3番目から4番目の要素
print(fatty_acids[3:]) # 4番目以降の要素
print(fatty_acids[:3]) # 4番目までの要素
print(fatty_acids[:-2]) # 後ろから2番目までの要素
```


# 辞書

## 辞書の作成

辞書（dictionary）というのは、「キー」と「値」を1対1に対応させ、このキーと値の組合せを配列にしたものです。
`辞書名 = {キー1: 値1, キー2: 値2, ・・・}`で作成することができます。

```python
Cn = 18 # 脂肪酸の炭素原子数（鎖長）
Un = 2 # 二重結合数（不飽和度）

num_C = Cn # 分子全体の炭素原子数
num_H = Cn * 2 - Un * 2 # 分子全体の水素原子の数
num_O = 2 # 分子全体の酸素原子の数

molecular_formula = {'C': num_C, 'H': num_H, 'O': num_O}
```

## 辞書の要素の参照

上の例では、元素記号をキーに、原子数を値とした辞書を作成しています。
辞書に含まれる全てのキーや値を参照するには、以下のようにします。

```python
print(molecular_formula.keys()) # キーのリスト
print(molecular_formula.values()) # 値のリスト
print(molecular_formula.items()) # キーと値のタプルのリスト
```

## 辞書の要素の更新、追加

`辞書名[キー] = 値`とすると、辞書の中にキーが既にあった場合は値が更新され、キーがなかった場合は新しくキーと値が追加されます。

```python
molecular_formula['C'] = 16 # 値の書き換え
molecular_formula['H'] = 32 # 値の書き換え
molecular_formula['N'] = 0 # 新たなキーと値の追加
print(molecular_formula)
```


# 集合

集合（set）は、`集合名 = {}`で作成することができます。
順番といった概念はなく、インデックス番号で要素を指定せずに、特定の要素があるかないかなどを判定するときに使います。

```python
fatty_acids = {'FA 16:0', 'FA 18:0', 'FA 18:1', 'FA 18:2', 'FA 18:3'}
```

# まとめ

ここでは、Pythonのデータ構造について、ケモインフォマティクスで使える実践的な知識を中心に解説しました。
もう一度要点をおさらいしておきましょう。

- リストは、複数の要素を格納できるほか、文字列をリストのように扱うこともできます。SMILES記法で記述された化合物の構造を取り扱うことにも応用できます。
- 辞書は、キーと値を対応させて複数のデータを格納できます。化合物の組成式の情報などを格納するのに使えます。

続いて、Pythonの条件分岐について以下の記事で解説しています。

[ケモインフォマティクスで学ぶPythonの条件分岐](https://qiita.com/yukiya285/items/498f15061b80159ecf2e)


# 参考資料・リンク

[意外と少ない！？製薬会社でのプログラミングに必要な「最低限」の知識](https://pharma-engineer.com/basic-programming/minimum-information/)