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 模块,以及四个可选的 随机数生成器,并改进了为并行进程设计的种子生成。当前可用的位生成器是 MT19937PCG64PhiloxSFC64。请参见下文“新功能”。

  • NumPy 的 FFT 实现已从 fftpack 更改为 pocketfft,从而实现了更快、更准确的变换,并能更好地处理素数长度的数据集。请参见下文“改进”。

  • 新增基数排序和 timsort 排序方法。目前无法选择使用哪种方法。它们已硬编码到数据类型中,并在传递 stablemergesort 作为方法时使用。请参见下文“改进”。

  • 现在可以默认覆盖 numpy 函数,请参见下文的 __array_function__

新增函数#

弃用#

numpy.polynomial 函数在传递 float 而非 int 时会发出警告#

以前,此模块中的函数会接受 float 值,只要它们是整数(例如 1.02.0)。为了与 numpy 的其他部分保持一致,这样做现在已被弃用,将来会引发 TypeError

同样,传递 0.5 这样的浮点数而不是整数现在会引发 TypeError,而不是之前的 ValueError

弃用 numpy.distutils.exec_commandtemp_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 开始,当使用 divmodfloor_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), ...)

takechooseput 中,out 会因内存重叠而缓冲#

如果这些函数的 out 参数被提供并且与其他参数存在内存重叠,现在它会被缓冲以避免出现依赖顺序的行为。

加载时的反序列化需要显式选择加入#

函数 loadlib.format.read_array 接受一个 allow_pickle 关键字参数,响应 CVE-2019-6446,该参数现在默认为 False

旧随机模块中随机流的潜在变化#

由于在对随机浮点数应用 log 时存在错误,当从 betabinomiallaplacelogisticlogseriesmultinomial 采样时,如果底层 MT19937 随机流生成了 0,则随机流可能会发生变化。这种情况发生的概率为 \(1\) in \(10^{53}\),因此任何给定种子的流发生变化的概率都极小。如果遇到底层生成器中的 0,那么现在将丢弃生成的错误值(numpy.infnumpy.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 模块,以及四个可选的随机数生成器,并改进了为并行进程设计的种子生成。当前可用的 位生成器MT19937PCG64PhiloxSFC64PCG64 是新的默认值,而 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 中找到。

packbitsunpackbits 接受 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.svdlinalg.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.expnumpy.log 对 float32 实现进行了加速#

float32 实现的 explog 现在受益于 AVX2/AVX512 指令集,这些指令集在运行时会被检测到。exp 的最大 ULP 误差为 2.52,log 的最大 ULP 误差为 3.83。

提高 numpy.pad 的性能#

通过在预先分配的数组中填充所需的填充形状,而不是使用连接,提高了函数在大多数情况下的性能。

numpy.interp 更稳健地处理无穷大#

在某些 interp 之前会返回 nan 的情况下,现在它会返回适当的无穷大。

fromfiletofilendarray.dump 支持 Pathlib#

fromfilendarray.ndarray.tofilendarray.dump 现在支持 file/fid 参数的 pathlib.Path 类型。

为 bool 和 int 类型提供专门的 isnanisinfisfinite ufuncs#

布尔类型和整数类型无法存储 naninf 值,这使得我们可以提供比以前的方法快 250 倍的专门 ufuncs。

isfinite 支持 datetime64timedelta64 类型#

以前,isfinite 在使用这两种类型时会引发 TypeError

nan_to_num 添加了新的关键字参数#

nan_to_num 现在接受 nanposinfneginf 关键字参数,允许用户定义替换 nan、正无穷大和负无穷大 np.inf 值的。

由于分配了过大的数组而导致的 MemoryErrors 更加具有描述性#

MemoryError 的原因通常是广播不正确,导致形状非常大且不正确。错误消息现在包含了该形状,以帮助诊断失败的原因。

floorceiltrunc 现在遵循内置的魔术方法#

这些 ufuncs 现在在对对象数组调用时会调用 __floor____ceil____trunc__ 方法,使其与 decimal.Decimalfractions.Fraction 对象兼容。

quantile 现在可以处理 fraction.Fractiondecimal.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

更改#

medianpercentile 系列函数不再警告 nan#

numpy.mediannumpy.percentilenumpy.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 接受 wherecasting 参数,并且可以用 __array_ufunc__ 进行重写。

此更改的一个后果是,旧 clip 的某些行为已被弃用。

  • nan 作为“不裁剪”的上下限。这在某些情况下 anyway 并不起作用,可以通过传递适当符号的无穷大来更好地处理。

  • 当提供 out 参数时,默认使用“不安全”转换。显式使用 casting="unsafe" 将会抑制此警告。

此外,还有一些行为发生变化的边缘情况。

  • 填充 max < min 的行为已更改,以在不同 dtype 之间更加一致,但不应依赖此行为。

  • 标量 minmax 像在所有其他 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'] 保持一致。