Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/348.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 找到与给定向量相似的所有向量的快速方法_Python_Numpy_Optimization_Numeric_Numerical Methods - Fatal编程技术网

Python 找到与给定向量相似的所有向量的快速方法

Python 找到与给定向量相似的所有向量的快速方法,python,numpy,optimization,numeric,numerical-methods,Python,Numpy,Optimization,Numeric,Numerical Methods,通过“相似向量”,我定义了一个向量,它与给定向量在一个位置上的差值为-1或1。但是,如果给定元素的元素为零,则只能相差1。示例: similar_vectors(np.array([0,0,0])) array([[ 1., 0., 0.], [ 0., 1., 0.], [ 0., 0., 1.]]) similar_vectors(np.array([1,0,2,3,0,0,1])) array([[ 0., 0., 2., 3., 0

通过“相似向量”,我定义了一个向量,它与给定向量在一个位置上的差值为-1或1。但是,如果给定元素的元素为零,则只能相差1。示例:

similar_vectors(np.array([0,0,0]))

array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])


similar_vectors(np.array([1,0,2,3,0,0,1]))

array([[ 0.,  0.,  2.,  3.,  0.,  0.,  1.],
       [ 2.,  0.,  2.,  3.,  0.,  0.,  1.],
       [ 1.,  1.,  2.,  3.,  0.,  0.,  1.],
       [ 1.,  0.,  1.,  3.,  0.,  0.,  1.],
       [ 1.,  0.,  3.,  3.,  0.,  0.,  1.],
       [ 1.,  0.,  2.,  2.,  0.,  0.,  1.],
       [ 1.,  0.,  2.,  4.,  0.,  0.,  1.],
       [ 1.,  0.,  2.,  3.,  1.,  0.,  1.],
       [ 1.,  0.,  2.,  3.,  0.,  1.,  1.],
       [ 1.,  0.,  2.,  3.,  0.,  0.,  0.],
       [ 1.,  0.,  2.,  3.,  0.,  0.,  2.]])
我想获得上述
相似向量(vector)
函数的最快实现。它在我的模拟中运行了数百万次,长度为几十次,因此速度至关重要。我对纯numpy解决方案以及其他语言的一些包装都感兴趣。代码可以是并行的

我目前的执行情况如下:

def singleOne(length,positionOne): #generates a vector of len length filled with zeros apart from a single one at positionOne
    arr=np.zeros(length)
    arr[positionOne]=1
    return arr

def similar_vectors(state):
    connected=[]
    for i in range(len(state)):
        if(state[i]!=0):
            connected.append(state-singleOne(state.shape[0],i))       
        connected.append(state+singleOne(state.shape[0],i))
    return np.array(connected)
这是非常缓慢,由于for循环,我不能轻易摆脱

为了便于参考,我附上了1000次执行的
相似向量(vector)

37003函数调用只需0.070秒
排序人:累计时间
ncalls tottime percall cumtime percall文件名:lineno(函数)
1 0.000 0.000 0.070 0.070{内置方法builtins.exec}
1    0.003    0.003    0.070    0.070 :2()
1000 0.035 0.000 0.064 0.000:6(类似的_向量)
11000.007 0.000 0.021 0.000:1(单件)
11000.014 0.000 0.014 0.000{内置方法numpy.core.multiarray.zeros}
2000 0.009 0.000 0.009 0.000{内置方法numpy.core.multiarray.array}
11000.002 0.000 0.002 0.000{“列表”对象的“附加”方法}
1000 0.000 0.000 0.000 0.000{内置方法内置.len}
1 0.000 0.000 0.000 0.000{方法'disable'的''lsprof.Profiler'对象}

这是一种矢量化方法。 您可以创建一个
1s
的对角矩阵和另一个
-1s
的对角矩阵, 然后将前者添加到原始数组,将后者分别添加到原始数组不为
0
的位置。然后使用连接两个nArray:

def similar_vectors(a):
    ones = np.ones(len(a))
    w = np.flatnonzero(a!=0)
    return np.concatenate([np.diag(-ones)[w]+a, np.diag(ones)+a])

样本运行

方法#1

这是一个基于掩蔽的-

def similar_vectors_masking(a):
    n = len(a)
    m = n*2+1
    ar = np.repeat(a[None],len(a)*2,0)
    ar.ravel()[::m] -= 1
    ar.ravel()[n::m] += 1
    mask = np.ones(len(ar),dtype=bool)
    mask[::2] = a!=0
    out = ar[mask]
    return out
样本运行-

In [142]: similar_vectors_masking(np.array([0,0,0]))
Out[142]: 
array([[1, 0, 0],
       [0, 1, 0],
       [0, 0, 1]])

In [143]: similar_vectors_masking(np.array([1,0,2,3,0,0,1]))
Out[143]: 
array([[0, 0, 2, 3, 0, 0, 1],
       [2, 0, 2, 3, 0, 0, 1],
       [1, 1, 2, 3, 0, 0, 1],
       [1, 0, 1, 3, 0, 0, 1],
       [1, 0, 3, 3, 0, 0, 1],
       [1, 0, 2, 2, 0, 0, 1],
       [1, 0, 2, 4, 0, 0, 1],
       [1, 0, 2, 3, 1, 0, 1],
       [1, 0, 2, 3, 0, 1, 1],
       [1, 0, 2, 3, 0, 0, 0],
       [1, 0, 2, 3, 0, 0, 2]])
方法#2

对于零主填充数组,我们最好使用
np复制到精确的输出大小。重复
并再次使用一点掩蔽来增加和减少
1s
,如下所示-

def similar_vectors_repeat(a):
    mask = a!=0
    ra = np.arange(len(a))
    r = mask+1
    n = r.sum()
    ar = np.repeat(a[None],n,axis=0)
    add_idx = r.cumsum()-1
    ar[add_idx,ra] += 1
    ar[(add_idx-1)[mask],ra[mask]] -= 1
    return ar
标杆管理 大型阵列上所有已发布方法的计时-

In [414]: # Setup input array with ~80% zeros
     ...: np.random.seed(0)
     ...: a = np.random.randint(1,5,(5000))
     ...: a[np.random.choice(range(len(a)),int(len(a)*0.8),replace=0)] = 0

In [415]: %timeit similar_vectors(a) # Original soln
     ...: %timeit similar_vectors_flatnonzero_concat(a) # @yatu's soln
     ...: %timeit similar_vectors_v2(a) # @Brenlla's soln
     ...: %timeit similar_vectors_masking(a)
     ...: %timeit similar_vectors_repeat(a)
1 loop, best of 3: 195 ms per loop
1 loop, best of 3: 234 ms per loop
1 loop, best of 3: 231 ms per loop
1 loop, best of 3: 238 ms per loop
10 loops, best of 3: 82.8 ms per loop

只是关于Yatu代码的注释。您可以就地添加并更改
dtype
,以获得大约33%的性能提升:

def similar_vectors_v2(a):
    eye = np.eye(len(a), dtype=a.dtype)
    neg_eye = -(eye[a!=0])
    eye += a
    neg_eye +=a
    return np.concatenate([neg_eye, eye])

另外,对于非常大的输出,去掉
连接
可能有助于使用Numba的两种解决方案

@nb.njit()
def similar_vectors_nb(data):
    n=data.shape[0]
    out=np.empty((n*2,n),dtype=data.dtype)
    ii=0
    for i in range(n):
        if data[i]==0:
            out[ii,:]=data[:]
            out[ii,i]+=1
            ii+=1
        else:
            out[ii,:]=data[:]
            out[ii,i]+=1
            ii+=1

            out[ii,:]=data[:]
            out[ii,i]-=1
            ii+=1
    return out[0:ii,:]

@nb.njit()
def similar_vectors_nb_2(data):
    n=data.shape[0]

    #Determine the final array size
    num=0
    for i in range(n):
        if data[i]==0:
            num+=1
        else:
            num+=2

    out=np.empty((num,n),dtype=data.dtype)
    ii=0
    for i in range(n):
        if data[i]==0:
            out[ii,:]=data[:]
            out[ii,i]+=1
            ii+=1
        else:
            out[ii,:]=data[:]
            out[ii,i]+=1
            ii+=1

            out[ii,:]=data[:]
            out[ii,i]-=1
            ii+=1
    return out
基准测试

#https://github.com/MSeifert04/simple_benchmark
from simple_benchmark import benchmark

np.random.seed(0)
data=[]
for i in range(10,1000,10):
    a = np.random.randint(1,5,(i))
    a[np.random.choice(range(len(a)),int(len(a)*0.5),replace=0)] = 0
    data.append(a)

arguments = arguments = {10*i: data[i] for i in range(len(data))}

b = benchmark([similar_vectors_nb,similar_vectors_nb_2,similar_vectors_yatu,similar_vectors_Brenlla,similar_vectors_repeat_Divakar,similar_vectors_masking_Divakar], arguments, warmups=[similar_vectors_nb,similar_vectors_nb_2])

%matplotlib notebook
b.plot()

谢谢你的回答!这确实导致模拟花费在
相似向量()上的时间减少了约3倍function@WojciechR您测试它的阵列大小是多少?数字的分布是什么样的?每次我的模拟使用这个函数时,向量大小都是20。大多数(>80%)都是零,有时有一个或两个。@WojciechR似乎看到了与您不同的计时。在我的帖子中发布了时间安排。我确实得到了完全不同的时间安排,不包括原始解决方案:360、150、140和50毫秒。您是否能够用实际数据集测试所有发布的性能方法?很高兴看到这些是如何叠加的。当然,下面是我模拟的示例结果,其中对于长度为24的向量,类似的_vectors()被调用了131996次。这是此函数花费的时间(以秒为单位)。我自己的原始解决方案122.473 Brenella 82.033 yatu 47.427 Divakar#2 43.709 Divakar#1 34.770感谢您返回这些结果!那里没有什么意外。此外,如果你的问题已经得到回答,请考虑接受其中之一。更多关于接受意味着什么以及如何接受的信息-。
@nb.njit()
def similar_vectors_nb(data):
    n=data.shape[0]
    out=np.empty((n*2,n),dtype=data.dtype)
    ii=0
    for i in range(n):
        if data[i]==0:
            out[ii,:]=data[:]
            out[ii,i]+=1
            ii+=1
        else:
            out[ii,:]=data[:]
            out[ii,i]+=1
            ii+=1

            out[ii,:]=data[:]
            out[ii,i]-=1
            ii+=1
    return out[0:ii,:]

@nb.njit()
def similar_vectors_nb_2(data):
    n=data.shape[0]

    #Determine the final array size
    num=0
    for i in range(n):
        if data[i]==0:
            num+=1
        else:
            num+=2

    out=np.empty((num,n),dtype=data.dtype)
    ii=0
    for i in range(n):
        if data[i]==0:
            out[ii,:]=data[:]
            out[ii,i]+=1
            ii+=1
        else:
            out[ii,:]=data[:]
            out[ii,i]+=1
            ii+=1

            out[ii,:]=data[:]
            out[ii,i]-=1
            ii+=1
    return out
#https://github.com/MSeifert04/simple_benchmark
from simple_benchmark import benchmark

np.random.seed(0)
data=[]
for i in range(10,1000,10):
    a = np.random.randint(1,5,(i))
    a[np.random.choice(range(len(a)),int(len(a)*0.5),replace=0)] = 0
    data.append(a)

arguments = arguments = {10*i: data[i] for i in range(len(data))}

b = benchmark([similar_vectors_nb,similar_vectors_nb_2,similar_vectors_yatu,similar_vectors_Brenlla,similar_vectors_repeat_Divakar,similar_vectors_masking_Divakar], arguments, warmups=[similar_vectors_nb,similar_vectors_nb_2])

%matplotlib notebook
b.plot()