有效的numpy子矩阵视图

有效的numpy子矩阵视图,numpy,scipy,hungarian-algorithm,Numpy,Scipy,Hungarian Algorithm,我希望应用于numpy矩阵C的许多子集,这些子集由列表的叉积行索引,列索引。目前,我可以看到以下选项: 双切片: linear_sum_assignment(C[row_ind,:][:,col_ind]) 问题:每个子集操作两个副本 高级切片通过: 问题:每个子集一个副本,np.ix效率低下(分配n x n矩阵) 更新:正如@hpaulj所指出的,np.ix实际上并没有分配nxn矩阵,但不知何故它仍然比1慢 问题:不适用于线性求和分配 因此,没有任何选择是令人满意的 理想情况下需要的是

我希望应用于numpy矩阵
C
的许多子集,这些子集由列表的叉积
行索引
列索引
。目前,我可以看到以下选项:

  • 双切片:

    linear_sum_assignment(C[row_ind,:][:,col_ind])
    
  • 问题:每个子集操作两个副本

  • 高级切片通过:

  • 问题:每个子集一个副本,
    np.ix
    效率低下(分配n x n矩阵)

    更新:正如@hpaulj所指出的,
    np.ix
    实际上并没有分配nxn矩阵,但不知何故它仍然比1慢

  • 问题:不适用于
    线性求和分配

    因此,没有任何选择是令人满意的

    理想情况下需要的是能够使用矩阵
    C
    和两个一维掩码分别为行和列指定子矩阵视图,因此这样的视图可以传递给
    线性求和赋值
    。对于另一个
    linear\u sum\u assignment
    调用,我会快速调整掩码,但不会修改或复制/子集整个矩阵

    numpy是否已经提供了类似的功能


    处理同一个大矩阵的多个子矩阵最有效的方法是什么(尽可能少的副本/内存分配)?

    使用列表/数组索引数组的不同方法时间大致相同。它们都生成副本,而不是视图

    比如说

    In [99]: arr = np.ones((1000,1000),int)
    In [100]: id1=np.arange(0,1000,10)
    In [101]: id2=np.arange(0,1000,20)
    
    In [105]: timeit arr[id1,:][:,id2].shape
    52.5 µs ± 243 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    In [106]: timeit arr[np.ix_(id1,id2)].shape
    66.5 µs ± 47.4 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    
    相反,如果我使用切片(在本例中选择相同的元素),我会得到一个
    视图
    ,这要快得多:

    In [107]: timeit arr[::10,::20].shape
    661 ns ± 18.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
    
    ix
    不创建(m,n)数组;它返回调整后的1d数组的元组。这相当于

    In [108]: timeit arr[id1[:,None], id2].shape
    54.5 µs ± 1.6 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    
    时间差主要是由于额外的一层函数调用

    您的
    scipy
    链接有一个[源]链接:

    优化。线性求和赋值
    函数使用
    成本矩阵
    创建一个
    \u匈牙利
    对象。这将创建一个副本,并通过搜索和操纵其值来解决问题

    使用文档示例:

    In [110]: optimize.linear_sum_assignment(cost)
    Out[110]: (array([0, 1, 2], dtype=int32), array([1, 0, 2], dtype=int32))
    
    它所做的是创建一个状态对象:

    In [111]: H=optimize._hungarian._Hungary(cost)
    In [112]: vars(H)
    Out[112]: 
    {'C': array([[4, 1, 3],
            [2, 0, 5],
            [3, 2, 2]]),
     'Z0_c': 0,
     'Z0_r': 0,
     'col_uncovered': array([ True,  True,  True], dtype=bool),
     'marked': array([[0, 0, 0],
            [0, 0, 0],
            [0, 0, 0]]),
     'path': array([[0, 0],
            [0, 0],
            [0, 0],
            [0, 0],
            [0, 0],
            [0, 0]]),
     'row_uncovered': array([ True,  True,  True], dtype=bool)}
    
    它迭代

    In [113]: step=optimize._hungarian._step1
    In [114]: while step is not None:
         ...:     step = step(H)
         ...:     
    
    由此产生的状态是:

    In [115]: vars(H)
    Out[115]: 
    {'C': array([[1, 0, 1],
            [0, 0, 4],
            [0, 1, 0]]),
     'Z0_c': 0,
     'Z0_r': 1,
     'col_uncovered': array([False, False, False], dtype=bool),
     'marked': array([[0, 1, 0],
            [1, 0, 0],
            [0, 0, 1]]),
     'path': array([[1, 0],
            [0, 0],
            [0, 0],
            [0, 0],
            [0, 0],
            [0, 0]]),
     'row_uncovered': array([ True,  True,  True], dtype=bool)}
    
    从标记的
    数组中提取解决方案

    In [116]: np.where(H.marked)
    Out[116]: (array([0, 1, 2], dtype=int32), array([1, 0, 2], dtype=int32))
    
    总成本是这些价值的总和:

    In [122]: cost[np.where(H.marked)]
    Out[122]: array([1, 2, 2])
    
    但最终状态下
    C
    数组的成本为0:

    In [124]: H.C[np.where(H.marked)]
    Out[124]: array([0, 0, 0])
    

    因此,即使您给
    optimize.linear\u sum\u assignment
    的子矩阵是一个视图,搜索仍然包含一个副本。搜索空间和时间随着成本矩阵的大小而显著增加。

    匈牙利算法在时间/复杂性方面将很容易控制子集准备,对吗?这里的
    C
    是什么?我并不完全清楚
    C
    与匈牙利算法输入之间的关系。更多关于匈牙利算法和python的信息,请访问(和链接)。屏蔽创建的是副本,而不是视图。为了显著改进
    scipy
    munkres
    实现,我必须对选定的步骤(尤其是第一次非零搜索)使用
    cython
    。@hpaulj适用于所有情况还是特定情况?对于大型矩阵,scipy应该优于它(至少我的IPM做到了;但它与scipy中使用的不同)。最初我认为您试图从头开始实现
    munkres
    ,因为(如
    scipy
    code中所示),它有一个2d成本矩阵和4个1d掩蔽阵列。但进一步阅读,它看起来像你在更高的层次上工作,掩盖了一个更大的矩阵。只要
    row\u ind
    col\u ind
    是列表或数组,而不是切片,您就会将一个副本传递给
    scipy
    函数。奇怪的是,
    np.ix
    与双切片相比没有任何好处。我希望单切片应该总是更快!你知道为什么不是这样吗?
    In [124]: H.C[np.where(H.marked)]
    Out[124]: array([0, 0, 0])