向量化numpys卷

时间:2019-04-28 04:47:43

标签: python arrays numpy

我现在正在从数组中进行一些采样,并且我正在使用布尔掩码来计算位于给定质心某个半径范围内的值的数量。我想知道是否有人可以向我展示如何对我的方法进行矢量化处理,而不是使用python循环遍历列表元素。

我有一个称为center的1d numpy数组,其中center的每个元素都是一个以1d numpy数组形式给出的质心。我还创建了一个布尔数组蒙版,它位于数组中间(在下面的代码中为'centre_Y','centre_X'),我将其从数组中间滚动到质心位置。然后,使掩码变得稀疏,因为我从中采样的实际数组(下面代码中的“ item”)稀疏。然后,我计算掩码下的非零元素的数量。下面是我的代码

for item in data:

    ### Do stuff

    for radius in radii:

        ### Do stuff

        # roll mask to centroid and count the number of elements within radius
        for centroid in centres:

            # roll in the vertical direction to centroid y coordinate
            mask_roll_y = np.roll(mask,centroid[0]-centre_Y,axis=0)

            # roll in the horizontal direction to centroid x coordinate and make sparse
            roll_mask = sparse.csr_matrix(np.roll(mask_roll_y,centroid[1]-centre_X,axis=1))

            # apply sparse mask to sparse data and count number of nonzero elements below
            number_count.append(np.count_nonzero(item[roll_mask]))

现在,上面的代码可以正常工作,并给出我想要的结果。我的问题是,经过一些计时后,“中心重心”循环需要大约0.4秒的时间来计算(对于一个数组中的数据,使用50个半径,每个半径取100个样本),这是最遥远的我的代码很耗时。我需要对大约100个数据集执行此操作,每个数据集中有大约1000个数组,我想比以前更多地获取半径和样本。因此,我想知道如何消除“中心重心”循环?我尝试了以下可怕的代码

number_count.append(np.count_nonzero(item[sparse.csr_matrix(np.roll(np.roll(mask,centres[:][0]-centre_Y,axis=0),centres[:][1]-centre_X,axis=1))]))

我尝试对中心进行矢量化处理,但这只是给了我一个长度为len(centres)的零计数列表。有人可以帮忙吗?

编辑:

我忘记指定我需要将面具从中心滚动到某些位置以保持面具的周期性。直接在布尔数组的边缘附近应用蒙版不会将蒙版环绕到平行边缘,并且我不希望蒙版具有截止值。这是因为我要应用蒙版的数据来自具有周期性边界条件的模拟。

更新:

因此,我设法使用Numba编写了一段代码,从而消除了对掩码的需求,而且速度非常快。它包含一个二维的numpy数组,一个要采样的位置(质心)列表以及一个要采样的半径。代码遍历这些位置,并在距每个位置的半径距离内搜索非零元素。边界会定期处理。代码的某些方面是多余的,我敢肯定它可以做得更好,更通用,但是可以满足我的需要。感谢那些为问题提供意见的人。

@nb.njit(fastmath=True)
def nonzero_count(array,positions,radius):

    stored_values = list()
    y,x = array.shape

    for k in range(len(positions)):

        pos_y,pos_x = positions[k][0],positions[k][1]
        nonzero = 0

        # this section handles the periodic boundary conditions
        if pos_y+radius+1>y:
            # recenter pos_y so it is given a negative value
            aa = y-(pos_y+radius+1)
            # iterate around new pos_y, from -'ve to +'ve
            yy = (aa-radius,aa+radius+1)
        else:
            aa = pos_y
            yy = (pos_y-radius,pos_y+radius+1)

        if pos_x+radius+1>x:
            # recenter pos_x so it is given a negative value
            bb = x-(pos_x+radius+1)
            # iterate around new pos_x, from -'ve to +'ve
            xx = (bb-radius,bb+radius+1)
        else:
            bb = pos_x
            xx = (pos_x-radius,pos_x+radius+1)

        # this section handles the count
        for i in range(yy[0],yy[1]):
            for j in range(xx[0],xx[1]):
                # check nonzero elements lie within radius
                if array[i,j] != 0 and (bb-j)**2+(aa-i)**2<=radius**2:
                    nonzero += 1

        stored_values.append(nonzero)

    return stored_values

2 个答案:

答案 0 :(得分:1)

我建议使用numba包来加快这些循环的速度,看来这对您的用例来说是完美的。只需执行以下操作,很可能需要进行一些重构:

import numba as nb

@nb.njit(nopython=True)
def slow_code():
    # roll in the vertical direction to centroid y coordinate
    mask_roll_y = np.roll(mask,centroid[0]-centre_Y,axis=0)

    # roll in the horizontal direction to centroid x coordinate and make sparse
    roll_mask = sparse.csr_matrix(np.roll(mask_roll_y,centroid[1]-centre_X,axis=1))

    # apply sparse mask to sparse data and count number of nonzero elements below
    number_count.append(np.count_nonzero(item[roll_mask]))

for item in data:

    ### Do stuff

    for radius in radii:

        ### Do stuff

        # roll mask to centroid and count the number of elements within radius

        for centroid in centres:
            slow_code()

答案 1 :(得分:0)

如果遮罩是稀疏的,最好先将其转换为稀疏然后滚动。这将大大提高您的速度。现在,在循环中将矩阵转换为稀疏矩阵,这既昂贵又不利于优势。

观察:

import numpy as np
import scipy.sparse as sparse

zero_matrix = np.zeros((1024, 1024))
%timeit np.roll(zero_matrix, (10, 10))
>>1000 loops, best of 3: 1.36 ms per loop

sparse_matrix = sparse.random(1024, 1024, density = 0.001)
%timeit np.roll(sparse_matrix, (10, 10))
>>100000 loops, best of 3: 15.4 µs per loop