Pythonで、タプルのリストから列を抽出する3種類の方法を比較してみた。
次のような要素が2つのタプルのリストからそれぞれの列を抽出する場合を考える。
抽出前
[('a', 1), ('b', 2), ('c', 3), ('d', 4)]
抽出後
(['a', 'b', 'c', 'd'], [1, 2, 3, 4])
方法1:for文を使った素朴な方法
def func1(samples): list1 = [] list2 = [] for a, b in samples: list1.append(a) list2.append(b) return list1, list2
Pythonのfor文は遅く、この方法は一般的に性能が良くない。
方法2:リスト内包表記を使った方法
def func2(samples): return [a for a, b in samples], [b for a, b in samples]
リスト内包表記を2回使って、それぞれの列を抽出している。
リスト内包表記のforを2回実行するが、方法1よりはましな結果になる。
方法3:zipとリスト内包表記を使った方法
def func3(samples): return [list(tup) for tup in zip(*original)]
一見、何をしているかわからないが、*でリストを展開して複数のタプルにして、zipで1番目の列のタプル、2番目の列のタプル、・・・として取り出している。
こうするとわかりやすい。
[tup for tup in zip(('a',1),('b',2),('c',3),('d',4))]
[('a', 'b', 'c', 'd'), (1, 2, 3, 4)]
この方法は、下記の記事で紹介されていた。
python - Transpose/Unzip Function (inverse of zip)? - Stack Overflow
性能測定
それぞれの方法の性能を比較してみた。
上記の例では要素数が少ないので、要素数をアルファベット26文字分に増やして測定する。
original = [(chr(i), i) for i in range(97, 97+26)] import timeit timeit.timeit(lambda: func1(original)) timeit.timeit(lambda: func2(original)) timeit.timeit(lambda: func3(original))
2.5222641000000294
1.7053095999999641
1.5412319000001844
方法3が一番速いことが確認できた。