内包表記(List Comprehension)の理解

#はじめに

Pythonの内包表記について、自分で分かったことを記録しておきます。

#内包表記とは？

最初、内包表記という単語を耳にした時に、正直にピンときませんでした。内包という漢字が、肉まん＋包むに見えて、何か横浜中華街の新しい流行りの食べ物？との印象でした。
オリジナルの英語表記では、内包表記を**List Comprehension**と言います。なるほど、**リスト**を作成するときに使うものだったんですね。

+ **リスト**を作成する
+ **for文**と**if文**が一行になった形式
+ single for loopだけではなく、double for loopにも使える。
+ 計算速度が速くなる

一言で表すと、下記の文章に変えられます。

###内包表記は、リストを作成するfor loopを理解しやすい一行にコードを圧縮するもので、計算速度が速い。###

それでは、順番に事例を書いてみます。

#1.for loop

０から4までの数字で構成するリストを作成するコードです。
既存表記と内包表記の順番にコードを記します。

```python:既存表記.py
list = []
for i in range(5):
    list.append(i)

print(list)

#結果
[0, 1, 2, 3, 4]
```


```python:内包表記.py
# [式 for 任意の変数名 in イテラブルオブジェクト]

list_n = [ i for i in range(5)]
print(list_n)

#結果
[0, 1, 2, 3, 4]
```

#2.for loop (少し複雑）
リストを作成するときに、演算を加えます。
今回は、0から4まで、それぞれの二乗した数字をリストにするコードです。

```python:既存表記.py
list = []
for i in range(5):
    val_new = i**2
    list.append(val_new)

print(list)

#結果
[0, 1, 4, 9, 16]

```

```python:内包表記.py
# [式 for 任意の変数名 in イテラブルオブジェクト]
list_n = [ i**2 for i in range(5)]
print(list_n)

#結果
[0, 1, 4, 9, 16]

```

#2.for loop + if

０から20までの偶数で構成するリストを作成するコードです。

```python:既存表記.py

list = []

for i in range(20):
    if i%2 ==0:
        list.append(i)

print(list)

#結果
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

```

```python:内包表記.py
#[式 for 任意の変数名 in イテラブルオブジェクト if 条件式]
list_n = [ i for i in range(20) if i%2 ==0]
print(list_n)

#結果
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

```


#3.double for Loop
次は2重ループです。画素数(5X5)の画像を想像してください。（行X列）の計算結果のリストを作成するコードです。

```python:既存表記.py
list = []
for row in range(5):
    for col in range(5):
        list.append(row * col)

print(list)

#結果
[0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 2, 4, 6, 8, 0, 3, 6, 9, 12, 0, 4, 8, 12, 16]

```

```python:内包表記.py
list_n = [ row * col for row in range(5) for col in range(5)]
print(list_n)

#結果
[0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 2, 4, 6, 8, 0, 3, 6, 9, 12, 0, 4, 8, 12, 16]

```


#4.double for loop + if

今度は、偶数の行と奇数の列だけで、計算するリストです。

```python:既存表記.py
list = []
for row in range(5):
    if row %2 ==0:
        for col in range(5):
            if col %2 ==1:
                list.append(row * col)

print(list)

#結果
[0, 0, 2, 6, 4, 12]
```
if文の位置に注目してください。基本的に、上記の既存表記を1行に伸ばした書き方をしています。（これを覚えると内包表記はもう大丈夫かと。）

```python:内包表記.py
list_n = [ row * col for row in range(5) if row%2 == 0 for col in range(5) if col %2 ==1]
print(list_n)
#結果
[0, 0, 2, 6, 4, 12]
```


#5. 計算速度比較

IPythonの##%%time%%で計測しました。

```python:既存表記.py
%%time 

list = []
for i in range(1000000):
    list.append(i)

CPU times: user 206 ms, sys: 33.2 ms, total: 239 ms
Wall time: 242 ms

```

```python:内包表記.py
%%time 
# [式 for 任意の変数名 in イテラブルオブジェクト]

list_n = [ i for i in range(1000000)]

CPU times: user 112 ms, sys: 34 ms, total: 146 ms
Wall time: 146 ms

```
内包表記の計算速度が速いです。二重for loop + if文の計算速度も見てみましょう。

```python:既存表記.py
%%time

list = []
for row in range(5000):
    if row %2 ==0:
        for col in range(5000):
            if col %2 ==1:
                list.append(row * col)

CPU times: user 2.27 s, sys: 116 ms, total: 2.39 s
Wall time: 2.39 s

```

```python:内包表記.py
%%time 

list_n = [ row * col for row in range(5000) if row%2 == 0 for col in range(5000) if col %2 ==1]

CPU times: user 1.4 s, sys: 106 ms, total: 1.51 s
Wall time: 1.52 s

```

##まとめ

+ 内包表記は、リストを作成するときに使う**効率的なコードの書き方**です。
+ 無暗に書き方を覚えるより、既存のコードと比較してみることで、内包表記の書き方が理解しやすいです。
+ 慣れると内包表記のほうが読みやすいことにと気付きます。
+ 計算速度も速いです。まさに一石二鳥。