4

我正在使用 Python 开发一个需要大量数值数组计算的项目。不幸的是(或者幸运的是,取决于你的 POV),我对 Python 很陌生,但多年来一直在做 MATLAB 和 Octave 编程(之前的 APL)。我非常习惯于将每个变量自动输入到矩阵浮点数,并且仍然习惯于检查输入类型。

在我的许多函数中,我要求输入 S 为numpy.ndarraysize (n,p),因此我必须测试 type(S) 是numpy.ndarray并获取 values (n,p) = numpy.shape(S)。一个潜在的问题是输入可能是一个列表/元组/int/etc...,另一个问题是输入可能是一个形状数组()S.ndim = 0。我突然想到我可以同时测试变量类型,解决S.ndim = 0问题,然后得到这样的尺寸:

# first simultaneously test for ndarray and get proper dimensions
try:
    if (S.ndim == 0):
        S = S.copy(); S.shape = (1,1);
    # define dimensions p, and p2
    (p,p2) = numpy.shape(S);
except AttributeError:  # got here because input is not something array-like
    raise AttributeError("blah blah blah");

虽然它有效,但我想知道这是否有效?ndim 的文档字符串说

如果它还不是 ndarray,则尝试转换。

我们当然知道 numpy 可以轻松地将 int/tuple/list 转换为数组,所以我很困惑为什么会为这些类型的输入引发 AttributeError,而 numpy 应该这样做

numpy.array(S).ndim;

这应该工作。

4

3 回答 3

4

在对 NumPy 代码进行输入验证时,我总是使用np.asarray

>>> np.asarray(np.array([1,2,3]))
array([1, 2, 3])
>>> np.asarray([1,2,3])
array([1, 2, 3])
>>> np.asarray((1,2,3))
array([1, 2, 3])
>>> np.asarray(1)
array(1)
>>> np.asarray(1).shape
()

这个函数有一个很好的特性,它只在必要时复制数据;如果输入已经是 a ndarray,则数据保留在原地(只有类型可以更改,因为它也摆脱了那个讨厌的np.matrix)。

ndim 的文档字符串说

那是函数的文档字符串,而不是非 NumPy 对象np.ndim没有的属性。ndim您可以使用该函数,但结果是数据可能会被复制两次,因此请改为:

S = np.asarray(S)
(p, p2) = S.shape

这将引发一个ValueErrorif S.ndim != 2

;[最后注意:如果你只遵循缩进规则,你不需要在 Python 中。事实上,Python 程序员避开了分号。]

于 2012-09-20T14:34:25.033 回答
3

鉴于对@larsmans 回答的评论,您可以尝试:

if not isinstance(S, np.ndarray):
    raise TypeError("Input not a ndarray")
if S.ndim == 0:
    S = np.reshape(S, (1,1))
(p, p2) = S.shape

首先,您明确检查是否S是 (subclass of) ndarray。然后,np.reshape如果需要,您可以使用 复制您的数据(当然还有重塑它)。最后,你得到了维度。

请注意,在大多数情况下,np函数会首先尝试访问 a 的相应方法ndarray,然后尝试将输入转换为 a ndarray(有时将其保留为子类,如 中np.asanyarray,有时不是(如中np.asarray(...))。换句话说,它总是使用方法而不是函数更有效:这就是我们使用S.shape而不是np.shape(S).

另一点:np.asarray, np.asanyarray, np.atleast_1D... 都是更通用函数的特例np.array。例如,设置toasarray的可选copy参数,做同样的事情和 sets ,sets ,sets ... 换句话说,与适当的参数一起使用总是更容易。但正如一些评论中提到的,这是风格问题。快捷方式通常可以提高可读性,这始终是一个需要牢记的目标。arrayFalseasanyarraysubok=Trueatleast_1Dndmin=1atleast_2dndmin=2np.array

无论如何,当您np.array(..., copy=True)使用list([....]). 即使没有其他任何更改,您的数据也会被复制。这具有其缺点的优点(正如我们用法语所说),例如,您可以将orderrow-first更改C为 column-first F。但无论如何,你得到你想要的副本。

使用np.array(input, copy=False)时,总是会创建一个新数组。它要么指向同一个内存块,就input好像后者已经是 a (也就是说,不浪费内存),或者如果不是ndarray,将“从头开始”创建一个新的内存块。input有趣的情况当然是 ifinput是一个ndarray.

在函数中使用这个新数组可能会也可能不会更改原始输入,具体取决于函数。您必须检查要使用的函数的文档,以查看它是否返回副本。NumPy 开发人员努力限制不必要的副本(按照 Python 示例),但有时无法避免。文档应明确说明会发生什么,如果没有或不清楚,请提及。

np.array(...)如果出现问题,可能会引发一些异常。例如,尝试将 adtype=float与类似的输入一起使用["STRING", 1]会引发 a ValueError但是,我必须承认我不记得在所有情况下都有哪些例外,请相应地编辑这篇文章

于 2012-09-20T15:13:23.677 回答
2

欢迎来到堆栈溢出。这几乎可以归结为一种样式选择,但我见过的处理这种情况的最常见方法是将输入转换为数组。Numpy 为此提供了一些有用的工具。numpy.asarray已经提到过,但这里还有一些。numpy.at_least1d与 类似asarray,但将 () 数组重新整形为 (1,)numpy.at_least2d与上面相同,但将 0d 和 1d 数组重新整形为 2d,即 (3,) 到 (1, 3)。我们将“array_like”输入转换为数组的原因部分是因为我们很懒,例如有时它foo([1, 2, 3])比更容易编写foo(numpy.array([1, 2, 3])),但这也是 numpy 本身的设计选择。请注意,以下工作:

>>> numpy.mean([1., 2., 3.])
>>> 2.0

在文档中,numpy.mean我们可以看到 x 应该是“array_like”。

Parameters
----------
a : array_like
    Array containing numbers whose mean is desired. If `a` is not an
    array, a conversion is attempted.

That being said, there are situations when you want to only accept arrays as arguments and not all "array_like" types.

于 2012-09-20T15:24:41.823 回答