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']
的一致性。