12

zip(*G)[0]私は(そして他の場所でzip(*G)[1]は、異なるGで) を含むいくつかのコードを持っていました。Gタプルのリストです。これは、Gの各タプルの最初の(または一般的にzip(*G)[n]はth番目の)要素のリストをタプルとして返すことです。n-1例えば、

>>> G = [(1, 2, 3), ('a', 'b', 'c'), ('you', 'and', 'me')]
>>> zip(*G)[0]
(1, 'a', 'you')
>>> zip(*G)[1]
(2, 'b', 'and')

zipこれはかなり賢い方法ですが、問題はPython3ではイテレータであるため機能しないことです。さらに、2to3はそれを修正するほど賢くはありません。したがって、明らかな解決策はを使用することlist(zip(*G))[0]ですが、それは私に考えさせられました:おそらくこれを行うためのより効率的な方法があります。zipが作成するすべてのタプルを作成する必要はありません。nGの各タプル のth要素が必要です。

これを行うためのより効率的ですが、同様にコンパクトな方法はありますか?標準ライブラリの何でも大丈夫です。私のユースケースでは、Gの各タプルは少なくとも長さnになるため、zipが最小の長さのタプルで停止する場合を心配する必要はありません(つまり、zip(*G)[n]常に定義されます)。

zipそうでない場合は、でラップすることに固執すると思いますlist()

(PS、私はこれが不必要な最適化であることを知っています。私はただ興味がありますすべてです)

アップデート:

誰かが気にする場合に備えて、私はt0, t1, t2 = zip(*G)オプションで行きました。まず、これにより、データに意味のある名前を付けることができます。私Gは実際には長さ2のタプル(分子と分母を表す)で構成されています。リスト内包表記はzipよりもわずかに読みやすくなりますが、この方法の方がはるかに優れています(ほとんどの場合、zipはリスト内包表記で繰り返し処理したリストであるため、これにより物事がフラットになります)。

第二に、@thewolfと@SvenMarnachの優れた回答で指摘されているように、この方法はリストが小さいほど高速です。私のGは実際にはほとんどの場合大きくありません(そしてそれが大きい場合、これは間違いなくコードのボトルネックにはなりません!)。

a, *b, c = Gしかし、これを行うには、私が知らなかったPython 3の新機能など、予想以上に多くの方法がありました。

4

3 に答える 3

18

リスト内包表記を使用できます

[x[0] for x in G]

またoperator.itemgetter()

from operator import itemgetter
map(itemgetter(0), G)

またはシーケンスの解凍

[x for x, y, z in G]

編集:Python 3.2でも、さまざまなオプションのタイミングについての私の見解は次のとおりです。

from operator import itemgetter
import timeit

G = list(zip(*[iter(range(30000))] * 3))

def f1():
    return [x[0] for x in G]
def f2():
    return list(map(itemgetter(0), G))
def f3():
    return [x for x, y, z in G]
def f4():
    return list(zip(*G))[0]
def f5():
    c0, *rest = zip(*G)
    return c0
def f6():
    c0, c1, c2 = zip(*G)
    return c0
def f7():
    return next(zip(*G))

for f in f1, f2, f3, f4, f5, f6, f7:
    print(f.__name__, timeit.timeit(f, number=1000))

私のマシンでの結果:

f1 0.6753780841827393
f2 0.8274149894714355
f3 0.5576457977294922
f4 0.7980241775512695
f5 0.7952430248260498
f6 0.7965989112854004
f7 0.5748469829559326

コメント:

  1. 10000トリプルのリストを使用して実際の処理時間を測定し、関数呼び出しのオーバーヘッド、名前の検索などを無視できるようにしました。そうしないと、結果に深刻な影響を及ぼします。

  2. 関数はリストまたはタプルを返します–特定のソリューションにとってより便利なものは何でも。

  3. オオカミの答えと比較して、からの冗長な呼び出しを削除tuple()f4()(式の結果はすでにタプルになっています)、f7()最初の列を抽出するためだけに機能する関数を追加しました。

予想通り、リスト内包表記は、やや一般的ではないものと一緒に、最速f7()です。

別の編集:コードを適切な場所に適合させた、3列ではなく10列の結果を次に示します。

f1 0.7429649829864502
f2 0.881648063659668
f3 1.234360933303833
f4 1.92038893699646
f5 1.9218590259552002
f6 1.9172680377960205
f7 0.6230220794677734
于 2012-09-15T19:27:17.463 に答える
14

少なくともPython2.7での最速の方法は

t0,t1,t2=zip(*G) for SMALLER lists and [x[0] for x in G] in general

テストは次のとおりです。

from operator import itemgetter

G = [(1, 2, 3), ('a', 'b', 'c'), ('you', 'and', 'me')]

def f1():
   return tuple(x[0] for x in G)

def f2():
   return tuple(map(itemgetter(0), G))

def f3():
    return tuple(x for x, y, z in G)     

def f4():
    return tuple(list(zip(*G))[0])

def f5():
    t0,*the_rest=zip(*G)
    return t0

def f6():
    t0,t1,t2=zip(*G)
    return t0                

cmpthese.cmpthese([f1,f2,f3,f4,f5,f6],c=100000) 

結果:

    rate/sec     f4     f5     f1     f2     f3     f6
f4   494,220     -- -21.9% -24.1% -24.3% -26.6% -67.6%
f5   632,623  28.0%     --  -2.9%  -3.0%  -6.0% -58.6%
f1   651,190  31.8%   2.9%     --  -0.2%  -3.2% -57.3%
f2   652,457  32.0%   3.1%   0.2%     --  -3.0% -57.3%
f3   672,907  36.2%   6.4%   3.3%   3.1%     -- -55.9%
f6 1,526,645 208.9% 141.3% 134.4% 134.0% 126.9%     --

結果がリストであるかどうかを気にしない場合は、リスト内包表記の方が高速です。

リストサイズが可変の、より拡張されたベンチマークは次のとおりです。

from operator import itemgetter
import time
import timeit 
import matplotlib.pyplot as plt

def f1():
   return [x[0] for x in G]

def f1t():
   return tuple([x[0] for x in G])

def f2():
   return tuple([x for x in map(itemgetter(0), G)])

def f3():
    return tuple([x for x, y, z in G])    

def f4():
    return tuple(list(zip(*G))[0])

def f6():
    t0,t1,t2=zip(*G)
    return t0     

n=100    
r=(5,35)
results={f1:[],f1t:[],f2:[],f3:[],f4:[],f6:[]}    
for c in range(*r):
    G=[range(3) for i in range(c)] 
    for f in results.keys():
        t=timeit.timeit(f,number=n)
        results[f].append(float(n)/t)

for f,res in sorted(results.items(),key=itemgetter(1),reverse=True):
    if f.__name__ in ['f6','f1','f1t']:
        plt.plot(res, label=f.__name__,linewidth=2.5)
    else:    
        plt.plot(res, label=f.__name__,linewidth=.5)

plt.ylabel('rate/sec')
plt.xlabel('data size => {}'.format(r))  
plt.legend(loc='upper right')
plt.show()

これにより、小さいデータサイズ(5〜35)のこのプロットが生成されます。

小さい

そして、より広い範囲(25から250)のこの出力:

大きい

f1リスト内包表記が最速であることがわかります。f6タプルをf1t返すのに最も速い場所としての取引場所。

于 2012-09-15T21:45:06.190 に答える
6

非常に賢いPython3の唯一の方法は、スター付きの割り当てまたは拡張された反復可能な解凍を使用することです。

>>> G = [(1, 2, 3), ('a', 'b', 'c'), ('you', 'and', 'me')]
>>> items_I_want,*the_rest=zip(*G)
>>> items_I_want
(1, 'a', 'you')
>>> the_rest
[(2, 'b', 'and'), (3, 'c', 'me')]

両方のコードを記述しているので、明示的な解凍を使用できます(Python2とPython3で機能します)。

>>> z1,z2,z3=zip(*G)
>>> z1
(1, 'a', 'you')
>>> z2
(2, 'b', 'and')
>>> z3
(3, 'c', 'me')
于 2012-09-15T20:56:41.653 に答える