防止numpy创建多维数组

MSe*_*ert 7 python arrays numpy

NumPy在创建数组时非常有用.如果第一个参数numpy.array包含a __getitem____len__方法,则可以使用它们,因为它可能是一个有效的序列.

不幸的是,我想创建一个包含dtype=objectNumPy"无用" 的数组.

分解为一个最小的例子,这个类是这样的:

import numpy as np

class Test(object):
    def __init__(self, iterable):
        self.data = iterable

    def __getitem__(self, idx):
        return self.data[idx]

    def __len__(self):
        return len(self.data)

    def __repr__(self):
        return '{}({})'.format(self.__class__.__name__, self.data)
Run Code Online (Sandbox Code Playgroud)

如果"iterables"有不同的长度,一切都很好,我得到了我想要的结果:

>>> np.array([Test([1,2,3]), Test([3,2])], dtype=object)
array([Test([1, 2, 3]), Test([3, 2])], dtype=object)
Run Code Online (Sandbox Code Playgroud)

但是如果NumPy恰好具有相同的长度,它会创建一个多维数组:

>>> np.array([Test([1,2,3]), Test([3,2,1])], dtype=object)
array([[1, 2, 3],
       [3, 2, 1]], dtype=object)
Run Code Online (Sandbox Code Playgroud)

不幸的是,只有一个ndmin论点,所以我想知道是否有办法强制执行ndmax或以某种方式阻止NumPy将自定义类解释为另一个维度(不删除__len____getitem__)?

hpa*_*ulj 7

之前已经多次讨论过这种行为(例如,用numpy支持覆盖一个字典). np.array尝试制作尽可能高的维度数组.模型案例是嵌套列表.如果它可以迭代并且子列表的长度相等,则它将"向下钻取".

在遇到不同长度的列表之前它下降了2级:

In [250]: np.array([[[1,2],[3]],[1,2]],dtype=object)
Out[250]: 
array([[[1, 2], [3]],
       [1, 2]], dtype=object)
In [251]: _.shape
Out[251]: (2, 2)
Run Code Online (Sandbox Code Playgroud)

没有形状或ndmax参数,它无法知道我是否想要它(2,)(2,2).这两个都适用于dtype.

它是已编译的代码,因此很难确切地看到它使用的测试.它尝试迭代列表和元组,但不在集或字典上.

制作具有给定维度的对象数组的最可靠方法是从空数据开始,然后填充它

In [266]: A=np.empty((2,3),object)
In [267]: A.fill([[1,'one']])
In [276]: A[:]={1,2}
In [277]: A[:]=[1,2]   # broadcast error
Run Code Online (Sandbox Code Playgroud)

另一种方法是从至少一个不同的元素(例如a None)开始,然后替换它.

有一个更原始的创造者,ndarray形成:

In [280]: np.ndarray((2,3),dtype=object)
Out[280]: 
array([[None, None, None],
       [None, None, None]], dtype=object)
Run Code Online (Sandbox Code Playgroud)

但这基本上是相同的np.empty(除非我给它一个缓冲区).

这些是软糖,但它们并不昂贵(时间明智).

================(编辑)

https://github.com/numpy/numpy/issues/5933,Enh: Object array creation function.是一个增强请求.另请https://github.com/numpy/numpy/issues/5303 the error message for accidentally irregular arrays is confusing.

开发人员的情绪似乎倾向于使用单独的函数来创建dtype=object数组,其中一个对初始维度和迭代深度有更多的控制.他们甚至可以加强错误检查,以防止np.array创建"不规则"数组.

这样的函数可以检测到规则嵌套迭代的形状直到指定的深度,并构建要填充的对象类型数组.

def objarray(alist, depth=1):
    shape=[]; l=alist
    for _ in range(depth):
        shape.append(len(l))
        l = l[0]
    arr = np.empty(shape, dtype=object)
    arr[:]=alist
    return arr
Run Code Online (Sandbox Code Playgroud)

有各种深度:

In [528]: alist=[[Test([1,2,3])], [Test([3,2,1])]]
In [529]: objarray(alist,1)
Out[529]: array([[Test([1, 2, 3])], [Test([3, 2, 1])]], dtype=object)
In [530]: objarray(alist,2)
Out[530]: 
array([[Test([1, 2, 3])],
       [Test([3, 2, 1])]], dtype=object)
In [531]: objarray(alist,3)  
Out[531]: 
array([[[1, 2, 3]],

       [[3, 2, 1]]], dtype=object)
In [532]: objarray(alist,4)
...
TypeError: object of type 'int' has no len()
Run Code Online (Sandbox Code Playgroud)


ev-*_*-br 5

解决方法当然是创建所需形状的数组,然后复制数据:

In [19]: lst = [Test([1, 2, 3]), Test([3, 2, 1])]

In [20]: arr = np.empty(len(lst), dtype=object)

In [21]: arr[:] = lst[:]

In [22]: arr
Out[22]: array([Test([1, 2, 3]), Test([3, 2, 1])], dtype=object)
Run Code Online (Sandbox Code Playgroud)

请注意,在任何情况下,如果 numpy 行为 wrt 解释可迭代对象(这是您想要使用的,对吧?)依赖于 numpy 版本,我不会感到惊讶。并且可能是越野车。或者其中一些错误实际上是功能。无论如何,当 numpy 版本更改时,我会担心损坏。

相反,复制到预先创建的数组中应该更加健壮。