Numpy学习笔记(三)

大兔子大兔子 提交于 2020-02-01 19:21:12

Numpy的基本操作

前面我们已经知道了新建Numpy数组和定义数组元素的方法。现在来学习数组的各种运算方法


算术运算符

数组的第一类运算是使用算术运算符进行的运算。最显而易见的是为数组加上或乘以一个标量

>>> a=np.arange(4)
>>> a
array([0, 1, 2, 3])
>>> a+4
array([4, 5, 6, 7])
>>> a*2
array([0, 2, 4, 6])
>>>

这些运算符还可以用于两个数组的运算,在numpy中,这些运算符为元素级,也就是说,它们只用于位置相同的元素之间,所得到的运算结果组成一个新的数组,运算结果在新数组中的位置跟操作数位置相同在这里插入图片描述

>>> a
array([0, 1, 2, 3])
>>> b=np.arange(4,8)
>>> b
array([4, 5, 6, 7])
>>> a+b
array([ 4,  6,  8, 10])
>>> a-b
array([-4, -4, -4, -4])
>>> a*b
array([ 0,  5, 12, 21])
>>>

此外,这些运算符还适用于返回值为Numpy数组的函数,

例如,你可以用数组a乘以数组b的正弦值或平方根

>>> a*np.sin(b)
array([-0.        , -0.95892427, -0.558831  ,  1.9709598 ])
>>> a*np.sqrt(b)
array([0.        , 2.23606798, 4.89897949, 7.93725393])
>>>

对于多维数组,这些运算符仍然是元素级

>>> A=np.arange(0,9).reshape(3,3)
>>> A
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])
>>> B=np.ones((3,3))
>>> B
array([[1., 1., 1.],
       [1., 1., 1.],
       [1., 1., 1.]])
>>> A*B
array([[0., 1., 2.],
       [3., 4., 5.],
       [6., 7., 8.]])
>>>

矩阵积

选择*号作为元素级运算符是Numpy库比较奇怪的一点,事实上,在很多其他数据分析工具中,*在用于两个矩阵之间的运算时指定的是矩阵积。
Numpy中用dot()函数表示这类乘法,注意,它不是元素积的

>>> np.dot(A,B)
array([[ 3.,  3.,  3.],
       [12., 12., 12.],
       [21., 21., 21.]])
>>>

所得到的数组中每个元素为,第一个矩阵中与该元素行号相同的元素与第二个矩阵中与该元素列号形同的元素,两两相乘后再求和。下图给出了矩阵积的计算过程

在这里插入图片描述
矩阵积的另外一种写法是把dot()函数当做其中一个矩阵对象的方法

>>> A.dot(B)
array([[ 3.,  3.,  3.],
       [12., 12., 12.],
       [21., 21., 21.]])
>>>

由于矩阵积的计算不遵循交换律,A* B确实不等于B*A

>>> np.dot(B,A)
array([[ 9., 12., 15.],
       [ 9., 12., 15.],
       [ 9., 12., 15.]])
>>>

自增和自减运算符

python没有++或–运算符,对变量的值进行自增与自减,需要使用+=或-+运算符。这两个运算符跟前面见过的只有一点不同,运算得到的结果不是赋给一个新数组而是赋给参与运算的数组自身

>>> a=np.arange(4)
>>> a
array([0, 1, 2, 3])
>>> a+=1
>>> a
array([1, 2, 3, 4])
>>> a-=1
>>> a
array([0, 1, 2, 3])
>>> a+=3
>>> a
array([3, 4, 5, 6])
>>>

因此,这类运算符比每次只能加1的自增运算符用途更广
例如,当你想修改数组元素的值而不想生成新数组时,就可以使用它们

>>> a+=4
>>> a
array([ 7,  8,  9, 10])
>>> a*=2
>>> a
array([14, 16, 18, 20])
>>>

通用函数

通用函数通常叫做ufunc,它对数组中的各个元素逐一进行操作。这表明,通用函数分别处理输入数组的每个元素,生成的结果组成一个新的输出数组。输出数组的大小跟输入数组相同

三角函数等很多数学运算符合通用函数的定义,
例如,计算平方根的sqrt()函数。用来取对数的log()函数和求正弦值的sin()函数

>>> a=np.arange(1,5)
>>> a
array([1, 2, 3, 4])
>>> np.sqrt(a)
array([1.        , 1.41421356, 1.73205081, 2.        ])
>>> np.log(a)
array([0.        , 0.69314718, 1.09861229, 1.38629436])
>>> np.sin(a)
array([ 0.84147098,  0.90929743,  0.14112001, -0.7568025 ])
>>>

Numpy实现了很多通用函数

聚合函数

聚合函数是指对一组值进行操作,返回一个单一值最为结果的函数,因而,求数组所有元素之和的函数就是聚合函数,ndarray类实现了多个这样的函数

>>> a=np.array([3.3,4.5,1.2,5.7,0.3])
>>> a.sum()
15.0
>>> a.min()
0.3
>>> a.max()
5.7
>>> a.mean()
3.0
>>> a.std()
2.0079840636817816
>>>

索引机制、切片和迭代方法

前面学习了数组创建和数组运算,现在血虚数组对象的操作方法,以及如何通过索引和切片的方法选择元素,以获取数组中某几个元素的视图或者赋值操作改变元素,随后讲解数组的迭代方法

索引机制

数组索引机制指的是方括号([])加序号的形式引用单个数组元素,它的用处很多,比如抽取元素,选取数组的几个元素,甚至为其赋一个新值

新建数组的同时,会生成跟数组大小一致的索引
在这里插入图片描述
要获取数组的单个元素,指定元素的索引即可

>>> a=np.arange(10,16)
>>> a
array([10, 11, 12, 13, 14, 15])
>>> a[4]
14
>>>

Numpy数组还可以使用负数作为索引,这些索引同样为递增序列,只不过从0开始,依次增加-1,但实际表示的是从数组的最后一个元素向数组的第一个元素移动。在负数索引机制中,数组的第一个元素的索引最小

>>> a[-1]
15
>>> a[-6]
10
>>>

方括号内传入多个索引值,可以同时选择多个元素

>>> a[[1,3,4]]
array([11, 13, 14])
>>>

再来看下二维数组,它也被称为矩阵。矩阵是由行和列组成的矩形数组,行和列用两条轴来定义,其中轴0用行来表示,轴1用列来表示,因此,二维数组的索引用一对值来表示:第一个值为行索引,第二个值为列索引。所以,如要获取或选取矩阵中的元素,仍使用方括号,但索引值为两个【行索引,列索引】
在这里插入图片描述

>>> A=np.arange(10,19).reshape((3,3))
>>> A
array([[10, 11, 12],
       [13, 14, 15],
       [16, 17, 18]])
>>>
>>>

因此,如想获取第二行第三列的元素,需要使用索引值【1,2】

>>> A[1,2]
15

切片操作

切片操作是指抽取数组的一部分元素生成新数组。对python列表进行切片操作得到的数组是原数组的副本,而对Numpy数组进行切片操作得到的数组则是指向相同的缓冲区得视图

如要抽取数组的一部分,必须使用切片句法:也就是,把几个用冒号(:)隔开的数字置于方括号内
如想抽取数组的一部分,例如从第二个到第六个元素的部分,就需要在方括号指定元素的起始索引1和结束元素的索引5

>>> a=np.arange(10,16)
>>> a
array([10, 11, 12, 13, 14, 15])
>>> a[1:5]
array([11, 12, 13, 14])
>>>

如想从上面那一部分元素中,每隔一定数量的元素中抽取一个,可以再用一个数字指定所抽取两个元素之间的间隔大小。例如,间隔为2,表示每隔一个元素抽取一个

>>> a[1:5:2]
array([11, 13])
>>>

为了更好的理解切片的句法,你还应该了解不明确指定起始和结束位置的情况。
如省略第一个数字,Numpy会认为第一个数字是0,;
如省去第二个数字,Numpy会认为第二个数字是数组的最大索引值;
如省去最后一个数字,它将会理解为1,也就是说抽取所有元素而不再考虑间隔

>>> a[::2]
array([10, 12, 14])
>>> a[:5:2]
array([10, 12, 14])
>>> a[:5:]
array([10, 11, 12, 13, 14])
>>>

对于二维数组,切分句法仍然适用,只不过需要分别指定行和列的索引值。例如只抽取第一行

>>> A=np.arange(10,19).reshape((3,3))
>>> A
array([[10, 11, 12],
       [13, 14, 15],
       [16, 17, 18]])
>>> A[0,:]
array([10, 11, 12])
>>>

上面代码中,第二个索引处只使用冒号,而没有指定任意数字,这样选择的是所有列,相反,如果想抽取第一列的所有元素,方括号内的两项应该交换位置

>>> A[:,0]
array([10, 13, 16])
>>>

如要抽取一个小点儿的矩阵,需要明确指定所有得抽取范围

>>> A[0:2,0:2]
array([[10, 11],
       [13, 14]])
>>>

如要抽取的行或列的索引不连续,可以把这几个索引放到数组中

>>> A[[0,2],0:2]
array([[10, 11],
       [16, 17]])
>>>

数组迭代

python的数组元素的迭代很简单,只需要使用for结构即可

>>> for i in a:
...     print(i)
...
10
11
12
13
14
15
>>>

二维数组当然也可以使用for结构,把两个嵌套在一起即可。第一层循环扫描数组的所有行,第二层循环扫描所有的列,实际上,如果遍历矩阵,你就会发现它总是按照第一条轴对称矩阵进行扫描

>>> for row in A:
...     print(row)
...
[10 11 12]
[13 14 15]
[16 17 18]
>>>

如果想遍历矩阵的每个元素,可以使用下面的结构,用for循环遍历A.flat

>>> for item in A.flat:
...     print(item)
...
10
11
12
13
14
15
16
17
18
>>>

除了for循环,NUmpy还提供另外一种更优雅的遍历方法。通常用函数处理行、列或单个元素时,需要用到遍历。如果想用聚合函数处理每一列或行,返货一个数值作为结果,最好用纯Numpy方法处理循环:apply_along_axis()函数

这个函数接收三个参数:聚合函数、对哪条轴应用迭代操作和数组,
如果axis选项的值为0,按列进行迭代操作,处理元素;
值为1,则按行操作。
例如,可以先求每一列的平均数,再求每一行的平均数

>>> np.apply_along_axis(np.mean,axis=0,arr=A)
array([13., 14., 15.])
>>> np.apply_along_axis(np.mean,axis=1,arr=A)
array([11., 14., 17.])
>>>

上述例子使用了Numpy库定义的函数,但是你也可以自己定义这样的函数,上面还使用了聚合函数,然而,用通用函数也未尝不可,下面的例子,先后按行、列进行迭代操作,但两者的最终结果一致,通用函数apply_along_axis()实际上是按照指定的轴逐元素遍历数组

>>> def foo(x):
...     return x/2
...
>>> np.apply_along_axis(foo,axis=1,arr=A)
array([[5. , 5.5, 6. ],
       [6.5, 7. , 7.5],
       [8. , 8.5, 9. ]])
>>> np.apply_along_axis(foo,axis=0,arr=A)
array([[5. , 5.5, 6. ],
       [6.5, 7. , 7.5],
       [8. , 8.5, 9. ]])
>>>

如上所见,不论是遍历行还是遍历列,通用函数都将输入数组的每个元素做折半处理

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!