【Python进阶笔记】—— “还担心不会数据处理?” Numpy详细笔记 之 概念用法篇

打算好好利用这个寒假继续夯实Python基础,由于ML、Deep Learning、数据分析的内容都会用得上Numpy,因此该系列的Python博文从Numpy的一系列学习入手。我们开始吧!

1. 数组的属性

  1. np.dtype是ndarray 对象的元素类型,常见的有int64,int32,int16,float32、、如果选用的dtype是int64之类的,那么存储的数据的精度会高一些,但是相应的,占用的空间就会较多。如果int32或者int16之类的,就会节约一些空间,相应地数据的精度会降低一些。
  2. np.shape,这是经常用到的一个属性,输出数组的维度,对于矩阵,就是表示n 行 m 列
    如果是三维的矩阵,像我们经常处理的RGB图片,那么它的维度含义就是:通道数、每个通道的长度、每个通道的宽度(顺序的话看情况,比如tensorflow和pytorch的顺序就不一样)
import numpy as np
array = np.array([[1,2,3],[4,5,6]])
print(array.shape)
print(array.shape[0])

Output:(2,3)
2

注意:这里我们输出的np.shape是一个元组,那么我们可以使用np.shape[n]来获取第n个维度

2.Numpy里面如何创建一个array?

(1)利用np.array(),将列表转换为矩阵array,array()括号里面的是列表:

import numpy as np
array = np.array([[1,2],[3,4]])
print(array)

output结果就是:
[[1 2]
[3 4]]
(2)生成一些特殊的矩阵:比如全0矩阵、全1矩阵等等,我们有以下的办法:

 np.ones(shape, dtype = None, order = 'C')  #注意:是ones!用于生成全1矩阵
np.zeros(shape, dtype = float, order = 'C')  #注意:是zeros,用于生成全0矩阵

对于np.ones和np.zeros的使用说明:假设我们要生成一个3行4列的全0矩阵,那么我们应该这样写:

np.zeros((3,4))   #大家注意:是有两对括号的!

(3)从数值范围创建数组
我们可以生成一个“线段数组”,换句话说:np.linspace 函数用于创建一个一维数组,数组是一个等差数列构成的

np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
  1. start表示这个等差数列的起始值(包括该值"start")
  2. stop表示这个等差数列的终止值(包括这个终止值stop)
  3. num表示这个等差数列有多少项
print(np.linspace(1,10,5))#生成一个起始值是1,终止值是10,有5项的等差数列,也就是说公差应该是2.25

output:[ 1. 3.25 5.5 7.75 10. ]

另外,我们还可以用arange()生成等差数列,但是和上面有很多不一样的地方!

np.arange(start, stop, step, dtype)
  1. start表示数列开始值(包括这个"start")
  2. stop表示数列的终止值(注意:不包括”stop”!!!)这里要特别说明:np.arange里面的start和stop其实是给出了一个范围,也就是start~stop-1,但是数组里面的最大元素不一定会等于stop-1,这取决与步长,下面的例子就会说明:
  3. step表示步长和Python里面的for循环中的步长类似概念
print(np.arange(1,10,2)  #生成一个范围从1~9,步长为2的数组

Output:[1,3,5,7,9]

print(np.arange(1,10,3)

Output:[1,4,7] (注意:不包括stop-1)

(4)np.reshape()方法将矩阵变成不同的形状:
我们可以先用np.arange()创建一个维度为1的数组,然后这个产生的ndarray 对象再使用reshape()方法,我们看看例子:

array = np.arange(1,12,2).reshape((2,3))
print(array)

Output:
[[ 1 3 5]
[ 7 9 11]]

3. Numpy里面array的基础运算

  1. array的+,-就是普通的array + array2和array - array2,结果都是array对应位置的相加或相减
  2. array的乘方:我们使用**表示乘方符号
  3. array的相乘:这里分两种,一类是array中对应元素的相乘,另一类是矩阵的乘法:
import numpy as np

A = np.arange(4).reshape((2,2))
B = np.array([[1,2],[3,1]])

C = A * B     #矩阵对应位置元素的相乘就是用 * 
C_2 = np.dot(A, B)   #矩阵乘法用的是np.dot(),当然也可以写成:C_2 = A.dot(B)

print(A, '\n', B)
print(C, '\n', C_2)

Output:
[[0 1]
[2 3]]
[[1 2]
[3 1]]
[[0 2]
[6 3]]
[[ 3 1]
[11 7]]

  1. np.max(),np.min(),np.sum()分变表示求矩阵的最大值,最小值和矩阵元素值的和。
    np.sum()里面可以定义参数axis,axis = 0表示求每一行的和;axis = 1表示求每一列的和, np.max()和np.min()也是类似的
    如果我们想要知道矩阵里面最大值或者最小值所对应的下标,我们可以使用np.argmax(),np.argmin()
import numpy as np

A = np.arange(8)
print(A)

print(np.sum(A))
print(np.max(A))
print(np.min(A))
print(np.argmax(A))

[0 1 2 3 4 5 6 7]
28
7
0
7

  1. numpy里面计算矩阵的逐差:使用np.diff()
import numpy as np

A = np.arange(12).reshape((3,4))
print(A)
print(np.diff(A))

[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[1 1 1]
[1 1 1]
[1 1 1]]
我们看到,np.diff()的作用是按行将A矩阵的后一个减前一个,第一行也就是1-0;然后是2-1、3-2

还有许多其他的方法,这里就不一一介绍了,大家以后需要用到的时候可以自行查询

4. Numpy里面矩阵的索引:

和Python里面列表的索引类似,numpy里面也有索引,用来找寻矩阵某一位置对应的数:
首先,我们先创建一个矩阵:

import numpy as np
array = np.arange(1,16).reshape((3,5))
print(array)

[[ 1 2 3 4 5]
[ 6 7 8 9 10]
[11 12 13 14 15]]
那么,我们应该如何找到10这个数字呢?首先,我们应该定位10是在矩阵的第几行第几列(注意:矩阵里面的行和列的索引都是从0开始的),那么我们就可以知道:10的行、列的索引分别为1,4,那么,我们可以有以下两种写法:

print(array[1][4])    #输出10
print(array[1,4])     #输出10

如果想取一整行或者一整列,我们可以这样写:

print(array[1,:])   #取array矩阵第2行的所有数
print(array[:,2])   #取array矩阵第3列的所有数
print(array[0:2, 2])   #输出array矩阵第1行和第2行,第3列的数字,输出结果是【3,8】

5. Numpy里面矩阵的迭代

首先,我们来看看单纯用一个for循环去迭代上文的array矩阵或是什么结果:

for i in array:
	print(i)

Output:
[1 2 3 4 5]
[ 6 7 8 9 10]
[11 12 13 14 15]
说明:只用for循环迭代是矩阵的每一行,也就是一次迭代一整行
【那么,要怎么迭代矩阵的列呢?】
其实不难,我们需要先将这个矩阵做一个转置就好了,这样,经过转置的矩阵,原来的行变成了列,原来的列变成了行,那么迭代转置矩阵的行就等价于迭代原本矩阵的列了

for col in array.T:
	print(col)

[ 1 6 11]
[ 2 7 12]
[ 3 8 13]
[ 4 9 14]
[ 5 10 15]

【如果想迭代输出矩阵的每一个数字怎么办?】
这里介绍一个np.flatten()方法,将矩阵转换成一维的矩阵,这在CNN的全连接层很常用(卷积之后的结果需要转换成一维的矩阵,才能输入进全连接层)

print(array.flatten())

[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
那么,我们就可以利用这种方法迭代矩阵的每一个元素:

for i in array.flatten():
	print(i)

6. Numpy里面array的合并

首先,我们创建两个数组:

import numpy as np
A = np.array([1,2,3])
B = np.array([4,5,6])

下面我们介绍两种array合并的方法:(1)np.vstack()【垂直合并】和(2)np.hstack() 【水平合并】,我们来看看效果如何:

C = np.vstack((A, B))
print(C, '\n',C.shape)

[[1 2 3]
[4 5 6]]
(2, 3)

D = np.hstack((A, B))
print(D, '\n', D.shape)

[1 2 3 4 5 6]
(6,)

因为我们这里A, B都是一维的序列,所以水平合并就是123456这样,但是如果想变成:
[1 4
2 5
3 6]
这样怎么办?

很多人的第一反应是:分别将A,B转置,然后再用np.hstack()。这样真的可以吗??
答案是:No!,对于一维的序列进行转置,转置出来的结果没有任何改变:

print(A.T)   #结果依然是[1 2 3]

【方法】:我们可以给一维的序列A和B再增加一个维度,使用np.newaxis方法:

A = np.array([1,2,3,4])
print(A.shape)    #输出为(3,)
A = A[:, np.newaxis]   #这句话的意思是给A增加一个列的维度
print(A, '\n', A.shape)   #现在的A的维度就变成了(3,1)

后来的A长这样:
[[1]
[2]
[3]]
那么:

A = np.array([1,2,3])[:, np.newaxis]
B = np.array([4,5,6])[:,np.newaxis]
C = np.hstack((A, B))
print(C, '\n', C.shape)

Output:
[[1 4]
[2 5]
[3 6]]
(3, 2)
如果上面改成:A = A[np.newaxis, :]就表示给A增加一个行的维度,输出为:(1,3)

这里加入一个小插曲:就是我之前在训练CNN的时候,图像的预处理是需要将RGB转换成灰度的,按理说RGB转灰度之后对应的通道那个维度是从3变成了1对吧,但是发现它会直接压缩掉通道那个维度,导致送入卷积层的时候维度不匹配,解决方法就是在shape对应的位置使用np.newaxis增加一个通道的维度。加入shape是这样(m, c, h, w)(m代表样本数,c代表通道数,h代表图像的高,w代表图像的宽度)

7. Numpy里面array的分割

和列表的操作相似,在Numpy里面我们对矩阵进行分割也有np.split()函数:

np.split(ary, indices_or_sections, axis=0)
  1. 第一个参数:ary:要切割的矩阵
  2. 第二个参数 indices_or_sections:要把这个矩阵切割分成多少个子矩阵
  3. 第三个参数:axis,要切割的维度:axis = 0表示按行切割(以第一个维度切割);axis=1表示按列切割(以第二个维度切割)
import numpy as np
A = np.arange(1,13).reshape((3,4))
print(A)
print(np.split(A, 3 axis = 0)) #切割对象A矩阵;按行切割,切割成3个子矩阵(A一共有3行)
print(np.split(A, 2, axis = 1))  #切割对象A矩阵;按列切割,切割成2个子矩阵(A一共有4列)

Output:
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
[array([[1, 2, 3, 4]]), array([[5, 6, 7, 8]]), array([[ 9, 10, 11, 12]])]
[array([[ 1, 2],
[ 5, 6],
[ 9, 10]]), array([[ 3, 4],
[ 7, 8],
[11, 12]])]

大家看到:我们使用np.split()方法,返回的对象是一个由子矩阵组成的列表,因此,我们可以使用列表的索引来获取所需要的子矩阵,像这样

print(np.split(A, 3 axis = 0)[1])   #获取第二个子矩阵

Output:[[5, 6, 7, 8]]

同样地,和array合并方法类似,array分割也有:np.vsplit()和np.hsplit():

print(np.vsplit(A, 3))     #按行切割
print((np.hsplit(A, 2))    #按列切割

Output:
[array([[1, 2, 3, 4]]), array([[5, 6, 7, 8]]), array([[ 9, 10, 11, 12]])]
[array([[ 1, 2],
[ 5, 6],
[ 9, 10]]), array([[ 3, 4],
[ 7, 8],
[11, 12]])]

结果和np.split()是一样的

【但是,问题来了】:大家有没有注意到:我们刚刚使用np.split()或是np.vsplit(),np.hsplit()方法的时候,不管是以行切割还是以列切割,所选取的子矩阵数量都是可以被行数(以行切割时)或被列数(以列切割时)整除
但是,大多数情况下,我们需要选取的子矩阵数是不能被行数或列数整除的,我们还能用np.split()吗?像下面的例子这样?

print(np.split(A, 2, axis=0))

答案是:NO!(会报错)

我们这时需要使用:np.array_split() 方法,参数和np.split()一样,我们来看看:

print(np.array_split(A, 2, axis=0))  #切割对象是A,按行切割分成2个子矩阵(A有3行)

Output:
[array([[1, 2, 3, 4],
[5, 6, 7, 8]]), array([[ 9, 10, 11, 12]])]
大家看到了吗:我们这样切割的结果是,第一个子矩阵是2行的,第二个子矩阵是1行的

8. Numpy里面的深复制和浅复制

我们试着输入下面的代码:

import numpy as np

A = np.arange(4)  #生成array([0,1,2,3])
print(A)
B = A    #A赋值给B
C = A    #A赋值给C
D = B    #B赋值给D
print(A, '\n', B, '\n', C, '\n', D)

Output:
[0 1 2 3]
[0 1 2 3]
[0 1 2 3]
[0 1 2 3]
[0 1 2 3]
如果我现在想改变A的值,那么,试问:B,C,D的值会不会跟着改变呢?我们来看看:

A[0] = 12  #把A矩阵的第一个值改成了12
print(A, '\n', B, '\n', C, '\n', D)

Output:
[12 1 2 3]
[12 1 2 3]
[12 1 2 3]
[12 1 2 3]
我们发现:单纯用 = 这种赋值符号,一旦赋值的那个数改变了,被赋值的所有数都会跟着改变。换句话说:它们现在是关联的!!那么,如果我们不想让他们关联,即:我只想让B复制到A现在的这个值,而B不受A的改变而改变,我们可以使用copy()方法:

import numpy as np

A = np.arange(4)
print(A)
B = A.copy()    #深复制
C = A.copy()
D = A           #浅复制
print(A, '\n', B, '\n', C, '\n', D)

A[0] = 12
print(A, '\n', B, '\n', C, '\n', D)

Output:
[0 1 2 3]
[0 1 2 3]
[0 1 2 3]
[0 1 2 3]
[0 1 2 3]
[12 1 2 3]
[0 1 2 3]
[0 1 2 3]
[12 1 2 3]

在python中,对象赋值实际上是对象的的引用,当创建一个对象,然后把它赋值给另外一个变量的时候,python没有拷贝这个对象,而只是拷贝了这个对象的引用,因此就出现了浅复制,即复制后原对象改变后,复制出来的对象也会改变,要防止复制出来的对象改变,就要使用深复制

9.Numpy里面的广播机制

还记得我们在上文所说的array的加减法吗?我们在线性代数的课程中要求做加减法的两个矩阵一定要大小相同,可是在numpy里面却不一定总是这样—— 因为numpy里面存在广播机制(Broadcast)

这里先贴上广播的定义,博主对里面有一些东西的理解还差点火候,待日后深入理解了再做补充:

广播的原则:如果两个数组的后缘维度(trailing dimension,即从末尾开始算起的维度)的轴长度相符,或其中的一方的长度为1,则认为它们是广播兼容的。广播会在缺失和(或)长度为1的维度上进行。

import numpy as np

A = np.arange(12).reshape((3,4))
print(A)
B = np.array([1,2,3,4])
C = A + B
print(C)

Output:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[ 1 3 5 7]
[ 5 7 9 11]
[ 9 11 13 15]]
我们看到,虽然B是维度为(4,)的矩阵,但是在相加的过程中,numpy把B扩展成了:
[[1,2,3,4]
[1,2,3,4]
[1,2,3,4]]这样的矩阵,再和A相加

触发广播机制时,输入矩阵要保证某一维度是1,不然会报错

我们再看看其他几种情况广播的示意图:

发布了98 篇原创文章 · 获赞 283 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44586473/article/details/104025494