こんにちは、@Yoshimiです。
Pythonといえば・・・内包表記!と誰かがいっていていたきがするのですが、「そんなバカな・・・」と思うのは私だけでしょうか?Pythonの現場では日常に内包表記でコーディングされていることも多いので少しでも慣れておくことをおすすめします。
目次
リスト内包表記の基本型
これが内包表記です。
a = [1, 2, 3] b = [x * 10 for x in a] print(b)
て、いきなりですいません。
通常のリスト生成を見てみましょう。
a = [1, 2, 3] b = [] for x in a: b.append(x * 10) print(b)
となります。
基本構文:
[式 for 任意の変数名 in イテレーター]
となります。
さてもう一例みてみましょう。
ex_a1 = [] for i in range(10): ex_a1.append(i) ex_a2 = [i for i in range(10)] print('ex_a1:', ex_a1) print('ex_a2:', ex_a2)
ex_a1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
ex_a2: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
もちろん同じ結果です。
ポイントは前後が逆になっているという部分です。こんなチープな覚え方ですが、でも早く覚えられた方がいいと思うのです、ということで私はそうやって覚えています。ただ、注意があります。
通常のリスト、for文でプログラムするときは
- (空の)リストを用意する
- appendで追加している
です。これはあくまでも基本的な部分なので、応用するときには様々な形・形式に変化しますが、ここを抑えておけば大丈夫だと思っています。
いきなり内包表記を覚えるにも何かメリットあるの?となりますが、しっかりメリットはあります。それな処理速度で内包表記の方が早く処理可能ということです。
基本構文作成
%%timeit extension_1 = [] for i in range(10000): extension_1.append(i)
内包表記で作成
%%timeit comprehension_1= [i for i in range(10000)]
µsはマイクロ秒のことです。基本構文(for)と内包表記では、673 µsと325 µsと倍以上の処理い速度です。データ分析では大量データを扱い、複雑なアルゴリズムを利用し解析を行ったり、そのアルゴリズムを複数扱ったりするなどあるので、処理速度は早いに越したことはありません!ということで内包表記をお勧めします。
ifで条件分岐したリスト内包表記
for文にはif文もよくセットで使われています。そのforとifが使われており条件分岐したリストの内包表記の礼を紹介します。
基本構文:
[式 for 任意の変数名 in イテレーター if 条件式]
# 基本系 odds = [] for i in range(10): if i % 2 == 1: odds.append(i) print(odds)
i / 2 の剰余を計算し、1か否かで条件分岐がされています。これを内包表記で記述するとこうなります。
odds = [i for i in range(10) if i % 2 == 1] print(odds)
結果はもちろんのことながら同じ[1, 3, 5, 7, 9]です。
基本構文(for)に当てはめてみます。
- for i in range(10)
- if i % 2 == 1
- odds.append(i)
というブロックになります。odds =
部分のoddsにappendをつける、append(i)のiを先に記述しています。ただこれだけです。
if else的な処理
三項演算子との組み合わせもみてみます。if~elseもよく使われる構文なので、例外なくチェックしましょう。
基本構文:
[真のときの値 if 条件式 else 偽のときの値 for 任意の変数名 in イテレーター]
まずは、一般的なプログラムをみてみます。i % 2 == 1
の計算を行い、計算結果で異なる結果を返しています。
ab_even = [] for i in range(10): if i % 2 == 1: ab_even.append('aa') else: ab_even.append('bb') print(ab_even)
これを内包表記で記述します。
odd_even = ['aa' if i % 2 == 1 else 'bb' for i in range(10)] print(odd_even)
もちろんのことながら同じ結果です。
if~elseの内包表記は少し、読みにくいかもしれませんが、これも慣れのようです。単純にコード数も短くなっています。
条件式を前に持ってきており、大元のfor部分は後ろにあります。
for文の中のfor文がある内包表記
まず一般的な(???)for文の中にfor文があるプログラム例です。
mx = [[1, 2, 3], [14, 15, 16], [27, 28, 29]] flat = [] for row in mx: for x in row: flat.append(x) print(flat)
内包表記で記述してみます。
flat = [x for row in matrix for x in row] print(flat)
for row in mx
部分が大元のfor文、for x in row
部分は大元の中でのfor文になります。
zip(), enumerate()との組み合わせ
zip()は複数のリストやタプルなどの要素をまとめる関数で、for文でも多く利用されます。相性がいいのです。
- 複数のイテラブルをまとめるzip()
- インデックスとともに値を返すenumerate()
まずはfor文をみてみましょう。
list_1 = ['a', 'b', 'c'] list_2 = ['x', 'y', 'z'] list_zip = [] for s1, s2 in zip(list_1, list_2): list_zip.append((s1, s2)) print(list_zip)
append((s1, s2))で(( ))となっていることに気がつきましたか?リストやタプルなどを格納するときに使いますね。内包表記でも同じです。
list_zip = [(s1, s2) for s1, s2 in zip(list_1, list_2)] print(list_zip)
要素が3つとなっても変わりません。
# 3つ以上も同様 list_3 = ['x', 'y', 'z'] list_zip = [(s1, s2, s3) for s1, s2, s2 in zip(list_1, list_2, list_3)] print(list_zip)
さらにif構文を組み合わせてみます。
まず、内包表記からみてみましょう。
l_zip_if = [(s1, s2) for s1, s2 in zip(l_str1, l_str2) if s1 != 'b'] print(l_zip_if)
l_zip_if =[] for s1, s2 in zip(l_str1, l_str2): if s1 != 'b': l_zip_if.append((s1, s2)) l_zip_if
x.appned(i)と()が基本なので、内包表記で(s1, s2) でセットされていれば、x.append(())となります。
辞書内包表記
辞書(dict型オブジェクト)も内包表記で生成できます。{}で囲み、式の部分でキーと値の2つをキー: 値のように指定します。
基本構文:
{キー: 値 for 任意の変数名 in イテラブルオブジェクト}
keys = ['k1', 'k2', 'k3'] values = [1, 2, 3] d = {k: v for k, v in zip(keys, values)} print(d)
最後に
ここまで備忘録的にまとめてきましたが、やはりコードを書いて行かないことには覚えていくことができないです。コードに触れることがあれば、とにかく内包表記で書いてみる。ということにチャレンジしても良いかもしれませんね。
ただ、条件式が非常に複雑な場合、無理して内包表記にしなくても良いと思っています。