NumPy 1.17.0 发行说明#
本次 NumPy 发布包含多项新功能,应能显著提升其性能和可用性,请参见下文“亮点”以获取摘要。支持的 Python 版本为 3.5-3.7,请注意已弃用 Python 2.7。Python 3.8b2 应该可以与发布的源代码包一起使用,但未来不作保证。
下游开发者应使用 Cython >= 0.29.11 以支持 Python 3.8,并使用 OpenBLAS >= 3.7(尚未发布)以避免 Skylake 架构上的问题。PyPI 上的 NumPy wheel 是从 OpenBLAS 开发分支构建的,以避免这些问题。
亮点#
添加了一个新的可扩展的
random模块,以及四个可选的 随机数生成器,并改进了为并行进程设计的种子生成。当前可用的位生成器是 MT19937、PCG64、Philox 和 SFC64。请参见下文“新功能”。NumPy 的
FFT实现已从 fftpack 更改为 pocketfft,从而实现了更快、更准确的变换,并能更好地处理素数长度的数据集。请参见下文“改进”。新增基数排序和 timsort 排序方法。目前无法选择使用哪种方法。它们已硬编码到数据类型中,并在传递
stable或mergesort作为方法时使用。请参见下文“改进”。现在可以默认覆盖 numpy 函数,请参见下文的
__array_function__。
新增函数#
numpy.errstate现在也是一个函数装饰器。
弃用#
numpy.polynomial 函数在传递 float 而非 int 时会发出警告#
以前,此模块中的函数会接受 float 值,只要它们是整数(例如 1.0、2.0)。为了与 numpy 的其他部分保持一致,这样做现在已被弃用,将来会引发 TypeError。
同样,传递 0.5 这样的浮点数而不是整数现在会引发 TypeError,而不是之前的 ValueError。
弃用 numpy.distutils.exec_command 和 temp_file_name#
这些函数的内部使用已重构,有更好的替代方案。将 exec_command 替换为 subprocess.Popen,将 temp_file_name 替换为 tempfile.mkstemp。
C-API 包装数组的可写标志#
当一个数组从 C-API 创建以包装数据指针时,我们能获得的关于数据读写性质的唯一指示就是在创建期间设置的 writeable 标志。强制将标志设置为可写是危险的。将来,将无法从 Python 将可写标志切换为 True。此弃用不应影响大量用户,因为以这种方式创建的数组在实践中非常罕见,并且只能通过 NumPy C-API 访问。
numpy.nonzero 不再应该用于 0d 数组#
numpy.nonzero 在 0d 数组上的行为令人惊讶,使得其用法几乎总是错误的。如果想要旧的行为,可以通过使用 nonzero(atleast_1d(arr)) 而不是 nonzero(arr) 来在不警告的情况下保留它。在未来的版本中,这很可能会引发 ValueError。
对 numpy.broadcast_arrays 结果的写入会发出警告#
通常 numpy.broadcast_arrays 返回一个可写且内部重叠的数组,使其写入不安全。未来版本将 writeable 标志设置为 False,并要求用户在确定要执行此操作时手动将其设置为 True。现在写入它会发出弃用警告,并提供有关如何将 writeable 标志设置为 True 的说明。请注意,如果事先检查标志,会发现它已经是 True。然而,在未来版本中,显式设置它将清除用于生成弃用警告的内部标志。为了缓解混淆,在访问 writeable 标志状态时会发出额外的 FutureWarning,以澄清这种矛盾。
请注意,对于 C 语言缓冲区协议,此类数组将立即返回只读缓冲区,除非请求了可写缓冲区。如果请求了可写缓冲区,则会发出警告。使用 cython 时,应使用 const 限定符处理此类数组,以避免警告(例如,cdef const double[::1] view)。
未来变化#
dtype 中的 shape-1 字段在未来版本中将不会折叠为标量#
当前,指定为 [(name, dtype, 1)] 或 "1type" 的字段被解释为标量字段(即,与 [(name, dtype)] 或 [(name, dtype, ()] 相同)。这现在会引发 FutureWarning;在未来的版本中,它将被解释为 shape-(1,) 字段,即与 [(name, dtype, (1,))] 或 "(1,)type" 相同(与 [(name, dtype, n)] / "ntype"(其中 n>1,它已等同于 [(name, dtype, (n,)] / "(n,)type")保持一致)。
兼容性说明#
float16 次正规数舍入#
从其他浮点精度转换为 float16 在某些边缘情况下使用了错误的舍入。这意味着在极少数情况下,次正规数结果现在将向上舍入而不是向下舍入,从而改变结果的最后一位(ULP)。
使用 divmod 时的带符号零#
从版本 1.12.0 开始,当使用 divmod 和 floor_divide 函数且结果为零时,numpy 会错误地返回一个负号的零。例如
>>> np.zeros(10)//1
array([-0., -0., -0., -0., -0., -0., -0., -0., -0., -0.])
在此版本中,结果将正确返回为正号的零。
>>> np.zeros(10)//1
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
MaskedArray.mask 现在返回掩码的视图,而不是掩码本身#
返回掩码本身是不安全的,因为它可能会被原地重塑,从而违反掩码数组代码的预期。现在 mask 的行为与 data 保持一致,后者也返回一个视图。
可以通过 ._mask 访问底层掩码,如果需要的话。包含 assert x.mask is not y.mask 或类似断言的测试需要更新。
在 numpy.frombuffer 中不要查找 __buffer__ 属性#
在 numpy.frombuffer 中查找 __buffer__ 属性是未记录且无效的。此代码已被移除。如果需要,请改用 frombuffer(memoryview(obj), ...)。
在 take、choose、put 中,out 会因内存重叠而缓冲#
如果这些函数的 out 参数被提供并且与其他参数存在内存重叠,现在它会被缓冲以避免出现依赖顺序的行为。
加载时的反序列化需要显式选择加入#
函数 load 和 lib.format.read_array 接受一个 allow_pickle 关键字参数,响应 CVE-2019-6446,该参数现在默认为 False。
旧随机模块中随机流的潜在变化#
由于在对随机浮点数应用 log 时存在错误,当从 beta、binomial、laplace、logistic、logseries 或 multinomial 采样时,如果底层 MT19937 随机流生成了 0,则随机流可能会发生变化。这种情况发生的概率为 \(1\) in \(10^{53}\),因此任何给定种子的流发生变化的概率都极小。如果遇到底层生成器中的 0,那么现在将丢弃生成的错误值(numpy.inf 或 numpy.nan)。
i0 现在始终返回形状与输入相同的数组#
以前,输出会被挤压,例如,单元素输入会导致返回一个数组标量,而形状为 (10, 1) 的输入会产生无法与输入广播的结果。
请注意,我们通常推荐使用 SciPy 实现而不是 numpy 的实现:它是一个用 C 编写的 proper ufunc,速度比 numpy 快一个数量级以上。
can_cast 不再假设所有不安全转换都允许#
以前,对于 casting='unsafe',can_cast 会为几乎所有输入返回 True,即使在转换不可能的情况下(例如从结构化 dtype 到常规 dtype)。这种情况已得到修复,使其更符合实际转换,例如使用 .astype 方法进行转换。
ndarray.flags.writeable 现在可以稍微更频繁地切换为 true#
在极少数情况下,即使基数组是可写的,也无法将数组从不可写状态切换为可写状态。如果中间的 ndarray.base 对象是可写的,则可能发生这种情况。以前,只考虑最深的基对象来做此决定。然而,在极少数情况下,该对象没有必要的信息。在这种情况下,切换到可写状态从来都不被允许。现在这种情况已经得到修复。
C API 更改#
维度或步幅输入参数现在通过 npy_intp const* 传递#
以前这些函数参数声明为更严格的 npy_intp*,这阻止了调用者传递常量数据。此更改向后兼容,但现在允许类似的代码
npy_intp const fixed_dims[] = {1, 2, 3};
// no longer complains that the const-qualifier is discarded
npy_intp size = PyArray_MultiplyList(fixed_dims, 3);
新功能#
新的可扩展 numpy.random 模块,具有可选的随机数生成器#
添加了一个新的可扩展的 numpy.random 模块,以及四个可选的随机数生成器,并改进了为并行进程设计的种子生成。当前可用的 位生成器是 MT19937、PCG64、Philox 和 SFC64。PCG64 是新的默认值,而 MT19937 保留用于向后兼容。请注意,旧的随机模块未更改,现在已冻结,您当前的结果不会改变。更多信息可在 API 更改说明 和 顶层视图 文档中找到。
libFLAME#
支持使用 libFLAME 线性代数包作为 LAPACK 实现来构建 NumPy,有关详细信息请参见 libFLAME。
用户定义的 BLAS 检测顺序#
distutils 现在使用一个环境变量,该变量以逗号分隔且不区分大小写,以确定 BLAS 库的检测顺序。默认情况下为 NPY_BLAS_ORDER=mkl,blis,openblas,atlas,accelerate,blas。但是,要强制使用 OpenBLAS,只需执行
NPY_BLAS_ORDER=openblas python setup.py build
即可强制使用 OpenBLAS。这对于拥有 MKL 安装但希望尝试不同实现的用户的用户可能很有帮助。
用户定义的 LAPACK 检测顺序#
numpy.distutils 现在使用一个环境变量,该变量以逗号分隔且不区分大小写,以确定 LAPACK 库的检测顺序。默认情况下为 NPY_LAPACK_ORDER=mkl,openblas,flame,atlas,accelerate,lapack。但是,要强制使用 OpenBLAS,只需执行
NPY_LAPACK_ORDER=openblas python setup.py build
即可强制使用 OpenBLAS。这对于拥有 MKL 安装但希望尝试不同实现的用户的用户可能很有帮助。
Timsort 和基数排序已取代 mergesort 来实现稳定排序#
已实现基数排序和 timsort,并已取代 mergesort。为了保持向后兼容,排序 kind 选项 "stable" 和 "mergesort" 已成为彼此的别名,实际的排序实现取决于数组类型。基数排序用于 16 位或更小的整数类型,而 timsort 用于其他类型。Timsort 在包含已排序或近乎排序数据的数据上具有改进的性能,在随机数据上表现与 mergesort 相似,并且需要 \(O(n/2)\) 的工作空间。Timsort 算法的详细信息可在 CPython listsort.txt 中找到。
packbits 和 unpackbits 接受 order 关键字#
该 order 关键字默认为 big,并相应地对比特进行排序。对于 'order=big',3 将变为 [0, 0, 0, 0, 0, 0, 1, 1],而对于 order=little,3 将变为 [1, 1, 0, 0, 0, 0, 0, 0]。
unpackbits 现在接受 count 参数#
count 允许提前子集化要解包的位数,而不是稍后重塑和子集化,从而使 packbits 操作可逆,并使解包效率更高。计数大于可用位数会添加零填充。负计数会从末尾截断位数,而不是从开头计数。None 计数实现了现有行为,即解包所有内容。
linalg.svd 和 linalg.pinv 在厄米特输入上可能更快#
这些函数现在接受一个 hermitian 参数,与 1.14.0 中添加到 linalg.matrix_rank 的参数相匹配。
现在支持两个 timedelta64 操作数之间的 divmod 操作#
divmod 运算符现在处理两个 timedelta64 操作数,类型签名为 mm->qm。
fromfile 现在接受 offset 参数#
此函数现在接受二进制文件的 offset 关键字参数,该参数指定从文件当前位置开始的偏移量(以字节为单位)。默认为 0。
pad 的新模式“empty”#
此模式将数组填充到所需的形状,而不初始化新条目。
浮点标量实现了 as_integer_ratio 以匹配内置浮点数#
这返回一个(分子,分母)对,可用于构建 fractions.Fraction。
结构化 dtype 对象可以用多个字段名进行索引#
arr.dtype[['a', 'b']] 现在返回一个 dtype,它等同于 arr[['a', 'b']].dtype,与 arr.dtype['a'] == arr['a'].dtype 保持一致。
与使用字段列表索引的结构化数组的 dtype 一样,此 dtype 具有与原始 dtype 相同的 itemsize,但只保留一部分字段。
这意味着 arr[['a', 'b']] 和 arr.view(arr.dtype[['a', 'b']]) 是等效的。
.npy 文件支持 Unicode 字段名#
引入了新的格式版本 3.0,它支持具有非 Latin-1 字段名的结构化类型。当需要时,会自动使用此版本。
改进#
数组比较断言包含最大差异#
数组比较测试(如 testing.assert_allclose)的错误消息现在包含“最大绝对差”和“最大相对差”,以及之前的“不匹配”百分比。此信息有助于更新绝对和相对误差容差。
用 pocketfft 库替换基于 fftpack 的 fft 模块#
两个实现都具有相同的祖先(Paul N. Swarztrauber 的 Fortran77 FFTPACK),但 pocketfft 包含额外的修改,在某些情况下可以提高精度和性能。对于包含大素数因子的 FFT 长度,pocketfft 使用 Bluestein 算法,该算法保持 \(O(N log N)\) 的运行时间复杂度,而不是在素数长度下恶化到 \(O(N*N)\)。此外,对于接近素数长度的实值 FFT 的精度已得到提高,并与复值 FFT 相当。
进一步改进 numpy.ctypeslib 中的 ctypes 支持#
新增了一个名为 numpy.ctypeslib.as_ctypes_type 的函数,可用于将 dtype 转换为最适合的 ctypes 类型。借助这个新函数,numpy.ctypeslib.as_ctypes 现在支持更广泛的数组类型,包括结构体、布尔值以及非原生字节序的整数。
numpy.errstate 现在也可以用作函数装饰器#
当前,如果您有一个函数,例如
def foo():
pass
并且您想将整个函数用 errstate 包裹起来,您必须这样重写它:
def foo():
with np.errstate(...):
pass
但有了这个改变,您可以这样做:
@np.errstate(...)
def foo():
pass
从而节省了一个缩进级别
numpy.exp 和 numpy.log 对 float32 实现进行了加速#
float32 实现的 exp 和 log 现在受益于 AVX2/AVX512 指令集,这些指令集在运行时会被检测到。exp 的最大 ULP 误差为 2.52,log 的最大 ULP 误差为 3.83。
提高 numpy.pad 的性能#
通过在预先分配的数组中填充所需的填充形状,而不是使用连接,提高了函数在大多数情况下的性能。
numpy.interp 更稳健地处理无穷大#
fromfile、tofile 和 ndarray.dump 支持 Pathlib#
fromfile、ndarray.ndarray.tofile 和 ndarray.dump 现在支持 file/fid 参数的 pathlib.Path 类型。
为 bool 和 int 类型提供专门的 isnan、isinf 和 isfinite ufuncs#
isfinite 支持 datetime64 和 timedelta64 类型#
以前,isfinite 在使用这两种类型时会引发 TypeError。
为 nan_to_num 添加了新的关键字参数#
nan_to_num 现在接受 nan、posinf 和 neginf 关键字参数,允许用户定义替换 nan、正无穷大和负无穷大 np.inf 值的。
由于分配了过大的数组而导致的 MemoryErrors 更加具有描述性#
MemoryError 的原因通常是广播不正确,导致形状非常大且不正确。错误消息现在包含了该形状,以帮助诊断失败的原因。
floor、ceil 和 trunc 现在遵循内置的魔术方法#
这些 ufuncs 现在在对对象数组调用时会调用 __floor__、__ceil__ 和 __trunc__ 方法,使其与 decimal.Decimal 和 fractions.Fraction 对象兼容。
quantile 现在可以处理 fraction.Fraction 和 decimal.Decimal 对象#
总的来说,这能更平滑地处理对象数组,并在使用精确算术类型时避免浮点运算。
matmul 中支持对象数组#
现在可以使用 matmul(或 @ 运算符)处理对象数组。例如,现在可以执行以下操作:
from fractions import Fraction
a = np.array([[Fraction(1, 2), Fraction(1, 3)], [Fraction(1, 3), Fraction(1, 2)]])
b = a @ a
更改#
median 和 percentile 系列函数不再警告 nan#
numpy.median、numpy.percentile 和 numpy.quantile 以前在遇到 nan 时会发出 RuntimeWarning。由于它们会返回 nan 值,因此警告是多余的,已被移除。
timedelta64 % 0 的行为已调整为返回 NaT#
两个 np.timedelta64 操作数的模运算在除以零的情况下现在返回 NaT,而不是返回零。
NumPy 函数现在始终支持使用 __array_function__ 进行重写#
NumPy 现在始终检查 __array_function__ 方法,以在非 NumPy 数组上实现 NumPy 函数的重写,如 NEP 18 中所述。该功能可通过设置适当的环境变量在 NumPy 1.16 中进行测试,但现在已始终启用。
lib.recfunctions.structured_to_unstructured 不再挤压单字段视图#
以前,structured_to_unstructured(arr[['a']]) 会产生一个挤压结果,与 structured_to_unstructured(arr[['a', b']]) 不一致。这是偶然的。旧的行为可以通过 structured_to_unstructured(arr[['a']]).squeeze(axis=-1) 或更简单地 arr['a'] 来保留。
clip 现在使用 ufunc 作为底层#
这意味着通过 C 注册自定义 dtype 的 clip 函数(通过 descr->f->fastclip)已被弃用 - 它们应该改用 ufunc 注册机制,并附加到 np.core.umath.clip ufunc。
这也意味着 clip 接受 where 和 casting 参数,并且可以用 __array_ufunc__ 进行重写。
此更改的一个后果是,旧 clip 的某些行为已被弃用。
将
nan作为“不裁剪”的上下限。这在某些情况下 anyway 并不起作用,可以通过传递适当符号的无穷大来更好地处理。当提供
out参数时,默认使用“不安全”转换。显式使用casting="unsafe"将会抑制此警告。
此外,还有一些行为发生变化的边缘情况。
填充
max < min的行为已更改,以在不同 dtype 之间更加一致,但不应依赖此行为。标量
min和max像在所有其他 ufuncs 中一样参与提升规则。
__array_interface__ 偏移量现在按文档工作#
该接口可能使用了被错误忽略的 offset 值。
savez 中的 Pickle 协议设置为 3,用于 force zip64 标志#
savez 未使用 force_zip64 标志,这限制了存档的大小为 2GB。但是使用该标志要求我们使用 pickle 协议 3 来写入 object 数组。使用的协议已升级到 3,这意味着该存档将无法被 Python2 读取。
使用不存在的字段索引的结构化数组会引发 KeyError 而不是 ValueError#
结构化类型的 arr['bad_field'] 会引发 KeyError,以与 dict['bad_field'] 保持一致。