文章目录
- pre :先来一张源码的切片
- 1. 参数和基本定义:
- 2. 将 reps 转换为元组:
- 3. 提升数组维度:
- 4. 特殊情况检查:
- 5. 处理数组维度的不同情况:
- 6. 计算输出数组的形状:
- 7. 通过重复构造数组:(核心部分,也是比较难理解的部分)
- 8. 返回结果:
pre :先来一张源码的切片
mac中按住cmd,鼠标放到tile函数上点一下就可以进入了,win是按住ctrl点一下。
源码部分的介绍非常详细,有一大段部分是注射,还有样例分析。小插曲:这个截图插件叫 Easy Code Screenshots,在JetBrains IDE都可以找到。
1. 参数和基本定义:
- A: 输入数组,可以是任意形状的数组。
- reps: 一个数组或整数,表示每个轴的重复次数。例如,如果 reps 是 (2, 3),则会对数组的第一个轴重复两次,第二个轴重复三次。
这里解释一下轴是什么:
在数组和矩阵操作中,轴(axis) 是指数组的维度方向。每个维度(或轴)有一个编号,通常从 0 开始。对于不同维度的数组,轴的概念如下:
• 一维数组:只有一个轴,即 axis=0。沿这个轴重复会将数组的元素进行平铺。
a = np.array([1, 2, 3]),只有一个轴 axis=0
• 二维数组:有两个轴,axis=0 表示行方向,axis=1 表示列方向。沿 axis=0 重复就是在行方向上增加行数。沿 axis=1 重复则在列方向上增加列数。
b = np.array([[1, 2], [3, 4]]),axis=0 是行方向,axis=1 是列方向
• 三维及更高维数组:有多个轴。例如,三维数组的 axis=0 是深度(通常表示“页”),axis=1 是行,axis=2 是列。
c = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]),axis=0 是“页”方向,axis=1 是行方向,axis=2 是列方向
• 在 tile 函数中,reps 指定了每个轴的重复次数,例如 reps=(2, 3):
2 表示在第一个轴上重复 2 次。
3 表示在第二个轴上重复 3 次。
2. 将 reps 转换为元组:
tup = tuple(reps)
- 如果 reps 是整数或列表,将其转换为元组 tup,方便在接下来的操作中使用。
3. 提升数组维度:
- 通过计算 d = len(tup) 得到 reps 的维度数。
- 如果 A 的维度数少于 d,函数会自动将 A 提升到 d 维。例如,如果 A 是一维数组,但 reps 是 (2, 2),那么会将 A 扩展到二维,变成 (1, n) 的形状。
4. 特殊情况检查:
if all(x == 1 for x in tup) and isinstance(A, _nx.ndarray):
return _nx.array(A, copy=True, subok=True, ndmin=d)
- 如果 reps 中所有元素都为 1,且 A 是一个 NumPy 数组,则直接返回 A 的副本,因为这样可以节省不必要的重复计算。
- 使用 copy=True 创建一个新的副本,以避免对原始数据的修改。
5. 处理数组维度的不同情况:
- 如果 reps 的维度 d 小于 A 的维度数 c.ndim,函数会通过在 reps 前面补 1 来扩展 tup,从而将 tup 调整到与 A 的维度一致。例如,如果 A 是 (2, 3, 4) 维的数组,reps 是 (2, 2),则将 tup 转换为 (1, 2, 2)。
6. 计算输出数组的形状:
shape_out = tuple(s * t for s, t in zip(c.shape, tup))
- 输出数组的形状是每个维度上 A.shape 与 reps 的对应值相乘所得。
7. 通过重复构造数组:(核心部分,也是比较难理解的部分)
for dim_in, nrep in zip(c.shape, tup):
if nrep != 1:
c = c.reshape(-1, n).repeat(nrep, 0)
n //= dim_in
- 使用 repeat 函数沿指定的轴重复元素。这个过程逐步沿每个维度(从最外层到最内层)将数组重复 nrep 次。
- zip(c.shape, tup) 将输入数组 c 的每个维度的大小和 reps 中的重复次数配对起来,进行逐一处理。
- dim_in 是当前维度的大小(c.shape 中的一个元素),nrep 是对应的重复次数(tup 中的一个元素)。
- c.reshape(-1, n) 这一步是为了将数组 c 重新调整形状,使得它的第一个维度为 -1(即自动计算大小),第二个维度为 n(总元素个数)。
- 例如,如果 c 原本是 (2, 3) 的数组,经过调整后会变成 (1, 6) 的数组,其中 6 是元素个数(2 * 3)。
- repeat(nrep, 0) 这一步则是在新的形状下沿着第一个轴(行方向)重复 nrep 次。即使经过 reshape, c 的形状会变成 (nrep, 6)。
- n //= dim_in 更新 n 的值,n 除以当前维度的大小 dim_in。这一步是为了在处理下一个维度时,能得到正确的元素个数。
- 这样做的原因是,随着每个维度的处理,n 会逐渐减少,因为每次处理一个维度时,元素总数 n 都会被分解成当前维度的大小。
8. 返回结果:
- 最后,将数组 c 重新调整到 shape_out,并返回该形状的输出数组。
示例分析
>>> a = np.array([0, 1, 2])
>>> np.tile(a, 2)
# 结果:[0, 1, 2, 0, 1, 2]
这里,reps=2 将 a 复制一次并拼接成一个一维数组。
>>> np.tile(a, (2, 2))
>>>> np.tile(a, (2, 2))
# 结果:[[0, 1, 2, 0, 1, 2],
# [0, 1, 2, 0, 1, 2]]
reps=(2, 2) 表示先在第一个维度(行)上复制一次,再在第二个维度(列)上复制一次,最终得到 2x6 的数组。