Python 矢量化numpys滚动

Python 矢量化numpys滚动,python,arrays,numpy,Python,Arrays,Numpy,我现在正在从一个数组中进行采样,我使用一个布尔掩码来计算给定质心的某个半径内的值的数量。我想知道是否有人能告诉我如何将我的方法矢量化,而不是使用python循环来循环列表中的元素 我有一个称为centers的1d numpy数组,其中centers的每个元素都是作为1d numpy数组给定的质心。我还创建了一个布尔掩码,它位于数组的中间(‘CyrErthy’,‘CyrRyxx’,在下面的代码中),我从数组的中间滚动到质心位置。然后我将掩码设置为稀疏,因为我从中采样的实际数组(“下面代码中的项”)

我现在正在从一个数组中进行采样,我使用一个布尔掩码来计算给定质心的某个半径内的值的数量。我想知道是否有人能告诉我如何将我的方法矢量化,而不是使用python循环来循环列表中的元素

我有一个称为centers的1d numpy数组,其中centers的每个元素都是作为1d numpy数组给定的质心。我还创建了一个布尔掩码,它位于数组的中间(‘CyrErthy’,‘CyrRyxx’,在下面的代码中),我从数组的中间滚动到质心位置。然后我将掩码设置为稀疏,因为我从中采样的实际数组(“下面代码中的项”)是稀疏的。然后我计算掩码下非零元素的数量。下面是我的代码

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(中心)计数中的零列表。有人能帮忙吗

编辑:

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

更新:

因此,我设法用Numba编写了一段代码,消除了对掩码的需要,而且速度非常快。它采用2d 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
@nb.njit(fastmath=True)
def非零计数(阵列、位置、半径):
存储的_值=列表()
y、 x=array.shape
对于范围内的k(透镜(位置)):
位置y,位置x=位置[k][0],位置[k][1]
非零=0
#本节处理周期性边界条件
如果位置y+半径+1>y:
#重新居中位置,使其为负值
aa=y-(位置y+半径+1)
#围绕新位置迭代,从-'ve到+'ve
yy=(aa半径,aa+半径+1)
其他:
aa=位置y
yy=(位置y-半径,位置y+半径+1)
如果位置x+半径+1>x:
#重新居中pos_x,使其为负值
bb=x-(位置x+半径+1)
#围绕新的pos_x进行迭代,从-'ve到+'ve
xx=(bb半径,bb+半径+1)
其他:
bb=位置x
xx=(位置x-半径,位置x+半径+1)
#本节处理计数
对于范围内的i(yy[0],yy[1]):
对于范围内的j(xx[0],xx[1]):
#检查非零元素是否位于半径内

如果数组[i,j]!=0和(bb-j)**2+(aa-i)**2我建议使用
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()

如果遮罩是稀疏的,最好先将其转换为稀疏,然后滚动它;这将给你显著的加速。当您现在这样做时,您正在将矩阵转换为循环中的稀疏,这既昂贵又否定了其优点

注意:

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

您确定使用
sparse
对您有帮助吗?即使是简单的情况下,
项[roll\u mask]
也比使用密集数组的等效项慢得多。即使是动态转换到数组也会更快,
item.A[roll\u mask.A]
@hpaulj Sparse在我有200个或更多大小为1024x1024或更大的数据数组并且每个数组中可能只有10^3个非零元素的情况下肯定会有所帮助。通过使用稀疏阵列,在存储200个1024x1024阵列时,我节省了大约1.6GB的内存。我将做一些计时转换为稠密并应用遮罩,看看有什么区别。但是,我认为降低计算速度的主要问题是python循环,不是吗?谢谢你的回答。我很久以前就尝试过使用numba包,但即使在阅读了文档之后,要让包装器真正工作还是有一大堆问题。我觉得很挑剔。我现在就试试看它是否管用,但我希望不用这个软件包就可以离开。再次感谢您的帮助。它可能很挑剔,但它可以按数量级优化您的代码(尤其是像这样的循环)。您只需要了解ctypes及其附带的限制。我以前尝试过numba软件包,对您的代码稍作修改,将centers列表、mask、center_Y、center_X和item作为参数传递给slow_code函数。似乎它不喜欢在我的代码中作为“item”传入的稀疏数组,因为我一直收到以下错误:“此错误可能是由以下参数引起的:-参数4:无法确定Numba类型”“我将尝试使用.todense()将项从稀疏转换为完整,并在完整数组usin上应用掩码。”