首页 > 解决方案 > 用列表理解解决方案替换 numpy isin

问题描述

一段时间以来,我一直试图让我的彗星程序的慢速部分运行得更快。我有一行如下

s1=np.isin(secondsplit,file2new)

secondsplit 是一个 numpy 数组。平均而言,它的大小约为 600 x 600,并且充满了浮动。file2new 是一个浮点数列表,通常大小为 300-700。

我在堆栈交换论坛上讨论了这个问题,并提供了一个使用列表理解和将 file2new 转换为集合以加快速度的解决方案。我的小规模测试代码有效,列表理解速度提高了 5 倍。当我扩大规模并将彗星程序更改为以相同方式使用列表时,问题就出现了。随着数据量的增加,这段代码的运行速度要慢 4 倍!

有人建议我应该做一个大规模的例子让人们看看我所做的。我现在在这里发布它,因为今天帮助论坛中没有很多人。

import numpy  as np
import timeit

mylist=[]
for i in range (0,700):
    mylist.append(i)


myarray = np.empty((600, 600))
for x in range (0,600):
    for y in range (0,600):
        p=y+(x*0.25)
        myarray[y,x]=p

myarray=np.asarray(myarray)


# object here is to produce an array of 600 x 600 with True/False values depending 
# on whether elements in myarray are in mylist.
# The question is - what ia thw fastest way to do so? Note that all array values and
# mylist values are floats. There must be an exact match. One 
# can't use isclose because it is slower 
    
    
# Example 1 - using isin (loop of 10000 used for timing illustration)

starttime = timeit.default_timer()

for i in range (0,10):
 s1=np.isin(myarray,mylist)

print (s1[0][0:10])
print (s1[1][0:10])
print (s1[2][0:10])

print("The time difference is :", timeit.default_timer() - starttime)
   
# Example 2- using list comprehension and conversion of mylist to a set (it runs way 
# slower without set conversion)

def quickisin(array,mylist):

    mylist=set(mylist)
    
    p=[]
    t=len(array)
    
    for j in range (0,t):
        h=array[j]
        s1 = [item in mylist for item in h]
        p.append(s1)
    
    q=np.asarray(p)
     
    return q    

starttime = timeit.default_timer()

for i in range (0,10):
    s1=quickisin(myarray,mylist)

 
print (s1[0][0:10])
print (s1[1][0:10])
print (s1[2][0:10])

print("The time difference is :", timeit.default_timer() - starttime)

如您所见,代码的第一部分设置了示例变量。我使用 myarray 和 mylist 让人们更容易理解。myarray 是 600x 600 数组, mylist 是大小为 700 的列表,所有浮点数。

预期的输出应该是一个与 myarray 大小相同但具有 True 和 False 值的 numpy 数组。每第 4 项为 True。

运行时生成的输出如下。

runfile('C:/A/untitled7.py', wdir='C:/A')
[ True False False False  True False False False  True False]
[ True False False False  True False False False  True False]
[ True False False False  True False False False  True False]
The time difference is : 0.5151480000000674
[ True False False False  True False False False  True False]
[ True False False False  True False False False  True False]
[ True False False False  True False False False  True False]
The time difference is : 2.0399810999999772

我的问题,如果你选择接受它,你的挑战是使用列表理解来使 quickisin 函数比使用 numpy 的 isin 更快。最快的列表理解解决方案的奖品是吹牛。

请注意,数组中的浮点数和列表之间的匹配必须准确,因此排除了使用 isclose 的可能性。

感谢您

标签: pythonlistperformancelist-comprehensionisin

解决方案


[更新]:在编写代码的过程中,我在测试的生成中犯了一个错误myarray(暂时减小了大小),忘记重置一些值。现在已修复。我的变体现在显示出不那么令人印象深刻的结果,但速度更快。

我同意 qouify 的观点,即列表推导无济于事。因此,我展示了另一种 numpy 解决方案,它比原始解决方案更快,但可能需要更多内存。

基本思想是排序mylist,使用二分法np.searchsorted找到最接近的值小于或等于,最后检查它是否真的相等。

我已将其附加为quickisin2函数:

import numpy  as np
import timeit

mylist=[]
for i in range (0,700):
    mylist.append(i)


myarray = np.empty((600, 600))
for x in range (0,600):
    for y in range (0,600):
        p=y+(x*0.25)
        myarray[y,x]=p

myarray=np.asarray(myarray)


# object here is to produce an array of 600 x 600 with True/False values depending 
# on whether elements in myarray are in mylist.
# The question is - what ia thw fastest way to do so? Note that all array values and
# mylist values are floats. There must be an exact match. One 
# can't use isclose because it is slower 
    
    
# Example 1 - using isin (loop of 10000 used for timing illustration)

starttime = timeit.default_timer()

for i in range (0,10):
 s1=np.isin(myarray,mylist)

print (s1[0][0:10])
print (s1[1][0:10])
print (s1[2][0:10])

print("The time difference is :", timeit.default_timer() - starttime)
   
# Example 2- using list comprehension and conversion of mylist to a set (it runs way 
# slower without set conversion)

def quickisin(array,mylist):

    mylist=set(mylist)
    
    p=[]
    t=len(array)
    
    for j in range (0,t):
        h=array[j]
        s1 = [item in mylist for item in h]
        p.append(s1)
    
    q=np.asarray(p)
     
    return q


def quickisin2(array,mylist):

    mylist=np.sort(mylist)
    shape = myarray.shape
    myarrayflat = np.reshape(myarray, -1)
    foundindices = np.searchsorted(mylist, myarrayflat)
    foundindices[foundindices >= mylist.shape[0]] = 0
    
    foundarray = mylist[foundindices]
    mask = myarrayflat == foundarray

    return np.reshape(mask, shape)



starttime = timeit.default_timer()

for i in range (0,10):
    s1=quickisin(myarray,mylist)

 
print (s1[0][0:10])
print (s1[1][0:10])
print (s1[2][0:10])

print("The time difference is :", timeit.default_timer() - starttime)




starttime = timeit.default_timer()

for i in range (0,10):
    s1=quickisin2(myarray,mylist)

 
print (s1[0][0:10])
print (s1[1][0:10])
print (s1[2][0:10])

print("The time difference is :", timeit.default_timer() - starttime)
print("The time difference is :", timeit.default_timer() - starttime)

在我的机器上使用 Python 3.7 64bit 我得到(最后一个是新方法):

[ True False False False  True False False False  True False]
[ True False False False  True False False False  True False]
[ True False False False  True False False False  True False]
The time difference is : 0.26915568
[ True False False False  True False False False  True False]
[ True False False False  True False False False  True False]
[ True False False False  True False False False  True False]
The time difference is : 1.052180994
[ True False False False  True False False False  True False]
[ True False False False  True False False False  True False]
[ True False False False  True False False False  True False]
The time difference is : 0.1133786440000002

推荐阅读