2017-10-19 134 views
1

想象一下,我们有一个唯一的整数数组。给定一个整数(N)的列表,我希望能够尽快得到它的索引(I)在数组中。搜索字典与搜索排序的numpy结构数组

我的想法是生成一个对象,给定N返回I。我虽然使用数据类型为(N,I)的结构化数组,并按N排序,或者只是一个带有键N的字典。

这两种方法的搜索速度似乎都与对象的大小无关,这导致我相信它们受开销控制。但是,我发现搜索字典的速度几乎快于搜索结构化数组的10倍,这让我有点惊讶。所以我的问题是:

  1. 为什么字典比我的数组实现更快?
  2. 有没有比这两个更快的替代方法?

MWE:

from __future__ import division 
import numpy as np 
import timeit 

#Time a function 
def Timeme(funct,var,NN=10,NNN=10): 
    for i in xrange(NN): 
     start =timeit.default_timer() 
     for t in xrange(NNN): 
      funct(*var) 
     end =timeit.default_timer() 
     print str(i)+': '+str((end - start)/NNN*1000) 

#Function to build a dictionary   
def mydict(Flist): 
    Mydict=dict() 
    for n,i in Flist: 
     Mydict[n]=i 
    return Mydict 

#Functions to access the data 
def myfd(Mydict,vtest): 
    return Mydict[vtest] 

def myfs(Flist,vtest): 
    n=Flist['N'].searchsorted(vtest) 
    return Flist['I'][n] #Flist[n]['I'] is slower 

#N=100000 
N=100 

# "Allocate empty structured array" 
Flist=np.empty(N,dtype=[('N','i4'),('I','i4')]) 

# "Fill N with randoms and I with sequence" 
Flist['N'] = np.random.randint(N*1000,size=N) 
Flist['I'] = np.arange(N) 

# "Create test value" 
ntest=np.random.randint(N) 
vtest=Flist['N'][ntest] 

# "Sort array on N" 
Flist.sort(order='N') 

# "Make dictionary" 
Mydict=dict(Flist) 

# "Get values"  
nrd=myfd(Mydict,vtest) 
nrs=myfs(Flist,vtest) 

print "Tests OK: " + str(ntest == nrd and ntest == nrs) 

print "\nSearch with Dictionary:" 
Timeme(myfd,[Mydict,vtest],NN=5,NNN=100) 
print "\nSearch directly in Array:" 
Timeme(myfs,[Flist,vtest],NN=5,NNN=100) 

结果:

Tests OK: True 

Search with Dictionary: 
0: 0.000404204885682 
1: 0.000409016848607 
2: 0.000418640774457 
3: 0.000404204885682 
4: 0.000394580959833 

Search directly in Array: 
0: 0.00455211692685 
1: 0.00465798011119 
2: 0.00458580066732 
3: 0.00464354422242 
4: 0.00476384329554 
+0

为什么你使用结构化数组而不仅仅是一个经典的平坦数组? – sascha

+0

当我使用平面阵列进行测试时,它没有改变速度。如果你能想出一种通过使用平面阵列来加速这个过程的方法,请让我知道! – Miguel

+0

@Miguel再次,有*无法*你会比'dict'实现更快的查找。不过,您所做的主要权衡是空间之一。 –

回答

1

这可能部分地由所述的方法调用/函数调用开销进行说明。您的字典搜索功能仅执行单个操作(索引),该操作将转化为对my_dict.__getitem__(key)的调用,而基于阵列的实现最终将调用两种方法,即.searchsorted__getitem__两次。 Python是一种动态语言,函数调用,特别是方法调用(由于方法解析)非常昂贵。

但基本上,您的基于dict的实现应该更好地扩展。 Python dict对象通常是具有恒定时间搜索的高度优化的哈希映射。你的基于数组的实现是二分搜索,所以它是O(log(n))。您将看到一个测试用例,您可以选择最差的情况,即搜索不在数组中的元素。假设searchsorted以对数形式进行扩展,则在看到明显的运行时效果之前,可能需要大幅增加数组的大小(例如,100x,1000x)。

绝对没有机会实现比Python中内置的dict更快的查找。

+0

谢谢你的回答。在我看来,在我看来,字典的主要缺点是1)无法指定数据的大小; 2)与排序数组相比,字典生成速度较慢;和3)阵列方法是矢量化的。我是否误解了这些不利之处和/或是否有办法规避它们? – Miguel

+0

@Miguel你是什么意思“无法指定数据的大小”?但关于2),是的,从数组构建字典比构建另一个数组要慢。关于3)是的,数组支持矢量化操作,但是你不会在这里使用任何...所以不知道为什么这是一个优点... –

+0

由于“无法指定数据的大小”,我认为我指的是您在对主要问题的评论中提到的内容:“您所做的主要交易是空间之一”。关于2)我只注意到我们应该比较dict和array + sort,这就是为什么dict要慢一些,但速度要慢几个数量级。 3)我在我的应用程序中使用矢量化,我只是想要一个简单的(ish)MWE。 – Miguel