2013-09-21 26 views
7

は、この例では、インデックス作成についてあり:python pandas:なぜマップが高速ですか?パンダマニュアルの

In [653]: criterion = df2['a'].map(lambda x: x.startswith('t')) 
In [654]: df2[criterion] 

その後、ウェスは書いた:マップのアプローチが高速である理由

**# equivalent but slower** 
In [655]: df2[[x.startswith('t') for x in df2['a']]] 

誰もここには少し説明できますか?これはPythonの機能ですか、これはpandasの機能ですか?

+0

私はあなたが 'from operator import methodcaller \\ df2 ['a']。map(methodcaller(" startswith "、" t "))'がかなり早いでしょう。 – Veedrac

+0

@TimPietzcker;それは組み込みの 'map'を使用していません(この場合は遅くなります)。 – Veedrac

+0

@Veedrac:そうだよ。私はちょうど 'map'の第2引数がどこにあるのか疑問に思っていました。 –

答えて

17

特定の状況で動作が異なる可能性のある実装の詳細を測定していることが多いため、Pythonで特定のやり方で「すべきである」ということはあまり重大ではありません。その結果、人々は何がより速くすべきかを推測するとき、しばしば(通常?)間違っています。たとえば、私はmapが実際には遅くなることがわかります。このセットアップコードを使用する:

import numpy as np, pandas as pd 
import random, string 

def make_test(num, width): 
    s = [''.join(random.sample(string.ascii_lowercase, width)) for i in range(num)] 
    df = pd.DataFrame({"a": s}) 
    return df 

はのは、彼らがインデックスオブジェクトを作成するのにかかる時間を比較してみましょう - かどうか Seriesまたは list - そしてそれは DataFrameへのインデックスにそのオブジェクトを使用するのにかかる結果の時間。たとえば、リストを作成するのは高速ですが、インデックスとして使用する前に、内部で Seriesまたは ndarrayなどに変換する必要があります。余分な時間が追加されます。

まず、小さなフレーム用:

>>> df = make_test(10, 10) 
>>> %timeit df['a'].map(lambda x: x.startswith('t')) 
10000 loops, best of 3: 85.8 µs per loop 
>>> %timeit [x.startswith('t') for x in df['a']] 
100000 loops, best of 3: 15.6 µs per loop 
>>> %timeit df['a'].str.startswith("t") 
10000 loops, best of 3: 118 µs per loop 
>>> %timeit df[df['a'].map(lambda x: x.startswith('t'))] 
1000 loops, best of 3: 304 µs per loop 
>>> %timeit df[[x.startswith('t') for x in df['a']]] 
10000 loops, best of 3: 194 µs per loop 
>>> %timeit df[df['a'].str.startswith("t")] 
1000 loops, best of 3: 348 µs per loop 

とlistcompが最速である。この場合、

lambdaを経由すると直接 str.startswithを使用するよりも遅くなる可能性が高いので、実際にはあまりにも私をあまりにも驚かせるわけではありませんが、推測するのは本当に難しいです。 10が十分小さいので、おそらくまだ Seriesの設定コストのようなものを測定しています。大きなフレームではどうなりますか?

>>> df = make_test(10**5, 10) 
>>> %timeit df['a'].map(lambda x: x.startswith('t')) 
10 loops, best of 3: 46.6 ms per loop 
>>> %timeit [x.startswith('t') for x in df['a']] 
10 loops, best of 3: 27.8 ms per loop 
>>> %timeit df['a'].str.startswith("t") 
10 loops, best of 3: 48.5 ms per loop 
>>> %timeit df[df['a'].map(lambda x: x.startswith('t'))] 
10 loops, best of 3: 47.1 ms per loop 
>>> %timeit df[[x.startswith('t') for x in df['a']]] 
10 loops, best of 3: 52.8 ms per loop 
>>> %timeit df[df['a'].str.startswith("t")] 
10 loops, best of 3: 49.6 ms per loop 

、インデックスとして使用した場合mapが勝っているように差が限界ですが、今では、と思われます。しかしそれほど速くはない:私たちが手作業でリストコンプをarrayまたはSeriesにするとどうなるだろうか?

>>> %timeit df[np.array([x.startswith('t') for x in df['a']])] 
10 loops, best of 3: 40.7 ms per loop 
>>> %timeit df[pd.Series([x.startswith('t') for x in df['a']])] 
10 loops, best of 3: 37.5 ms per loop 

今、listcompが再び優勝!

結論:誰が知っていますか?しかし、timeitの結果がなければ何も信じてはいけません。あなたがあなたが思っているものをテストしているかどうか尋ねなければなりません。

+0

あなたはこれをdocsのためのPRとして提出できますか:https://github.com/pydata/pandas/issues/3871、新しいセクションを作成しようとしています – Jeff

+0

+ 1良い答え。 –

+0

これはおそらく、Wesが[startswithはスライシングよりも遅いと述べている]ドキュメントのセクションです(http://stackoverflow.com/questions/13270888/why-is-startswith-slower-than-slicing)! –

関連する問題