Numpy进阶难点 - 关于维度操作与广播

发布时间 2023-08-03 00:08:19作者: 倦鸟已归时

Tensor数据是更高维度的数组,其关于坐标轴的操作总是难以理解。特在 Jupyter Notebook 中尝试,然后总结一些重点的案例,便于理解学习。(输出太长了,仅放出代码,import numpy as np 即可 run)

1:按照指定的索引顺序,取一个矩阵中的某几行、列的元素。

arr = np.arange(32).reshape((8, 4))
print("arr:\n", arr)

# 仅仅是获得了4个元素组成的向量,[1,5,7,2]为行索引,[0,3,1,2]为列索引
print(arr[[1,5,7,2], [0, 3, 1, 2]])  # return a vector
# print(arr[[1, 0],[5, 3],[7, 1],[2, 2]])  # error

# get a rectangular area
# 按照指定的索引取二维子序列

# 错误示例:仅仅是两次调换行索引而已,先获取[1,5,7,2]行的元素,然后再按照[0,3,1,2]的行标为顺序,重新获取对应行元素而已。
print("arr[[1,5,7,2,]][[0,3,1,2]]:\n",arr[[1,5,7,2]][[0,3,1,2]])

# 正确示例:先按指定顺序取行,然后对所得视图的每一行,根据指定的列顺序取列
# 也就是说,[:, [0,3,1,2]]是对应列索引, : 代表第一维(所有行)。
print("arr[[1,5,7,2,]][:,[0,3,1,2]]:\n",arr[[1,5,7,2,]][:,[0,3,1,2]])
# 一个更简单的方法
print("arr[np.ix_([],[])]:\n", arr[np.ix_([1,5,7,2],[0,3,1,2])])

2 重点操作:高维数据的轴对换。最经典的案例就是矩阵转置 transpose ,对换了axis 0 和 axis 1,但如果是更高维度呢?(张量,即 tensor 数据结构)

# # 2 dim data, matrix
# arr = np.arange(15).reshape((3, 5))
# print("arr:\n", arr)
# print("arr.T\n", arr.T, "\ntranspose(arr):\n", np.transpose(arr))

# 3 dim data
# 第0维:2个矩阵;第1维:每个矩阵3行;第2维,每行4个元素
arr = np.arange(24).reshape((2,3,4))
print("arr:\n", arr)
# 调换 0 1 两维度 --> 第0维:3个矩阵;第1维:每个矩阵2行;第2维,每行4个元素
# 其实是先获取每个二维张量的一行、得到一个2×4 二维张量,如此逐行读取,得到总共3个二维张量。
print("arr.transpose((1,0,2)):\n", arr.transpose((1,0,2)))  # swap the order of axes
print("arr.swapaxes(1,2):\n", arr.swapaxes(1,2))  # swap the order of axes

3 Broadcast广播,轴

Broadcast 的条件。简洁地说,假如 tensor2 要在 tensor1上进行广播的话,tensor2 的 shape的每个元素,都要小于或等于 tensor1。(至于是否需要除尽,还没研究,大概很少见的情况吧,遇到再看)。

# ### very important, crucial, vital, momentous, critical !!
# Observe the principles of broadcasting mechanisms

# 二维张量广播
arr = np.random.randn(4,3)
print("arr:\n", arr)
print("col mean, arr.mean(0):", arr.mean(0))
print("row mean, arr.mean(1):", arr.mean(1))
# 必须把 mean 的结果转为 shape(4,1),因为这样才能贴合 原tensor的shape(4,3)
# 否则 mean(1)结果为 长度为4的向量(1行4列),然而原矩阵为4行3列,形状都对不齐,无法broadcast。
print("arr - arr.mean(1).reshape((4,1))",arr - arr.mean(1).reshape((4,1)))

# mean(0)为3列的,自然和 (4,3) 符合broadcast的条件
demeaned = arr - arr.mean(0)
print("arr - arr.mean(0):\n", demeaned)
# print("demeaned.mean(0):", demeaned.mean(0))

# # 3 dim data 三维张量广播
# arr = np.random.randint(-5, 6, (3, 4, 2))
# # print("arr:\n",arr)
# print("arr.mean(1):\n", arr.mean(1))
#  (3 1 2)在(3 4 2)上广播,符合Broadcast的条件
# print("arr - arr.mean(1).reshape((3,1,2))\n")
# print(arr - arr.mean(1).reshape((3,1,2)))

# print("arr.mean(1):\n", arr.mean(1))

# reshape的问题在于,无法胜任任意情况,因为需要手动构造一个元组作为参数传递给reshape
# 理想的方法是,添加一个轴就行,不需要知道各维度具体数值,这样才能“自动化”
# The problem with reshape is that it cannot handle any situation as it requires manually constructing a tuple as a parameter to pass to the reshape
# 重点:关于广播添加新的轴
# Key point: Adding new axes for tuples to utilize broadcasting
# arr2d = np.random.randint(-5 ,6, (3, 2))
# arr_3d = arr2d[:, np.newaxis, :]
# print("arr_3d shape:", arr_3d.shape)
# print(arr_3d)

# arr_1d = np.random.normal(size=3)
# print("arr_1d:", arr_1d)
# arr_2d = arr_1d[:,np.newaxis]
# print(arr_2d)

# arr = np.zeros((4,3))
# col = np.array([3,5,7,5])
# arr[:] = col[:, np.newaxis]
# print("arr:\n", arr)
# arr[:2] = np.array([[-2], [-3]])
# print(arr)

4 常见的操作,reshape 与 flatten / ravel,向量 reshape 变为其他形状的 tensor,高维 tensor 也可以 flatten/ravel 为一维向量。

arr = np.arange(15)
print("arr.reshape((3, 5))")
arr1 = arr.reshape((3, 5))
print("arr1, arr.reshape((3, 5)):\n", arr1)
arr2 = arr1.ravel()
print("arr2, arr1.ravel():\n", arr2)
arr3 = arr1.flatten()
print("arr3, after flatten():\n", arr3)

5 常见的操作,concatenate 和 split 及其类似操作(hstack vstack np.c_ np.r_)

# ### very important, crucial, vital, momentous, critical !!
arr1 = np.array([[1,2,3],[4,5,6]])
arr2 = np.array([[7,8,9],[10,11,12]])

print("concatenate rows, align the column(axis=0)")
print("np.concatenate([arr1, arr2], axis=0)\n",np.concatenate([arr1, arr2], axis=0))
print("np.vstack((arr1, arr2))\n",np.vstack((arr1, arr2)))
print("np.r_[arr1, arr2]:\n", np.r_[arr1, arr2])

print("concatenate columns, align the row   (axis=1)")
print("np.concatenate([arr1, arr2], axis=1)\n",np.concatenate([arr1, arr2], axis=1))
arr3 = np.hstack((arr1, arr2))
print("np.hstack((arr1, arr2))\n",np.hstack((arr1, arr2)))
print("np.c_[arr1, arr2]:\n", np.c_[arr1, arr2])

f, s, t = np.split(arr3, [1, 3], axis=1)
print("f:\n{0}\ns:\n{1}\nt:\n{2}".format(f, s, t))