Python 在不实际索引数组的情况下,如何获得已知形状数组的索引元素数?

Python 在不实际索引数组的情况下,如何获得已知形状数组的索引元素数?,python,arrays,numpy,numpy-ndarray,numpy-indexing,Python,Arrays,Numpy,Numpy Ndarray,Numpy Indexing,我有一个索引IDX(可能是索引列表、布尔掩码、切片元组等)索引已知形状形状的抽象numpy数组(可能很大) 我知道我可以创建一个虚拟数组,对它进行索引并计算元素: A = np.zeros(shape) print(A[IDX].size) 在不创建任何(可能较大的)数组的情况下,是否有任何合理的方法可以获得索引元素的数量 我需要将3D空间中某些点的函数列表制成表格。这些点是矩形网格的子集,表示为X,Y,Z列表,并且IDX正在索引其笛卡尔积: XX, YY, ZZ = [A[IDX] for

我有一个索引
IDX
(可能是索引列表、布尔掩码、切片元组等)索引已知形状
形状的抽象numpy数组(可能很大)

我知道我可以创建一个虚拟数组,对它进行索引并计算元素:

A = np.zeros(shape)
print(A[IDX].size)
在不创建任何(可能较大的)数组的情况下,是否有任何合理的方法可以获得索引元素的数量

我需要将3D空间中某些点的函数列表制成表格。这些点是矩形网格的子集,表示为
X
Y
Z
列表,并且
IDX
正在索引其笛卡尔积:

XX, YY, ZZ = [A[IDX] for A in np.meshgrid(X, Y, Z)]
这些函数接受
X
Y
Z
参数(以及需要索引的笛卡尔积的返回值)或
XX
YY
ZZ
。 此时,我创建了
XX
YY
ZZ
数组,无论它们是否被使用,然后我为函数值分配了一个数组:

self.TAB = np.full((len(functions), XX.size),
                   np.nan)

但是我想创建
XX
YY
ZZ
,只要它们是必要的。我还想将
选项卡
分配与其行填充分开,因此我需要提前知道列的数量。

只是为了好玩,让我们看看是否可以在这里做一个可接受的近似值。您的输入可以是以下任一项:

  • 切片
  • 类数组(包括标量)
    • 整数数组不需要复杂的索引
    • 布尔数组不做掩蔽
  • 元组
如果输入不是一个明确的元组开始,使它成为一个元组。现在可以沿着元组进行迭代,并将其与形状匹配。您不能将它们完全压缩在一起,因为布尔数组占用了形状的多个元素,并且包含了大量的尾随轴

像这样的东西应该可以做到:

def pint(x):
    """ Mimic numpy errors """
    if isinstance(x, bool):
        raise TypeError('an integer is required')
    try:
        y = int(x)
    except TypeError:
        raise TypeError('an integer is required')
    else:
        if y < 0:
            raise ValueError('negative dimensions are not allowed')
    return y


def estimate_size(shape, index):
    # Ensure input is a tuple
    if not isinstance(index, tuple):
        index = (index,)

    # Clean out Nones: they don't change size
    index = tuple(i for i in index if i is not None)

    # Check shape shape and type
    try:
        shape = tuple(shape)
    except TypeError:
        shape = (shape,)
    shape = tuple(pint(s) for s in shape)

    size = 1

    # Check for scalars
    if not shape:
        if index:
            raise IndexError('too many indices for array')
        return size

    # Process index dimensions
    # you could probably use iter(shape) instead of shape[s]
    s = 0

    # fancy indices need to be gathered together and processed as one
    fancy = []

    def get(n):
        nonlocal s
        s += n
        if s > len(shape):
            raise IndexError('too many indices for array')
        return shape[s - n:s]

    for ind in index:
        if isinstance(ind, slice):
            ax, = get(1)
            size *= len(range(*ind.indices(ax)))
        else:
            ind = np.array(ind, ndmin=1, subok=True, copy=False)
            if ind.dtype == np.bool_:
                # Boolean masking
                ax = get(ind.ndim)
                if ind.shape != ax:
                    k = np.not_equal(ind.shape, ax).argmax()
                    IndexError(f'IndexError: boolean index did not match indexed array along dimension {s - n.ndim + k}; dimension is {shape[s - n.ndim + k]} but corresponding boolean dimension is {ind.shape[k]}')
                size *= np.count_nonzero(ind)
            elif np.issubdtype(ind.dtype, np.integer):
                # Fancy indexing
                ax, = get(1)
                if ind.min() < -ax or ind.max() >= ax:
                    k = ind.min() if ind.min() < -ax else ind.max()
                    raise IndexError(f'index {k} is out of bounds for axis {s} with size {ax}')
                fancy.append(ind)
            else:
                raise IndexError('arrays used as indices must be of integer (or boolean) type')

    # Add in trailing dimensions
    size *= np.prod(shape[s:])

    # Add fancy indices
    if fancy:
        size *= np.broadcast(*fancy).size

    return size
def品脱(x):
“”“模拟numpy错误”“”
如果存在(x,布尔):
raise TypeError('需要整数')
尝试:
y=int(x)
除类型错误外:
raise TypeError('需要整数')
其他:
如果y<0:
raise VALUERROR('不允许负维度')
返回y
def估计尺寸(形状、索引):
#确保输入是一个元组
如果不是isinstance(索引、元组):
索引=(索引,)
#清理掉不需要的东西:它们不会改变大小
index=tuple(如果i不是None,则i代表索引中的i)
#检查形状和类型
尝试:
shape=元组(shape)
除类型错误外:
形状=(形状,)
形状=元组(形状为s的品脱)
尺寸=1
#检查标量
如果不是形状:
如果索引:
raise IndexError('数组的索引太多')
返回大小
#过程索引维度
#你可以用iter(形状)代替形状
s=0
#花式指数需要收集在一起,并作为一个整体进行处理
花式=[]
def get(n):
非局部s
s+=n
如果s>len(形状):
raise IndexError('数组的索引太多')
返回形状[s-n:s]
对于索引中的索引:
如果存在(ind,切片):
ax,=get(1)
尺寸*=长度(范围(*索引(ax)))
其他:
ind=np.array(ind,ndmin=1,subok=True,copy=False)
如果ind.dtype==np.bool\ux:
#布尔掩蔽
ax=获取(ind.ndim)
如果是ind.shape!=ax:
k=np.不相等(ind.shape,ax).argmax()
索引器(f'索引器:布尔索引与沿维度{s-n.ndim+k}的索引数组不匹配;维度为{shape[s-n.ndim+k]},但对应的布尔维度为{ind.shape[k]})
大小*=np.计数非零(ind)
elif np.issubdtype(ind.dtype,np.integer):
#花式索引
ax,=get(1)
如果ind.min()<-ax或ind.max()>=ax:
k=ind.min(),如果ind.min()<-ax else ind.max()
raise IndexError(f'index{k}超出大小为{ax}的轴{s}的界限)
花式追加(ind)
其他:
raise IndexError('用作索引的数组必须是整数(或布尔)类型')
#添加尾部尺寸标注
尺寸*=np.prod(形状:)
#添加花式索引
如果喜欢:
尺寸*=np.广播(*花式)。尺寸
返回大小

这只是一个近似值。您需要在API更改时随时更改它,并且它已经有一些不完整的特性。测试、修复和扩展留给读者作为练习。

我想不出任何接近索引通用性的东西。有太多的选择。你为什么需要这个?也许你展示它的方式是最好的;否则,您必须找到每种类型的index@hpaulj请查看编辑如果您只需要
XX.size
,您可能可以执行
XX=np.meshgrid(X,Y,Z)[0][IDX]
,而不是现在执行的
XX,YY,ZZ=[A[IDX]表示np.meshgrid(X,Y,Z)]
。据我所知,
XX.size
YY.size
ZZ.size
的值都是相同的。@hpaulj。如果我错过了什么重要的事情,请告诉我。我确信我的答案没有涵盖所有的细节,但希望它涵盖了大部分的基础知识。如果
ind.dtype
是整数,为什么我们要做
size*=np.unique(ind%ax).size
?我本以为只要
size*=ind.size
,但我相信你这么做是有原因的。唷,太复杂了+1已经存在。如果索引对象由两个整数数组组成,
size
的值是提升了两次(使用
size*=
)还是只提升了一次?(我希望这只是一次,但我只是想知道在哪里可以保证这一点)。@fountainhead。抢手货应该是一次,actu