python – Numpy安全编程

有关numpy的安全,无错误的数值编程是否有任何来源或指南?

我问,因为我痛苦地了解到numpy做了许多似乎真正要求发生错误的事情,例如……

添加不同大小的矩阵(“广播”)而不抱怨:

In: np.array([1]) + np.identity(2)
Out: array([[ 2.,  1.],
            [ 1.,  2.]])

根据输入返回不同的数据类型:

In: scalar1 = 1
In: scalar2 = 1.
In: np.array(scalar1).dtype
Out: dtype('int32')
In: np.array(scalar2).dtype
Out: dtype('float64')

或者根本不执行所需的操作(同样,取决于数据类型)而不引发任何警告:

In: np.squeeze(np.array([[1, 1]])).ndim
Out: 1
In: np.squeeze(np.matrix([[1, 1]])).ndim
Out: 2

这些都很难发现错误,因为它们不会引发任何异常或警告,并且通常会返回有效数据类型/形状的结果.因此,我的问题是:是否有任何一般指导方针可以改善安全性并防止数学编程中出现numpy错误?

[请注意,我不相信这个答案会吸引“自以为是的答案和讨论”,因为它不是关于个人建议,而是询问是否有关于这个主题的任何现有指南或来源 – 我找不到任何.]

最佳答案 经常我问提问者,形状是什么? dtype?甚至类型.跟踪这些属性是良好的numpy编程的重要组成部分.即使在MATLAB中,我发现正确的大小是调试的80%.

类型

squeeze示例围绕类型,ndarray类与np.matrix子类:

In [160]: np.squeeze(np.array([[1, 1]]))
Out[160]: array([1, 1])
In [161]: np.squeeze(np.matrix([[1, 1]]))
Out[161]: matrix([[1, 1]])

根据定义,np.matrix对象总是2d.这是重新定义ndarray操作的核心.

许多numpy函数将他们的工作委托给方法. fornp.squeeze`的代码是:

try:
    squeeze = a.squeeze
except AttributeError:
    return _wrapit(a, 'squeeze')
try:
    # First try to use the new axis= parameter
    return squeeze(axis=axis)
except TypeError:
    # For backwards compatibility
    return squeeze()

所以[161]真的是:

In [163]: np.matrix([[1, 1]]).squeeze()
Out[163]: matrix([[1, 1]])

np.matrix.squeeze有自己的文档.

作为一般规则,我们不鼓励使用np.matrix.这是几年前创建的,为任性的MATLAB程序员提供了便利.那时候MATLAB只有2d矩阵(即使现在MATLAB’scalars’也是2d).

D型

np.array是一个强大的功能.通常它的行为是直观的,但有时它会做出太多的假设.

通常它从输入中获取线索,无论是整数,浮点数,字符串和/或列表:

In [170]: np.array(1).dtype
Out[170]: dtype('int64')
In [171]: np.array(1.0).dtype
Out[171]: dtype('float64')

但它提供了许多参数.如果您需要更多控制,请使用它们:

array(object, dtype=None, copy=True, order='K', subok=False, ndmin=0)

In [173]: np.array(1, float).dtype
Out[173]: dtype('float64')
In [174]: np.array('1', float).dtype
Out[174]: dtype('float64')
In [177]: np.array('1', dtype=float,ndmin=2)
Out[177]: array([[1.]])

查看它的文档,以及列出许多其他数组创建函数的https://docs.scipy.org/doc/numpy/reference/routines.array-creation.html页面.看一下他们的代码.

例如,np.atleast_2d进行了大量的形状检查:

def atleast_2d(*arys):
    res = []
    for ary in arys:
        ary = asanyarray(ary)
        if ary.ndim == 0:
            result = ary.reshape(1, 1)
        elif ary.ndim == 1:
            result = ary[newaxis,:]
        else:
            result = ary
        res.append(result)
    if len(res) == 1:
        return res[0]
    else:
        return res

像这样的函数是防御性编程的好例子.

关于dtype = object的1d数组我们得到了很多问题.

In [272]: np.array([[1,2,3],[2,3]])
Out[272]: array([list([1, 2, 3]), list([2, 3])], dtype=object)

np.array尝试创建一个具有统一dtype的多维数组.但是如果元素大小不同或者不能转换为相同的dtype,它将回退到对象dtype.这是我们需要注意形状和dtype的情况之一.

广播

广播一直是numpy的一部分,并且无法将其关闭. Octave和MATLAB稍后添加了它,并启用了警告开关.

第一个防御步骤是了解广播原则,即

>它可以扩展起始尺寸以匹配
>它强制统一尺寸匹配.

所以一个基本的例子是:

In [180]: np.arange(3)[:,None] + np.arange(4)
Out[180]: 
array([[0, 1, 2, 3],
       [1, 2, 3, 4],
       [2, 3, 4, 5]])

第一项是(3),扩展为(3,1).第二个是(4,),通过广播扩展到(1,4). (3,1)和(1,4)一起广播到(3,4).

许多numpy函数都有参数,可以更容易地跟踪尺寸.例如sum(和其他)有一个keepdims参数:

In [181]: arr = _
In [182]: arr.sum(axis=0)
Out[182]: array([ 3,  6,  9, 12])         # (4,) shape
In [183]: arr.sum(axis=0,keepdims=True)
Out[183]: array([[ 3,  6,  9, 12]])       # (1,4) shape
In [184]: arr/_                           # (3,4) / (1,4) => (3,4)
Out[184]: 
array([[0.        , 0.16666667, 0.22222222, 0.25      ],
       [0.33333333, 0.33333333, 0.33333333, 0.33333333],
       [0.66666667, 0.5       , 0.44444444, 0.41666667]])

在这种情况下,keepdims不是必需的,因为(3,4)/(4,)有效.但是当轴= 1和时,形状变为(3,),不能用(3,4)广播.但是(3,1)可以:

In [185]: arr/arr.sum(axis=1,keepdims=True)
Out[185]: 
array([[0.        , 0.16666667, 0.33333333, 0.5       ],
       [0.1       , 0.2       , 0.3       , 0.4       ],
       [0.14285714, 0.21428571, 0.28571429, 0.35714286]])

为了管理我喜欢的形状:

>调试时显示形状
>以交互方式测试片段
>用诊断形状测试,例如np.arange(24).reshape(2,3,4)
>函数中的断言语句可以是有用的断言(arr.ndim == 1)

打字

最近的Python 3版本添加了一个输入模块

https://docs.python.org/3/library/typing.html

即使对于内置的Python类型,它也是临时的.我不确定是否为numpy添加了很多东西.

点赞