2013-09-21 30 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功能還是這是一個熊貓功能?

+0

我敢打賭,你從操作員導入methodcaller \\ df2 ['a']。map(methodcaller(「startswith」,「t」))'會明顯更快。 – Veedrac

+0

@TimPietzcker;它不使用內置的map(在這種情況下會更慢)。 – Veedrac

+0

@Veedrac:我明白了;我只是想知道'map'的第二個參數是在哪裏: –

回答

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 

讓我們比較一下,他們才能使索引對象的時間 - 無論是Serieslist - 它需要使用該對象索引到DataFrame產生的時間。例如,可以將列表製作得很快,但在將其用作索引之前,需要將其內部轉換爲Seriesndarray或其他東西,因此在那裏添加了額外的時間。

首先,對於一個小幀:

>>> 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是贏,但差異是微不足道的。但速度並不快:如果我們手動將listcomp變成arraySeries,該怎麼辦?

>>> %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

您可以將它作爲文檔的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)! –

相關問題