<jupyter_start><jupyter_text>文档阅读说明：- 🐧 表示 Tip- ⚠️ 表示注意事项  广播广播描述了NumPy在数值计算如何处理不同形状的数组。在一定限制条件下，小数组会广播大大数组以适配其形状。<jupyter_code># 最简单的例子
a = np.array([1., 2., 3.])
a
a * 2
# 上面的例子等价于
b = np.array([2, 2, 2])
a * b<jupyter_output><empty_output><jupyter_text>广播的规则如下：- 从右到左比较- 兼容时相等或某个维度为1- 数组可以不同维度<jupyter_code>a = np.ones((8, 1, 6, 1))
b = np.ones((7, 1, 5))
# b 等价于变成了 (1, 7, 1, 5) 的shape
c = a + b
c.shape
# 不能广播的例子
a = np.ones((2, 3, 4))
b = np.ones((2, 3))
a + b
# 当然一维向量数字的数量就是维度
# 这样是不行的
a = np.ones((2, 3))
b = np.ones((2, ))
b.shape
a + b
# 这样就可以
b = np.ones((3, ))
a + b<jupyter_output><empty_output><jupyter_text>大部分时候我们可以无伤地使用它，但当数据量很大时，广播会复制数组，可能会导致内存溢出。<jupyter_code>rng = np.random.default_rng(42)
a = rng.random((102400, 256, 64))
b = rng.random((102400, 256))
c = rng.random((256, 64))
# a=12.5G
102400*256*64*8/1024/1024/1024
# 如果内存小于25G，这里会超出内存，因为差的结果还是会存在临时空间
a[:] = b[:, :, np.newaxis] - c
# 可以用之前提到的 out 参数，不会额外增加内存
d = np.subtract(b[:, :, np.newaxis], c, out=a)<jupyter_output><empty_output><jupyter_text>不过，一般会使用虚拟内存，不用过于担心。  通函数通函数（ufunc）是以逐个元素的方式对ndarray进行操作的函数，支持数组广播、类型转换等。在NumPy中，通用函数都是`np.ufunc`的实例。<jupyter_code>isinstance(np.add, np.ufunc)<jupyter_output><empty_output><jupyter_text>`+`是`np.add`的快捷方式，其他的函数也类似：<jupyter_code>x1 = np.arange(9).reshape((3, 3))
x2 = np.arange(3)
x1 + x2
np.add(x1, x2)
x1 * x2
np.multiply(x1, x2)<jupyter_output><empty_output><jupyter_text>out 参数主要是用来存储计算结果：<jupyter_code>x3 = np.zeros((3, 3), dtype=np.int8)
x3
np.add(x1, x2, out=x3)
x3<jupyter_output><empty_output><jupyter_text>where 参数确定哪些可以存储：<jupyter_code>x4 = np.zeros((3, 3), dtype=np.int8)
x4
np.add(x1, x2, out=x4, where=[True, False, False])
x4<jupyter_output><empty_output><jupyter_text>NumPy中的ufunc数量很多，包括数学运算、三角函数、位操作、逻辑函数、浮点函数等，具体可查看：- https://numpy.org/devdocs/reference/ufuncs.htmlavailable-ufuncsufunc可以被`__array_ufunc__`方法覆盖，具体可参考第一章《核心概念：自定义数组容器》。ufunc支持以下方法：- reduce：沿着某个维度累积- accumulate：所有元素累积- reduceat：沿着某个维度指定的slice累积- outer：对A和B中所有元素对运算- at：对指定索引的元素的执行无缓冲的就地运算<jupyter_code>a = np.arange(12).reshape(4, 3)
a<jupyter_output><empty_output><jupyter_text>`reduce`的参数和初级课程介绍的很多接口差不多：- array：数组- axis：维度- dtype：数据类型- out：同上- where：同上- keepdims：是否保持维度，《基础教程》中有介绍- initial：初始值<jupyter_code>np.add.reduce(a, axis=0, initial=10)
np.add.reduce(a, axis=1, initial=10, keepdims=True)<jupyter_output><empty_output><jupyter_text>`accumulate`的参数少很多：- array：数组- axis：维度- dtype：数据类型- out：同上<jupyter_code>a
# 沿着行
np.multiply.accumulate(a, axis=1)
np.multiply.accumulate(a)<jupyter_output><empty_output><jupyter_text>`reduceat`相比`accumulate`增加了索引位置：- indices：index的复数- 其他参数同`accumulate`计算 `array[indices[i]:indices[i+1]]`，i 表示第i行/列，计算规则如下：- 当`i = len(indices) - 1`（最后一个index）：`indices[i+1] = array.shape[axis]`- 当`indices[i] >= indices[i + 1]`，第i个就是 `array[indices[i]]`- 当`indices[i] >= len(array)` 或 `indices[i] < 0`，错误<jupyter_code>a
np.add.reduceat(a, [1,2])
# 四列
# 第0列：indices[0]: indices[1]，即第0:2累积（0+1列）
# 第1列：indices[1] > indices[2]，等于第2列
# 第2列：indices[2] > indices[3]，等于第1列
# 第3列：最后一个，indices[3]: indices[a.shape[1]=3]，等于0:3累积（0,1,2列）
np.add.reduceat(a, [0, 2, 1, 0], axis=1)<jupyter_output><empty_output><jupyter_text>`outer`接受两个数组，等价于以下结果：```pythonr = empty(len(A),len(B))for i in range(len(A)):    for j in range(len(B)):        r[i,j] = op(A[i], B[j])```<jupyter_code>np.add.outer(a, a).shape
np.add.outer([1,2,3], [4,5,6])<jupyter_output><empty_output><jupyter_text>`at` 参数如下：- a：数组- indices：索引- b：两个操作对象时另一个操作对象<jupyter_code>a = np.arange(12).reshape(4, 3)
a
# 对第0和1行加1
np.add.at(a, [0, 1], [1])
a
# 4x3 和 1x3 可以通过广播运算
a = np.arange(12).reshape(4, 3)
a
np.add.at(a, [0, 1], np.array([[1, 2, 3]]))
a<jupyter_output><empty_output><jupyter_text>可以使用`np.fromfunc`来生成通函数实例。本章后面会进一步介绍，不再赘述。  基本操作其中不少常用的操作我们在《从入门到小白》中已有介绍，比如`shape`, `reshape`, `ravel`, `squeeze`, `expand_dims`, `stack`, `concatenate`, `split`, `repeat`等，接下来就介绍下剩下的一些使用频率稍低但也比较重要的操作。  Shape<jupyter_code>rng = np.random.default_rng(42)
a = rng.integers(0, 10, (3, 4))
a
# flat
list(a.flat)
a.flatten()
# 不同的Style
a.flatten("F")
# 等价于ravel
np.ravel(a)
np.ravel(a, "F")<jupyter_output><empty_output><jupyter_text>轴变换<jupyter_code>a = np.ones((3, 4, 5))
np.moveaxis(a, 0, 1).shape
# 坐标轴的值可以是数组
np.moveaxis(a, [0, 2], [1, 0]).shape
# 坐标轴的值必须是整数
np.swapaxes(a, 0, 1).shape<jupyter_output><empty_output><jupyter_text>维度<jupyter_code>np.atleast_1d(1)
np.atleast_1d(1, 2)
np.atleast_2d(1, 2)
a = np.ones((2, 3))
np.atleast_3d(a).shape<jupyter_output><empty_output><jupyter_text>构造 `block`经常被用于构建block矩阵。<jupyter_code>a = np.eye(2)*2
b = np.eye(3)*3
np.block([
    [a, np.zeros((2, 3))],
    [np.ones((3, 2)), b]
])<jupyter_output><empty_output><jupyter_text>另外，block在某些情况下等价于其他几个API：- depth=1时，可以作为`hstack`- depth=2时，可以作为`vstack`- 可以替换`atleast_1d`和`atleast_2d` 《从小白到入门》中我们提过，`concatenate`和`hstack`, `vstack`, `dstack`是可以通用的，前者加上不同的维度就可以达到后几者的效果。不过后几个是可以处理0维的（也就是整数），前者不可以；处理向量时也有一些不同。如果是二维以上的，建议还按照之前的做法；对于一维或零维的，也可以先处理一下再按之前的做法。<jupyter_code>a = np.array([0, 1, 2])
np.hstack((a, 3))
# 这样方可以
np.concatenate((a, [3]))
np.vstack((a, (3,4,5)))
np.concatenate((np.atleast_2d(a), [[3,4,5]]))<jupyter_output><empty_output><jupyter_text>`tile`用于构建对输入数组值重复给定次数的数组。<jupyter_code>a = np.array([0, 1, 2])
np.tile(a, 2)<jupyter_output><empty_output><jupyter_text>注意与`repeat`的区别：<jupyter_code>np.repeat(a, 2)<jupyter_output><empty_output><jupyter_text>可以指定重复多个维度：<jupyter_code>np.tile(a, (2, 2, 2)).shape<jupyter_output><empty_output><jupyter_text>对于多维数组也是类似的：<jupyter_code>b = np.array([[1,2], [3,4]])
np.tile(b, 2)
np.repeat(b, 2, axis=1)
np.tile(b, (4, 1)).shape<jupyter_output><empty_output><jupyter_text>增删元素 `delte`用来删除元素，如果不指定axis，则会被打平；否则会删除对应index的所有元素。<jupyter_code>a = np.arange(6).reshape(3, 2)
a
np.delete(a, 5)
np.delete(a, [3, 5])
# 指定axis=0，删除行
np.delete(a, 1, 0)<jupyter_output><empty_output><jupyter_text>`insert`用来插入元素，与删除类似。<jupyter_code>a
# 在index=1的位置插入-1，不指定axis
np.insert(a, 1, -1)
# 在index=[1,2]的位置插入-1，不指定axis
np.insert(a, [1,2], -1)
# 在index=1的位置插入[-1, -2]，不指定axis
np.insert(a, 1, [-1, -2])
# 在index=[1,2]的位置插入[-1, -2]，不指定axis
np.insert(a, [1,2], [-1, -2])
# 指定axis，index=1的位置插入
np.insert(a, 1, -1, axis=0)
# index=1的位置插入不同值
np.insert(a, 1, [-1, -2], axis=0)
np.insert(a, [1,2], -1, axis=0)
np.insert(a, [1,2], [-1, -2], axis=0)
np.insert(a, [1,2], [[-1, -2]], axis=0)<jupyter_output><empty_output><jupyter_text>`append`类似于Python的append。<jupyter_code>np.append(a, 1)
np.append(a, [1,2])
a
np.append(a, [[6,7]], axis=0)
np.append(a, [[6],[7],[8]], axis=1)<jupyter_output><empty_output><jupyter_text>`trim_zeros`用于清理一维数组或序列的零值。<jupyter_code>a = np.array((0, 0, 0, 1, 2, 3, 0, 2, 1, 0))
np.trim_zeros(a)
np.trim_zeros(a, "b")
np.trim_zeros(a, "f")<jupyter_output><empty_output><jupyter_text>重排元素 `flip`主要是翻转元素：<jupyter_code>a = np.arange(8).reshape((2,2,2))
np.flip(a)
np.flip(a, [0,1,2])
np.flip(a, 0)
np.flip(a, 1)
np.flip(a, 2)
np.flip(a, [0,1])<jupyter_output><empty_output><jupyter_text>`roll`和`rot90`用于旋转元素，不过两者并不一样。 `roll`用于旋转给定轴的元素，参数包括：- 数组- shift：整数或元组整数，旋转的数量，如果是tuple，要和对应的axis一样长- axis：坐标轴，整数或元组整数，默认会打平后shift，然后再reshape回去<jupyter_code>a = np.arange(10)
np.roll(a, 3)
np.roll(a, -2)
np.roll(a, (1, 2))
a = np.arange(10).reshape(2, 5)
a
np.roll(a, 1, axis=0)
np.roll(a, 1, axis=1)
np.roll(a, 1)
# 等价于
np.roll(a.flatten(), 1).reshape((2, 5))
np.roll(a, -1)
# 两个tuple等长
np.roll(a, (1, 1), axis=(1, 0))
# 上式等价于
np.roll(a, 1, axis=(1, 0))
np.roll(a, (2, 1), axis=(1, 0))<jupyter_output><empty_output><jupyter_text>`rot90`表示按指定轴旋转90°，方向是从第一个轴到第二个轴。参数包括：- 数组：二维或更高维- k：整数，旋转次数，默认1- axes：两个以上元素的tuple，且元素必须不同，默认（0,1）<jupyter_code>a
np.rot90(a)
# 180°
np.rot90(a, 2)
# 转回来
np.rot90(a, 4)
# 顺时针
np.rot90(a, 1, (1, 0))<jupyter_output><empty_output><jupyter_text>排序搜索  极值 关于最大最小值的方法不少，我们看看。`np.maximum`和`np.minimum`是通函数，以max为例，其他相关的还有`np.max`（等价的`np.amax`），`np.fmax`和`np.nanmax`。区分如下：- `minimum`：两个数组逐元素比较- `fmax`：同上，但忽略缺失值- `amax`：沿着给定维度- `nanmax`：同上，但忽略缺失值我们以极大值为例。<jupyter_code># 等价于 np.where(x1 >= x2, x1, x2)
np.fmax([np.nan, 2, 3], [1, 5, np.nan])
np.nanmax([[np.nan, 3, 5], [2, 1, np.nan]], axis=0)<jupyter_output><empty_output><jupyter_text>搜索 `argmax/argmin`我们在《从小白到入门》中已经做了介绍，这里主要介绍带非数值（NaN）的版本，以极小值为例。<jupyter_code>a = np.array([
    [np.nan, 2, 3],
    [1, np.nan, 4]
])
np.argmin(a, axis=0)
np.nanargmin(a, axis=0)<jupyter_output><empty_output><jupyter_text>`argwhere`和《从小白到入门》中介绍的`where`有所不同，它返回所有非0元素的index，是`where`的弱化版本。<jupyter_code>a = np.arange(6).reshape(2, 3)
a
# 默认返回非0的元素index
np.argwhere(a)
# 这个不是指定条件，而是a>3本身是个数组
np.argwhere(a>3)<jupyter_output><empty_output><jupyter_text>对于非0位置的索引，还有两个API：<jupyter_code># 返回非0元素的索引
np.nonzero(a>2)
# 返回打平后的索引
np.flatnonzero(a>2)<jupyter_output><empty_output><jupyter_text>最后是`searchsorted`，返回给定值应该插入的位置索引。给定的数组应为一维，但插入的值可以是数组。参数如下：- 被插入的一维数组。- 要插入的值，数组。- side：默认left，第一个合适的位置；right为最后一个合适的位置。- sorter：一维数组，可选的索引，将要插入的数组排序为升序。<jupyter_code>a = np.arange(1, 6)
b = np.array([3, 2, 1, 5, 4])
a
np.searchsorted(a, 2)
np.searchsorted(b, 2)
# 要插入的值可以是多维数组
np.searchsorted(a, [[2,2],[2,2]])<jupyter_output><empty_output><jupyter_text>side控制在所有合适位置中插入位置的index：<jupyter_code>np.searchsorted([1,1,1,1,1], 1, "right")
np.searchsorted([1,1,1,1,1], 1, "left")<jupyter_output><empty_output><jupyter_text>sorter表示被插入数组元素的索引，该索引将数组按升序排列。<jupyter_code>b
np.searchsorted(b, 2)
# 1 2 3 4 5
np.searchsorted(b, 2, sorter=[2,1,0,4,3])
np.searchsorted([1,2,3,4,5],2)
# 5 4 3 2 1
np.searchsorted(b, 2, sorter=[3,4,0,1,2])
np.searchsorted([5,4,3,2,1],2)<jupyter_output><empty_output><jupyter_text>排序`argsort`在《从小白到入门》中已有介绍，此处不再赘述。这里介绍其他几个和排序相关的API：- `sort/lexsort/ndarray.sort`：数组排序- `msort`：排序第一轴- `sort_complex`：复数排序- `prtition/argpartition/ndarray.partition`：部分排序<jupyter_code>rng = np.random.default_rng(42)
a = rng.integers(0, 10, (3, 4))
a<jupyter_output><empty_output><jupyter_text>`sort`包括以下参数：- 数组- axis：默认最后一个轴（-1）- kind：kind是排序算法，支持：quicksort、mergesort、heapsort和stable，默认quicksort- order：用来排序的field（数组有field时）沿着最后一个维度（默认）排序时不创建临时复制，因此速度最快，不占用额外空间。stable会根据被排序的数据类型自动选择最稳定的排序算法。<jupyter_code># 默认axis=-1
np.sort(a)
# 指定轴
np.sort(a, axis=0)
# 指定排序算法
np.sort(a, kind="stable")
# 指定order
# String, float16, int32
dtype = [("name", "U10"), ("height", "f2"), ("age", "i4")]
values = [
    ("Arthur", 1.8, 41), 
    ("Lancelot", 1.9, 38), 
    ("Galahad", 1.7, 38)
]
b = np.array(values, dtype=dtype)
np.sort(b, order=["height"])<jupyter_output><empty_output><jupyter_text>`lexsort`，后面的key优先。<jupyter_code>surnames =    ('Zertz',    'Halilei', 'Halilei')
first_names = ('Heinrich', 'Gzlileo', 'Gustav')
# 先surname，在按firstname
ind = np.lexsort((first_names, surnames))
ind
[surnames[i] + ", " + first_names[i] for i in ind]<jupyter_output><empty_output><jupyter_text>`ndarray.sort`是in-place排序。其余参数与`np.sort`一样。<jupyter_code>rng = np.random.default_rng(42)
a = rng.integers(0, 10, (3, 4))
a
a.sort()
a<jupyter_output><empty_output><jupyter_text>`msort`与`np.sort(a, axis=0)`等价。<jupyter_code>rng = np.random.default_rng(42)
a = rng.integers(0, 10, (3, 4))
a
np.msort(a)<jupyter_output><empty_output><jupyter_text>`sort_complex`先用实部排，再用虚部。<jupyter_code>np.sort_complex([1 + 2j, 1+1j, 2 - 1j, 3 - 2j, 3 - 3j, 3 + 5j])<jupyter_output><empty_output><jupyter_text>`partition`和`argpartition`的关系和`sort`与`argsort`类似。参数包括：- 数组- kth：切分的位置，整数或整数序列- axis：轴，默认-1- kind：选择算法，默认`introselect`- order：str或List[str]，和前面介绍的`sort`中的参数用法一样。注意，返回的结果中，第k个元素的位置是排好序时的位置（不是原始数组中的index）。<jupyter_code>a = [100, 99, 87, 101, 88, 78, 98]
# 88 排好时在index=2的数字
# 比88小的在左边，比88大或相等的在右边
np.partition(a, 2)
np.argpartition(a, 2)
a = [3, 4, 2, 1]
# 4 是排好序时index=3的数字
np.partition(a, 3)
np.argpartition(a, 3)
rng = np.random.default_rng(42)
a = rng.integers(0, 10, (3, 8))
a
# 注意第一行，比4大或相等的在右边 
np.partition(a, 2)
np.argpartition(a, 2)<jupyter_output><empty_output><jupyter_text>集合操作  包含 第一个数组的元素是否在第二个数组：<jupyter_code>a = np.array([[1, 2], [2, 3]])
np.unique(a)
np.in1d([1,2,3,4], a)
# 翻转
np.in1d([1,2,3,4], a, invert=True)
# 两个数组里的值均unique
# 可以加速计算
np.in1d([1,2,3,4], a, assume_unique=True)
# 打平
np.in1d([[1,2],[3,4]], a)
# 不打平
np.isin([1,2,3,4], a)
np.isin([[1,2],[3,4]], a)<jupyter_output><empty_output><jupyter_text>其余参数和`in1d`一样。  交集 交集，输入的数组会打平：<jupyter_code>a
b = np.array([[2, 4, 1]])
np.intersect1d(a, b)
# 如果假设为unique，其实不是，结果会有误
np.intersect1d(a, b, assume_unique=True)
# 返回索引，如果有多个，返回第一个
np.intersect1d(a, b, return_indices=True)<jupyter_output><empty_output><jupyter_text>并集 并集，输入的数组会打平：<jupyter_code>a
b
np.union1d(a, b)<jupyter_output><empty_output><jupyter_text>差集 差集，返回在数组1不在数组2的：<jupyter_code>np.setdiff1d(a, b)
np.setdiff1d(b, a)<jupyter_output><empty_output><jupyter_text>异或集 异或集：<jupyter_code>np.setxor1d(a, b)<jupyter_output><empty_output><jupyter_text>对于多个数组，可以使用`reduce`方法：<jupyter_code>from functools import reduce
reduce(
    np.union1d, 
    ([1, 3, 4, 3], [3, 1, 2, 1], [6, 3, 4, 2])
)<jupyter_output><empty_output><jupyter_text>函数式编程NumPy的向量化计算也可以被自定义使用。下面我们就看看如何在NumPy上使用自定义方法。 `apply_along_axis`是沿着某个维度应用一个函数，等于是沿着某个维度对原数组值进行映射。<jupyter_code>def func(a):
    return (a[0] + a[-1]) / 2
b = np.arange(1, 10).reshape(3, 3)
b
np.apply_along_axis(func, 0, b)
np.apply_along_axis(func, 1, b)
b = np.array([[8,1,7], [4,3,9], [5,2,6]])
b
np.apply_along_axis(sorted, 1, b)
np.apply_along_axis(sorted, 0, b)
np.apply_along_axis(np.diag, 1, b)
np.apply_along_axis(np.diag, 0, b).T<jupyter_output><empty_output><jupyter_text>`apply_over_axes`会将一个函数重复多次应用到多个轴。注意：函数接受两个参数（和`apply_along_axis`的一个参数区别），分别是数组和维度。<jupyter_code>b
np.apply_over_axes(np.sum, b, 1)
np.apply_over_axes(np.sum, b, 0)
np.apply_over_axes(np.sum, b, [0, 1])<jupyter_output><empty_output><jupyter_text>`vectorize`是通用类，将自定义函数向量化，主要是方便，而不是为了提高性能，内部实际是一个for循环。<jupyter_code>def func(a, b):
    if a > b:
        return a - b
    else:
        return a + b
vfunc = np.vectorize(func)
vfunc(np.arange(10), 3)<jupyter_output><empty_output><jupyter_text>可以指定输出的类型：<jupyter_code>vfunc = np.vectorize(func, otypes=[np.float16])
vfunc(np.arange(10), 3)<jupyter_output><empty_output><jupyter_text>或者指定不进行向量化的参数：<jupyter_code>def func(a, b):
    res = np.zeros_like(b)
    for i in a:
        res += i * b
    return res
func([1,2,3], np.array([2,4]))
vfunc = np.vectorize(func, excluded=["a"])
vfunc(a=[1,2,3], b=[2,4])<jupyter_output><empty_output><jupyter_text>或事后指定：<jupyter_code>vfunc = np.vectorize(func)
# 第一个参数排除
vfunc.excluded.add(0)
vfunc([1,2,3], [2,4])<jupyter_output><empty_output><jupyter_text>如果输入的参数不能直接执行，则需要签名：<jupyter_code>conv = np.vectorize(np.convolve, signature="(n),(m)->(k)")
conv([[1,2,3],[4,5,6],[7,8,9]], [1, 0.5])
np.convolve([1,2,3,4,5,6,7,8,9], [1, 0.5])<jupyter_output><empty_output><jupyter_text>相比`vectorize`，`frompyfunc`更加通用，它可以将任意的Python函数转为通函数。<jupyter_code>def func(x):
    return str(x)
ufunc = np.frompyfunc(func, 1, 1)
ufunc(np.arange(5))<jupyter_output><empty_output><jupyter_text>`piecewise`会执行一组条件和对应的函数，当条件为真时执行该函数。<jupyter_code>a = np.arange(10)
np.piecewise(a, [a<5, a==5, a>5], [-1, 0, 1])
def func(a, b):
    return a + b
# 注意后面的参数是所有函数通用的
np.piecewise(a, [a<5, a==5, a>5], [func, 0, lambda x, b: x**2], b=5)<jupyter_output><empty_output><jupyter_text>测试主要介绍与Asserts相关的API，与第三节中的《逻辑运算》有一定联系。  相等 相等的Asserts：- `assert_equal`- `assert_array_equal`- `assert_string_equal`三者API有点类似，前两个除了实际值和期望值，还多了：- `err_msg`：失败时打印的消息- `verbose`：为True时，冲突的值会append到消息后面 `assert_equal`比较两个对象（标量，数组，元组，字典，NumPy数组等）。如果其中一个是标量，另一个是数组，则会比较标量与数组的每一个元素。如果同样位置都是非数值（NaN），也算相等。<jupyter_code>np.testing.assert_equal([1,2], [1,4], "msg: not equal")
# 标量与NumPy array_like 数组
np.testing.assert_equal(3, np.array([3, 3, 3]))
# 含有非数值（NaN）的情况
np.testing.assert_equal([1, np.nan], [1, np.nan])<jupyter_output><empty_output><jupyter_text>`assert_array_equal`的范围比`assert_equal`小，后者内部调用了前者。如果输入是array_like，则两者无区别。后者在复数、时间、非数值（NaN）等方面有所不同。<jupyter_code>np.testing.assert_array_equal(np.nan, [np.nan])
np.testing.assert_equal(np.nan, [np.nan])<jupyter_output><empty_output><jupyter_text>`assert_string_equal`用以比较字符串，输入两个字符串即可。<jupyter_code>np.testing.assert_string_equal("abc", "abc")
np.testing.assert_string_equal("abc", "abC")<jupyter_output><empty_output><jupyter_text>相近 相近的Asserts：- `assert_allclose`：使用最多- `assert_array_almost_equal_nulp`- `assert_array_max_ulp` `assert_allclose`包含以下参数：- 实际值- 期望值- rtol=1e-07- atol=0- equal_nan=True- err_msg=""- verbose=True后两个参数和前面一样，不再赘述。`equal_nan`为True表示非数值（NaN）会认为是一样的。rtol和atol用于评估精度，如果两者相差小于：`atol + rtol*|desired|`就认为足够接近。<jupyter_code>np.testing.assert_allclose(1+1e-7, 1+1e-8)
1e-7-1e-8 < (1+1e-8)*1e-7
np.testing.assert_allclose(5, 4, atol=1)
np.testing.assert_allclose([5, np.nan], [4, np.nan], 
                           atol=1, equal_nan=True)<jupyter_output><empty_output><jupyter_text>`assert_array_almost_equal_nulp`可用于比较两个幅度可变队列相对稳健。- x- y- nulp=1，最后一位公差的最大单位数，满足下式时：`|x-y| <= nulps * spacing(max(|x|, |y|))`算几乎相等。<jupyter_code>np.spacing(np.float64(1)) == np.finfo(np.float64).eps
x = np.array([1, 0.1, 1e-10, 1e-20])
x.dtype
eps = np.finfo(x.dtype).eps
eps
x * eps <= 1 * np.spacing(x + x*eps)
np.testing.assert_array_almost_equal_nulp(
    x, x + x*eps, nulp=1
)
np.testing.assert_array_almost_equal_nulp(
    .1, .1+eps, nulp=1
)
np.testing.assert_array_almost_equal_nulp(
    1, 1+eps, nulp=1
)
np.testing.assert_array_almost_equal_nulp(
    4, 8, nulp=1e16
)
1e16 * np.spacing(8)<jupyter_output><empty_output><jupyter_text>`assert_array_max_ulp`用于检查数组中所有元素最多相差N个单位。<jupyter_code>np.testing.assert_array_max_ulp(1, 1)
a = np.linspace(0., 1., 10)
np.testing.assert_array_max_ulp(a, np.arcsin(np.sin(a)))
np.testing.assert_array_max_ulp(1, 2, maxulp=1e16)
# NaN不区分
np.testing.assert_array_max_ulp(np.nan, np.nan)<jupyter_output><empty_output><jupyter_text>小于 小于：- `assert_array_less`：参数与`assert_array_equal`一样，用来比较是否小于，NaN也会比较，如果同样位置都是NaN，则跳过（不抛出异常）。<jupyter_code>np.testing.assert_array_less(
    [1, 1, np.nan],
    [2, 2, np.nan]
)
np.testing.assert_array_less(
    3, [1, 4]
)<jupyter_output><empty_output><jupyter_text>异常 异常：- `assert_raises`- `assert_raises_regex`- `assert_warns`这三个API都可以作为上下文管理器使用。 `assert_raises`有两种使用方法：- `assert_raises(exception_class, callable, *args, **kwargs)`- `assert_raises(exception_class)`<jupyter_code>1/0
with np.testing.assert_raises(ZeroDivisionError):
    1/0
def div(a,b):a/b
np.testing.assert_raises(ZeroDivisionError, div, 1, 0)
with np.testing.assert_raises(ZeroDivisionError):
    1/1<jupyter_output><empty_output><jupyter_text>`assert_raises_regex`相比前者可以指定正则。<jupyter_code>with np.testing.assert_raises_regex(
    ZeroDivisionError, "division by zero"
):
    1/0
with np.testing.assert_raises_regex(
    ZeroDivisionError, "[a-z]+"
):
    1/0<jupyter_output><empty_output><jupyter_text>`assert_warns`和`assert_raises`一样的使用方式，只不过会警告而不是抛出异常。<jupyter_code>import warnings
def func(n):
    warnings.warn("msg", UserWarning)
    return n * n
np.testing.assert_warns(UserWarning, func, 2)
func(2)<jupyter_output><ipython-input-187-23e97a00966d>:2: UserWarning: msg
  warnings.warn("msg", UserWarning)
{'fraction_non_alphanumeric': 0.19618804237874674, 'fraction_numerical': 0.04573284352872278, 'mean_word_length': 8.230466830466831, 'pattern_counts': {'":': 0, '<': 279, '<?xml version=': 0, '>': 286, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '68', 'dir': 'jupyter-structured-clean-dedup', 'id': '621073', 'license': 'Apache-2.0', 'n_tokens_mistral': 11185, 'n_tokens_neox': 11281, 'n_words': 1093, 'path': 'src/skilled/ch02-manipulation.ipynb', 'repo_name': 'datawhalechina/powerful-numpy', 'size': '18783'}