Python 更快的numpy.argmax/argmin替代品,速度较慢

Python 更快的numpy.argmax/argmin替代品,速度较慢,python,numpy,Python,Numpy,我在Python中使用了很多argmin和argmax 不幸的是,这个功能非常慢 我已经四处搜索过了,我能找到的最好的地方是: 不幸的是,这个解决方案的速度仍然只有np.max的一半,我想我应该能够找到和np.max一样快的东西 x = np.random.randn(10) %timeit np.argmax( x ) 10000 loops, best of 3: 21.8 us per loop %timeit fastest_argmax( x ) 10000 loops,

我在Python中使用了很多argmin和argmax

不幸的是,这个功能非常慢

我已经四处搜索过了,我能找到的最好的地方是:

不幸的是,这个解决方案的速度仍然只有np.max的一半,我想我应该能够找到和np.max一样快的东西

x = np.random.randn(10)
%timeit np.argmax( x )
10000 loops, best of 3: 21.8 us per loop

%timeit fastest_argmax( x )    
10000 loops, best of 3: 20.8 us per loop
请注意,我将此应用于Pandas DataFrame Groupby

例如

其中数据如下所示:

grp2[ 'ODDS' ].head()
Out[60]: 
EVENT_ID   SELECTION_ID        
104601100  4367029       682508    3.05
                         682509    3.15
                         682510    3.25
                         682511    3.35
           5319660       682512    2.04
                         682513    2.08
                         682514    2.10
                         682515    2.12
                         682516    2.14
           5510310       682520    4.10
                         682521    4.40
                         682522    4.50
                         682523    4.80
                         682524    5.30
           5559264       682526    5.00
                         682527    5.30
                         682528    5.40
                         682529    5.50
                         682530    5.60
           5585869       682533    1.96
                         682534    1.97
                         682535    1.98
                         682536    2.02
                         682537    2.04
           6064546       682540    3.00
                         682541    2.74
                         682542    2.76
                         682543    2.96
                         682544    3.05
104601200  4916112       682548    2.64
                         682549    2.68
                         682550    2.70
                         682551    2.72
                         682552    2.74
           5315859       682557    2.90
                         682558    2.92
                         682559    3.05
                         682560    3.10
                         682561    3.15
           5356995       682564    2.42
                         682565    2.44
                         682566    2.48
                         682567    2.50
                         682568    2.52
           5465225       682573    1.85
                         682574    1.89
                         682575    1.91
                         682576    1.93
                         682577    1.94
           5773661       682588    5.00
                         682589    4.40
                         682590    4.90
                         682591    5.10
           6013187       682592    5.00
                         682593    4.20
                         682594    4.30
                         682595    4.40
                         682596    4.60
104606300  2489827       683438    4.00
                         683439    3.90
                         683440    3.95
                         683441    4.30
                         683442    4.40
           3602724       683446    2.16
                         683447    2.32
Name: ODDS, Length: 65, dtype: float64

你能发一些代码吗?以下是我电脑上的结果:

x = np.random.rand(10000)
%timeit np.max(x)
%timeit np.argmax(x)
输出:

100000 loops, best of 3: 7.43 µs per loop
100000 loops, best of 3: 11.5 µs per loop

事实证明,
np.argmax
的速度非常快,但只适用于本机numpy数组。对于外来数据,几乎所有的时间都花在转换上:

In [194]: print platform.architecture()
('64bit', 'WindowsPE')

In [5]: x = np.random.rand(10000)
In [57]: l=list(x)
In [123]: timeit numpy.argmax(x)
100000 loops, best of 3: 6.55 us per loop
In [122]: timeit numpy.argmax(l)
1000 loops, best of 3: 729 us per loop
In [134]: timeit numpy.array(l)
1000 loops, best of 3: 716 us per loop
我称您的函数为“低效”,因为它首先将所有内容转换为列表,然后对其进行2次迭代(实际上是3次迭代+列表构造)

我打算提出这样的建议,只重复一次:

def imax(seq):
    it=iter(seq)
    im=0
    try: m=it.next()
    except StopIteration: raise ValueError("the sequence is empty")
    for i,e in enumerate(it,start=1):
        if e>m:
            m=e
            im=i
    return im
但是,您的版本速度更快,因为它迭代了很多次,但使用的是C语言,而不是Python代码。C的速度就快多了——即使考虑到转换也花费了大量时间:

In [158]: timeit imax(x)
1000 loops, best of 3: 883 us per loop
In [159]: timeit fastest_argmax(x)
1000 loops, best of 3: 575 us per loop

In [174]: timeit list(x)
1000 loops, best of 3: 316 us per loop
In [175]: timeit max(l)
1000 loops, best of 3: 256 us per loop
In [181]: timeit l.index(0.99991619010758348)  #the greatest number in my case, at index 92
100000 loops, best of 3: 2.69 us per loop
因此,进一步加快这一速度的关键知识是了解序列中数据的本机格式(例如,是否可以省略转换步骤或使用/写入该格式的其他本机功能)


顺便说一句,您可能会通过使用
聚合(max\u fn)
而不是
agg([max\u fn])

来获得一些加速,对于那些为返回第一个最小值的索引的简短无numpy代码段:

def argmin(a):
    return min(range(len(a)), key=lambda x: a[x])
a = [6, 5, 4, 1, 1, 3, 2]
argmin(a)  # returns 3

我建议查看numpy源代码,了解他们是如何做到这一点的。这里有一个链接:试着模仿他们的方法。“低效”甚至还没有开始描述你所谓的“最快的”\u argmax。在我的笔记本电脑上
%timeit np.argmax(x)
with
x.shape==10
只花了1个我们,你的numpy版本是什么?你链接的代码主要是关于正确处理
axis
参数的。实际的
argmin/max
由定义的函数执行。请注意,它是模板化的代码,有关syntax.ivan的描述,请参阅。您对更高效的版本有什么建议吗?您的终端是什么?普通控制台字体无法打印mu字母。@ivan_pozdeev-大多数现代控制台模拟器至少有一些(通常是完全的)unicode支持(甚至是xterm)。谢谢ivan的帮助!mhm,只需将
min
更改为
max
即可转换为
argmax
?mhm,是的,它可以提供非常优雅的解决方案。谢谢你,先生。
In [158]: timeit imax(x)
1000 loops, best of 3: 883 us per loop
In [159]: timeit fastest_argmax(x)
1000 loops, best of 3: 575 us per loop

In [174]: timeit list(x)
1000 loops, best of 3: 316 us per loop
In [175]: timeit max(l)
1000 loops, best of 3: 256 us per loop
In [181]: timeit l.index(0.99991619010758348)  #the greatest number in my case, at index 92
100000 loops, best of 3: 2.69 us per loop
def argmin(a):
    return min(range(len(a)), key=lambda x: a[x])
a = [6, 5, 4, 1, 1, 3, 2]
argmin(a)  # returns 3