Python 是否有一个函数可以将NumPy的广播规则应用于形状列表并返回最终形状?

Python 是否有一个函数可以将NumPy的广播规则应用于形状列表并返回最终形状?,python,numpy,array-broadcasting,Python,Numpy,Array Broadcasting,这不是一个关于广播如何工作的问题,也就是说,它不是广播的复制品 我只想找到一个可以将NumPy的广播规则应用于形状列表并返回最终形状的函数,例如: >>>广播形状[6],[4,2,3,1],[2,1,1] [4, 2, 3, 6] 谢谢 In [120]: shapes = [6], [4, 2, 3, 1], [2, 1, 1] In [121]: arrs = np.broadcast_arrays(*[np.empty(sh

这不是一个关于广播如何工作的问题,也就是说,它不是广播的复制品

我只想找到一个可以将NumPy的广播规则应用于形状列表并返回最终形状的函数,例如:

>>>广播形状[6],[4,2,3,1],[2,1,1] [4, 2, 3, 6] 谢谢

In [120]: shapes = [6], [4, 2, 3, 1], [2, 1, 1]                                 
In [121]: arrs = np.broadcast_arrays(*[np.empty(shape,int) for shape in shapes])
     ...:                                                                       
In [122]: [a.shape for a in arrs]                                               
Out[122]: [(4, 2, 3, 6), (4, 2, 3, 6), (4, 2, 3, 6)]

In [124]: np.lib.stride_tricks._broadcast_shape(*[np.empty(shape,int) for shape 
     ...: in shapes])                                                           
Out[124]: (4, 2, 3, 6)

In [131]: np.broadcast(*[np.empty(shape,int) for shape in shapes]).shape        
Out[131]: (4, 2, 3, 6)
第二次速度要快很多,4.79µs比42.4µs。第三个稍微快一点

正如我第一次评论的那样,我从广播数组开始,并查看了代码。那我就,广播,然后再广播

第二次速度要快很多,4.79µs比42.4µs。第三个稍微快一点


正如我第一次评论的那样,我从广播数组开始,并查看了代码。这是一个简单的实现,以防有人需要它,它可能有助于理解广播。不过我更喜欢使用NumPy函数

def广播形状*形状: max_rank=max[lenshape代表形状中的形状] shapes=[[1]*最大秩-lenshape+shape中的shape的shape] 最终形状=[1]*最大秩 对于形状中的形状: 对于dim,形状中的大小: 如果是尺寸!=1: 最终尺寸=最终形状[尺寸] 如果最终尺寸=1: 最终形状[尺寸]=尺寸 elif最终尺寸!=尺寸: 提升值错误无法广播这些形状 返回最终形状 编辑

我将此函数与其他几个答案进行了比较,结果证明它是最快的编辑,Paul Panzer编写了一个更快的函数,请参见他的答案,我将其添加到下面的列表中:

%timeit bs_pp(*shapes) # Peter Panzer's answer
2.33 µs ± 10.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit broadcast_shapes1(*shapes)  # this answer
4.21 µs ± 11.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit broadcast_shapes2(*shapes) # my other answer with shapes.max(axis=0)
12.8 µs ± 67.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit broadcast_shapes3(*shapes) # user2357112's answer
18 µs ± 26.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit broadcast_shapes4(*shapes) # hpaulj's answer
18.1 µs ± 263 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

这里是一个简单的实现,以防有人需要它,它可能有助于理解广播。不过我更喜欢使用NumPy函数

def广播形状*形状: max_rank=max[lenshape代表形状中的形状] shapes=[[1]*最大秩-lenshape+shape中的shape的shape] 最终形状=[1]*最大秩 对于形状中的形状: 对于dim,形状中的大小: 如果是尺寸!=1: 最终尺寸=最终形状[尺寸] 如果最终尺寸=1: 最终形状[尺寸]=尺寸 elif最终尺寸!=尺寸: 提升值错误无法广播这些形状 返回最终形状 编辑

我将此函数与其他几个答案进行了比较,结果证明它是最快的编辑,Paul Panzer编写了一个更快的函数,请参见他的答案,我将其添加到下面的列表中:

%timeit bs_pp(*shapes) # Peter Panzer's answer
2.33 µs ± 10.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit broadcast_shapes1(*shapes)  # this answer
4.21 µs ± 11.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit broadcast_shapes2(*shapes) # my other answer with shapes.max(axis=0)
12.8 µs ± 67.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit broadcast_shapes3(*shapes) # user2357112's answer
18 µs ± 26.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit broadcast_shapes4(*shapes) # hpaulj's answer
18.1 µs ± 263 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

我在NumPy文档中没有看到任何关于这个的内容。可以向每个目标形状广播单个0维数组,然后相互广播所有结果:

def broadcast_shapes(*shapes):
    base = numpy.array(0)
    broadcast1 = [numpy.broadcast_to(base, shape) for shape in shapes]
    return numpy.broadcast(*broadcast1).shape

这样可以避免为大型形状分配大量内存。但是,需要创建阵列感觉有点傻。

我在NumPy文档中没有看到任何关于这方面的内容。可以向每个目标形状广播单个0维数组,然后相互广播所有结果:

def broadcast_shapes(*shapes):
    base = numpy.array(0)
    broadcast1 = [numpy.broadcast_to(base, shape) for shape in shapes]
    return numpy.broadcast(*broadcast1).shape

这样可以避免为大型形状分配大量内存。但是,需要创建阵列感觉有点傻。

假设形状实际上可以广播,那么这就行了:

def广播形状*形状: max_rank=max[lenshape代表形状中的形状] shapes=np.数组[[1]*最大秩-lenshape+shape 形状中的形状] 形状[形状==1]=-1 最终形状=形状。最大轴=0 最终形状[最终形状==-1]=1 返回最终形状 如果假设没有空维度,则不需要-1 hack:

def广播形状*形状: max_rank=max[lenshape代表形状中的形状] shapes=np.数组[[1]*最大秩-lenshape+shape 形状中的形状] 返回形状。maxaxis=0
假设形状实际上可以广播,那么这是可行的:

def广播形状*形状: max_rank=max[lenshape代表形状中的形状] shapes=np.数组[[1]*最大秩-lenshape+shape 形状中的形状] 形状[形状==1]=-1 最终形状=形状。最大轴=0 最终形状[最终形状==-1]=1 返回最终形状 如果假设没有空维度,则不需要-1 hack:

def广播形状*形状: max_rank=max[lenshape代表形状中的形状] shapes=np.数组[[1]*最大秩-lenshape+shape 形状中的形状] 返回形状。maxaxis=0
下面是另一个直接实现,它恰好在示例中击败了其他实现。值得一提的是@hpaulj的@Warren Weckesser的hack,它几乎同样快,而且更简洁:

def bs_pp(*shapes):
    ml = max(shapes, key=len)
    out = list(ml)
    for l in shapes:
        if l is ml:
            continue
        for i, x in enumerate(l, -len(l)):
            if x != 1 and x != out[i]:
                if out[i] != 1:
                    raise ValueError
                out[i] = x
    return (*out,)

def bs_mq1(*shapes):
    max_rank = max([len(shape) for shape in shapes])
    shapes = [[1] * (max_rank - len(shape)) + shape for shape in shapes]
    final_shape = [1] * max_rank
    for shape in shapes:
        for dim, size in enumerate(shape):
            if size != 1:
                final_size = final_shape[dim]
                if final_size == 1:
                    final_shape[dim] = size
                elif final_size != size:
                    raise ValueError("Cannot broadcast these shapes")
    return (*final_shape,)

import numpy as np

def bs_mq2(*shapes):
    max_rank = max([len(shape) for shape in shapes])
    shapes = np.array([[1] * (max_rank - len(shape)) + shape
                      for shape in shapes])
    shapes[shapes==1] = -1
    final_shape = shapes.max(axis=0)
    final_shape[final_shape==-1] = 1
    return (*final_shape,)

def bs_hp_ww(*shapes):
    return np.broadcast(*[np.empty(shape + [0,], int) for shape in shapes]).shape[:-1]

L = [6], [4, 2, 3, 1], [2, 1, 1]

from timeit import timeit

print('pp:       ', timeit(lambda: bs_pp(*L), number=10_000)/10)
print('mq 1:     ', timeit(lambda: bs_mq1(*L), number=10_000)/10)
print('mq 2:     ', timeit(lambda: bs_mq2(*L), number=10_000)/10)
print('hpaulj/ww:', timeit(lambda: bs_hp_ww(*L), number=10_000)/10)

assert bs_pp(*L) == bs_mq1(*L) and bs_pp(*L) == bs_mq2(*L) and bs_pp(*L) == bs_hp_ww(*L)
样本运行:

pp:        0.0021552839782088993
mq 1:      0.00398325570859015
mq 2:      0.01497043427079916
hpaulj/ww: 0.003267909213900566

下面是另一个直接实现,它恰好在示例中击败了其他实现。值得一提的是@hpaulj's和@Warren Weckesser's hack,这几乎是 更快更简洁:

def bs_pp(*shapes):
    ml = max(shapes, key=len)
    out = list(ml)
    for l in shapes:
        if l is ml:
            continue
        for i, x in enumerate(l, -len(l)):
            if x != 1 and x != out[i]:
                if out[i] != 1:
                    raise ValueError
                out[i] = x
    return (*out,)

def bs_mq1(*shapes):
    max_rank = max([len(shape) for shape in shapes])
    shapes = [[1] * (max_rank - len(shape)) + shape for shape in shapes]
    final_shape = [1] * max_rank
    for shape in shapes:
        for dim, size in enumerate(shape):
            if size != 1:
                final_size = final_shape[dim]
                if final_size == 1:
                    final_shape[dim] = size
                elif final_size != size:
                    raise ValueError("Cannot broadcast these shapes")
    return (*final_shape,)

import numpy as np

def bs_mq2(*shapes):
    max_rank = max([len(shape) for shape in shapes])
    shapes = np.array([[1] * (max_rank - len(shape)) + shape
                      for shape in shapes])
    shapes[shapes==1] = -1
    final_shape = shapes.max(axis=0)
    final_shape[final_shape==-1] = 1
    return (*final_shape,)

def bs_hp_ww(*shapes):
    return np.broadcast(*[np.empty(shape + [0,], int) for shape in shapes]).shape[:-1]

L = [6], [4, 2, 3, 1], [2, 1, 1]

from timeit import timeit

print('pp:       ', timeit(lambda: bs_pp(*L), number=10_000)/10)
print('mq 1:     ', timeit(lambda: bs_mq1(*L), number=10_000)/10)
print('mq 2:     ', timeit(lambda: bs_mq2(*L), number=10_000)/10)
print('hpaulj/ww:', timeit(lambda: bs_hp_ww(*L), number=10_000)/10)

assert bs_pp(*L) == bs_mq1(*L) and bs_pp(*L) == bs_mq2(*L) and bs_pp(*L) == bs_hp_ww(*L)
样本运行:

pp:        0.0021552839782088993
mq 1:      0.00398325570859015
mq 2:      0.01497043427079916
hpaulj/ww: 0.003267909213900566

np.broadcast_数组将相互广播数组。您可以采用任何一个返回数组的形状。它返回实际的数组,但由于它们是广播的,所以不应该特别昂贵;它们都是原始的视图。广播数组使用np.lib.stride\u技巧。广播形状*参数获取目标形状。直接使用它可能会快一点,但它被标记为private。它又使用np.broadcast。我没有太多地使用这些数组,但我怀疑广播数组是最简单/最安全的。np.broadcast\u数组将相互广播数组。您可以采用任何一个返回数组的形状。它返回实际的数组,但由于它们是广播的,所以不应该特别昂贵;它们都是原始的视图。广播数组使用np.lib.stride\u技巧。广播形状*参数获取目标形状。直接使用它可能会快一点,但它被标记为private。它又使用np.broadcast。这些我都没怎么用过,但我怀疑广播阵列是最简单/最安全的。谢谢@hpaulj。这些解决方案不会为空阵列分配RAM吗?如果我想使用巨大的输出形状,它可能会中断,对吗?操作系统级别的分配惰性可能会缓解这里涉及的内存分配的一些问题,但请求所有这些内存似乎仍然是个坏主意。下面是一个避免最后一个选项的内存分配的方法:np.broadcast*[np.emptyshape+[0,],int表示形状中的形状].shape[:-1]@WarrenWeckesser聪明!事实上,它看起来相当快。谢谢@hpaulj。这些解决方案不会为空阵列分配RAM吗?如果我想使用巨大的输出形状,它可能会中断,对吗?操作系统级别的分配惰性可能会缓解这里涉及的内存分配的一些问题,但请求所有这些内存似乎仍然是个坏主意。下面是一个避免最后一个选项的内存分配的方法:np.broadcast*[np.emptyshape+[0,],int表示形状中的形状].shape[:-1]@WarrenWeckesser聪明!实际上,它看起来相当快。很有趣,谢谢@user2357112。您确定“广播到”和“广播”实际上不分配内存吗?他们只是返回视图吗?@MiniQuark:broadcast_制作视图。广播返回一个广播对象,而不是数组,广播对象也不涉及有问题的分配。至少在我的机器上,它们只返回视图。a=np.broadcast_-tobase[4,2,3,6]->a.strips=0,0,0,0.@Warren对我的答案的“hack”评论更快。很有趣,谢谢@user2357112。您确定“广播到”和“广播”实际上不分配内存吗?他们只是返回视图吗?@MiniQuark:broadcast_制作视图。广播返回一个广播对象,而不是数组,广播对象也不涉及有问题的分配。至少在我的机器上,它们只返回视图。a=np.broadcast_-tobase[4,2,3,6]>a.strips=0,0,0,0.@Warren对我的答案的“hack”评论更快。