2024年Numpy基本使用_numpy基本用法,2024年最新五面阿里拿下飞猪事业部offer

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事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行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 13
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值