list内包表記系男子

たまには真面目に

櫻井研らしくたまには真面目な記事を。巷ではこんな言葉が流行っているのをご存知ですか?

list内包表記系男子

list内包表記とは、pythonの特徴的なリスト表記の方法です。具体例を見てみましょう。0から20までの偶数を要素として持つリストを作ってみます。

# リスト内包表記を使わない
range(0, 21, 2)

# リスト内包表記
[2 * i for i in range(11)]

このくらい単純な配列であれば、range関数をうまく使うことでリスト内包表記を使わなくても大丈夫です。しかし次の場合はどうでしょうか。

問題

# この配列を生成
[[0,   4,   8,   12,  16,  20,  24,  28,  32,  36],
 [1,   5,   9,   13,  17,  21,  25,  29,  33,  37],
 [2,   6,   10,  14,  18,  22,  26,  30,  34,  38],
 [3,   7,   11,  15,  19,  23,  27,  31,  35,  39],
 [40,  44,  48,  52,  56,  60,  64,  68,  72,  76],
 [41,  45,  49,  53,  57,  61,  65,  69,  73,  77],
 [42,  46,  50,  54,  58,  62,  66,  70,  74,  78],
 [43,  47,  51,  55,  59,  63,  67,  71,  75,  79],
 [80,  84,  88,  92,  96,  100, 104, 108, 112, 116],
 [81,  85,  89,  93,  97,  101, 105, 109, 113, 117],
 [82,  86,  90,  94,  98,  102, 106, 110, 114, 118],
 [83,  87,  91,  95,  99,  103, 107, 111, 115, 119],
 [120, 124, 128, 132, 136, 140, 144, 148, 152, 156],
 [121, 125, 129, 133, 137, 141, 145, 149, 153, 157],
 [122, 126, 130, 134, 138, 142, 146, 150, 154, 158],
 [123, 127, 131, 135, 139, 143, 147, 151, 155, 159],
 [160, 164, 168, 172, 176, 180, 184, 188, 192, 196],
 [161, 165, 169, 173, 177, 181, 185, 189, 193, 197],
 [162, 166, 170, 174, 178, 182, 186, 190, 194, 198],
 [163, 167, 171, 175, 179, 183, 187, 191, 195, 199]]

言葉で説明するならば、次のような2次元配列です。

  • n個の数字が重複なしで出てくる(n=200)
  • 各要素の配列はdim個(dim=10)
  • 各要素の配列の要素はstep刻みで増えていく(step=4)

これはn個の時系列データからstep刻みでデータを取って間引いたdim次元のデータを取り出すときのインデックスになります。これをコードで表すとどうなるか見てみましょう。

回答例

step = 4
dim = 10
n = 200
length = n / step / dim

# リスト内包表記を使わない
indexs = []
for l in range(length):
    for s in range(step):
        index = range(l * step * dim + s, (l + 1) * step * dim + s, step)
        indexs.append(index)

# リスト内包表記
indexs = [range(l * step * dim + s, (l + 1) * step * dim + s, step)
          for l in range(length) for s in range(step)]

どうですか。リスト内包表記では5行も必要なところ、リスト内包表記では2行で書けます。(pep8に従って途中で改行しているが、つながっているので実質1行)簡潔なコードは、デバッグするときも拡張するときも優秀です。

さらに実行時間にも影響があります。それぞれのコードを100万回繰り返したときの実行時間は以下の通りです。

リスト内包表記なし 25.2(s)
リスト内包表記あり 21.8(s)

リスト内包表記のほうが実行時間が短いです。Deep Learningでは実行時間がネックになることが多々あるため、こうした実行時間削減の工夫は重要です。

でも注意が必要

リスト内包表記は確かに短く速いコードが書けます。しかし「何を目的としたどんなコードなのか」が少し分かりにくくなっています。そのため、何回も呼び出されたり、リスト内包表記でもシンプルに書けたりするときは積極的に使うべきですが、1回しか呼び出されないしリスト内包表記だと見にくくなる場合は無理に使う必要はないと思います。for文にするかリスト内包表記にするかを一瞬で判断できるようになれば、今日から君もPythonista!