矢量化形式,以获取大于参考

时间:2018-04-22 10:49:06

标签: python numpy vectorization

...而且该引用来自一个单独的矩阵。

这个问题是前面回答的问题的扩展,其中引用元素直接来自与之比较的同一列。一些聪明的排序和引用排序的索引似乎解决了那个。

原始问题和新问题都提出了广播。我在 n ~ 3000 附近内存不足,需要另一个数量级更大。

目标(生产级)缩放定义:

为了让提议的解决方案在 [SPACE] - 和 [TIME] -domains <登记/> 我们假设 n = 50000; m = 20; k = 50; a = np.random.rand( n, m ); ...

我现在对更通用的形式感兴趣,其中参考值来自另一个参考值矩阵。

原始问题:

Vectorized pythonic way to get count of elements greater than current element

新问题:我们可以编写一个矢量化形式来执行以下角色吗?

函数接收2个2-d数组作为输入。

A = n x m
B = k x m

并返回

C = k x m.

C[i,j] A[:,j] (仅j - 列)中大于<的观察比例强> B[i,j]

这是我用于循环实现的令人尴尬的慢速双倍。

import numpy as np

n = 100
m = 20
k = 50
a = np.random.rand(n,m)
b = np.random.rand(k,m)
c = np.zeros((k,m))

for j in range(0,m): #cols
   for i in range(0,k):  # rows
     r = b[i,j]  
     c[i,j] = ( ( a[:,j] > r).sum() ) / (n) 

2 个答案:

答案 0 :(得分:3)

方法#1

我们可以再次使用this solution中讨论的argsort技巧,但有点扭曲。我们将第二个数组连接到第一个数组,然后执行argsort-ing。我们需要对连接数组和第二个数组使用argsort并得到我们想要的输出。实现看起来像这样 -

ab = np.vstack((a,b))
len_a, len_b = len(a), len(b)
b_argoffset = b.argsort(0).argsort(0)
total_args = ab.argsort(0).argsort(0)[-len_b:]
out = len_a - total_args + b_argoffset

<强>解释

  1. 连接第二个数组,其值将被计算到第一个数组中。
  2. 现在,由于我们要追加,我们稍后会在第一个数组长度结束后获得其索引位置。
  3. 我们使用一个argsort来获取第二个数组w.r.t到整个连接数组的相对位置,再使用一个argsort来追溯原始顺序的那些索引。
  4. 我们需要为第二个数组重复双argsort-ing,以便补偿连接。
  5. 这些索引针对b中的每个元素进行比较:a[:,j] > b[i,j]。现在,这些索引顺序是基于0的,即更接近0的索引表示a[:,j]中的元素数量大于当前元素b[i,j],因此计数更多,反之亦然。因此,我们需要从最终输出的a[:,j]长度中减去这些索引。
  6. 方法#1 - 改进

    我们会使用array-assignment进一步优化它,再次受到来自同一解决方案的Approach #2的启发。那么,那些arg输出:b_argoffsettotal_args可以替代地计算,如此 -

    def unqargsort(a):
        n,m = a.shape     
        idx = a.argsort(0)
        out = np.zeros((n,m),dtype=int)
        out[idx, np.arange(m)] = np.arange(n)[:,None]
        return out
    
    b_argoffset = unqargsort(b)
    
    total_args = unqargsort(ab)[-len_b:]
    

    方法#2

    我们还可以利用searchsorted来实现完全不同的方法 -

    k,m = b.shape
    sidx = a.argsort(0)
    out = np.empty((k,m), dtype=int)
    for i in range(m): #cols
        out[:,i] = np.searchsorted(a[:,i], b[:,i],sorter=sidx[:,i])
    out = len(a) - out  
    

    <强>解释

    1. 我们获取a的每列的排序顺序索引。
    2. 然后,使用这些索引来了解我们如何将值b放入带有a的已排序searcshorted中。这使我们与Approach#1中的步骤#3,4的输出相同。
    3. 请注意,这些方法可以为我们提供支持。因此,对于最终输出,将由此获得的输出除以n

答案 1 :(得分:2)

我认为你可以使用广播:

c = (a[:,None,:] > b).mean(axis=0)

演示:

In [207]: n = 5

In [208]: m = 3

In [209]: a = np.random.randint(10, size=(n,m))

In [210]: b = np.random.randint(10, size=(n,m))

In [211]: c = np.zeros((n,m))

In [212]: a
Out[212]:
array([[2, 2, 8],
       [5, 0, 8],
       [2, 5, 7],
       [4, 4, 4],
       [2, 6, 7]])

In [213]: b
Out[213]:
array([[3, 6, 8],
       [2, 7, 5],
       [8, 9, 2],
       [9, 8, 7],
       [2, 7, 2]])

In [214]: for j in range(0,m): #cols
     ...:    for i in range(0,n):  # rows
     ...:      r = b[i,j]
     ...:      c[i,j] = ( ( a[:,j] > r).sum() ) / (n)
     ...:
     ...:

In [215]: c
Out[215]:
array([[0.4, 0. , 0. ],
       [0.4, 0. , 0.8],
       [0. , 0. , 1. ],
       [0. , 0. , 0.4],
       [0.4, 0. , 1. ]])

In [216]: (a[:,None,:] > b).mean(axis=0)
Out[216]:
array([[0.4, 0. , 0. ],
       [0.4, 0. , 0.8],
       [0. , 0. , 1. ],
       [0. , 0. , 0.4],
       [0.4, 0. , 1. ]])

检查:

In [217]: ((a[:,None,:] > b).mean(axis=0) == c).all()
Out[217]: True