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模块,以及四个可选择的随机数生成器和改进的种子,专为并行进程设计。目前可用的比特生成器有 MT19937、PCG64、Philox 和 SFC64。详见下文的“新特性”部分。NumPy 的
FFT实现已从 fftpack 更改为 pocketfft,从而实现了更快、更准确的变换,并能更好地处理素数长度数据集。详见下文的“改进”部分。新增了基数排序 (radix sort) 和 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#
这些函数的内部使用已被重构,现在有更好的替代方案。请用 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 在使用 divmod 和 floor_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), ...)。
在 take、choose、put 中,out 为内存重叠进行缓冲#
如果提供给这些函数的 out 参数与其它参数存在内存重叠,现在会对其进行缓冲以避免依赖于顺序的行为。
加载时反序列化需要明确选择加入#
鉴于 CVE-2019-6446,函数 load 和 lib.format.read_array 接受的 allow_pickle 关键字参数现在默认为 False。
旧随机模块中随机流的潜在变化#
由于 log 应用于随机浮点数时存在的错误,当从 beta、binomial、laplace、logistic、logseries 或 multinomial 采样时,如果底层 MT19937 随机流中生成了 0,则流可能会改变。发生这种情况的概率为 \(10^{53}\) 分之一,因此对于任何给定的种子,流发生变化的概率极小。如果底层生成器中遇到 0,则现在会丢弃产生的不正确值(即 numpy.inf 或 numpy.nan)。
i0 现在总是返回与输入相同形状的结果#
以前,输出会被压缩,例如,仅包含单个元素的输入会导致返回一个数组标量,而形状如 (10, 1) 的输入会产生不会与输入进行广播的结果。
请注意,我们通常推荐 SciPy 的实现而不是 NumPy 的:它是一个用 C 编写的合适 ufunc,并且速度快一个数量级以上。
can_cast 不再假设允许所有不安全类型转换#
以前,can_cast 对 casting='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 模块,以及四个可选择的随机数生成器和改进的种子,专为并行进程设计。目前可用的 比特生成器 有 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 则为 [1, 1, 0, 0, 0, 0, 0, 0]。
unpackbits 现在接受一个 count 参数#
count 允许预先选择要解包的比特数子集,而不是后续重塑和子集化,这使得 packbits 操作可逆,并且解包过程减少浪费。大于可用比特数的计数会添加零填充。负数计数会从末尾裁剪比特,而不是从开头计数。None 计数实现了解包所有内容的现有行为。
linalg.svd 和 linalg.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.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 实现进行了加速#
exp 和 log 的 float32 实现现在受益于 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 ufunc#
isfinite 支持 datetime64 和 timedelta64 类型#
以前,isfinite 在用于这两种类型时会引发 TypeError。
新增关键字到 nan_to_num#
nan_to_num 现在接受关键字 nan、posinf 和 neginf,允许用户分别定义替换 nan、正 np.inf 和负 np.inf 值的值。
由分配过大数组引起的 MemoryError 更具描述性#
MemoryError 的常见原因是广播不正确,导致形状非常大且不正确。现在,错误消息中包含此形状,以帮助诊断失败原因。
floor、ceil 和 trunc 现在尊重内置的魔术方法#
这些 ufunc 在对象数组上调用时会调用 __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 语言中的 descr->f->fastclip 为自定义 dtype 注册 clip 函数已被弃用——它们应该改为使用 ufunc 注册机制,附加到 np.core.umath.clip ufunc。
这也意味着 clip 接受 where 和 casting 参数,并且可以通过 __array_ufunc__ 进行重写。
此更改的一个结果是旧 clip 的某些行为已被弃用:
传入
nan表示“不裁剪”作为边界之一或全部边界。这无论如何也不是在所有情况下都有效,并且通过传入带有适当符号的无穷大值可以更好地处理。默认情况下在传递
out参数时使用“不安全”类型转换。显式使用casting="unsafe"将抑制此警告。
此外,某些特殊情况下的行为也有变化:
填充
max < min的行为已更改,以在不同 dtype 之间更一致,但不应依赖此行为。标量
min和max参与提升规则,就像它们在所有其他 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'] 的一致性。