有关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添加了很多东西.