程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长

+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

python numpy模块教程与使用numpy进行机器学习相关用法示例

发布于2020-02-17 21:19     阅读(916)     评论(0)     点赞(25)     收藏(5)


本博客介绍 numpy 模块的使用方法,并对机器学习中应用到的较多的一些方法进行重点讲解。

机器学习入门系列博客

一、创建numpy的数组(矩阵)

在机器学习中,用到最多的就是矩阵(数组)的运算了,使用 python 的 list 可以完成这些运算,但是效率不高,使用numpy 提供的函数接口可以很高效的完成这些功能。下面介绍了几种创建 numpy 数组(矩阵) 的方法

1 np.array()
import numpy as np
arr=np.array([1,2,3,4],dtype=int)
print(arr)
print(arr.dtype)
arr2=np.array([1,2,3])
print(arr2)
print(arr2.dtype)
arr3=np.array([1,2,3.0])
print(arr3)
print(arr3.dtype)

输出结果:
[1 2 3 4]
int64
[1 2 3]
int64
[1. 2. 3.]
float64
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

主要参数

  • arr: python list ,元组,numpy array ,用于初始化数组中的元素
  • dtype : 数据类型,非必选参数,numpy 会根据初始化的数据自动进行数据类型选择,详见例子 arr2 和 arr3。
2 np.asarray()
import numpy as np
arr=np.asarray([1,2,3,4],dtype=int)
print(arr)
print(arr.dtype)
arr2=np.asarray([1,2,3])
print(arr2)
print(arr2.dtype)
arr3=np.asarray([1,2,3.0])
print(arr3)
print(arr3.dtype)

输出结果:
输出结果:
[1 2 3 4]
int64
[1 2 3]
int64
[1. 2. 3.]
float64
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

基本用法与 np.array() 一致

3 生成某一个值的特定矩阵
import numpy as np
arr1=np.ones(shape=(4,4),dtype=int)
print(arr1)
print("---------------")
arr2=np.zeros(shape=5,dtype=float)
print(arr2)
print("---------------")
arr3=np.full(shape=(2,5),dtype=int,fill_value=520)
print(arr3)

输出结果:
[[1 1 1 1]
 [1 1 1 1]
 [1 1 1 1]
 [1 1 1 1]]
---------------
[0. 0. 0. 0. 0.]
---------------
[[520 520 520 520 520]
 [520 520 520 520 520]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

有三种方法可以生成某一个值的特定矩阵,分别为 np.ones(),np.zeros(),np.full()。三个函数中的参数 dtype 均为可选,判断元素类型的机制和 np.array() 一致。三种函数的用法和返回结果如例所示,理解起来比较简单。

4 创建等步长数组
import numpy as np
arr1=np.arange(0,10)
print(arr1)
arr2=np.arange(0,10,0.5)
print(arr2)
arr3=np.linspace(0,10,11)
print(arr3)

输出结果
[0 1 2 3 4 5 6 7 8 9]
[0.  0.5 1.  1.5 2.  2.5 3.  3.5 4.  4.5 5.  5.5 6.  6.5 7.  7.5 8.  8.5
 9.  9.5]
[ 0.  1.  2.  3.  4.  5.  6.  7.  8.  9. 10.]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

np.arange() 可以用于生成等步长的数组,用法类似于 range() ,参数分别为起始点,终止点,步长。终止值不会出出现在最终的数组中。所不同的是步长可以是浮点数,如例子所示。

除了np.arange() 之外,np.linspace() 函数也可以用于生成等步长的数组,与 arange 函数不同的是,生成的数组中包括终止值,第三个参数不是步长,而是平均分成了多少份,如例子所示。

5 使用随机的方法创建数组
import numpy as np
arr1=np.random.randint(low=0,high=10,size=(2,5))
print(arr1)
arr2=np.random.random(size=(3,3))
print(arr2)
arr3=np.random.normal(loc=0,scale=1,size=20)
print(arr3)

输出结果:
[[2 8 1 2 7]
 [6 9 0 3 5]]
[[0.74831657 0.32991162 0.53259451]
 [0.7005176  0.17805907 0.58690404]
 [0.19568012 0.59133077 0.31332564]]
[-0.16324593  0.12253359  1.2257147   2.35137786 -0.79876596 -0.1480339
  1.03034397  0.63034462  0.91966266 -1.24178813 -0.02773027  0.68352114
  0.01390874 -0.04098013  0.52609043 -0.83886096  0.07309302  0.49478078
  1.7656865   1.1617786 ]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

函数 np.random.randint(low,high,size) 用于生成在 low 和 high 之前的一个随机的整数,注意,high 取不到。size 可以是一个整数或者元组,使用整数表示创建一个一位数组,元组表示创建一个shape 为 size 的矩阵。

函数 np.random.random(size) 用于均匀的生成在 0-1 范围内的浮点数。size 的含义和 randint 一致。

函数 np.random.normal(loc,scale,size) 用于生成正太分布的随机数,size 的含义没有变化,loc 表示均值,scale 可以理解为方差,例子中arr3 就是一个均值为0,方差为 1 的正态分布的具有20个元素的数组。

多说一句,计算机中的随机数字不是真正的随机数字,而是使用随机算法生成的伪随机数字,如果想要得到相同的随机数字用于实验的时候复现算法,可以人为的设置随机种子:

np.random.seed(seed)  #seed 是一个整数
  • 1

二、numpy array 的基本属性和操作

import numpy as np
arr1=np.random.randint(low=0,high=10,size=(2,5))
print(arr1.shape)
print(arr1.size)
print(arr1.ndim)
print(arr1.dtype)

输出
(2, 5)
10
2
int64
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
1 基本属性

ndim:数组(矩阵)的维度
shape:数组(矩阵)的形状
dtype:数组(矩阵)的数据类型
size:数组中(矩阵)的元素的个数

2 数据访问方法

对于python 的list 数组,我们可以使用 方括号+ 下标的方式进行数据访问。例如一个二维的 python list 数组 arr,可以使用 arr[0][0] 来访问二维数组的第一行,第一列的元素。但是这种方法在 numpy 数组中虽然也可以,但是不建议使用,在numpy 数组中建议使用如下方式访问:

import numpy as np
arr1=np.random.randint(low=0,high=10,size=(2,5))
print(arr1)
print(arr1[1,2])

输出结果:
[[4 5 1 4 0]
 [7 4 9 4 7]]
9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

arr1[1,2] 表示arr1矩阵的 第第二行,第三列的元素,就是 9 。
除此之外,numpy 还支持切片访问(如果不清楚可以查看Python的基础语法)

import numpy as np
arr=np.array([1,2,3,4,5])
print(arr[:3]) # 访问第四个元素之前的元素(前三个元素)
print(arr[2:]) # 访问第三个元素以及之后的元素
print(arr[0:2]) # 访问第1个元素到第三个元素之间的元素,不包括第三个元素
  • 1
  • 2
  • 3
  • 4
  • 5

前面提到过numpy array 不建议使用 arr[索引1][索引2] 来访问二位数组中的元素,为什么呢?其实是容易混淆,下面的例子就演示了这种情况:

import numpy as np
arr=np.random.randint(0,10,size=(5,5))
print(arr)
print("-------------------")
print(arr[:3,:3]) # 取 arr 数组中的 前三行,前三列
print("-------------------")
print(arr[:3][:3]) # 返回的结果是啥?

输出结果:
[[9 2 9 5 8]
 [8 3 9 5 5]
 [4 7 5 7 2]
 [4 2 7 3 3]
 [8 8 6 7 7]]
-------------------
[[9 2 9]
 [8 3 9]
 [4 7 5]]
-------------------
[[9 2 9 5 8]
 [8 3 9 5 5]
 [4 7 5 7 2]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

是不是觉得很懵? arr[:3][:3] 怎么会返回一个 3行5列的数组呢?其实是 arr[:3] 首先取到了arr 数组的前三行元素,得到了一个中间的数组 arr1,然后 arr1[:3] 又得到了arr1的前三行,最终的结果就是上面展示的那样,看吧,其实 arr[:3][:3] 是对 arr 数组两次取前三行的结果,特别容易混淆,所以,听我一句劝,numpy 数组别用这种双重方括号加索引的方式访问。

特别提醒一下:使用切片的方式得到的子数组其实是对于原数组的引用,修改子数组,原数组也会收到影响,这与python 的list 不同,python list 是默认会复制一个副本到子数组。示例如下:

import numpy as np
arr=np.random.randint(0,10,size=(5,5))
print(arr)
print("-------------------")
subarr=arr[:3,:3]
print(subarr) # 取 arr 数组中的 前三行,前三列
print("--------------------")
subarr[0,0]=250
print(subarr)
print("--------------------")
print(arr)

输出结果:
[[3 6 6 7 8]
 [8 7 1 9 2]
 [8 6 8 3 3]
 [9 5 8 4 2]
 [6 0 1 6 0]]
-------------------
[[3 6 6]
 [8 7 1]
 [8 6 8]]
--------------------
[[250   6   6]
 [  8   7   1]
 [  8   6   8]]
--------------------
[[250   6   6   7   8]
 [  8   7   1   9   2]
 [  8   6   8   3   3]
 [  9   5   8   4   2]
 [  6   0   1   6   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
  • 32

想要建立副本要调用 copy 方法

subarr=arr[:3,:3].copy()
  • 1
3 数组形状改变
import numpy as np
arr=np.random.randint(0,10,size=25)
print(arr)
print("-------------------")
arr1=arr.reshape(5,5)
print(arr1)
print("--------------------")
print(arr) # 取 arr 数组中的 前三行,前三列
print("--------------------")
arr2=arr.reshape(5,-1)
print(arr2)
print("--------------------")
print(arr.reshape(10,-1))

输出结果:
[2 1 2 2 6 3 4 1 6 7 8 8 4 2 7 2 4 6 8 3 2 3 9 5 8]
-------------------
[[2 1 2 2 6]
 [3 4 1 6 7]
 [8 8 4 2 7]
 [2 4 6 8 3]
 [2 3 9 5 8]]
--------------------
[2 1 2 2 6 3 4 1 6 7 8 8 4 2 7 2 4 6 8 3 2 3 9 5 8]
--------------------
[[2 1 2 2 6]
 [3 4 1 6 7]
 [8 8 4 2 7]
 [2 4 6 8 3]
 [2 3 9 5 8]]
--------------------
Traceback (most recent call last):
  File "/home/qianqianjun/CODE/Python/ML_ Mooc/draw.py", line 3, in <module>
    print(arr.reshape(10,-1))
ValueError: cannot reshape array of size 25 into shape (10,newaxis)
  • 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
  • 32
  • 33
  • 34
  • 35

reshape(shape) 可以获得形状改变之后的数组,shape 可以是元组,但是可以不写括号
如例子所示,arr1是一个 5 × 5 的矩阵,由原来的数组变换而来。需要注意的是 reshape不会改变原数组,如例子所示。

另外,为了方便起见,numpy 还提供了一种自动计算维度的机制,只要待计算的维度可以唯一确定,numpy 会自动计算,我们只需要填写 -1 即可,如例子所示。

此外,不是随便写啥维度都可以的,必须要可以整除,如例子所示,25 没有办法整除 10,所以 reshape 会报错。

三、numpy数组合并和分割

1 合并操作
import numpy as np
arr1=np.array([1,2,3])
arr2=np.array([4,5,6])
arr3=np.array([7,8,9])

arr12=np.concatenate([arr1,arr2])
print(arr12)
print("--------------")
arr123=np.concatenate([arr1,arr2,arr3])
print(arr123)

输出结果:
[1 2 3 4 5 6]
--------------
[1 2 3 4 5 6 7 8 9]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

对于向量,使用 np.concatenate() 函数即可实现连接合并。参数是要连接的数组,可以是多个。
对于矩阵来说,要比单纯的向量要复杂一点,因为涉及到一个 axis 参数:

import numpy as np
arr1=np.array([[1,2,3]])
arr2=np.array([[4,5,6]])
arr3=np.array([[7,8,9]])

arr12=np.concatenate([arr1,arr2])
print(arr12)
print("--------------")
arr12_=np.concatenate([arr1,arr2],axis=1)
print(arr12_)
print("--------------")

arr123=np.concatenate([arr1,arr2,arr3])
print(arr123)
print("--------------")
arr123_=np.concatenate([arr1,arr2,arr3],axis=1)
print(arr123_)

输出结果:
[[1 2 3]
 [4 5 6]]
--------------
[[1 2 3 4 5 6]]
--------------
[[1 2 3]
 [4 5 6]
 [7 8 9]]
--------------
[[1 2 3 4 5 6 7 8 9]]
  • 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

axis 默认是 0,按照第一个维度进行拼接,(即按照行进行堆叠)如果是 1 ,就按照第二个维度拼接(即按照列进行堆叠)。如例子所示。

特别注意的是,concatenate 只支持具有相同ndim 的数组进行拼接,下面的例子展示了这种情况

import numpy as np
arr1=np.array([[1,2,3]])
vector=np.array([250,250,250])
arr=np.concatenate([arr1,vector])

输出结果:
Traceback (most recent call last):
  File "/home/qianqianjun/CODE/Python/ML_ Mooc/draw.py", line 4, in <module>
    arr=np.concatenate([arr1,vector])
ValueError: all the input arrays must have same number of dimensions
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

对此,numpy 特别的设置了两个很智能的函数 vstack,示例如下:

import numpy as np
arr1=np.array([[1,2,3]])
arr2=np.array([[4,5,6]])
arr3=np.array([[7,8,9]])

vector=np.array([250,250,250])
arrv=np.vstack([arr1,vector])
print(arrv)

输出结果:
[[  1   2   3]
 [250 250 250]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

对于矩阵之间的合并,除了 vstack 之外还有 hstack ,其实和 concatenate函数很类似,vstack 相当于 axis=0,hstack 相当于axis=1。示例如下:

import numpy as np
arr1=np.array([[1,2,3]])
arr2=np.array([[4,5,6]])
arr3=np.array([[7,8,9]])

arrv=np.vstack([arr1,arr2])
arrh=np.hstack([arr1,arr2])
print(arrv)
print("------------------")
print(arrh)
print("------------------")
arrv2=np.concatenate([arr1,arr2],axis=0)
arrh2=np.concatenate([arr1,arr2],axis=1)
print(arrv2)
print("------------------")
print(arrh2)

输出结果
[[1 2 3]
 [4 5 6]]
------------------
[[1 2 3 4 5 6]]
------------------
[[1 2 3]
 [4 5 6]]
------------------
[[1 2 3 4 5 6]]
  • 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
2 分割操作

使用np.split() 函数来进行数组(矩阵)的分割功能。

import numpy as np
arr=np.random.randint(0,10,size=30)
arr1,arr2,arr3=np.split(arr,[10,20])
print(arr)
print(arr1)
print(arr2)
print(arr3)

输出结果
[8 4 0 8 8 6 5 1 2 5 7 3 0 4 8 3 6 1 2 4 5 3 0 3 4 2 3 6 7 1]
[8 4 0 8 8 6 5 1 2 5]
[7 3 0 4 8 3 6 1 2 4]
[5 3 0 3 4 2 3 6 7 1]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

如例子所示,np.split() 按照在隔断点数组中的元素进行分割,将一个长度为 30 的数组分成等长的三部分,只需要添加两个隔断点即可,需要注意的是,隔断点是开区间,这也就是为啥arr1 不包含arr[10] 的原因。

对于 ndim 大于1 的情况,就又涉及到了 axis 参数。示例如下:

import numpy as np
arr=np.random.randint(0,10,size=(10,10))
arr1,arr2,arr3=np.split(arr,[3,6])
print(arr)
print("--------------------------")
print(arr1)
print("--------------------------")
print(arr2)
print("--------------------------")
print(arr3)
print("--------------------------")

arr4,arr5=np.split(arr,[5],axis=1)
print(arr4)
print("--------------------------")
print(arr5)

输出结果:
[[4 3 5 1 6 8 1 8 0 9]
 [4 8 1 8 6 1 0 0 2 7]
 [1 8 6 2 4 1 5 2 0 4]
 [3 7 9 2 2 2 8 6 9 9]
 [5 1 9 7 9 0 6 0 3 1]
 [8 8 9 2 9 0 8 8 4 2]
 [8 1 2 9 2 5 3 1 3 7]
 [5 0 9 0 5 1 6 7 7 8]
 [1 5 9 5 0 8 1 1 3 2]
 [4 3 0 6 4 3 9 4 4 6]]
--------------------------
[[4 3 5 1 6 8 1 8 0 9]
 [4 8 1 8 6 1 0 0 2 7]
 [1 8 6 2 4 1 5 2 0 4]]
--------------------------
[[3 7 9 2 2 2 8 6 9 9]
 [5 1 9 7 9 0 6 0 3 1]
 [8 8 9 2 9 0 8 8 4 2]]
--------------------------
[[8 1 2 9 2 5 3 1 3 7]
 [5 0 9 0 5 1 6 7 7 8]
 [1 5 9 5 0 8 1 1 3 2]
 [4 3 0 6 4 3 9 4 4 6]]
--------------------------
[[4 3 5 1 6]
 [4 8 1 8 6]
 [1 8 6 2 4]
 [3 7 9 2 2]
 [5 1 9 7 9]
 [8 8 9 2 9]
 [8 1 2 9 2]
 [5 0 9 0 5]
 [1 5 9 5 0]
 [4 3 0 6 4]]
--------------------------
[[8 1 8 0 9]
 [1 0 0 2 7]
 [1 5 2 0 4]
 [2 8 6 9 9]
 [0 6 0 3 1]
 [0 8 8 4 2]
 [5 3 1 3 7]
 [1 6 7 7 8]
 [8 1 1 3 2]
 [3 9 4 4 6]]
  • 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
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

对于 axis参数,已经讲过好多次了,这里不再赘述。
此外,还有两个特定的切分函数,不再赘述了:

up,dowm=np.vsplit(arr,[断点数组])  #垂直方向上进行拆分,相当于 axis=0
left,right=np.hsplit(arr,[断点数组])  #水平方向进行拆分,相当于 axis =1  
  • 1
  • 2

这里实验数据都是一堆数字,可能不太形象,本人还有一篇博客专门写了维度的切分和合并操作,详见这里:https://blog.csdn.net/qq_38863413/article/details/103526645

四、numpy 相关运算

1 Universial Function

这里介绍了numpy数组(矩阵)的一种机制,可以将特定的运算在数组(矩阵)每个元素上执行,不清楚我在说什么可以看下面的例子:

对于有个python 的列表,如果我们想要得到列表中每个元素的2倍 的结果,该如何操作?

L=[1,2,3,4]
print(2*L)

res=[]
for i in L:
    res.append(i * 2)

result=[ i*2 for i in L]

print(res)
print(result)

输出结果:
[1, 2, 3, 4, 1, 2, 3, 4]
[2, 4, 6, 8]
[2, 4, 6, 8]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

可见,对于每一个元素乘以 2 的任务,python list L 2×L 会复制一遍,两个 L 拼接在一起,无法达到效果,只能使用循环或者列表生成式来计算。而numpy 就不一样,引入universal Function 机制来简化这类操作:

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

print(2*arr)
print(10-arr)
print(3+arr)
print(60/arr)

输出结果
[ 2  4  6  8 10]
[9 8 7 6 5]
[4 5 6 7 8]
[60. 30. 20. 15. 12.]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

除了支持加减乘除,还支持幂运算(**),取余(%),取倒数(1/arr);三角函数 np.sin(),np.cos() 等,指数和对数函数:np.exp(x),np.power(3,x) 即:x中每一个数字 取 3 的 x[i] 次方,np.log(x),np.log2(x),np.log10(x)……

两个矩阵之间也可以进行这种 universal Function 操作,也就是对应位置的元素进行对应运算:

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

print(arr-arr2)
print(arr+arr2)
print(arr * arr2)
print(arr/arr2)

输出结果:
[-4 -2  0  2  4]
[6 6 6 6 6]
[5 8 9 8 5]
[0.2 0.5 1.  2.  5. ]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

需要注意的是,上面提到的 + - × / 两个矩阵 都是对应元素做运算,要求矩阵形状一样,否则会报错。

2 矩阵运算

线性代数中提到的矩阵乘法也在numpy中得到了实现,示例如下:

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

输出结果:
[[26 28]
 [26 28]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

使用传统的方法代码如下:

import numpy as np
arr1=np.array([[1,2,3],[3,2,1]])
arr2=np.array([[4,5],[5,4],[4,5]])
def multiply(arr1,arr2):
    """
    矩阵乘法实现
    :param arr1:  ndarray 
    :param arr2:  ndarray 
    :return:  arr1.dot(arr2) 的结果
    """
    assert arr1.ndim==2 and arr2.ndim==2,"输入数据不是矩阵"
    assert arr1.shape[1] == arr2.shape[0],"两个矩阵无法相乘"
    res=np.zeros(shape=(arr1.shape[0],arr2.shape[1]),dtype=int)

    for i in range(arr1.shape[0]):
        for k in range(arr2.shape[0]):
            for j in range(arr2.shape[1]):
                res[i][j]+= arr1[i][k] * arr2[k][j]
    return res
print(multiply(arr1,arr2))

输出结果:
[[26 28]
 [26 28]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

可见,numpy 真的很方便。

矩阵转置和求逆矩阵操作也很轻松实现:

import numpy as np
# 求转置矩阵
arr=np.array([[1,2,3],[4,5,6]])
print(arr)
arrT=arr.T
print(arrT)

# 求逆矩阵
arr2=np.array([[1,2],[3,4]])
arr2inv=np.linalg.inv(arr2)
print(arr2)
print(arr2inv)
print(arr2.dot(arr2inv))

输出结果:
[[1 2 3]
 [4 5 6]]
[[1 4]
 [2 5]
 [3 6]]
[[1 2]
 [3 4]]
[[-2.   1. ]
 [ 1.5 -0.5]]
[[1.00000000e+00 1.11022302e-16]
 [0.00000000e+00 1.00000000e+00]]
  • 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

补充一下,只有方阵才有逆矩阵。

3 向量和矩阵的运算

看到这个题目你可能觉得很奇怪,矩阵和向量数学上是不能运算的,没意义。说的没错,确实没意义,这里所说的矩阵和向量运算实际上是想说一下 numpy 的智能之处。如下所示:

import numpy as np
# 矩阵向量相加
arr=np.array([[1,2,3],[4,5,6],[7,8,9]])
vector=np.array([1,2,3])
print(arr+vector)
print("-----------------------------")

# 矩阵向量相乘
arr1=np.array([[1,2,3],[4,5,6],[7,8,9]])
vector1=np.array([3,2,1])
print(arr1.dot(vector1))
print("-----------------------------")

vector1=np.array([3,4,5])
arr2=np.array([[1,2],[2,3],[3,4]])
print(arr2.dot(vector1))

输出结果:
[[ 2  4  6]
 [ 5  7  9]
 [ 8 10 12]]
-----------------------------
[10 28 46]
-----------------------------
Traceback (most recent call last):
  File "/home/qianqianjun/CODE/Python/ML_ Mooc/draw.py", line 16, in <module>
    print(arr2.dot(vector1))
ValueError: shapes (3,2) and (3,) not aligned: 2 (dim 1) != 3 (dim 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

由例子可知:当矩阵和向量进行矩阵乘法运算的时候,numpy 可以自动根据需要智能相乘,但是要要求 矩阵的 shape[1] == 向量的shape[0]。

对于矩阵和向量相加,其实和 universal Function 类似。本质上是先对向量进行堆叠,然后再进行相加,相当于下面的运算:

import numpy as np
arr=np.array([[1,2,3],[4,5,6],[7,8,9]])
vector=np.array([1,2,3])
print(arr+vector)
print("----------------------")
stackV=np.tile(vector,reps=(3,1))
print(stackV)
print("----------------------")
print(arr+stackV)

输出结果:
[[ 2  4  6]
 [ 5  7  9]
 [ 8 10 12]]
----------------------
[[1 2 3]
 [1 2 3]
 [1 2 3]]
----------------------
[[ 2  4  6]
 [ 5  7  9]
 [ 8 10 12]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

其中tail函数用于复制向量,堆叠成数组,reps参数(r,c) 分别表示对于行的维度复制几次,列的维度复制几次。

五、numpy 聚合运算

向量上的聚合运算

np.sum(arr) 或者 arr.sum()
np.min(arr) 或者 arr.min()
np.max(arr) 或者 arr.max()
  • 1
  • 2
  • 3

不再赘述。

矩阵上的聚合运算

np.sum(arr,axis=0)  # 每一列相加。 axis=1 的时候每一行相加,即沿着 axis 的方向进行相加。
np.prod(arr) # 所有元素相乘
np.mean()  #平均值
np.median() #中位数
np.var() #方差
np.std() #标准差
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

比较简单,不再赘述。

六、索引相关运算(arg… 函数的使用)

import numpy as np
arr=np.array([1,2,3,0,4,5,6,520,7,8])
maxIndex=np.argmax(arr)
minIndex=np.argmin(arr)
argIndex=np.argsort(arr)
print("最大值的索引为:"+str(maxIndex))
print("最小值的索引为:"+str(minIndex))
print("排序之后的元素在原数组中的索引分别为:"+str(argIndex))

输出结果:
最大值的索引为:7
最小值的索引为:3
排序之后的元素在原数组中的索引分别为:[3 0 1 2 4 5 6 8 9 7]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

np.argmin(arr) 返回arr中最小元素所在的 索引
np.argmax(arr) 返回最大值所在的索引
np.random.shuffle(arr) 打乱arr 的顺序
np.argsort(arr) 返回按照顺序排序之后的元素分别在原来数组中的索引。

对于矩阵,和前面的类似,需要一个 axis 参数:
np.argsort(arr,axis=1) 沿着列进行排序(对每一行分别排序),返回排好序的元素在未排序数组中的索引。axis=0 的时候是沿着 行进行排序(对每一列进行排序)。

七、Fancy Indexing

这一小节是我觉得 numpy 最方便的地方,很多功能让起初是 Java,C++ 铁杆粉丝的我开始更加喜欢 python。

首先,一个列表可以作为索引,先看一个例子:

import numpy as np
arr=np.array([2,4,6,8,10,12,14,16,18,20])
ind=[3,5,8]
print(arr[ind])

输出结果
[ 8 12 18]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

ind=[3,5,8],arr[ind] 返回arr 中的第三个,第五个和第八个元素,十分方便。
这里可以看到,arr[ind] 返回的元素个数就是索引的个数,ind索引数组里面有多少个索引,就从arr 中取到对应位置的元素,组成一个数组返回。

更酷的是ind 甚至可以是 二维的,这样最终的结果是一个二维矩阵。和ind 的shape一样(因为是按照 ind 索引数组中的索引取的元素):

import numpy as np
arr=np.array([2,4,6,8,10,12,14,16,18,20])
ind=np.array([[1,2],[3,4]])
print(arr[ind])

输出结果
[[ 4  6]
 [ 8 10]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

还可以这样操作:假设arr 是一个 4 × 4的方阵,如果想得到其中的 (0,1),(1,2),(2,3) 三个点怎么办?

import numpy as np
arr=np.random.randint(0,50,size=(5,5))
row=[0,1,2]
col=[1,2,3]
result=arr[row,col]
print(arr)
print("------------------------")
print(result)

输出结果
[[17 49  4  7 16]
 [ 2 16  8 38 44]
 [31 22 36 46 33]
 [17 23 18 47 26]
 [18  8 16  4 14]]
------------------------
[49  8 46]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

更加灵活的是 row,col 都可以变换为 数字或者切片操作,例如:
arr[2,col] 固定了行,取到的三个点分别为 (2,1),(2,2),(2,3)
arr[:2,col] 取到 6 个点,分别为 (0,1),(0,2),(0,3),(1,1),(1,2),(1,3)

另外,使用 bool 数组也可以作为 索引,使用bool 数组的意思是当为false 的时候,对于所在的位置不感兴趣:

import numpy as np
arr=np.array([0,1,2,3])
b=[True,True,False,True]
print(arr[b])

输出结果:
[0 1 3]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

bool数据有非常大的用处,numpy 的比较运算符返回的是 bool 数组,示例如下:

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

输出结果:
[False False False  True]
[False False  True False]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

读者可以自己尝试 < , >= , <=,!=等比较运算符。
这里有一个非常好用的写法,比如我想找到一个数组中所有大于 3 的元素,可以这么写:

import numpy as np
arr=np.array([1,2,3,4,5,6,7,8])
print(arr[arr>3])

输出结果:
[4 5 6 7 8]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

最后再介绍一下 any 和 all 函数,字面意思很好理解:
np.any(x==0) 如果 x中有一个元素是 0 。则返回为 True;
np.all(x>0) x中必须所有的元素都大于0 才返回 True;示例如下:

import numpy as np
arr=np.array([1,2,3,4,5,6,7,8])
print(np.any(arr>3))
print(np.any(arr>100))
print(np.all(arr<100))
print(np.all(arr>3))

输出结果
True
False
True
False
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

另外,np.all() 和 np.any() 在处理 二维数组的时候,都可以传入参数 axis,不再赘述了。

发布了57 篇原创文章 · 获赞 27 · 访问量 4万+


所属网站分类: 技术文章 > 博客

作者:vike

链接:https://www.pythonheidong.com/blog/article/231680/975ce7e2fc8590567c6c/

来源:python黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

25 0
收藏该文
已收藏

评论内容:(最多支持255个字符)