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