机器学习-Numpy的学习

(41) 2024-09-01 23:01:02

文章目录

1.引入库

2.数组的创建

2.1.简单的生成一维数组

2.2.生成0到100的随机数组

2.3.生成10个符合正态分布的数

2.4.生成元素全为1的矩阵

2.5.生成等差数列

2.6.生成等比数列

2.7.生成0到1(左闭右开)的随机数组 

3.查看操作

3.1.查看数组的大小

3.2.查看数组的数据类型

3.3.查看数组的元素数量

3.4.查看数组的维度

4.文件(IO)操作

4.1.文件保存

4.2.文件读取

5.数组的数据类型

6.数组运算

6.1.幂运算

6.2.整除和模运算

6.3.逻辑运算

7.数组的复制

7.1.完全没有复制

7.2.深拷贝

7.3.浅拷贝(视图)

7.4.深拷贝的应用

 8.索引和切片

8.1.索引

8.2.切片

9.形状操作

9.1.reshape操作

9.2.转置操作(.T)

10.堆叠操作(np.concatenate)


前言

本人现在在学习机器学习以及深度学习方面的知识,想通过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[::].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)

上面两个数组行列都不相等,则会出现类似如下的报错

机器学习-Numpy的学习 (https://mushiming.com/)  第1张

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)

机器学习-Numpy的学习 (https://mushiming.com/)  第2张

此时,我们可以这样修改

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为什么可行:

机器学习-Numpy的学习 (https://mushiming.com/)  第3张

 如果整个三维数组的形状是(3,4,2),类比上图可知,(3,1,2)是轴1方向。下面是个人理解:无论是一维数组还是二维数组的广播机制,都是要满足在任意一个轴方向(行方向和列方向)上的形状相等;(3,1,2)正好是三维数组的其中一个轴方向,所以满足三维数组的广播机制。


THE END

发表回复