当前位置: 首页 > 面试题库 >

每行的Bin元素-NumPy的矢量化2D Bincount

姬选
2023-03-14
问题内容

我有一个带有整数值的NumPy数组。矩阵的值的范围是从0到矩阵中的最大元素(换句话说,其中显示的所有数字是从0到最大数据元素)。我需要建立有效的(
有效的手段是快速的完全矢量化解决方案 )来搜索每一行中的元素数量,并根据矩阵值对它们进行编码。

我找不到类似的问题,或者以某种方式帮助解决了这个问题。

所以如果我data在输入中有这个:

# shape is (N0=4, m0=4) 
1   1   0   4
2   4   2   1
1   2   3   5
4   4   4   1

所需的输出是:

# shape(N=N0, m=data.max()+1):
1   2   0   0   1   0
0   1   2   0   1   0
0   1   1   1   0   1
0   1   0   0   3   0

我知道如何解决这个问题,方法是简单地逐一data 迭代的每一行中计算唯一值,然后结合考虑data数组中所有可能值的结果。

使用NumPy进行矢量化处理时,关键问题在于逐个搜索每个数字的速度很慢,并且假设存在很多唯一数字,但这并不是有效的解决方案。通常N,唯一数字计数都很大(顺便说一句,N似乎比唯一数字计数大)。

有人有好主意吗?)


问题答案:

嗯,这基本上是做np.bincount与做1D阵列。但是,我们需要在每行上迭代使用它(简单地考虑一下)。为了使其向量化,我们可以使每行偏移最大数量。想法是每行具有不同的bin,以使它们不受编号相同的其他行元素的影响。

因此,实施将是-

# Vectorized solution
def bincount2D_vectorized(a):    
    N = a.max()+1
    a_offs = a + np.arange(a.shape[0])[:,None]*N
    return np.bincount(a_offs.ravel(), minlength=a.shape[0]*N).reshape(-1,N)

样品运行-

In [189]: a
Out[189]: 
array([[1, 1, 0, 4],
       [2, 4, 2, 1],
       [1, 2, 3, 5],
       [4, 4, 4, 1]])

In [190]: bincount2D_vectorized(a)
Out[190]: 
array([[1, 2, 0, 0, 1, 0],
       [0, 1, 2, 0, 1, 0],
       [0, 1, 1, 1, 0, 1],
       [0, 1, 0, 0, 3, 0]])

Numba调整

我们可以带来numba进一步的提速。现在,numba允许进行一些调整。

  • 首先,它允许JIT编译。

  • 同样,最近他们引入了实验性parallel功能,该功能可自动并行化已知具有并行语义的功能中的操作。

  • 最终的调整将prange用作的替代range。文档指出,这可以并行运行循环,类似于OpenMP并行循环和Cython的prange。prange在较大的数据集上表现良好,这可能是由于设置并行工作所需的开销。

因此,通过这两项新的调整以及njit针对非Python模式的调整,我们将获得三种变体-

# Numba solutions
def bincount2D_numba(a, use_parallel=False, use_prange=False):
    N = a.max()+1
    m,n = a.shape
    out = np.zeros((m,N),dtype=int)

    # Choose fucntion based on args
    func = bincount2D_numba_func0
    if use_parallel:
        if use_prange:
            func = bincount2D_numba_func2
        else:
            func = bincount2D_numba_func1
    # Run chosen function on input data and output
    func(a, out, m, n)
    return out

@njit
def bincount2D_numba_func0(a, out, m, n):
    for i in range(m):
        for j in range(n):
            out[i,a[i,j]] += 1

@njit(parallel=True)
def bincount2D_numba_func1(a, out, m, n):
    for i in range(m):
        for j in range(n):
            out[i,a[i,j]] += 1

@njit(parallel=True)
def bincount2D_numba_func2(a, out, m, n):
    for i in prange(m):
        for j in prange(n):
            out[i,a[i,j]] += 1

为了完整起见并在以后进行测试,该循环版本为-

# Loopy solution
def bincount2D_loopy(a):
    N = a.max()+1
    m,n = a.shape
    out = np.zeros((m,N),dtype=int)
    for i in range(m):
        out[i] = np.bincount(a[i], minlength=N)
    return out

运行时测试

情况1 :

In [312]: a = np.random.randint(0,100,(100,100))

In [313]: %timeit bincount2D_loopy(a)
     ...: %timeit bincount2D_vectorized(a)
     ...: %timeit bincount2D_numba(a, use_parallel=False, use_prange=False)
     ...: %timeit bincount2D_numba(a, use_parallel=True, use_prange=False)
     ...: %timeit bincount2D_numba(a, use_parallel=True, use_prange=True)
10000 loops, best of 3: 115 µs per loop
10000 loops, best of 3: 36.7 µs per loop
10000 loops, best of 3: 22.6 µs per loop
10000 loops, best of 3: 22.7 µs per loop
10000 loops, best of 3: 39.9 µs per loop

案例2:

In [316]: a = np.random.randint(0,100,(1000,1000))

In [317]: %timeit bincount2D_loopy(a)
     ...: %timeit bincount2D_vectorized(a)
     ...: %timeit bincount2D_numba(a, use_parallel=False, use_prange=False)
     ...: %timeit bincount2D_numba(a, use_parallel=True, use_prange=False)
     ...: %timeit bincount2D_numba(a, use_parallel=True, use_prange=True)
100 loops, best of 3: 2.97 ms per loop
100 loops, best of 3: 3.54 ms per loop
1000 loops, best of 3: 1.83 ms per loop
100 loops, best of 3: 1.78 ms per loop
1000 loops, best of 3: 1.4 ms per loop

案例3:

In [318]: a = np.random.randint(0,1000,(1000,1000))

In [319]: %timeit bincount2D_loopy(a)
     ...: %timeit bincount2D_vectorized(a)
     ...: %timeit bincount2D_numba(a, use_parallel=False, use_prange=False)
     ...: %timeit bincount2D_numba(a, use_parallel=True, use_prange=False)
     ...: %timeit bincount2D_numba(a, use_parallel=True, use_prange=True)
100 loops, best of 3: 4.01 ms per loop
100 loops, best of 3: 4.86 ms per loop
100 loops, best of 3: 3.21 ms per loop
100 loops, best of 3: 3.18 ms per loop
100 loops, best of 3: 2.45 ms per loop

看起来这些numba变体的效果非常好。从三个变体中选择一个将取决于输入数组的形状参数,并在某种程度上取决于其中的唯一元素的数量。



 类似资料:
  • 问题内容: 和是先前已计算出的相同长度的NumPy向量,因此需要创建一个新向量。在它只是为了说明什么是想要的元素索引。 在这种情况下是否需要for循环? 问题答案: 您可能认为这会起作用: 但事实并非如此:您实际上无法以这种方式在numpy中进行递归(因为numpy计算了整个RHS,然后将其分配给LHS)。 因此,除非您可以提出该公式的非递归版本,否则您将陷入显式循环:

  • 问题内容: 假设我有一个numpy数组: 我有一个对应的“向量”: 我如何沿每一行进行减法或除法运算,所以结果是: 长话短说:如何使用对应于每一行的1D标量数组在2D数组的每一行上执行操作? 问题答案: 干得好。您只需要与广播结合使用(或):

  • 例如,对于 我想得到 有没有办法不用for循环或使用? 编辑:实际数据由1000行组成,每行100个元素,每个元素的范围从1到365。最终目标是确定有重复的行的百分比。这是一个作业问题,我已经解决了(用for循环),但我只是想知道是否有更好的方法来做它与Numpy。

  • 问题内容: 我有2 x 4和3 x 4的矩阵。我想找到各行之间的欧几里得距离,并在最后得到2 x 3的矩阵。这是一个带for循环的代码,它针对所有b行向量计算a中每个行向量的欧式距离。在不使用for循环的情况下该如何做? 问题答案: 只需在正确的位置使用:

  • 问题内容: 我出现了不想要的行为,即,它更改了进入原始函数的参数的数据类型。我最初的问题是关于一般情况的,我将使用这个新问题来提出更具体的情况。 (为什么要问第二个问题?为了说明问题,我创建 了一个关于更具体案例 的问题-从具体问题到更一般的问题总是比较容易。而且我 单独 创建了这个问题,因为我认为这是有助于保持一般情况以及对一般情况的答复(应该找到),而不会因为考虑解决任何特定问题而受到“污染”

  • 问题内容: 我在2D数组上恒定大小的移动窗口上执行操作。我可以实现高效的类似于矢量化的操作来执行此操作而无需在Python中循环吗?我目前的结构看起来像这样 这些评论可以 吃 留在这个问题暗指矢量化这种操作这种可能性,但没有进一步的细节矢量索引/切片在numpy的/SciPy的? 问题答案: 如果可以表达功能 作为一个线性算,你可以使用SciPy的的signal.convolve2d函数来做到这一