Numpy进阶难点 - 关于维度操作与广播
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))