ケモインフォマティクスで学ぶPythonの反復処理

# はじめに

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

[製薬企業研究者がPythonの制御文についてまとめてみた](https://qiita.com/yukiya285/items/1db03558c38f1405dbbb)


# for文

`for`は、あらかじめ決まった回数だけ反復処理を行うときに使えます。
`for 変数 in イテラブル（リストなど）:`として、改行して半角スペース4つ分インデントして処理内容を記述します。

```python
carbon_numbers = [16, 18, 20, 22, 24]

for Cn in carbon_numbers:
    print(Cn, 0, sep=':')
```

上の例では、`carbon numbers`を脂肪酸の炭素原子数（鎖長）のリストとして、それぞれに対応する飽和脂肪酸の略号を作成しています。


## range

イテラブルとして`range`を使うこともできます。
`range`は、指定した範囲の連続した整数のリストのようなイメージです。

```python
Cn = 22

for Un in range(4, 7):
    print(Cn, Un, sep=':')
    
for Un in range(7):
    print(Cn, Un, sep=':')
```

上の例では、`range(4, 7)`は4から6までの連続した整数（4, 5, 6）を示します。
また、`range(7)`と書くと、0から6までの連続した整数（0, 1, 2, 3, 4, 5, 6）を指すことになります。
ここでは、脂肪酸の不飽和度（炭素鎖における二重結合数）を示す数値を生成しています。


## enumerate

以下のように、`enumerate`を使うことで、イテラブルのインデックス番号と要素をセットで取り出すことができます。

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

for fatty_acid in fatty_acids:
    print(fatty_acid)

for i, fatty_acid in enumerate(fatty_acids):
    print(f'{i}: {fatty_acid}')
```

上の例では、`i`にリスト`fatty_acids`のインデックス番号を、`fatty_acid`にリスト`fatty_acids`の要素の値を順番に格納していき、`print`で出力するという処理になっています。

## 辞書

辞書に対しても`for`を使った反復処理を行うことができます。
以下に例を示します。

```python
fatty_acids = {'Palmitic acid': 'FA 16:0', 'Stearic acid': 'FA 18:0', 'Oleic acid': 'FA 18:1'} # 脂肪酸名（common name）と略号

for key in fatty_acids.keys(): # 辞書のキー
    print(key)
    
for value in fatty_acids.values(): # 辞書の値
    print(value)
    
for key, value in fatty_acids.items(): # 辞書のキーと値
    print(f'{key} is {value}')

    
exact_mass = {'C': 12, 'H': 1.00783, 'O': 15.99491} # 元素記号と原子量
formula_pa = {'C': 16, 'H': 32, 'O': 2} # 元素記号と原子数（組成式）
em_pa = 0

for key, value in formula_pa.items():
    em_pa += exact_mass[key] * value # 原子量と原子数を掛け算
print(em_pa)


fatty_acids = {16: [0, 1], 18: [0, 1, 2, 3, 4], 20: [0, 3, 4, 5], 22: [0, 4, 5, 6]} # 脂肪酸の炭素原子数をキーに、二重結合数を値とした辞書

for Cn, Uns in fatty_acids.items():
    for Un in Uns:
        print(Cn, Un, sep=':')
```

ここで、`a = a + n`は、`a += n`と書くことができます。`em_pa += exact_mass[key] * value`の部分は、原子ごとに原子量と原子数を掛け合わせて、精密質量を計算する変数`em_pa`に足し合わせるということを意味しています。


## 条件分岐との組み合わせ

もちろん、`if`と組み合わせることもできます。

```python
fatty_acids = ['FA 16:0', 'FA 18:0', 'FA 18:1', 'FA 18:2', 'FA 20:4', 'FA 22:6', 'FA 24:0']

saturated_fatty_acids = [] # 空のリスト（この後の処理で値を入れていく）
unsaturated_fatty_acids = [] # 空のリスト（この後の処理で値を入れていく）

for fatty_acid in fatty_acids:
    if fatty_acid[-1] == '0':
        saturated_fatty_acids.append(fatty_acid) # 飽和脂肪酸
    else:
        unsaturated_fatty_acids.append(fatty_acid) # 不飽和脂肪酸
        
print(saturated_fatty_acids)
print(unsaturated_fatty_acids)
```


## breakとcontinue

以下のように`break`を使うと反復処理を中断します。
`else`は`break`されずに反復処理が終了した場合に行われる処理を記述するのに使います。
`else`はなくても問題ありません。

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

for fatty_acid in fatty_acids:
    if fatty_acid == '':
        print('空です。処理を中止します。')
        break
    print(fatty_acid) # 「FA 18:0」まで出力される
else:
        print('処理が最後まで完了しました。') # ここでは出力されない
```

上の例では、リスト`fatty_acids`の左から3番目の要素が空となっているため、その前の要素まで反復処理が行われ、要素の値が出力されます。
今回の例では、`break`により、処理が途中で中断されるため`else`以下は実行されません。

一方、`continue`を使うと、反復処理がスキップされます。

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

for fatty_acid in fatty_acids:
    if fatty_acid == '':
        print('空です。スキップします。')
        continue
    print(fatty_acid) # 空の要素は飛ばして「FA 18:1」まで出力される
```

上の例では、空白の要素は`continue`によって処理がスキップされるため、要素の値が出力されることはありませんが、最後まで処理が行われ、「FA 18:1」も出力されます。
仮に、`else`を入れた場合は、`else`以下も実行されます。

## 応用：SMILES記法

ここで応用編として、SMILES記法で書かれた文字列から、炭素原子の数と、炭素鎖の二重結合の数を求めることを考えます。

```python
smiles_fa = 'OC(CCCCCCC/C=C\C/C=C\CCCCC)=O'

Cn = 0
Un = 0

for i in range(len(smiles_fa)):
    if smiles_fa[i] == 'C':
        Cn += 1
    elif smiles_fa[i] == '=' and smiles_fa[i+1] == 'C':
        Un += 1

print(Cn, Un, sep=':')
```

左から文字列を見て行って、`C`だった場合は炭素原子数を数える変数`Cn`を1大きくし、`=`で次の文字が`C`だった場合（カルボニル炭素もあるため）に二重結合数を数える変数`Un`を1大きくしています。


# while文

`while`を使うと、指定した条件が成立する限り、反復処理を行います。
`while 条件:`として改行して半角スペース4つ分インデントして処理を記述します。

```python
saturated_fatty_acids = ['FA 16:0', 'FA 18:0']
unsaturated_fatty_acids = ['FA 18:1', 'FA 18:2', 'FA 18:3', 'FA 20:4', 'FA 22:6']

fatty_acids = []

while len(saturated_fatty_acids) > 0:
    fatty_acids.append(saturated_fatty_acids[-1])
    saturated_fatty_acids.pop()

while len(unsatturated_fatty_acids) > 0:
    fatty_acids.append(unsaturated_fatty_acids[-1])
    unsaturated_fatty_acids.pop()

print(fatty_acids)
```

上の例では、元々あるリスト`saturated_fatty_acids`と`unsaturated_fatty_acids`の後ろから要素を取り出して新しい空のリスト`fatty_acids`に移し替えるという処理をしています。
ここで、`len(リスト) > 0`はリストに何らかの要素が含まれている場合を指します。`fatty_acids`に要素を移した後は、`pop`で元のリストからは要素を削除するようにしています。この削除を忘れると、処理が無限回繰り返される（無限ループになる）ので注意が必要です。

## 応用：SMILES記法

最後に応用編として、SMILES記法で`while`を使って炭素原子数を数えることを考えてみます。

```python
smiles_fa = 'OC(CCCCCCCCCCCCCCC)=O'

Cn = 0

while 'C' in smiles_fa:
    if smiles_fa[0] == 'C':
        Cn += 1
    smiles_fa = smiles_fa[1:]
print(Cn)
```

SMILESの文字列に`C`という文字が含まれ、なおかつ一番左の文字が`C`の場合は、炭素原子数を数える変数`Cn`を1大きくします。
一番左の文字は`C`であるかどうかに関わらず毎回消去します。
こうすることで、SMILESの中の`C`の数を数えることができます。

以上のように、`for`は繰り返し回数が決まっている場合に、`while`は繰り返し回数が決まっていない場合に使うことになります。


# まとめ

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

- `for`は、繰り返し回数が決まっているときに使える。`break`や`continue`で特定の条件のもとで処理を中止したり、スキップしたりすることもできる。
- `while`は、繰り返し回数が決まっておらず、あらかじめ設定した条件を満たすまで処理を繰り返すのに使える。「無限ループ」とならないように注意する必要がある。

続いて、Pythonの関数について以下の記事で解説しています。

[ケモインフォマティクスで学ぶPythonの関数](https://qiita.com/yukiya285/items/e3ea92fe0fc74a546325)


# 参考資料・リンク

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