NumPy 1.14.0 发布说明#
Numpy 1.14.0 是七个月工作的成果,包含了大量的错误修复和新功能,以及一些可能存在兼容性问题的更改。用户最明显的变化是 numpy 数组和标量打印方式的风格变化,这将影响 doctests。关于如何在需要时保留旧的打印风格的详细信息,请参见下文。
一项影响未来开发的重要决策是关于在 2020 年前逐步停止支持 Python 2.7 的计划。已决定在 2018 年的所有发布版本中支持 2.7,最后一个版本将被指定为长期支持版本,bug 修复支持将延长至 2019 年。2019 年,所有新发布版本将停止支持 2.7。更多细节可在 NEP 12 中找到。
此版本支持 Python 2.7 和 3.4 - 3.6。
亮点#
在可能的情况下,np.einsum 函数将使用 BLAS。
genfromtxt、loadtxt、fromregex和savetxt现在可以处理具有 Python 支持的任意编码的文件。对 NumPy 数组和标量的打印进行了重大改进。
新增函数#
parametrize:在 numpy.testing 中添加了装饰器。chebinterpolate:在 Chebyshev 点插值函数。format_float_positional和format_float_scientific:通过控制舍入和填充来无歧义地格式化浮点标量。PyArray_ResolveWritebackIfCopy和PyArray_SetWritebackIfCopyBase,新的 C-API 函数,对于实现 PyPy 兼容性很有用。
弃用#
使用
np.bool_对象代替整数已弃用。以前operator.index(np.bool_)是合法的,并允许像[1, 2, 3][np.True_]这样的构造。这是误导性的,因为它与np.array([1, 2, 3])[np.True_]的行为不同。空数组的真值测试已弃用。要检查数组是否非空,请使用
array.size > 0。使用
minlength=None调用np.bincount已弃用。应改用minlength=0。使用
sep参数的默认值调用np.fromstring已弃用。当未提供该参数时,将使用np.frombuffer的一个损坏版本,该版本会默默接受 unicode 字符串,并在(python 3 中将其编码为 utf-8,python 2 中编码为默认编码后)将其视为二进制数据。如果需要读取二进制数据,应直接使用np.frombuffer。在非遗留打印模式下,array2string 的
style选项已弃用。PyArray_SetUpdateIfCopyBase已弃用。对于 NumPy 版本 >= 1.14,请改用PyArray_SetWritebackIfCopyBase,更多详细信息请参见下方的 C API 更改。使用
UPDATEIFCOPY数组已弃用,详情请参见下方的 C API 更改。我们不会停止支持这些数组,但它们与 PyPy 不兼容。
未来变化#
np.issubdtype将停止向下转换 dtype 类参数。可能期望issubdtype(np.float32, 'float64')和issubdtype(np.float32, np.float64)的含义相同——然而,有一个未公开的特殊情况会将前者翻译为issubdtype(np.float32, np.floating),从而得出意外的 True 结果。此翻译现在会发出警告,解释正在发生的翻译。将来,翻译将禁用,第一个示例将等同于第二个。
np.linalg.lstsq的rcond默认值将更改。np.linalg.lstsq的rcond参数的默认值将更改为机器精度乘以输入数组维度的最大值。当未显式传递rcond时,会发出 FutureWarning。a.flat.__array__()在a非连续时将返回一个可写副本。以前,当a可写时,它会返回一个 UPDATEIFCOPY 数组。目前它返回一个不可写副本。有关此问题的讨论,请参见 gh-7054。非结构化 void 数组的
.item方法将返回一个 bytes 对象。将来,对np.void数据类型的数组或标量调用.item()将返回一个bytes对象,而不是 buffer 或 int 数组,这与bytes(void_scalar)返回的对象相同。这可能会影响假设返回值是可变的代码,因为它将不再是这种情况。现在,当发生这种情况时,会发出FutureWarning。
兼容性说明#
掩码数组视图的掩码也是视图而非副本#
NumPy 1.11.x 中曾就此更改发出过 FutureWarning。简而言之,现在当更改掩码数组的视图时,对掩码的更改会传播到原始数组。以前并非如此。此更改特别影响切片。请注意,如果原始数组的掩码是 nomask 且视图的掩码被更改,此功能尚不能正常工作。有关详细讨论,请参见 gh-5580。通过调用视图的 unshare_mask 方法可以获得原始行为,即拥有掩码的副本。
np.ma.masked 不再可写#
尝试修改 masked 常量现在会报错,因为底层数组被标记为只读。过去,可以进行
# emulating a function that sometimes returns np.ma.masked
val = random.choice([np.ma.masked, 10])
var_arr = np.asarray(val)
val_arr += 1 # now errors, previously changed np.ma.masked.data
np.ma 生成 fill_value 的函数已更改#
以前,np.ma.default_fill_value 会返回一个 0d 数组,但 np.ma.minimum_fill_value 和 np.ma.maximum_fill_value 会返回字段的元组。现在,这三个方法都返回一个结构化的 np.void 对象,这与在 .fill_value 属性中找到的内容相同。
此外,dtype 的推断现在与 np.array 匹配——因此,当传递 python 标量 x 时,maximum_fill_value(x) 始终与 maximum_fill_value(np.array(x)) 相同。以前,Python 2 上的 x = long(1) 会违反此假设。
a.flat.__array__() 在 a 非连续时返回不可写数组#
目的是,以前在 a 非连续时返回的 UPDATEIFCOPY 数组将在未来被一个可写副本取代。这一临时措施旨在通知那些期望在此情况下修改底层数组的人,这种情况将不再发生。最有可能注意到这一点的地方是使用 np.asarray(a.flat) 形式的表达式,或者当 a.flat 作为 ufunc 的 out 参数传递时。
np.tensordot 现在在收缩 0 长度维度时返回零数组#
以前 np.tensordot 在收缩 0 长度维度时会引发 ValueError。现在它返回一个零数组,这与 np.dot 和 np.einsum 的行为一致。
numpy.testing 重组#
这预计不会引起问题,但可能有所遗漏。如果您在使用 numpy.testing 时遇到意外的导入问题,请告知我们。
np.asfarray 不再通过 dtype 参数接受非 dtypes#
以前,这会接受 dtype=some_array,其隐含的语义是 dtype=some_array.dtype。这是未文档化的,在 numpy 函数中是唯一的,并且如果使用,很可能代表一个拼写错误。
1D np.linalg.norm 保持浮点输入类型,即使对于任意阶数#
以前,这会在传递任意阶数时提升到 float64,尽管在简单情况下不会这样做。
>>> f32 = np.float32([[1, 2]])
>>> np.linalg.norm(f32, 2.0, axis=-1).dtype
dtype('float32')
>>> np.linalg.norm(f32, 2.0001, axis=-1).dtype
dtype('float64') # numpy 1.13
dtype('float32') # numpy 1.14
此更改仅影响 float32 和 float16 数组。
count_nonzero(arr, axis=()) 现在计算零个轴,而不是所有轴#
在其他地方,axis==() 始终被理解为“无轴”,但 count_nonzero 有一个特殊情况将其视为“所有轴”。这是不一致且令人惊讶的。计算所有轴的正确方法始终是传递 axis == None。
添加到测试目录的 __init__.py 文件#
这是为了在不同目录中重复的测试文件名情况下兼容 pytest。因此,run_module_suite 不再工作,即 python <path-to-test-file> 会导致错误。
对非结构化 void 数组调用 .astype(bool) 现在会调用每个元素的 bool#
在 Python 2 上,void_array.astype(bool) 总是会返回一个 True 数组,除非 dtype 是 V0。在 Python 3 上,此操作通常会崩溃。今后,astype 将匹配 bool(np.void) 的行为,将所有零的缓冲区视为 false,将其他任何内容视为 true。仍然可以使用 arr.dtype.itemsize == 0 来检查 V0。
MaskedArray.squeeze 永远不会返回 np.ma.masked#
np.squeeze 的文档说明它返回一个视图,但掩码变体有时会返回 masked,它不是一个视图。这已得到修复,因此结果始终是原始掩码数组的视图。这会破坏任何使用 masked_arr.squeeze() is np.ma.masked 的代码,但会修复写入 squeeze() 结果的代码。
将 can_cast 的第一个参数从 from 重命名为 from_#
以前的参数名 from 是 Python 中的保留关键字,这使得按名称传递参数变得困难。已通过将参数重命名为 from_ 来解决此问题。
isnat 在传递错误类型时引发 TypeError#
ufunc isnat 以前在未传递 datetime 或 timedelta 类型变量时会引发 ValueError。这已更改为引发 TypeError。
dtype.__getitem__ 在传递错误类型时引发 TypeError#
当使用浮点数进行索引时,dtype 对象以前会引发 ValueError。
用户定义的类型现在需要实现 __str__ 和 __repr__#
以前,用户定义的类型可以回退到 numpy 中实现的 __str__ 和 __repr__ 的默认实现,但现在已移除。现在用户定义的类型将回退到 python 的默认 object.__str__ 和 object.__repr__。
数组打印的许多更改,可以通过新的“遗留”打印模式禁用#
ndarray 和 numpy 标量的 str 和 repr 已以多种方式更改。这些更改很可能会破坏下游用户的 doctests。
可以通过调用 np.set_printoptions(legacy="1.13"),或使用 np.array2string 的新 legacy 参数(例如 np.array2string(arr, legacy='1.13'))来启用新的 1.13 “遗留”打印模式,从而禁用这些新行为以在很大程度上重现 numpy 1.13 的行为。
总而言之,主要的更改是
对于浮点类型
浮点数组的
repr通常会省略先前在符号位置打印的一个空格。请参见np.set_printoptions的新sign选项。浮点数组和标量使用新的十进制表示算法,给出最短的唯一表示。这通常会缩短
float16的小数输出,有时也会缩短float32和float128的输出。float64应不受影响。请参见np.set_printoptions的新floatmode选项。以科学记数法打印的浮点数组不再使用固定精度,而是显示最短的唯一表示。
浮点标量的
str在 python2 中不再被截断。
对于其他数据类型
非有限复数标量打印为
nanj而不是nan*j。NaT值在 datetime 数组中现在会正确对齐。np.void数据类型的数组和标量现在使用十六进制表示法打印。
对于换行
如果 ndarray repr 的“dtype”部分在数组输出的最后一行没有空间,它现在将被打印到下一行。
linewidth格式选项现在始终被遵守。数组的 repr 或 str 永远不会超过此宽度,除非单个元素太宽。数组字符串的最后一行永远不会比前几行包含更多的元素。
如果元素太宽,第一行不再插入额外的空格。
对于摘要(使用
...来缩短长数组)对于
str,不再插入尾随逗号。以前,str(np.arange(1001))会给出'[ 0 1 2 ..., 998 999 1000]',其中有一个额外的逗号。对于 2 维及以上的数组,当
...被打印在单独一行以总结除最后轴以外的所有轴时,现在会在该行附加换行符以匹配其开头的换行符,并删除尾随空格字符。
MaskedArray数组现在使用逗号分隔打印的元素,始终打印 dtype,并正确地将长数组的元素换行到多行。如果维度大于 1,数组属性现在以新的“左对齐”打印样式打印。recarray数组不再在其 dtype 前打印尾随空格,并能正确换行到适当的列数。0d 数组不再拥有自己特有的
str和repr实现。np.array2string的style参数已弃用。布尔(
bool)数据类型的数组将在repr中省略数据类型。用户定义的
dtypes(np.generic的子类)现在需要实现__str__和__repr__。
其中一些更改将在下面更详细地描述。如果您需要保留以前的行为以进行 doctests 或其他原因,您可能需要执行类似以下的操作:
# FIXME: We need the str/repr formatting used in Numpy < 1.14.
try:
np.set_printoptions(legacy='1.13')
except TypeError:
pass
C API 更改#
UPDATEIFCOPY 数组的 PyPy 兼容替代方案#
UPDATEIFCOPY 数组是现有数组的连续副本,可能具有不同的维度,当它们的引用计数归零并被释放时,其内容会被复制回原始数组。由于 PyPy 不使用引用计数,因此它们与 PyPy 无法正确工作。NumPy 正在逐步淘汰其内部使用,并添加了两个新的 C-API 函数,
PyArray_SetWritebackIfCopyBasePyArray_ResolveWritebackIfCopy,
以及一个补充标志 NPY_ARRAY_WRITEBACKIFCOPY。使用新功能还需要在创建新数组时更改某些标志,即:NPY_ARRAY_INOUT_ARRAY 应被替换为 NPY_ARRAY_INOUT_ARRAY2,NPY_ARRAY_INOUT_FARRAY 应被替换为 NPY_ARRAY_INOUT_FARRAY2。使用这些新标志创建的数组将具有 WRITEBACKIFCOPY 语义。
如果 PyPy 兼容性不是问题,可以忽略这些新函数,尽管会有 DeprecationWarning。如果您确实希望实现 PyPy 兼容性,可以在 c-api 文档和 how-to-extend 中的示例中找到有关这些函数及其用法的更多信息。
新功能#
文本 IO 函数的 encoding 参数#
genfromtxt、loadtxt、fromregex 和 savetxt 现在可以通过 encoding 参数处理支持 Python 的任意编码的文件。为向后兼容,该参数默认为特殊的 bytes 值,该值继续将文本视为原始字节值,并继续将 latin1 编码的字节传递给自定义转换器。使用任何其他值(包括 None 表示系统默认值)会将函数切换到真正的文本 IO,这样在结果数组中就会收到 unicode 字符串而不是字节。
numpy.testing.Tester 可使用外部 nose 插件#
numpy.testing.Tester 现在可以识别 nose 插件(这些插件不在 nose 内置插件之外)。这允许使用例如 nose-timer,如下所示: np.test(extra_argv=['--with-timer', '--timer-top-n', '20']) 以获取 20 个最慢测试的运行时间。Tester.test 还添加了一个额外的关键字参数 timer,因此 np.test(timer=20) 也会报告 20 个最慢的测试。
在 numpy.testing 中添加了 parametrize 装饰器#
NumPy 的 numpy.testing 中现在提供了一个基本的 parametrize 装饰器。它旨在允许重写 pytest 中已弃用的 yield 方式的测试,以方便将来迁移到 pytest。nose 测试框架已弃用多年,看起来像是被放弃的软件。
新的 parametrize 装饰器不具备 pytest 中装饰器的全部功能。它不适用于类,不支持嵌套,也不替换变量名。即便如此,它也应该足够用于重写 NumPy 的测试。
在 numpy.polynomial.chebyshev 中添加了 chebinterpolate 函数#
新的 chebinterpolate 函数在第一类 Chebyshev 点上对给定函数进行插值。一个新的 Chebyshev.interpolate 类方法通过第一类 Chebyshev 点的缩放和移位来支持任意区间的插值。
Python 3 中支持读取 lzma 压缩的文本文件#
对于包含 lzma 模块的 Python 版本,文本 IO 函数现在可以透明地读取扩展名为 xz 或 lzma 的文件。
在 np.setprintoptions 和 np.array2string 中添加了 sign 选项#
此选项控制浮点类型符号的打印,可以是字符‘-’、‘+’或‘ ’之一。使用‘+’时,numpy总是打印正值的符号;使用‘ ’时,它在正值的符号位置总是打印一个空格(空白字符);使用‘-’时,它将省略正值的符号字符。新的默认值为‘-’。
这个新默认值改变了与numpy 1.13相比的浮点输出。在1.13“旧式”打印模式下可以获得旧的行为,请参阅上文的兼容性说明。
hermitian选项已添加到np.linalg.matrix_rank#
新的hermitian选项允许在标准SVD矩阵秩计算和针对对称/hermitian矩阵更高效的特征值方法之间进行选择。
threshold和edgeitems选项已添加到np.array2string#
这些选项以前可以使用np.set_printoptions进行控制,但现在可以作为np.array2string的参数进行每次调用的控制。
concatenate和stack增加了out参数#
现在可以使用所需dtype的预分配缓冲区作为这些函数的输出。
支持Windows上的PGI flang编译器#
PGI flang编译器是NVIDIA发布的LLVM的Fortran前端,采用Apache 2许可证。可以通过以下方式调用:
python setup.py config --compiler=clang --fcompiler=flang install
对于这个新编译器,经验很少,所以任何使用它的用户的反馈都将不胜感激。
改进#
random.noncentral_f中的分子自由度只需为正即可。#
在NumPy 1.14.0之前,分子自由度需要大于1,但该分布对大于0的值有效,而现在要求是大于0。
所有np.einsum变体都释放了GIL#
在NumPy 1.14.0之前,一些具有加速循环版本的特定循环结构没有释放GIL。这个疏忽已经得到修复。
如果可能,np.einsum函数将使用BLAS并默认优化#
现在np.einsum函数将在适当时调用np.tensordot。由于np.tensordot在可能的情况下使用BLAS,这将加快执行速度。默认情况下,np.einsum也将尝试优化,因为开销相对于潜在的速度提升很小。
f2py现在处理0维数组#
f2py现在允许分配0维数组。这允许在下游对边界情况进行更一致的处理。
numpy.distutils支持同时使用MSVC和mingw64-gfortran#
Numpy distutils现在支持同时使用Mingw64 gfortran和MSVC编译器。这使得在Windows上生成包含Fortran代码的Python扩展模块成为可能,同时保留与Python.org分发的二进制文件的兼容性。并非所有用例都得到支持,但包装Fortran用于Python的最常见方法是有效的。
在此模式下编译通常会自动启用,并且可以通过setup.py的--fcompiler和--compiler选项进行选择。此外,还支持将Fortran代码链接到静态OpenBLAS;默认情况下,会查找与gfortran兼容的静态存档openblas.a。
np.linalg.pinv现在适用于堆叠矩阵#
以前它仅限于单个二维数组。
numpy.save将数据对齐到64字节而不是16字节#
使用numpy.save以npy格式保存NumPy数组时,会在数组数据之前插入填充以将其对齐到64字节。以前只有16字节(由于版本2代码中的一个错误,有时会更少)。现在对齐是64字节,这与最宽的常用SIMD指令集和最常见的缓存行大小相匹配。这使得npy文件更容易在打开它们的程序中使用mmap,尤其是在Linux上,其中mmap偏移量必须是页面大小的倍数。
NPZ文件现在可以写入,而无需使用临时文件#
在Python 3.6+中,numpy.savez和numpy.savez_compressed现在直接写入ZIP文件,而无需创建中间临时文件。
更好地支持空结构体和字符串类型#
结构体类型可以包含零个字段,字符串dtype可以包含零个字符。零长度字符串仍然无法直接创建,必须通过结构体dtype构建。
str0 = np.empty(10, np.dtype([('v', str, N)]))['v']
void0 = np.empty(10, np.void)
一直以来都可以处理这些,但现在支持以下对这些数组的操作:
arr.sort()
arr.view(bytes)
arr.resize(…)
pickle.dumps(arr)
支持np.lib.financial中的decimal.Decimal#
除非另有说明,否则financial包中的所有函数现在都支持使用decimal.Decimal内置类型。
浮点打印现在使用“dragon4”算法获得最短的十进制表示#
浮点值(16、32、64和128位)的str和repr现在会打印出最短的十进制表示,以便从同类型的值中唯一地标识该值。以前只有float64值才有这种情况。其余的浮点类型现在通常比numpy 1.13中的值要短。以科学记数法打印的数组现在也使用最短的科学表示,而不是像以前那样固定精度。
此外,浮点标量的
str在python2中将不再被截断,不像python2的float。np.double标量的str和repr现在与python3的float相同。
提供了新的函数np.format_float_scientific和np.format_float_positional来生成这些十进制表示。
floatmode新选项已添加到np.set_printoptions和np.array2string中,该选项控制数组中打印元素的唯一性和四舍五入。新默认值为floatmode='maxprec',precision=8,它将打印最多8位小数,如果元素可以用更少位数唯一表示,则打印更少。一个有用新模式是floatmode="unique",它将输出足够的位数来唯一指定数组元素。
值如inf*j或nan*j的NumPy复数浮点标量现在打印为infj和nanj,就像纯Python的complex类型一样。
FloatFormat和LongFloatFormat类已被弃用,应全部替换为FloatingFormat。同样,ComplexFormat和LongComplexFormat应替换为ComplexFloatingFormat。
void数据类型元素现在以十六进制表示形式打印#
现在为非结构化np.void元素(例如V4数据类型)打印与Pythonbytes类型兼容的十六进制表示。以前,在python2中,原始void元素数据会打印到stdout,或者在python3中,会显示整数字节值。
用于void数据类型的打印样式现在可以独立自定义#
现在可以使用np.set_printoptions的formatter参数,使用'void'键,而不是像以前那样使用笼统的numpystr键,来独立自定义np.void数组的打印样式。
减少了np.loadtxt的内存使用#
np.loadtxt现在分块读取文件,而不是一次性读取,这显著减少了其对大文件的内存使用。
更改#
结构化数组的多字段索引/赋值#
结构体数组使用多个字段的索引和赋值在多个方面发生了变化,如之前版本中已警告的那样。
首先,使用多个字段索引结构体数组,例如arr[['f1', 'f3']],将返回原始数组的视图而不是副本。返回的视图将包含对应于原始数组中中间字段的额外填充字节,这与1.13中的副本不同,这将影响类似arr[['f1', 'f3']].view(newdtype)的代码。
其次,结构体数组之间的赋值现在将“按位置”发生,而不是“按字段名”发生。目标数组的第N个字段将被设置为源数组的第N个字段,而不管字段名,这与numpy 1.6到1.13版本不同,在那些版本中,目标数组的字段被设置为源数组中同名字段,或者如果源没有该字段则设置为0。
相应地,在计算dtype相等性时,结构体dtype中字段的顺序现在很重要。例如,对于dtype
x = dtype({'names': ['A', 'B'], 'formats': ['i4', 'f4'], 'offsets': [0, 4]})
y = dtype({'names': ['B', 'A'], 'formats': ['f4', 'i4'], 'offsets': [4, 0]})
表达式x == y现在将返回False,而以前则不是。这使得基于字典的dtype规范(如dtype({'a': ('i4', 0), 'b': ('f4', 4)}))在python < 3.6中是危险的,因为在那些版本中不保留dict键的顺序。
从结构体数组赋值给布尔数组现在会引发ValueError,而1.13中则始终将目标元素设置为True。
从具有多个字段的结构体数组赋值给非结构体数组现在会引发ValueError。在1.13中,这只会将源的第一个字段复制到目标。
现在不允许在多字段索引中使用字段“标题”,重复字段名在多字段索引中也是不允许的。
用户指南中关于结构体数组的文档已显著更新,以反映这些更改。
整数和Void标量现在不受np.set_string_function的影响#
以前,与大多数其他numpy标量不同,整数和void标量的str和repr可以通过np.set_string_function控制。现在不再可能。
0d数组打印已更改,array2string的style参数已弃用#
以前,0d数组的str和repr具有特有的实现,对于0d数组a,它们分别返回str(a.item())和'array(' + repr(a.item()) + ')',这与numpy标量和更高维的ndarray都不同。
现在,0d数组的str表现得像numpy标量,使用str(a[()]),而repr表现得像更高维的数组,使用formatter(a[()]),其中formatter可以使用np.set_printoptions指定。 np.array2string的style参数已被弃用。
此新行为在1.13旧式打印模式下是禁用的,请参阅上文的兼容性说明。
使用数组播种RandomState需要一维数组#
RandomState以前会接受空数组或具有2个或更多维度的数组,这导致了播种失败(空数组)或在设置种子时忽略了部分传入值。
MaskedArray对象显示了更有用的repr#
现在MaskedArray的repr更接近于能生成它的Python代码,数组现在会显示逗号和dtype。与其他格式更改一样,可以通过1.13旧式打印模式禁用此功能,以帮助迁移doctests。
np.polynomial类的repr更加明确#
它现在显示domain和window参数作为关键字参数,使其更加清晰。
>>> np.polynomial.Polynomial(range(4))
Polynomial([0., 1., 2., 3.], domain=[-1, 1], window=[-1, 1])