网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
arr2d
array([[1, 2, 3],
[4, 5, 6]])
print(“The typeis :”, type(arr2d))
print(“The dimension is:”, arr2d.ndim)
print(“The length of array is:”, len(arr2d))
print(“The number of elments is:”, arr2d.size)
print(“The shape of array is:”, arr2d.shape)
print(“The stride of array is:”, arr2d.strides)
print(“The type of elements is:”, arr2d.dtype)
The typeis : <class ‘numpy.ndarray’>
The dimension is: 2
The length of array is: 2
The number of elments is: 6
The shape of array is: (2, 3)
The stride of array is: (12, 4)
The type of elements is: int32
同样,我们来分析一下上面属性:
* type:数组类型 numpy.ndarray
* ndim:维度个数是 2
* len():数组长度为 2 (严格定义 len 是数组在「轴 0」的元素个数)
* size:数组元素个数为 6
* shape:数组形状 (2, 3)
* strides:跨度 (12, 4) 看完下图再解释
* dtype:数组元素类型 int32
对于二维数组,Python 视图」看它和「内存块」存储它的形式是不一样的,如下图所示:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190619111407170.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3h1X2Z1X3lvbmc=,size_16,color_FFFFFF,t_70)
在 numpy 数组中,默认的是行主序 (row-major order),意思就是每行的元素在内存块中彼此相邻,而列主序 (column-major order) 就是每列的元素在内存块中彼此相邻。
回顾跨度 (stride) 的定义,即在某一维度下为了获取到下一个元素需要「跨过」的字节数。注:每一个 int32 元素是 4 个字节数。对着上图:
第一维度 (轴 0):沿着它获取下一个元素需要跨过 3 个元素,即 12 = 3×4 个字节
第二维度 (轴 1):沿着它获取下一个元素需要跨过 1 个元素,即 4 = 1×4 个字节
因此该二维数组的跨度为 (12, 4)。
##### n 维数组
我们使用np.random.random()来生成一个多维数组
arr4d = np.random.random((2, 2, 2, 3))
print(“The type is:”, type(arr4d))
print(“The dimension is:”, arr4d.ndim)
print(“The length of array is:”, len(arr4d))
print(“The number of elments is:”, arr4d.size)
print(“The shape of array is:”, arr4d.shape)
print(“The stride of array is:”, arr4d.strides)
print(“The type of elments is:”, arr4d.dtype)
arr4d
The type is: <class ‘numpy.ndarray’>
The dimension is: 4
The length of array is: 2
The number of elments is: 24
The shape of array is: (2, 2, 2, 3)
The stride of array is: (96, 48, 24, 8)
The type of elments is: float64
array([[[[0.12097602, 0.85898477, 0.16043155],
[0.87946725, 0.96358488, 0.4051294 ]],
[[0.65434255, 0.21199418, 0.68517886],
[0.53783086, 0.93971496, 0.14067768]]],
[[[0.93745553, 0.95048614, 0.81013165],
[0.36328647, 0.16424842, 0.96708327]],
[[0.90332958, 0.30356389, 0.50146022],
[0.05044289, 0.49204122, 0.2607904 ]]]])
这里的stride是(96, 48, 24, 8)注意:一个float64的元素占8个字节
![在这里插入图片描述](https://img-blog.csdnimg.cn/2019061911142562.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3h1X2Z1X3lvbmc=,size_16,color_FFFFFF,t_70)
跨度 (stride) 的定义,即在某一维度下为了获取到下一个元素需要「跨过」的字节数。注:每一个 float64 元素是 8 个字节数
* 第一维度 (轴 0):沿着它获取下一个元素需要跨过 12 个元素,即 96 = 12×8 个字节
* 第二维度 (轴 1):沿着它获取下一个元素需要跨过 6 个元素,即 48 = 6×8 个字节
* 第三维度 (轴 2):沿着它获取下一个元素需要跨过 3 个元素,即 24 = 3×8 个字节
* 第四维度 (轴 3):沿着它获取下一个元素需要跨过 1 个元素,即 8 = 1×8 个字节
因此该四维数组的跨度为 (96, 48, 24, 8)。
### 2、数组的存储和加载
假设你已经训练完一个深度神经网络,该网络就是用无数参数来表示的。比如权重都是 numpy 数组,为了下次不用训练而重复使用,将其保存成 .npy 格式或者 .csv 格式是非常重要的。
#### numpy自身的.npy格式
用 np.save 函数将 numpy 数组保存为 .npy 格式,具体写法如下:
arr_disk = np.arange(8)
np.save(“arr_disk”, arr_disk)
arr_disk
array([0, 1, 2, 3, 4, 5, 6, 7])
arr\_disk.npy 保存在 Jupyter Notebook 所在的根目录下。要加载它也很简单,用 np.load( “文件名” ) 即可:
np.load(“arr_disk.npy”)
array([0, 1, 2, 3, 4, 5, 6, 7])
#### 文本 .txt 格式
用 np.savetxt 函数将 numpy 数组保存为 .txt 格式,具体写法如下:
arr_text = np.array([[1., 2., 3.],[4., 5., 6.]])
np.savetxt(“arr_from_text.txt”, arr_text)
用 np.loadtxt( “文件名” ) 即可加载该文件
np.loadtxt(“arr_from_text.txt”)
array([[1., 2., 3.],
[4., 5., 6.]])
#### 文本 .csv 格式
arr_csv = np.array([[1., 2., 3.],[4., 5., 6.]])
np.savetxt(“arr_csv.csv”, arr_csv, delimiter=‘;’)
用 np.genfromtxt( “文件名” ) 即可加载该文件
带上「分隔符 ;」
np.genfromtxt(“arr_csv.csv”, delimiter=‘;’)
array([[1., 2., 3.],
[4., 5., 6.]])
### 3、数组的获取
获取数组是通过索引 (indexing) 和切片 (slicing) 来完成的,
* 切片是获取一段特定位置的元素
* 索引是获取一个特定位置的元素
索引和切片的方式和列表一模一样。对于一维数组 arr,
* 切片写法是 arr[start : stop : step]
* 索引写法是 arr[index]
因此,切片的操作是可以用索引操作来实现的 (一个一个总能凑成一段),只是没必要罢了。为了简化,我们在本章三节标题里把切片和索引都叫做索引。
索引数组有三种形式,正规索引 (normal indexing)、布尔索引 (boolean indexing) 和花式索引 (fancy indexing)。
#### 正规索引
虽然切片操作可以由多次索引操作替代,但两者最大的区别在于
* 切片得到的是原数组的一个视图 (view) ,修改切片中的内容会改变原数组
* 索引得到的是原数组的一个复制 (copy),修改索引中的内容不会改变原数组
请看下面一维数组的例子来说明上述两者的不同。
一维数组
arr = np.arange(10)
arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
a = arr[6]
a = 100
arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
b = arr[5:8]
b[1] = 12
arr
array([ 0, 1, 2, 3, 4, 5, 12, 7, 8, 9])
这就证实了切片得到原数组的视图 (view),更改切片数据会更改原数组,而索引得到原数组的复制 (copy), 更改索引数据不会更改原数组。希望用下面一张图可以明晰 view 和 copy 的关系。
![image.png](https://img-blog.csdnimg.cn/20190619111455423.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3h1X2Z1X3lvbmc=,size_16,color_FFFFFF,t_70)
二维数组
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
arr2d
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
索引的使用
#情况一:用 arr2d[2] 来索引第三行,更严格的说法是索引「轴 0」上的第三个元素。
arr2d[2]
array([7, 8, 9])
#情况二:用 arr2d[0][2] 来索引第一行第三列
arr2d[0][2]
arr2d[0, 2]
3
切片的使用
情况一:用 arr2d[:2] 切片前两行,更严格的说法是索引「轴 0」上的前两个元素。
arr2d[:2]
array([[1, 2, 3],
[4, 5, 6]])
#情况二:用 arr2d[:, [0,2]] 切片第一列和第三列
arr2d[:, [0, 2]]
array([[1, 3],
[4, 6],
[7, 9]])
情况三:用 arr2d[1, :2] 切片第二行的前两个元素
arr2d[1, :2]
array([4, 5])
#情况四:用 arr2d[:2, 2] 切片第三列的前两个元素
arr2d[:2, 2]
array([3, 6])
#### 布尔索引
布尔索引,就是用一个由布尔 (boolean) 类型值组成的数组来选择元素的方法。
假设我们有阿里巴巴 (BABA),脸书 (FB) 和京东 (JD) 的
* 股票代码 code 数组
* 股票价格 price 数组:每行记录一天开盘,最高和收盘价格。
code = np.array([‘BABA’, ‘FB’, ‘JD’, ‘BABA’, ‘JD’, ‘FB’])
price = np.array([[170,177,169],[150,159,153],
[24,27,26],[165,170,167],
[22,23,20],[155,116,157]])
假设我们想找出 BABA 对应的股价,首先找到 code 里面是 ‘BABA’ 对应的索引 (布尔索引),即一个值为 True 和 False 的布尔数组。
code == ‘BABA’
array([ True, False, False, True, False, False])
用该索引可以获取 BABA 的股价:
price[code == ‘BABA’]
array([[170, 177, 169],
[165, 170, 167]])
用该索引还可以获取 BABA 的最高和收盘价格:
price[code ==‘BABA’, 1:]
array([[177, 169],
[170, 167]])
试试把股价小于 25 的清零。
price[price < 25] = 0
price
array([[170, 177, 169],
[150, 159, 153],
[ 0, 27, 26],
[165, 170, 167],
[ 0, 0, 0],
[155, 116, 157]])
#### 花式索引
花式索引是获取数组中想要的特定元素的有效方法。考虑下面数组:
arr = np.arange(32).reshape(8, 4)
arr
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23],
[24, 25, 26, 27],
[28, 29, 30, 31]])
假设你想按特定顺序来获取第 5, 4 和 7 行时,用 arr[ [4,3,6] ]
arr[[4, 3, 6]]
array([[16, 17, 18, 19],
[12, 13, 14, 15],
[24, 25, 26, 27]])
假设你想按特定顺序来获取倒数第 4, 3 和 6 行时 (即正数第 4, 5 和 2 行),用 arr[ [-4,-3,-6] ]
arr[[-4, -3, -6]]
array([[16, 17, 18, 19],
[20, 21, 22, 23],
[ 8, 9, 10, 11]])
此外,你还能更灵活的设定「行」和「列」中不同的索引,如下
arr[[1, 5, 7, 2],[0, 3, 1, 2]]
array([ 4, 23, 29, 10])
最后,我们可以交换列,把原先的 [0,1,2,3] 的列换成 [0,3,1,2]。
arr[:, [0, 3, 1, 2]]
array([[ 0, 3, 1, 2],
[ 4, 7, 5, 6],
[ 8, 11, 9, 10],
[12, 15, 13, 14],
[16, 19, 17, 18],
[20, 23, 21, 22],
[24, 27, 25, 26],
[28, 31, 29, 30]])
### 小结一下
数组创建、数组存载和数组获取。同样把 numpy 数组当成一个对象,要学习它,无非就是学习怎么
* 创建它:按步就班法、定隔定点法、一步登天法
* 存载它:保存成 .npy, .txt 和 .csv 格式,下次加载即用
* 获取它:一段用切片,一个用索引;有正规法、布尔法、花式法
接下来学习一下NumPy 的其他硬核操作
* 变形它:重塑和打平,合并和分裂,元素重复和数组重复
* 计算它:元素层面计算,线性代数计算,广播机制计算
先说一下数组转置的问题
arr = np.arange(16).reshape((2, 2, 4))
arr
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7]],
[[ 8, 9, 10, 11],
[12, 13, 14, 15]]])
数组转置的本质:交换每个轴 (axis) 的形状 (shape) 和跨度 (stride)。
将第 1, 2, 3 维度转置到第 2, 1, 3 维度,即将轴 0, 1, 2 转置到轴 1, 0, 2。
![image.png](https://img-blog.csdnimg.cn/20190619111551657.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190619111738718.png)
![image.png](https://img-blog.csdnimg.cn/20190619111751763.png)
![image.png](https://img-blog.csdnimg.cn/20190619111803979.png)
arr.transpose(2, 0, 1)
array([[[ 0, 4],
[ 8, 12]],
[[ 1, 5],
[ 9, 13]],
[[ 2, 6],
[10, 14]],
[[ 3, 7],
[11, 15]]])
arr.shape
(2, 2, 4)
arr.strides
(32, 16, 4)
### 4、数组的变形
四大类数组层面上的操作,具体有
* 1.重塑 (reshape) 和打平 (ravel, flatten)
* 2.合并 (concatenate, stack) 和分裂 (split)
* 3.重复 (repeat) 和拼接 (tile)
* 4.其他操作 (sort, insert, delete, copy)
#### 重塑和打平
重塑 (reshape) 和打平 (ravel, flatten) 这两个操作仅仅只改变数组的维度
* 重塑是从低维到高维
* 打平是从高维到低维
重塑
用reshape()函数将一维数组 arr 重塑成二维数组。
arr = np.arange(12)
print(arr.reshape((4, 3)))
print(arr)
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
[ 0 1 2 3 4 5 6 7 8 9 10 11]
当你重塑高维矩阵时,不想花时间算某一维度的元素个数时,可以用「-1」取代,程序会自动帮你计算出来。比如把 12 个元素重塑成 (2, 6),你可以写成 (2,-1) 或者 (-1, 6)。
print(arr.reshape((2, -1)))
print(arr.reshape((-1, 6)))
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]]
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]]
打平
用 ravel() 或flatten() 函数将二维数组 arr 打平成一维数组。
arr = np.arange(12).reshape((4, 3))
print(arr)
revel_arr = arr.ravel()
print(revel_arr)
flatten_arr = arr.flatten()
print(flatten_arr)
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
[ 0 1 2 3 4 5 6 7 8 9 10 11]
[ 0 1 2 3 4 5 6 7 8 9 10 11]
思考:为什么打平后的数组不是
[ 0 3 6 9 1 4 7 10 2 5 8 11 ]
要回答这个问题,需要了解 numpy 数组在内存块的存储方式。
行主序和列主序
行主序 (row-major order) 指每行的元素在内存块中彼此相邻,而列主序 (column-major order) 指每列的元素在内存块中彼此相邻。
在众多计算机语言中,
* 默认行主序的有 C 语言(下图 order=‘C’ 等价于行主序)
* 默认列主序的有 Fortran 语言(下图 order=‘F’ 等价于列主序)
![image.png](https://img-blog.csdnimg.cn/20190619112048850.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3h1X2Z1X3lvbmc=,size_16,color_FFFFFF,t_70)
在 numpy 数组中,默认的是行主序,即 order =‘C’。现在可以回答本节那两个问题了。
如果你真的想在「重塑」和「打平」时用列主序,只用把 order 设为 ‘F’,以重塑举例:
print(arr.reshape((4, 3), order=‘F’))
print(arr.flatten(order=‘F’))
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
[ 0 3 6 9 1 4 7 10 2 5 8 11]
两个函数 ravel() flatten()它们的区别在哪里?
函数 ravel() 或 flatten() 的不同之处是
* 1.ravel() 按「行主序」打平时没有复制原数组,按「列主序」在打平时复制了原数组
* 2.flatten() 在打平时复制了原数组
用代码验证一下,首先看 flatten(),将打平后的数组 flatten 第一个元素更新为 10000,并没有对原数组 arr 产生任何影响 (证明 flatten() 是复制了原数组)
arr = np.arange(6).reshape(2,3)
print( arr )
flatten = arr.flatten()
print( flatten )
flatten_arr[0] = 10000
print( arr )
[[0 1 2]
[3 4 5]]
[0 1 2 3 4 5]
[[0 1 2]
[3 4 5]]
再看 ravel() 在「列主序」打平,将打平后的数组 ravel\_F 第一个元素更新为 10000,并没有对原数组 arr 产生任何影响 (证明 ravel(order=‘F’) 是复制了原数组)
ravel_F = arr.ravel( order=‘F’ )
ravel_F[0] = 10000
print( ravel_F )
print( arr )
[10000 3 1 4 2 5]
[[0 1 2]
[3 4 5]]
最后看 ravel() 在「行主序」打平,将打平后的数组 ravel\_C 第一个元素更新为 10000,原数组 arr[0][0] 也变成了 10000 (证明 ravel() 没有复制原数组)
ravel_C = arr.ravel()
ravel_C[0] = 10000
print( ravel_C )
print( arr )
[10000 1 2 3 4 5]
[[10000 1 2]
[ 3 4 5]]
#### 合并和分裂
合并 (concatenate, stack) 和分裂 (split) 这两个操作仅仅只改变数组的分合
* 合并是多合一
* 分裂是一分多
使用「合并」函数有三种选择
* 1.有通用的 concatenate
* 2.有专门的 vstack, hstack, dstack
* 3.有极简的 r\_, c\_
用下面两个数组来举例:
arr1 = np.array([[1, 2, 3], [4, 5, 6]])
arr2 = np.array([[7, 8, 9], [10, 11, 12]])
**concatenate**
在 concatenate() 函数里通过设定轴,来对数组进行竖直方向合并 (轴 0) 和水平方向合并 (轴 1)。
np.concatenate([arr1, arr2], axis=0)
np.concatenate([arr1, arr2], axis=1)
array([[ 1, 2, 3, 7, 8, 9],
[ 4, 5, 6, 10, 11, 12]])
**vstack, hstack, dstack**
通用的东西是好,但是可能效率不高,NumPy 里还有专门合并的函数
* vstack:v 代表 vertical,竖直合并,等价于 concatenate(axis=0)
* hstack:h 代表 horizontal,水平合并,等价于 concatenate(axis=1)
* dstack:d 代表 depth-wise,按深度合并,深度有点像彩色照片的 RGB 通道
一图胜千言:
![image.png](https://img-blog.csdnimg.cn/20190619112119409.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3h1X2Z1X3lvbmc=,size_16,color_FFFFFF,t_70)
print(np.vstack((arr1, arr2)))
print(np.hstack((arr1, arr2)))
print(np.dstack((arr1, arr2)))
[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[10 11 12]]
[[ 1 2 3 7 8 9]
[ 4 5 6 10 11 12]]
[[[ 1 7]
[ 2 8]
[ 3 9]]
[[ 4 10]
[ 5 11]
[ 6 12]]]
和 vstack, hstack 不同,dstack 将原数组的维度增加了一维。
np.dstack((arr1, arr2)).shape
(2, 3, 2)
**r\_, c\_**
此外,还有一种更简单的在竖直和水平方向合并的函数,r\_() 和 c\_()。
print(np.r_[arr1, arr2])
print(np.c_[arr1, arr2])
[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[10 11 12]]
[[ 1 2 3 7 8 9]
[ 4 5 6 10 11 12]]
除此之外,r\_() 和 c\_() 有什么特别之处么?(如果完全和 vstack() 和hstack() 一样,那也没有存在的必要了)
1. 参数可以是切片。
print(np.r_[-2:2:1,[0]*3,5,6]) #-2-2 + 3个0+ 5,6
[-2 -1 0 1 0 0 0 5 6]
2. 第一个参数可以是控制参数,如果它用 ‘r’ 或 ‘c’ 字符可生成线性代数最常用的 matrix (和二维 numpy array 稍微有些不同)
np.r_[‘r’, [1, 2, 3],[4, 5, 6]]
matrix([[1, 2, 3, 4, 5, 6]])
3. 第一个参数可以是控制参数,如果它写成 ‘a,b,c’ 的形式,其中
a:代表轴,按「轴 a」来合并
b:合并后数组维度至少是 b
c:在第 c 维上做维度提升
print( np.r_[‘0,2,0’, [1,2,3], [4,5,6]] )
print( np.r_[‘0,2,1’, [1,2,3], [4,5,6]] )
print( np.r_[‘1,2,0’, [1,2,3], [4,5,6]] )
print( np.r_[‘1,2,1’, [1,2,3], [4,5,6]] )
[[1]
[2]
[3]
[4]
[5]
[6]]
[[1 2 3]
[4 5 6]]
[[1 4]
[2 5]
[3 6]]
[[1 2 3 4 5 6]]
现在可以看出字符串 ‘a,b,c’ 的 b 起的作用,完事后的维度是 2。看个图:
![image.png](https://img-blog.csdnimg.cn/20190619112144562.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3h1X2Z1X3lvbmc=,size_16,color_FFFFFF,t_70)
字符串 ‘a,b,c’ 总共有四类,分别是
* ‘0, 2, 0’
* ‘0, 2, 1’
* ‘1, 2, 0’
* ‘1, 2, 1’
函数里两个数组 [1,2,3], [4,5,6] 都是一维
* c = 0 代表在「轴 0」上升一维,因此得到 [[1],[2],[3]] 和 [[4],[5],[6]]
* c = 1 代表在「轴 1」上升一维,因此得到 [[1,2,3]] 和 [[4,5,6]]
接下来如何合并就看 a 的值了
* a = 0, 沿着「轴 0」合并
* a = 1, 沿着「轴 1」合并
分裂
使用「分裂」函数有两种选择
* 有通用的 split
* 有专门的 hsplit, vsplit
用下面数组来举例:
arr = np.arange(25).reshape((5, 5))
print(arr)
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]
[15 16 17 18 19]
[20 21 22 23 24]]
和 concatenate() 函数一样,我们可以在 split() 函数里通过设定轴,来对数组沿着竖直方向分裂 (轴 0) 和沿着水平方向分裂 (轴 1)。
first, second, third = np.split(arr, [1,3])
print(‘The first split is:’, first)
print(‘The second split is:’, second)
print(‘The third split is:’, third)
The first split is: [[0 1 2 3 4]]
The second split is: [[ 5 6 7 8 9]
[10 11 12 13 14]]
The third split is: [[15 16 17 18 19]
[20 21 22 23 24]]
split() 默认沿着轴 0 分裂,其第二个参数 [1, 3] 相当于是个切片操作,将数组分成三部分:
* 第一部分 - :1 (即第 1 行)
* 第二部分 - 1:3 (即第 2 到 3 行)
* 第三部分 - 3: (即第 4 到 5 行)
**hsplit, vsplit**
vsplit() 和 split(axis=0) 等价,hsplit() 和 split(axis=1) 等价。一图胜千言:
![image.png](https://img-blog.csdnimg.cn/20190619112211713.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3h1X2Z1X3lvbmc=,size_16,color_FFFFFF,t_70)
first, second, third = np.hsplit(arr,[1,3])
print( ‘The first split is’, first )
print( ‘The second split is’, second )
print( ‘The third split is’, third )
The first split is [[ 0]
[ 5]
[10]
[15]
[20]]
The second split is [[ 1 2]
[ 6 7]
[11 12]
[16 17]
[21 22]]
The third split is [[ 3 4]
[ 8 9]
[13 14]
[18 19]
[23 24]]
#### 重复和拼接
重复 (repeat) 和拼接 (tile) 这两个操作本质都是复制
* 重复是在元素层面复制
* 拼接是在数组层面复制
**重复**
函数 repeat() 复制的是数组的每一个元素,参数有几种设定方法:
* 一维数组:用标量和列表来控制复制元素的个数
* 多维数组:用标量和列表来控制复制元素的个数,用轴来控制复制的行和列
**标量**
标量参数 3 - 数组 arr 中每个元素复制 3 遍。
arr = np.arange(3)
print(arr)
print(arr.repeat(3))
[0 1 2]
[0 0 0 1 1 1 2 2 2]
**列表**
列表参数 [2,3,4] - 数组 arr 中每个元素分别复制 2, 3, 4 遍。
print(arr.repeat([2, 3, 4]))
[0 0 1 1 1 2 2 2 2]
**标量和轴**
标量参数 2 和轴 0 - 数组 arr2d 中每个元素沿着轴 0 复制 2 遍。
arr2d = np.arange(6).reshape((2, 3))
print(arr2d)
print(arr2d.repeat(2, axis=0))
[[0 1 2]
[3 4 5]]
[[0 1 2]
[0 1 2]
[3 4 5]
[3 4 5]]
**列表和轴**
列表参数 [2,3,4] 和轴 1 - 数组 arr2d 中每个元素沿着轴 1 分别复制 2, 3, 4 遍。
print(arr2d.repeat([2, 3, 4], axis=1))
[[0 0 1 1 1 2 2 2 2]
[3 3 4 4 4 5 5 5 5]]
**拼接**
函数 tile() 复制的是数组本身,参数有几种设定方法:
* 标量:把数组当成一个元素,一列一列复制
* 形状:把数组当成一个元素,按形状复制
**标量**
标量参数 2 - 数组 arr 按列复制 2 遍。
arr2d = np.arange(6).reshape((2, 3))
print(arr2d)
print(np.tile(arr2d, 2))
[[0 1 2]
[3 4 5]]
[[0 1 2 0 1 2]
[3 4 5 3 4 5]]
**形状**
标量参数 (2,3) - 数组 arr 按形状复制 6 (2×3) 遍,并以 (2,3) 的形式展现。
print(np.tile(arr2d, (2, 3)))
[[0 1 2 0 1 2 0 1 2]
[3 4 5 3 4 5 3 4 5]
[0 1 2 0 1 2 0 1 2]
[3 4 5 3 4 5 3 4 5]]
#### 其他操作
数组的其他操作,包括排序 (sort),插入 (insert),删除 (delete) 和复制 (copy)。
**排序**
排序包括直接排序 (direct sort) 和间接排序 (indirect sort)。
**直接排序**
sort()函数是按升序 (ascending order) 排列的,该函数里没有参数可以控制 order
arr = np.array([5, 3, 2, 6, 1, 4])
print(“Before sorting:”,arr)
arr.sort()
print(“After sorting:”, arr)
Before sorting: [5 3 2 6 1 4]
After sorting: [1 2 3 4 5 6]
**区别**
用来排序 numpy 用两种方式:
arr.sort()
np.sort( arr )
第一种 sort 会改变 arr,第二种 sort 在排序时创建了 arr 的一个复制品,不会改变 arr。看下面代码,用一个形状是 (3, 4) 的「二维随机整数」数组来举例,用整数是为了便于读者好观察排序前后的变化:
arr = np.random.randint(40, size=(3, 4))
print(arr)
arr[:, 0].sort()
print(arr)
np.sort(arr[:, 1])
print(arr)
[[26 7 37 22]
[12 9 13 32]
[33 15 21 17]]
[[12 7 37 22]
[26 9 13 32]
[33 15 21 17]]
[[12 7 37 22]
[26 9 13 32]
[33 15 21 17]]
此外也可以在不同的轴上排序,对于二维数组,在「轴 0」上排序是「跨行」排序,在「轴 1」上排序是「跨列」排序。
arr.sort(axis=1)
print(arr)
[[ 7 12 22 37]
[ 9 13 26 32]
[15 17 21 33]]
**间接排序**
有时候我们不仅仅只想排序数组,还想在排序过程中提取每个元素在原数组对应的索引(index),这时 argsort() 就派上用场了。以排列下面五个学生的数学分数为例:
score = np.array([100, 60, 99, 80, 91])
idx = score.argsort()
print(idx)
[1 3 4 2 0]
这个 idx = [1 3 4 2 0] 怎么理解呢?很简单,排序完之后分数应该是 [60 80 91 99 100],
* 60,即 score[1] 排在第0位, 因此 idx[0] =1
* 80,即 score[3] 排在第1 位, 因此 idx[1] =3
* 91,即 score[4] 排在第2 位, 因此 idx[2] =4
* 99,即 score[2] 排在第3 位, 因此 idx[3] =2
* 100,即 score[0] 排在第4 位, 因此 idx[4] =0
用这个 idx 对 score 做一个「花式索引」得到
print(score[idx])
[ 60 80 91 99 100]
看一个二维数组的例子。
对其第一行 arr[0] 排序,获取索引,在应用到所用行上。
arr = np.random.randint(40, size=(3, 4))
print(arr)
arr[:, arr[0].argsort()]
[[27 14 7 20]
[ 6 7 8 1]
[ 0 23 20 16]]
array([[ 7, 14, 20, 27],
[ 8, 7, 1, 6],
[20, 23, 16, 0]])
插入和删除
和列表一样,我们可以给 numpy 数组
* 用insert()函数在某个特定位置之前插入元素
* 用delete()函数删除某些特定元素
a = np.arange(6)
print(a)
print(np.insert(a, 1, 100))
print(np.delete(a, [1, 3]))
[0 1 2 3 4 5]
[ 0 100 1 2 3 4 5]
[0 2 4 5]
**复制**
用copy()函数来复制数组 a 得到 a\_copy,很明显,改变 a\_copy 里面的元素不会改变 a。
a = np.arange(6)
a_copy = a.copy()
print( ‘Before changing value, a is’, a )
print( ‘Before changing value, a_copy is’, a_copy )
a_copy[-1] = 99
print( ‘After changing value, a is’, a )
print( ‘After changing value, a_copy is’, a_copy )
Before changing value, a is [0 1 2 3 4 5]
Before changing value, a_copy is [0 1 2 3 4 5]
After changing value, a is [0 1 2 3 4 5]
After changing value, a_copy is [ 0 1 2 3 4 99]
### 5、数组的计算
介绍四大类数组计算,具体有
1.元素层面 (element-wise) 计算
2.线性代数 (linear algebra) 计算
3.元素整合 (element aggregation) 计算
4.广播机制 (broadcasting) 计算
#### 元素层面计算
Numpy 数组元素层面计算包括:
1. 二元运算 (binary operation):加减乘除
2. 数学函数:倒数、平方、指数、对数
3. 比较运算 (comparison)
先定义两个数组 arr1 和 arr2。
arr1 = np.array([[1., 2., 3.],[4., 5., 6.]])
arr2 = np.ones((2, 3)) * 2
print(arr1)
print(arr2)
[[1. 2. 3.]
[4. 5. 6.]]
[[2. 2. 2.]
[2. 2. 2.]]
#加、减、乘、除
print(arr1 + arr2 +1)
print(arr1 - arr2)
print(arr1 * arr2)
print(arr1 / arr2)
[[4. 5. 6.]
[7. 8. 9.]]
[[-1. 0. 1.]
[ 2. 3. 4.]]
[[ 2. 4. 6.]
[ 8. 10. 12.]]
[[0.5 1. 1.5]
[2. 2.5 3. ]]
#倒数、平方、指数、对数
print(1 / arr1)
print(arr1 ** 2)
print(np.exp(arr1))
print(np.log(arr1))
[[1. 0.5 0.33333333]
[0.25 0.2 0.16666667]]
[[ 1. 4. 9.]
[16. 25. 36.]]
[[ 2.71828183 7.3890561 20.08553692]
[ 54.59815003 148.4131591 403.42879349]]
[[0. 0.69314718 1.09861229]
[1.38629436 1.60943791 1.79175947]]
#比较
arr1 > arr2
array([[False, False, True],
[ True, True, True]])
arr1 > 3
array([[False, False, False],
[ True, True, True]])
从上面结果可知
* 「数组和数组间的二元运算」都是在元素层面上进行的
* 「作用在数组上的数学函数」都是作用在数组的元素层面上的。
* 「数组和数组间的比较」都是在元素层面上进行的
但是在「数组和标量间的比较」时,python 好像先把 3 复制了和 arr1 形状一样的数组 [[3,3,3], [3,3,3]],然后再在元素层面上作比较。上述这个复制标量的操作叫做「广播机制」,是 NumPy 里最重要的一个特点
#### 线性代数计算
在机器学习、金融工程和量化投资的编程过程中,因为运行速度的要求,通常会向量化 (vectorization) 而涉及大量的线性代数运算,尤其是矩阵之间的乘积运算。
但是,在 NumPy 默认不采用矩阵运算,而是数组 (ndarray) 运算。矩阵只是二维,而数组可以是任何维度,因此数组运算更通用些。
如果你非要二维数组 arr2d 进行矩阵运算,那么可以通过调用以下函数来实现:
* A = np.mat(arr2d)
* A = np.asmatrix(arr2d)
下面我们分别对「数组」和「矩阵」从创建、转置、求逆和相乘四个方面看看它们的同异。
**创建**
创建数组 arr2d 和矩阵 A,注意它们的输出有 array 和 matrix 的关键词。
arr2d = np.array([[1, 2], [3, 1]])
arr2d
array([[1, 2],
[3, 1]])
A = np.asmatrix(arr2d)
A
matrix([[1, 2],
[3, 1]])
**转置**
数组用 arr2d.T 操作或 arr.tranpose() 函数,而矩阵用 A.T 操作。主要原因就是 .T 只适合二维数据.
print(arr2d.T)
print(arr2d.transpose())
print(A.T)
[[1 3]
[2 1]]
[[1 3]
[2 1]]
[[1 3]
[2 1]]
![img](https://img-blog.csdnimg.cn/img_convert/19e038717373299c9602c77500a3dd96.png)
![img](https://img-blog.csdnimg.cn/img_convert/06939b1b89a21cf6b97d4b21d182679b.png)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
. 0.5 0.33333333]
[0.25 0.2 0.16666667]]
[[ 1. 4. 9.]
[16. 25. 36.]]
[[ 2.71828183 7.3890561 20.08553692]
[ 54.59815003 148.4131591 403.42879349]]
[[0. 0.69314718 1.09861229]
[1.38629436 1.60943791 1.79175947]]
#比较
arr1 > arr2
array([[False, False, True],
[ True, True, True]])
arr1 > 3
array([[False, False, False],
[ True, True, True]])
从上面结果可知
- 「数组和数组间的二元运算」都是在元素层面上进行的
- 「作用在数组上的数学函数」都是作用在数组的元素层面上的。
- 「数组和数组间的比较」都是在元素层面上进行的
但是在「数组和标量间的比较」时,python 好像先把 3 复制了和 arr1 形状一样的数组 [[3,3,3], [3,3,3]],然后再在元素层面上作比较。上述这个复制标量的操作叫做「广播机制」,是 NumPy 里最重要的一个特点
线性代数计算
在机器学习、金融工程和量化投资的编程过程中,因为运行速度的要求,通常会向量化 (vectorization) 而涉及大量的线性代数运算,尤其是矩阵之间的乘积运算。
但是,在 NumPy 默认不采用矩阵运算,而是数组 (ndarray) 运算。矩阵只是二维,而数组可以是任何维度,因此数组运算更通用些。
如果你非要二维数组 arr2d 进行矩阵运算,那么可以通过调用以下函数来实现:
- A = np.mat(arr2d)
- A = np.asmatrix(arr2d)
下面我们分别对「数组」和「矩阵」从创建、转置、求逆和相乘四个方面看看它们的同异。
创建
创建数组 arr2d 和矩阵 A,注意它们的输出有 array 和 matrix 的关键词。
arr2d = np.array([[1, 2], [3, 1]])
arr2d
array([[1, 2],
[3, 1]])
A = np.asmatrix(arr2d)
A
matrix([[1, 2],
[3, 1]])
转置
数组用 arr2d.T 操作或 arr.tranpose() 函数,而矩阵用 A.T 操作。主要原因就是 .T 只适合二维数据.
print(arr2d.T)
print(arr2d.transpose())
print(A.T)
[[1 3]
[2 1]]
[[1 3]
[2 1]]
[[1 3]
[2 1]]
[外链图片转存中…(img-6BJD0Tbf-1715585168503)]
[外链图片转存中…(img-yWKT9jts-1715585168503)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!