Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/295.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 将N-dim阵列广播到(N+;1)-dim阵列并在除1个dim以外的所有阵列上求和_Python_Arrays_Numpy_Array Broadcasting - Fatal编程技术网

Python 将N-dim阵列广播到(N+;1)-dim阵列并在除1个dim以外的所有阵列上求和

Python 将N-dim阵列广播到(N+;1)-dim阵列并在除1个dim以外的所有阵列上求和,python,arrays,numpy,array-broadcasting,Python,Arrays,Numpy,Array Broadcasting,假设您有一个形状为(a,b,c)的numpy数组和一个形状为(a,b,c,d)的布尔掩码。 我想将掩码应用于在最后一个轴上迭代的数组,沿着前三个轴对掩码数组求和,并获得长度/形状(d,)的列表(或数组)。 我试着用一个列表来做这件事: Result = [np.sum(Array[Mask[:,:,:,i]], axis=(0,1,2)) for i in range(d)] 它可以工作,但看起来不太像蟒蛇,而且速度也有点慢。 我也试过类似的东西 Array = Array[:,:,:,np.

假设您有一个形状为(a,b,c)的numpy数组和一个形状为(a,b,c,d)的布尔掩码。 我想将掩码应用于在最后一个轴上迭代的数组,沿着前三个轴对掩码数组求和,并获得长度/形状(d,)的列表(或数组)。 我试着用一个列表来做这件事:

Result = [np.sum(Array[Mask[:,:,:,i]], axis=(0,1,2)) for i in range(d)]
它可以工作,但看起来不太像蟒蛇,而且速度也有点慢。 我也试过类似的东西

Array = Array[:,:,:,np.newaxis]
Result = np.sum(Array[Mask], axis=(0,1,2))
但这当然不起作用,因为遮罩沿最后一个轴d的尺寸大于阵列最后一个轴1的尺寸。 此外,考虑每个轴可以具有100或200阶的维数,因此使用<代码> NP重复新的最后一个轴的数组D倍。重复< /代码>将是真正的内存密集型的,并且我希望避免这种情况。 除了列表理解,还有其他更快、更具Python风格的方法吗?

怎么样

Array.reshape(-1)@Mask.reshape(-1,d)
因为你是在前三个轴上求和,所以你也可以合并它们,之后很容易看到操作可以写成矩阵向量积

例如:

a,b,c,d = 4,5,6,7
Mask = np.random.randint(0,2,(a,b,c,d),bool)
Array = np.random.randint(0,10,(a,b,c))
[np.sum(Array[Mask[:,:,:,i]]) for i in range(d)]
# [310, 237, 253, 261, 229, 268, 184]    
Array.reshape(-1)@Mask.reshape(-1,d)
# array([310, 237, 253, 261, 229, 268, 184])

将N维数组广播到匹配的(N+1)维数组的最直接方法是使用:

但是,正如@hpaulj已经指出的,如果不松开尺寸,就不能使用
mask
进行切片
b_arr


假设您只想将元素相加,并且将零相加“不会造成伤害”,您可以简单地将数组和掩码按元素相乘,以保持正确的维度,但是掩码中
False
的元素与相应数组元素的后续
无关:

result = np.sum(b_arr * mask, axis=tuple(range(mask.ndim - 1)))
或者,由于
*
将自动进行广播:

result = np.sum(arr[..., None] * mask, axis=tuple(range(mask.ndim - 1)))
无需首先使用(但仍需匹配维度数量,即使用
arr[…,无]
,而不仅仅是
arr


作为@PaulPanzer,由于您希望对除一个维度以外的所有维度进行汇总,因此可以使用以下方法进一步简化:

对于涉及求和的更奇特的运算,请查看


编辑 广播的好处在于,它将在表达式求值期间创建临时数组

对于您似乎正在处理的数字,我无法使用广播数组,因为我遇到了
MemoryError
,但从时间角度来看,元素相乘可能仍然是一种比您最初建议的更好的方法

或者,如果您追求速度,您可以在Cython或Numba中使用显式循环在较低的级别上实现这一点

下面您可以找到几个基于Numba的解决方案(处理
ravel()
-ed数据):

  • \u vector\u matrix\u product()
    :不使用任何临时数组
  • \u vector\u matrix\u product\u mp()
    :如上所述,但使用并行执行
  • \u vector\u matrix\u product\u sum()
    :使用
    np.sum()
    和并行执行
与任何
列表
-基于理解的解决方案相比,时间安排有所改进:

arr = np.random.randint(0, 100, (256, 256, 256))
mask = np.random.randint(0, 2, (256, 256, 256, 128), dtype=bool)


%timeit np.sum(arr[..., None] * mask, axis=tuple(range(mask.ndim - 1)))
# MemoryError

%timeit arr.ravel() @ mask.reshape(-1, mask.shape[-1])
# MemoryError

%timeit np.array([np.sum(arr * mask[..., i], axis=tuple(range(mask.ndim - 1))) for i in range(mask.shape[-1])])
# 24.1 s ± 105 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit np.array([np.sum(arr[mask[..., i]]) for i in range(mask.shape[-1])])
# 46 s ± 119 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit vector_matrix_product(arr.ravel(), mask.reshape(-1, mask.shape[-1]))
# 408 ms ± 2.12 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit vector_matrix_product(arr.ravel(), mask.reshape(-1, mask.shape[-1]), mode='mp')
# 1.63 s ± 3.58 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit vector_matrix_product(arr.ravel(), mask.reshape(-1, mask.shape[-1]), mode='sum')
# 7.17 s ± 258 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
正如预期的那样,JIT加速的版本是最快的,并且在代码上强制执行并行性并不会提高速度。 还要注意的是,元素相乘的方法比切片快(对于这些基准测试,大约是切片速度的两倍)


编辑2 按照@max9111的建议,先按行循环,然后按cols循环会导致最耗时的循环在连续数据上运行,从而显著提高速度。
没有这个技巧,
\u vector\u matrix\u product\u sum()
\u vector\u matrix\u product\u mp()
将以基本相同的速度运行。

进行
重复的廉价方法是:
np.broadcast\u to(arr[…],None],mask.shape)[mask]
。但结果是1d,丢失了所有“按行”信息。一般来说,每行(最后一个维度)的真实值的数量是不同的;然而,对于我正在使用的数组的大小(通常我有a=b=c=256和d=128),我要么会得到很大的内存消耗,要么当我解决这个问题时,这些解决方案似乎比列表理解慢。这正常吗?还是我遗漏了什么?@Quasark请参见编辑。基本上,是的,广播的内存效率很低,需要一些变通方法来提高速度。通常,此操作需要时间处理您拥有的数字(
mask
本身就有20亿个条目)。循环顺序在代码中是次优的。如果你交换循环顺序,你可以在向量矩阵乘积中得到很大的加速。在我的例子中,从15.3秒下降到193毫秒。并行化版本是不必要的,因为它运行速度比单线程版本慢changes@max9111抢手货我更新了我的答案以反映您的建议。@请参阅最新更新,以获得显著的速度提升,同时提高内存效率。
result2 = arr.ravel() @ mask.reshape(-1, mask.shape[-1])
print(np.all(result == result2))
# True
import numpy as np
import numba as nb


@nb.jit(nopython=True)
def _vector_matrix_product(
        vect_arr,
        mat_arr,
        result_arr):
    rows, cols = mat_arr.shape
    if vect_arr.shape == result_arr.shape:
        for i in range(rows):
            for j in range(cols):
                result_arr[i] += vect_arr[j] * mat_arr[i, j]
    else:
        for i in range(rows):
            for j in range(cols):            
                result_arr[j] += vect_arr[i] * mat_arr[i, j]


@nb.jit(nopython=True, parallel=True)
def _vector_matrix_product_mp(
        vect_arr,
        mat_arr,
        result_arr):
    rows, cols = mat_arr.shape
    if vect_arr.shape == result_arr.shape:
        for i in nb.prange(rows):
            for j in nb.prange(cols):
                result_arr[i] += vect_arr[j] * mat_arr[i, j]
    else:
        for i in nb.prange(rows):
            for j in nb.prange(cols):        
                result_arr[j] += vect_arr[i] * mat_arr[i, j]


@nb.jit(nopython=True, parallel=True)
def _vector_matrix_product_sum(
        vect_arr,
        mat_arr,
        result_arr):
    rows, cols = mat_arr.shape
    if vect_arr.shape == result_arr.shape:
        for i in nb.prange(rows):
            result_arr[i] = np.sum(vect_arr * mat_arr[i, :])
    else:
        for j in nb.prange(cols):
            result_arr[j] = np.sum(vect_arr * mat_arr[:, j])


def vector_matrix_product(
        vect_arr,
        mat_arr,
        swap=False,
        dtype=None,
        mode=None):
    rows, cols = mat_arr.shape
    if not dtype:
        dtype = (vect_arr[0] * mat_arr[0, 0]).dtype
    if not swap:
        result_arr = np.zeros(cols, dtype=dtype)
    else:
        result_arr = np.zeros(rows, dtype=dtype)
    if mode == 'sum':
        _vector_matrix_product_sum(vect_arr, mat_arr, result_arr)
    elif mode == 'mp':
        _vector_matrix_product_mp(vect_arr, mat_arr, result_arr)
    else:
        _vector_matrix_product(vect_arr, mat_arr, result_arr)
    return result_arr


np.random.seed(0)
arr = np.random.randint(0, 100, (2, 3, 4))
mask = np.random.randint(0, 2, (2, 3, 4, 5), dtype=bool)
target = arr.ravel() @ mask.reshape(-1, mask.shape[-1])
print(target)
# [820 723 861 486 408]
result1 = vector_matrix_product(arr.ravel(), mask.reshape(-1, mask.shape[-1]))
print(result1)
# [820 723 861 486 408]
result2 = vector_matrix_product(arr.ravel(), mask.reshape(-1, mask.shape[-1]), mode='mp')
print(result2)
# [820 723 861 486 408]
result3 = vector_matrix_product(arr.ravel(), mask.reshape(-1, mask.shape[-1]), mode='sum')
print(result3)
# [820 723 861 486 408]
arr = np.random.randint(0, 100, (256, 256, 256))
mask = np.random.randint(0, 2, (256, 256, 256, 128), dtype=bool)


%timeit np.sum(arr[..., None] * mask, axis=tuple(range(mask.ndim - 1)))
# MemoryError

%timeit arr.ravel() @ mask.reshape(-1, mask.shape[-1])
# MemoryError

%timeit np.array([np.sum(arr * mask[..., i], axis=tuple(range(mask.ndim - 1))) for i in range(mask.shape[-1])])
# 24.1 s ± 105 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit np.array([np.sum(arr[mask[..., i]]) for i in range(mask.shape[-1])])
# 46 s ± 119 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit vector_matrix_product(arr.ravel(), mask.reshape(-1, mask.shape[-1]))
# 408 ms ± 2.12 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit vector_matrix_product(arr.ravel(), mask.reshape(-1, mask.shape[-1]), mode='mp')
# 1.63 s ± 3.58 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit vector_matrix_product(arr.ravel(), mask.reshape(-1, mask.shape[-1]), mode='sum')
# 7.17 s ± 258 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)