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 wheels 包是基于 OpenBLAS 开发分支构建的,以避免这些问题。

亮点#

  • 新增了一个可扩展的 random 模块,以及四个可选择的随机数生成器和改进的种子,专为并行进程设计。目前可用的比特生成器有 MT19937PCG64PhiloxSFC64。详见下文的“新特性”部分。

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

  • 新增了基数排序 (radix sort) 和 TimSort 排序方法。目前无法选择使用哪种方法。它们硬编码到数据类型中,并在将 stablemergesort 作为方法传入时使用。详见下文的“改进”部分。

  • 默认情况下现在可以重写 NumPy 函数,详见下文的 __array_function__

新增函数#

弃用#

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

以前,此模块中的函数会接受 float 值,只要它们是整型的(例如 1.0, 2.0 等)。为了与 NumPy 的其余部分保持一致,现在弃用这种做法,未来将引发 TypeError

同样,传入 0.5 这样的浮点数而非整数,现在将引发 TypeError,而不是以前的 ValueError

弃用 numpy.distutils.exec_commandtemp_file_name#

这些函数的内部使用已被重构,现在有更好的替代方案。请用 subprocess.Popen 替换 exec_command,用 tempfile.mkstemp 替换 temp_file_name

C-API 封装数组的可写标志#

当从 C-API 创建数组以封装数据指针时,我们判断数据读写特性的唯一依据是在创建过程中设置的 writeable 标志。强制将该标志设置为可写是危险的。未来将无法从 Python 将可写标志切换为 True。此弃用不应影响太多用户,因为以这种方式创建的数组在实践中非常罕见,且只能通过 NumPy C-API 获得。

numpy.nonzero 不应再在 0 维数组上调用#

numpy.nonzero 在 0 维数组上的行为令人惊讶,导致其使用几乎总是不正确的。如果意图保留旧行为,则可以使用 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 中形状为 1 的字段不会被折叠成标量#

目前,指定为 [(name, dtype, 1)]"1type" 的字段被解释为标量字段(即与 [(name, dtype)][(name, dtype, ()] 相同)。这现在会引发一个 FutureWarning;在未来版本中,它将被解释为形状为 (1,) 的字段,即与 [(name, dtype, (1,))]"(1,)type" 相同(与 n>1 时的 [(name, dtype, n)] / "ntype" 保持一致,它们已经等价于 [(name, dtype, (n,))] / "(n,)type")。

兼容性说明#

float16 次正规数舍入#

在某些边界情况下,从不同的浮点精度转换为 float16 使用了不正确的舍入。这意味着在极少数情况下,次正规数结果现在将向上舍入而不是向下,从而改变结果的最后一位(ULP)。

使用 divmod 时的有符号零#

从版本 1.12.0 开始,NumPy 在使用 divmodfloor_divide 函数且结果为零时,错误地返回了一个负零。例如:

>>> 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 参数与其它参数存在内存重叠,现在会对其进行缓冲以避免依赖于顺序的行为。

加载时反序列化需要明确选择加入#

鉴于 CVE-2019-6446,函数 loadlib.format.read_array 接受的 allow_pickle 关键字参数现在默认为 False

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

由于 log 应用于随机浮点数时存在的错误,当从 betabinomiallaplacelogisticlogseriesmultinomial 采样时,如果底层 MT19937 随机流中生成了 0,则流可能会改变。发生这种情况的概率为 \(10^{53}\) 分之一,因此对于任何给定的种子,流发生变化的概率极小。如果底层生成器中遇到 0,则现在会丢弃产生的不正确值(即 numpy.infnumpy.nan)。

i0 现在总是返回与输入相同形状的结果#

以前,输出会被压缩,例如,仅包含单个元素的输入会导致返回一个数组标量,而形状如 (10, 1) 的输入会产生不会与输入进行广播的结果。

请注意,我们通常推荐 SciPy 的实现而不是 NumPy 的:它是一个用 C 编写的合适 ufunc,并且速度快一个数量级以上。

can_cast 不再假设允许所有不安全类型转换#

以前,can_castcasting='unsafe' 的几乎所有输入都返回 True,即使在类型转换不可能的情况下,例如从结构化 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 则为 [1, 1, 0, 0, 0, 0, 0, 0]

unpackbits 现在接受一个 count 参数#

count 允许预先选择要解包的比特数子集,而不是后续重塑和子集化,这使得 packbits 操作可逆,并且解包过程减少浪费。大于可用比特数的计数会添加零填充。负数计数会从末尾裁剪比特,而不是从开头计数。None 计数实现了解包所有内容的现有行为。

linalg.svdlinalg.pinv 在厄米输入上可以更快#

这些函数现在接受一个 hermitian 参数,与 1.14.0 中添加到 linalg.matrix_rank 的参数相匹配。

divmod 操作现在支持两个 timedelta64 操作数#

divmod 运算符现在处理两个 timedelta64 操作数,其类型签名为 mm->qm

fromfile 现在接受一个 offset 参数#

此函数现在接受一个 offset 关键字参数用于二进制文件,该参数指定了从文件当前位置开始的(以字节为单位的)偏移量。默认为 0

pad 的新模式“empty”#

此模式将数组填充到所需形状,但不初始化新条目。

浮点标量实现了 as_integer_ratio 以匹配内置浮点数#

这会返回一个(分子,分母)对,可用于构造 fractions.Fraction

结构化 dtype 对象可以使用多个字段名进行索引#

arr.dtype[['a', 'b']] 现在返回一个等价于 arr[['a', 'b']].dtype 的 dtype,以与 arr.dtype['a'] == arr['a'].dtype 保持一致。

与使用字段列表索引的结构化数组的 dtype 类似,此 dtype 具有与原始 dtype 相同的 itemsize,但只保留字段的子集。

这意味着 arr[['a', 'b']]arr.view(arr.dtype[['a', 'b']]) 是等价的。

.npy 文件支持 Unicode 字段名#

引入了新的格式版本 3.0,该版本支持具有非 latin1 字段名的结构化类型。在需要时会自动使用此版本。

改进#

数组比较断言包含最大差异#

来自数组比较测试(如 testing.assert_allclose)的错误消息现在除了之前的“不匹配”百分比外,还包含“最大绝对差值”和“最大相对差值”。此信息使得更新绝对和相对误差容限变得更容易。

基于 fftpack 的 fft 模块被 pocketfft 库取代#

两种实现都拥有相同的祖先(Paul N. Swarztrauber 的 Fortran77 FFTPACK),但 pocketfft 包含了额外的修改,在某些情况下同时提高了准确性和性能。对于包含大素数因子的 FFT 长度,pocketfft 使用布鲁斯坦算法,该算法保持 \(O(N log N)\) 运行时复杂度,而不是对于素数长度退化为 \(O(N*N)\)。此外,接近素数长度的实值 FFT 的准确性已得到改善,并与复值 FFT 持平。

numpy.ctypeslibctypes 支持的进一步改进#

新增了 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 实现进行了加速#

explog 的 float32 实现现在受益于 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 ufunc#

布尔和整数类型无法存储 naninf 值,这使我们能够提供专门的 ufunc,它们比之前的方法快 250 倍。

isfinite 支持 datetime64timedelta64 类型#

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

新增关键字到 nan_to_num#

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

由分配过大数组引起的 MemoryError 更具描述性#

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

floorceiltrunc 现在尊重内置的魔术方法#

这些 ufunc 在对象数组上调用时会调用 __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 语言中的 descr->f->fastclip 为自定义 dtype 注册 clip 函数已被弃用——它们应该改为使用 ufunc 注册机制,附加到 np.core.umath.clip ufunc。

这也意味着 clip 接受 wherecasting 参数,并且可以通过 __array_ufunc__ 进行重写。

此更改的一个结果是旧 clip 的某些行为已被弃用:

  • 传入 nan 表示“不裁剪”作为边界之一或全部边界。这无论如何也不是在所有情况下都有效,并且通过传入带有适当符号的无穷大值可以更好地处理。

  • 默认情况下在传递 out 参数时使用“不安全”类型转换。显式使用 casting="unsafe" 将抑制此警告。

此外,某些特殊情况下的行为也有变化:

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

  • 标量 minmax 参与提升规则,就像它们在所有其他 ufunc 中一样。

__array_interface__ 偏移现在按文档工作#

该接口可能会使用一个被错误地忽略了的 offset 值。

savez 中的 Pickle 协议在 force zip64 标志下设置为 3#

savez 以前没有使用 force_zip64 标志,这限制了存档的大小为 2GB。但使用该标志要求我们使用 pickle 协议 3 来写入 object 数组。所使用的协议已提升到 3,这意味着该存档将无法被 Python2 读取。

使用不存在的字段索引结构化数组现在引发 KeyError 而不是 ValueError#

在结构化类型上使用 arr['bad_field'] 会引发 KeyError,这与 dict['bad_field'] 的一致性。