文章目录
前言
本人现在在学习机器学习以及深度学习方面的知识,想通过CSDN平台去记录自己的学习历程,也希望可以和大家一起学习,共同进步。
1.引入库
代码如下:
import numpy as np
2.数组的创建
我们经常在很多demo中看到用Numpy生成数组的情况,这里就简单列举几个。
2.1.简单的生成一维数组
arr1=np.array([1,2,3])
2.2.生成0到100的随机数组
arr2=np.random.randint(0,100,size=(10))
注:这里的size可以理解为生成数组的大小;举个例子,若要生成一个二维数组,则上述代码可以改为
arr2=np.random.randint(0,100,size=(2,5))
2.3.生成10个符合正态分布的数
arr3=np.random.randn(10)
2.4.生成元素全为1的矩阵
np.ones(shape=(5,3))
注:shape跟上面的size是一样的,表示数组的大小
2.5.生成等差数列
#方法1-表示0到10之间,公差为3的等差数列
np.arange(0,10,3)
#方法2-表示1-99之间,数列长度为50的等差数列
np.linspace(1,99,50)
2.6.生成等比数列
#公比为2,数列长度为10的等比数列
np.logspace(1,10,num=10,base=2)
2.7.生成0到1(左闭右开)的随机数组
np.random.random(size=(3,5))
3.查看操作
3.1.查看数组的大小
arr.shape
3.2.查看数组的数据类型
arr.dtype
3.3.查看数组的元素数量
arr.size
3.4.查看数组的维度
arr.ndim
4.文件(IO)操作
4.1.文件保存
np.save('./file1',arr1) #'./file1'表示保存的文件路径;arr1表示要保存的数组
也可以同时保存两个数组
np.savaz('./file2',arr_x=arr1,arr_y=arr2)
文件后缀名默认为npy
也可以保存为csv和txt格式
#保存为txt文件
np.savetxt('./file3.txt',arr1,delimiter=',',fmt='%.5f')
#delimiter表示以逗号作为分隔符
#fmt表示以数据类型为float的五位小数,如果是'%.5e'表示科学计数法的五位小数
#保存为csv文件
np.savetxt('./file3.csv',arr1,delimiter=';',fmt='%.5e')
注:对于csv文件,分隔符一般设置为逗号,这样打开csv文件时可以实现自动划分
4.2.文件读取
np.load('./arr.npy')
如果是要读取具体的某个数组,可以这样
np.load('./file2.npz')['arr_x']
注意:必须是该保存文件中存在的数组,否则会报错!
5.数组的数据类型
常见的有int、float、uint(无符号整数)等
如果要改变数据类型,可以用如下代码
arr1=arr2.astype(np.int64)
注意:数据类型不同,比如int8,int16和int64,它们的精度不同并且它们所占的内存也不同
6.数组运算
6.1.幂运算
#平方
arr1**2
#开方
arr1**0.5
6.2.整除和模运算
#整除
arr1 // 2 #不要余数,只保留整数项
#模运算
arr1 % 2 #只保留余数项
6.3.逻辑运算
与:& ;或:| ;非(取反):~
7.数组的复制
7.1.完全没有复制
a=np.random.randint(0,100,size=(3,4))
display(a)
b=a
b[0,0]=1
display(a)
a和b是"命运共同体",即a(b)变化,b(a)也会跟着变化
7.2.深拷贝
a=np.random.randint(0,100,size=(5,6))
b=a.copy()
可以理解为a,b为两个互不影响的数组,即无论b怎么变化,都不会影响到a
7.3.浅拷贝(视图)
a=np.random.randint(0,100,size=(5,6))
b=a.view()
可以理解为a,b共用一个数组,即a变化,b也会跟着变,反之亦然
补充说明:b=a跟浅拷贝类似,但是区别在于前者两者的内存地址相同,而浅拷贝是不同的
7.4.深拷贝的应用
对于比较大的数组,我们可以通过“切片+深拷贝”的方法,减少所占内存
import gc#垃圾回收
a=np.arange(1e8)#生成一个较大数组
b=a[::100000].copy()#每10万条数据选一个
del a
gc.collect()#垃圾清理
8.索引和切片
索引和切片的区别可以用一句话来概括:索引用来对单个元素进行访问,切片则对一定范围内的元素进行访问
8.1.索引
对于简单索引,这边可以举几个例子:
#先定义一个数组
a=np.random.randint(0,100,size=(6,5))
a[5,4]
a[-1,-1]#两者是等价的
a[0,[0,1]]
a[[0,0],[0,1]]#两者是等价的
此外还有一种叫boolean(布尔)类型的索引,大致的格式是这样的:
a[(a>2)&(a<5)]#找出数组a中满足大于2且小于5的元素
8.2.切片
关于切片操作,这边总结了两点:
1.切片是“左闭右开”
2.[ , ]:逗号之前表示行操作,逗号之后表示列操作
下面简单举几个例子:
例子1
#取第一行的前三列
display(a,a[0,:3])
Output:
array([[22, 78, 39, 33, 55],
[40, 84, 25, 68, 17],
[86, 69, 66, 71, 1],
[71, 83, 52, 92, 48],
[84, 96, 38, 92, 25],
[46, 52, 76, 52, 29]])
array([22, 78, 39])
例子2:
#取2,3两行且是首尾两列的元素
display(a,a[1:3,[0,-1]])
Output:
array([[22, 78, 39, 33, 55],
[40, 84, 25, 68, 17],
[86, 69, 66, 71, 1],
[71, 83, 52, 92, 48],
[84, 96, 38, 92, 25],
[46, 52, 76, 52, 29]])
array([[40, 17],
[86, 1]])
例子3:
display(a,a[::-1],a[::2],a[1::2])
Output:
array([[22, 78, 39, 33, 55],
[40, 84, 25, 68, 17],
[86, 69, 66, 71, 1],
[71, 83, 52, 92, 48],
[84, 96, 38, 92, 25],
[46, 52, 76, 52, 29]])#原来的a
array([[46, 52, 76, 52, 29],
[84, 96, 38, 92, 25],
[71, 83, 52, 92, 48],
[86, 69, 66, 71, 1],
[40, 84, 25, 68, 17],
[22, 78, 39, 33, 55]])#将a按行进行逆向输出
array([[22, 78, 39, 33, 55],
[86, 69, 66, 71, 1],
[84, 96, 38, 92, 25]])#选取a的偶数列
array([[40, 84, 25, 68, 17],
[71, 83, 52, 92, 48],
[46, 52, 76, 52, 29]])#选取a的奇数列
我们可以采用如下方法提取数组中的元素,并使其保持二维数组的形式
a[[0,1,3]][:,[1,2]]
也可以采用这种方法,两者的结果是相同的
a[np.ix_([0,1,3],[1,2])]
Output:
array([[96, 81],
[89, 82],
[91, 86]])
但是当我们使用下面的方法时,却出现了报错
a[[0,1,3],[1,2]]
可以这样去解释:逗号前面的行操作必须和后面的列操作的数量保持相等,即
a[[0,1,3],[1,2,3]]
这样就不会出现报错了。但值得一提的是,用该方法取出来的元素是一维的;且位置是一一对应,而上面的是行列是任意组合的
Output:
array([96, 82, 91])
9.形状操作
9.1.reshape操作
可以改变数组的大小,但是改变前后必须使数组中元素总量不变。话不多说,直接代码演示:
#原来是6行5列,我们通过reshape操作,将其转化为3行10列
display(a,a.reshape(3,10))
Output:
array([[54, 96, 81, 63, 14],
[ 8, 89, 82, 59, 9],
[ 5, 57, 47, 86, 88],
[39, 91, 86, 91, 82],
[48, 81, 9, 49, 76],
[21, 27, 56, 62, 47]])
array([[54, 96, 81, 63, 14, 8, 89, 82, 59, 9],
[ 5, 57, 47, 86, 88, 39, 91, 86, 91, 82],
[48, 81, 9, 49, 76, 21, 27, 56, 62, 47]])
9.2.转置操作(.T)
实现数组的行列互换,类比我们线代中矩阵的转置
display(a,a.T)
Output:
array([[54, 96, 81, 63, 14],
[ 8, 89, 82, 59, 9],
[ 5, 57, 47, 86, 88],
[39, 91, 86, 91, 82],
[48, 81, 9, 49, 76],
[21, 27, 56, 62, 47]])
array([[54, 8, 5, 39, 48, 21],
[96, 89, 57, 91, 81, 27],
[81, 82, 47, 86, 9, 56],
[63, 59, 86, 91, 49, 62],
[14, 9, 88, 82, 76, 47]]
但是对于高维的矩阵,它是怎么转置的呢,我们可以用代码来看一下:
b=np.random.randint(0,100,size=(3,4,5))#创建一个三维数组
display(b.shape,b.T.shape)#查看数组的大小
Output:
(3, 4, 5)
(5, 4, 3)
所以我们可以知道 .T默认第1维和第3维的转置。
10.堆叠操作(np.concatenate)
可以理解为拼接;但是对于一维数组和二维数组,它们还是有一点区别。
我们先看一维数组的堆叠:
c=np.random.randint(0,10,size=(10))#创建1个一维数组
c1=c[::2]
c2=c[::3]#每3个里面取1个
display(c,np.concatenate([c,c1,c2]))
Output:
array([4, 4, 2, 3, 3, 6, 2, 8, 1, 7])
array([4, 4, 2, 3, 3, 6, 2, 8, 1, 7, 4, 2, 3, 2, 1, 4, 3, 2, 7])
我们接下来看一下二维数组:
arr1=np.random.randint(0,10,size=(5,3))
arr2=np.random.randint(0,10,size=(2,3))
arr3=np.random.randint(0,10,size=(5,2))#创建3个不同的二维数组
#按第1维度(行)进行堆叠
np.concatenate([arr1,arr2])#默认为axis=0
#按第2维度(列)进行堆叠
np.concatenate([arr1,arr3],axis=1)
对于二维数组,需要注意两点:1.axis=0表示第一个维度,axis=1表示第二个维度;2.对于上面例子,如果按行进行堆叠,则需要两个数组的列相等,同样按列进行堆叠,需要两个数组的行相等
np.concatenate([arr2,arr3],axis=1)
np.concatenate([arr2,arr3],axis=0)
上面两个数组行列都不相等,则会出现类似如下的报错
11.广播机制
当两个数组的形状不相同的时候,通过扩展数组的方法实现相加、相减、相乘等操作,这种机制叫广播
11.1.一维数组广播
arr1=np.sort([0,1,2,3]*3).reshape(4,3)#np.sort:对数组中的元素进行排序
arr2=np.array([1,2,3])
arr3=arr1+arr2
display(arr1.shape,arr2.shape)
display(arr1,arr2,arr3)
我们由下面的输出结果可以看到:当一个二维数组和一维数组做运算的时候,在保证列方向上的元素数量相等的情况下,会将二维数组(arr1)中的每一行都与一维数组(arr2)进行运算
Output:
(4, 3)
(3,)
array([[0, 0, 0],
[1, 1, 1],
[2, 2, 2],
[3, 3, 3]])#arr1
array([1, 2, 3])#arr2
array([[1, 2, 3],
[2, 3, 4],
[3, 4, 5],
[4, 5, 6]])#arr3
11.2.二维数组广播
arr1=np.sort([0,1,2,3]*3).reshape(4,3)
arr2=np.array([[1],[2],[3],[4]])#创建一个二维数组
arr3=arr1+arr2
display(arr1.shape,arr2.shape)
display(arr1,arr2,arr3)
我们由下面的输出结果可以看到:当两个二维数组做运算的时候,在保证横方向上的元素数量相等的情况下,会将二维数组(arr1)中的每一列都与另一个二维数组(arr2)进行运算
Output:
(4, 3)
(4, 1)
array([[0, 0, 0],
[1, 1, 1],
[2, 2, 2],
[3, 3, 3]])#arr1
array([[1],
[2],
[3],
[4]])#arr2
array([[1, 1, 1],
[3, 3, 3],
[5, 5, 5],
[7, 7, 7]])#arr3
11.3.高维(>2)数组广播
arr1=np.sort([0,1,2,3,4,5,6,7]*3).reshape(3,4,2)#创建1个三维数组
arr2=np.array([0,1,2,3,4,5,6,7]).reshape(4,2)
arr3=arr1+arr2
display(arr1.shape,arr2.shape)
display(arr1,arr2,arr3)
这种情况下没有出现报错,这因为虽然广播机制强调形状匹配,但是一般情况下默认补全第1维。如果对arr2进行如下的调整,那就会出现报错问题
arr2=np.array([0,1,2,3,4,5,6,7]).reshape(2,4)
此时,我们可以这样修改
arr2=np.array([0,1,2,3]).reshape(4,1)
#或者
arr2=np.array([0,1,2,3]*3).reshape(3,4,1)
#或者
arr2=np.random.randint(0,10,size=(3,1,2))
三种方案都是可以的
这里我们解释一下方案3为什么可行:
如果整个三维数组的形状是(3,4,2),类比上图可知,(3,1,2)是轴1方向。下面是个人理解:无论是一维数组还是二维数组的广播机制,都是要满足在任意一个轴方向(行方向和列方向)上的形状相等;(3,1,2)正好是三维数组的其中一个轴方向,所以满足三维数组的广播机制。