2013-09-21 24 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)! –

相关问题