凌云时刻 · 技术

导读:NumPy是Python中的一个类库,它支持高阶维度数组(矩阵)的创建及各种操作、运算,是我们在机器学习中经常会使用的一个类库。这一节再看一些NumPy的矩阵用法。

作者 | 计缘

来源 | 凌云时刻(微信号:linuxpk)

 numpy.random

NumPy也提供了生成随机数和随机元素数组的方法,我们来看一下:

# 生成从0到10之间的随机数
np.random.randint(0, 10)
# 结果
3# 生成元素从0到10,一共4个随机元素的数组
np.random.randint(0, 10, size=4)
# 结果
array([4, 7, 8, 1])# 生成元素随机从0到10,3行5列的矩阵
np.random.randint(0, 10, size=(3, 5))
# 结果
array([[6, 9, 7, 0, 9],[7, 4, 8, 7, 8],[4, 4, 9, 7, 2]])

如果我们希望每次使用随机方法生成的结果都是一样的,一般调试时候有这个需求,此时NumPy的random()方法也提供了方便简单的方式,既随机种子的概念:

# 生成随机矩阵前给定一个种子
np.random.seed(123)
# 然后生成随机矩阵
np.random.randint(0, 10, size=(4, 5))
# 结果
array([[2, 2, 6, 1, 3],[9, 6, 1, 0, 1],[9, 0, 0, 9, 3],[4, 0, 0, 4, 1]])# 再次生成随机矩阵时,只要传入相同的种子,就可以得到相同结果的矩阵
np.random.seed(123)
np.random.randint(0, 10, size=(4, 5))
# 结果
array([[2, 2, 6, 1, 3],[9, 6, 1, 0, 1],[9, 0, 0, 9, 3],[4, 0, 0, 4, 1]])# 默认范围是从0.0到1.0,返回值为float型
np.random.random()
# 结果
0.18249173045349998# 传入的参数是数组的大小
np.random.random(10)
# 结果
array([ 0.17545176,  0.53155137,  0.53182759,  0.63440096,  0.84943179,0.72445532,  0.61102351,  0.72244338,  0.32295891,  0.36178866])# 创建4行5列,元素值的范围从0.0到1.0的矩阵
np.random.random((4, 5))
# 结果
array([[ 0.22826323,  0.29371405,  0.63097612,  0.09210494,  0.43370117],[ 0.43086276,  0.4936851 ,  0.42583029,  0.31226122,  0.42635131],[ 0.89338916,  0.94416002,  0.50183668,  0.62395295,  0.1156184 ],[ 0.31728548,  0.41482621,  0.86630916,  0.25045537,  0.48303426]])


 指定均值和标准差生成随机数数组或矩阵

我们先来看看均值、方差、标准差的概念。均值很好理解,就是所有样本数据的平均值,描述了样本集合的中间点:

方差是衡量样本点和样本期望值相差的度量值:

标准差描述的是样本集合的各个样本点到均值的距离之平均:

标准差也就是对方差开根号。举个例子,[0, 8, 12, 20][8, 9, 11, 12],两个集合的均值都是10,但显然两个集合的差别是很大的,计算两者的标准差,前者是8.3后者是1.8,显然后者较为集中,标准差描述的就是这种散布度或者叫做波动大小。综上,方差的意义在于描述随机变量稳定与波动、集中与分散的状况。标准差则体现随机变量取值与其期望值的偏差。

NumPy也提供了指定均值和标准差生成随机数的方法,我们来看一下:

# 第一个参数是均值,第二个参数是标准差
np.random.normal(10, 100)
# 结果
53.781947121910044# 创建均值为10,方差为100的3行5列矩阵
np.random.normal(10, 100, size=(3, 5))
# 结果
array([[ 124.10915759,   27.14517732, -144.95788359,  -87.40234817,-94.91106048],[ -36.4834381 ,  -39.05598871,  110.07456975,  224.85141913,153.24092557],[  -3.33533336,   10.57740526,  -56.76208107,  -84.06189149,103.08098119]])# 创建一个正态分布的3行5列矩阵,既均值为0,标准差为1
np.random.normal(0, 1, size=(3, 5))
# 结果
array([[-0.94574322,  2.0742057 ,  0.34477911,  0.1375712 ,  0.45385364],[-2.07928914,  1.26474497,  1.56236822, -1.0032234 , -0.14807477],[ 0.01992922,  0.3924738 , -0.11268871,  2.04509319,  0.01095378]])


 查看数组维度

# 生成10个元素的一维数组和3行5列的矩阵
import numpy as np
x = np.arange(10)
X = np.arange(15).reshape(3, 5)
x
X
# 结果
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
array([[ 0,  1,  2,  3,  4],[ 5,  6,  7,  8,  9],[10, 11, 12, 13, 14]])# 查看x的维度
x.ndim
# 结果
1# 查看X的维度
X.ndim
# 结果
2# 查看数组每个维度的具体信息
x.shape
# 结果
(10,)X.shape
# 结果
(3, 5)



 numpy.array的数据访问

# 一维数组访问第1个元素
x[0]
# 结果
0# 一维数组访问最后一个元素
x[-1]
# 结果
9# 二维数组访问第3行,第4列的元素
X[2, 3]
# 结果
13



 切片

Python中,有一个获取数组片段非常方便的方法,叫做切片,numpy.array中同样支持切片,我们来看一下:

# 获取x数组中从第1个元素到第5个元素的片段
x[0:5]
# 结果
array([0, 1, 2, 3, 4])# 如果冒号前不指定位置,那么默认从第一个元素开始
x[:3]
# 结果
array([0, 1, 2])# 如果冒号后面不指定位置,那么默认取到最后一个元素
x[3:]
# 结果
array([3, 4, 5, 6, 7, 8, 9])# 切片也支持步长
x[0:10:2]
# 结果
array([0, 2, 4, 6, 8])x[::2]
# 结果
array([0, 2, 4, 6, 8])# 取X矩阵的前2行,前3列
X[:2, :3]
# 结果
array([[0, 1, 2],[5, 6, 7]])# 对于每个维度都可以指定步长
X[:2, ::2]
# 结果
array([[0, 2, 4],[5, 7, 9]])

一般将高维矩阵降为低维矩阵其实也是使用切片来处理:

# 取X矩阵所有行的第一列
X[:, 0]
# 结果
array([ 0,  5, 10])

另外需要注意的是通过切片获取NumPy的数组或者矩阵的子数组,子矩阵是通过引用方式的,而Python中的数组通过切片获取的子数组是拷贝方式的。NumPy主要是考虑到性能效率问题。我们来看一下:

# 取X矩阵的前2行,前3列作为子矩阵
subX = X[:2, :3]
subX
# 结果
array([[0, 1, 2],[5, 6, 7]])# 给subX矩阵的第1行,第1列的元素赋值
subX[0, 0] = 100
subX
# 结果
array([[100,   1,   2],[  5,   6,   7]])# 再看看X矩阵
X
# 结果
array([[100,   1,   2,   3,   4],[  5,   6,   7,   8,   9],[ 10,  11,  12,  13,  14]])# 复制子矩阵
subX1 = X[:2, :3].copy()
subX1
# 结果
array([[100,   1,   2],[  5,   6,   7]])subX1[0, 0] = 0
subX1
# 结果
array([[0, 1, 2],[5, 6, 7]])X
# 结果
array([[100,   1,   2,   3,   4],[  5,   6,   7,   8,   9],[ 10,  11,  12,  13,  14]])


 改变数组维度

NumPy也提供了修改数组维度的方法,我们来看看:

# x是一个一维数组
x
# 结果
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])# 将x改为二维数组,既2行5列的矩阵
x.reshape(2, 5)
x
# 结果
array([[0, 1, 2, 3, 4],[5, 6, 7, 8, 9]])# 如果想让NumPy自动计算某个维度的数,比如我只想将x转换为只有2列的矩阵,有多少行交给NumPy处理
x.reshape(-1, 2)
# 结果
array([[0, 1],[2, 3],[4, 5],[6, 7],[8, 9]])



 数组合并操作

NumPy也提供两个数组合并的操作:

x = np.arange(5)
x
# 结果
array([0, 1, 2, 3, 4])y = np.arange(5)
y
# 结果
array([0, 1, 2, 3, 4])# 将x,y这两个一维数组合并
np.concatenate([x, y])
# 结果
array([0, 1, 2, 3, 4, 0, 1, 2, 3, 4])z = np.array([6, 6, 6, 6, 6])
z
# 结果
array([6, 6, 6, 6, 6])# 将x,y,z三个一维数组合并
np.concatenate([x, y, z])
# 结果
array([0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 6, 6, 6, 6, 6])

多维数组也支持合并:

# 2行3列的矩阵
V = np.random.randint(0, 10, size=(2, 3))
V
# 结果
array([[9, 2, 0],[8, 0, 2]])# 将两个V矩阵合并
np.concatenate([V, V])
# 结果
array([[9, 2, 0],[8, 0, 2],[9, 2, 0],[8, 0, 2]])# 合并多维数组时,可以设置按照哪个维度合并,axis参数为0时按照行合并,axis参数为1时按照列合并,默认axis为0
np.concatenate([V, V], axis=1)
# 结果
array([[9, 2, 0, 9, 2, 0],[8, 0, 2, 8, 0, 2]])

上面的示例都是同维度的数组进行合并,那么不同维度的数组如何合并呢,我们来看一下:

# z为一个一维数组
z = np.array([1, 2, 3])
z
# 结果
array([1, 2, 3])# V为一个2行3列的矩阵
V
# 结果
array([[9, 2, 0],[8, 0, 2]])# 直接将V和z合并会抛异常
np.concatenate([V, z])
# 结果
ValueError: all the input arrays must have same number of dimensions# 在合并时将一维数组z转变为二维数组
np.concatenate([V, z.reshape(1, -1)])
# 结果
array([[8, 2, 9],[7, 3, 8],[1, 2, 3]])

其实NumPy提供了更智能的不同维度数组合并的方法,我们来看一下:

# 按垂直方向合并
np.vstack([V, z])
# 结果
array([[8, 2, 9],[7, 3, 8],[1, 2, 3]])# 创建一个2行2列的矩阵
V1 = np.full((2, 2), 10)
V1
# 结果
array([[10, 10],[10, 10]])# 按水平方向合并
np.hstack([V, V1])
# 结果
array([[ 8,  2,  9, 10, 10],[ 7,  3,  8, 10, 10]])



 数组分割操作

有合并自然就会有分割,我们来看看NumPy提供的分割方法:

x = np.arange(10)
x
# 结果
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])# 对x进行分割,然后传入分割点,如下有两个分割点,所以将会把x分割为3个数组
x1, x2, x3 = np.split(x, [3, 7])
x1
x2
x3
# 结果
array([0, 1, 2])
array([3, 4, 5])
array([6, 7, 8, 9])# 对于多维数组也是一样
X = np.arange(16).reshape((4, 4))
X
# 结果
array([[ 0,  1,  2,  3],[ 4,  5,  6,  7],[ 8,  9, 10, 11],[12, 13, 14, 15]])# 传入一个分割点,既将X矩阵分割为两个矩阵
X1, X2 = np.split(X, [2])
X1
X2
# 结果
array([[0, 1, 2, 3],[4, 5, 6, 7]])
array([[ 8,  9, 10, 11],[12, 13, 14, 15]])# 分割多维数组同样可以设定按照哪个维度分割,axis默认为0,既按行分割
# axis为1时按列分割
X3, X4 = np.split(X, [2], axis=1)
X3
X4
# 结果
array([[ 0,  1],[ 4,  5],[ 8,  9],[12, 13]])
array([[ 2,  3],[ 6,  7],[10, 11],[14, 15]])

和合并一样,分割也有更快接的方法:

# 按垂直方向分割,既按行分割
X5, X6 = np.vsplit(X, [2])
X5
X6
# 结果
array([[0, 1, 2, 3],[4, 5, 6, 7]])
array([[ 8,  9, 10, 11],[12, 13, 14, 15]])# 按水平方向分割,既按列分割
X7, X8 = np.hsplit(X, [2])
X7
X8
# 结果
array([[ 0,  1],[ 4,  5],[ 8,  9],[12, 13]])
array([[ 2,  3],[ 6,  7],[10, 11],[14, 15]])

 矩阵运算

NumPy中提供了完整的矩阵的运算,我们从加减法来看一下:

# A为一个2行5列的矩阵
A = np.arange(10).reshape(2, 5)
A
# 结果
array([[0, 1, 2, 3, 4],[5, 6, 7, 8, 9]])# B也是一个2行5列的矩阵
B = np.random.randint(0, 10, size=(2, 5))
B
# 结果
array([[4, 8, 3, 5, 7],[9, 6, 6, 6, 6]])# 矩阵加常数
A + 1
#结果
array([[ 1,  2,  3,  4,  5],[ 6,  7,  8,  9, 10]])# 矩阵减常数
A - 1
# 结果
array([[-1,  0,  1,  2,  3],[ 4,  5,  6,  7,  8]])# 矩阵加矩阵
A + B
# 结果
array([[ 4,  9,  5,  8, 11],[14, 12, 13, 14, 15]])# 矩阵减矩阵
A - B
# 结果
array([[-4, -7, -1, -2, -3],[-4,  0,  1,  2,  3]])

下面我们再来看看数乘:

# 矩阵乘常数
2*A
# 结果
array([[ 0,  2,  4,  6,  8],[10, 12, 14, 16, 18]])# C为5行3列的矩阵
C = np.arange(15).reshape(5, 3)
C
# 结果
array([[ 0,  1,  2],[ 3,  4,  5],[ 6,  7,  8],[ 9, 10, 11],[12, 13, 14]])# 矩阵乘矩阵,真正的矩阵相乘
A.dot(C)
# 结果
array([[ 90, 100, 110],[240, 275, 310]])# 矩阵中每个对应元素相乘
A*B
# 结果
array([[ 0,  8,  6, 15, 28],[45, 36, 42, 48, 54]])

我们再来看看矩阵的转置:

A.T
# 结果
array([[0, 5],[1, 6],[2, 7],[3, 8],[4, 9]])


 聚合操作

NumPy中有很多对数组的聚合操作方法,我们先来看看一维数组:

# 随机取10个元素的 一维数组
D = np.random.random(10)
D
# 结果
array([ 0.70908471,  0.29268356,  0.69885019,  0.28796429,  0.04189265,0.36932107,  0.0641322 ,  0.63989077,  0.02753356,  0.0605743 ])# 求每个元素的和
np.sum(D)
# 结果
3.1919272951030706# 求元素的最小值
np.min(D)
# 结果
0.027533561561906672# 求元素最大值
np.max(D)
# 结果
0.70908470606410545# 求元素的均值
np.mean(D)
# 结果
0.31919272951030708# 求元素的标准差
np.std(D)
# 结果
0.26402525382852743

我们再来看看矩阵的聚合操作:

# X为2行3列的矩阵
X = np.arange(6).reshape(2, 3)
X
# 结果
array([[0, 1, 2],[3, 4, 5]])# 矩阵中所有元素的和
np.sum(X)
# 结果
15# 矩阵中所有元素的乘积
np.prod(X)
# 结果
0np.prod(X + 1)
# 结果
720# 矩阵元素的均值
np.mean(X)
# 结果
2.5# 矩阵元素的中位数,median可有效避免元素中出现极值,从而导致均值不准的问题
np.median(X)
# 结果
2.5# 矩阵元素的方差
np.var(X)
# 结果
2.9166666666666665# np.std(X)
# 结果
1.707825127659933


 索引和排序的相关操作

NumPy提供了一系列对数组索引操作的方法,我们来看一下:

# 随机一维数组
x = np.random.random(10)
x
# 结果
array([ 0.17035458,  0.8968506 ,  0.01007584,  0.45925501,  0.6838149 ,0.32393039,  0.53746647,  0.68561243,  0.66195346,  0.32696068])# x中元素最小值
np.min(x)
# 结果
0.010075835471876626# x中最小值元素所在的索引位置
np.argmin(x)
# 结果
2

我们再来看看排序:

# 对x进行排序
np.sort(x)
# 结果
array([ 0.01007584,  0.17035458,  0.32393039,  0.32696068,  0.45925501,0.53746647,  0.66195346,  0.6838149 ,  0.68561243,  0.8968506 ])# 获取排序后的索引,返回的数组中的元素是索引
np.argsort(x)
# 结果
array([2, 0, 5, 9, 3, 6, 8, 4, 7, 1])# 对矩阵排序
X1 = np.random.randint(0, 15, size=(3, 5))
X1
# 结果
array([[ 8,  1,  8, 12,  4],[ 8,  6,  6,  6, 13],[13,  2,  5, 11,  4]])np.sort(X1)
# 结果
array([[ 1,  4,  8,  8, 12],[ 6,  6,  6,  8, 13],[ 2,  4,  5, 11, 13]])# 获取排序后的索引
np.argsort(X1)
# 结果
array([[1, 4, 0, 2, 3],[1, 2, 3, 0, 4],[1, 4, 2, 3, 0]])


 NumPy的Fancy Indexing

一般情况下我们访问NumPy数组的数据,可以使用索引,甚至可以用步长来取:

x = np.arange(16)
x
# 结果
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15])# 取从0到6范围内,步长为2的元素
x[0:6:2]
# 结果
array([0, 2, 4])

但是有时候我们需要取数组中没有什么规律的元素,比如元素之间步长不等的,这就需要用到NumPy提供的Fancy Indexing机制来获取了:

# 将我们需要访问的索引生产一个数组,然后将索引数组传入x数组
ind = [3, 5, 8]
x[ind]
# 结果
array([3, 5, 8])# 矩阵也是同样的,先生成索引矩阵
ind1 = np.array([[0, 1],[2, 3]])
x[ind1]
# 结果
array([[0, 1],[2, 3]])# 将x转换为4行4列的矩阵X
X = x.reshape(4, -1)
X
# 结果
array([[ 0,  1,  2,  3],[ 4,  5,  6,  7],[ 8,  9, 10, 11],[12, 13, 14, 15]])# 生成希望查询的行和列的索引矩阵,然后传入矩阵X
row = np.array([0, 1, 2])
col = np.array([1, 2, 3])
X[row, col]
# 结果
array([ 1,  6, 11])# 或者指定前两行
X[:2, col]
# 结果
array([[1, 2, 3],[5, 6, 7]])

除了使用指定索引以外,我们还可以使用布尔数组或者矩阵来使用Fancy Indexing,我们来看一下:

X
# 结果
array([[ 0,  1,  2,  3],[ 4,  5,  6,  7],[ 8,  9, 10, 11],[12, 13, 14, 15]])# 生成一个布尔数组,True表示感兴趣的索引,False表示不感兴趣的索引
col = [True, False, True, True]
# 然后传入矩阵X,比如我们要获取前三行,第1列,第3列,第4列的元素
X[1:3, col]
# 结果
array([[ 4,  6,  7],[ 8, 10, 11]])x
# 结果
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15])# x数组中的元素小于2的有几个
np.sum(x < 2)
# 结果
2# x数组中的元素大于3小于10的有几个
np.sum((x > 3) & (x < 10))
# 结果
6# 判断x数组中的所有元素是否满足一个条件,若有一个满足返回True,若都不满足返回False
np.any(x == 0)
# 结果
Truenp.any(x < 0)
# 结果
False# 判断x数组中的所有元素是否满足一个条件,若有所有元素都满足返回True,若有一个元素不满足返回False
np.all(X > 0)
# 结果
Falsenp.all(X >= 0)
# 结果
True# 获取x数组中元素小于5的是哪几个元素
x[x < 5]
# 结果
array([0, 1, 2, 3, 4])# 获取x数组中元素是偶数的是哪几个元素
x[x % 2 == 0]
# 结果
array([ 0,  2,  4,  6,  8, 10, 12, 14])X
# 结果
array([[ 0,  1,  2,  3],[ 4,  5,  6,  7],[ 8,  9, 10, 11],[12, 13, 14, 15]])X[:, 3]
# 结果
array([ 3,  7, 11, 15])X[:, 3] % 3 == 0
# 结果
array([ True, False, False,  True], dtype=bool)X[X[:, 3] % 3 == 0, :]
# 结果
array([[ 0,  1,  2,  3],[12, 13, 14, 15]])

Matplotlib

在Python中,除了有NumPy这种对数组操作的类库,还有一个类一个在机器学习中使用比较广泛的类库是Matplotlib,这是一个绘制二维图像的类库,我们来看一下:

# 首先导入matplotlib的类库
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt# 创建数组x,元素从0到10,一共100个元素
x = np.linspace(0, 10, 100)
# 对x数组求sin,获得siny
siny = np.sin(x)# 使用matplotlib将x数组和y数组中的元素绘制出来
plt.plot(x, y)
plt.show()

# 可以同时绘制两条线
cosy = np.cos(x)
plt.plot(x, siny)
plt.plot(x, cosy)
plt.show()

# 可以指定某条线的颜色
plt.plot(x, siny)
plt.plot(x, cosy, color="red")
plt.show()

# 可以指定线的样式
plt.plot(x, siny)
plt.plot(x, cosy, color="red", linestyle="--")
plt.show()

# 可以指定x轴,y轴的区间
plt.plot(x, siny)
plt.plot(x, cosy, color="red", linestyle="--")
plt.xlim(-5, 15)
plt.ylim(0, 1.5)
plt.show()

# 另一种指定x轴,y轴区间的方法
plt.plot(x, siny)
plt.plot(x, cosy, color="red", linestyle="--")
plt.axis([-1, 11, -2, 2])
plt.show()

# 给x轴和y轴加说明
plt.plot(x, siny)
plt.plot(x, cosy, color="red", linestyle="--")
plt.axis([-1, 11, -2, 2])
plt.xlabel("x axis")
plt.ylabel("y value")
plt.show()

# 加图例
plt.plot(x, siny, label="sin(x)")
plt.plot(x, cosy, color="red", linestyle="--", label="cos(x)")
plt.axis([-1, 11, -2, 2])
plt.xlabel("x axis")
plt.ylabel("y value")
plt.legend()
plt.show()

# 加标题
plt.plot(x, siny, label="sin(x)")
plt.plot(x, cosy, color="red", linestyle="--", label="cos(x)")
plt.axis([-1, 11, -2, 2])
plt.xlabel("x axis")
plt.ylabel("y value")
plt.legend()
plt.title("Welcome to ML!")
plt.show()

以上都是利用matplotlib画折线图,下面来看看如何画散点图:

plt.scatter(x, siny)
plt.show()

plt.scatter(x, siny)
plt.scatter(x, cosy)
plt.show()

x = np.random.normal(0, 1, 100)
y = np.random.normal(0, 1, 100)
plt.scatter(x, y)
plt.show()

# 设置点的透明度
plt.scatter(x, y, alpha=0.5)
plt.show()

基于Scikit Learn的数据探索

Scikit-learn是Python语言中专门针对机器学习应用而发展起来的一款开源框架,其中有一个模块叫Datasets,它提供了机器学习的一些常用的数据集以及产生数据集的方法,比如波士顿房价数据集、乳腺癌数据集、糖尿病数据集、手写字体数据集、鸢尾花数据集等等。这一小节我们就通过Scikit Learn的Datasets来初步对机器学习的数据进行探索。

我们使用NumPy和Matplotlib对Scikit Learn Datasets中的鸢尾花这个数据集进行探索:

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
# 我们只导入Scikit Learn中的datasets模块
from sklearn import datasets# 加载鸢尾花数据集,获取到的iris的数据结构是一个字典
iris = datasets.load_iris()
# 看看字典的key都有什么
iris.keys()
# 结果
dict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names'])

从上面的示例可以看到鸢尾花这个字典一共包含五种信息,我们逐一来看看这五种信息:

# 先看一下DESCR信息,该信息解释了鸢尾花这个数据集
print(iris.DESCR)
# 结果
Iris Plants Database
====================Notes
-----
Data Set Characteristics::Number of Instances: 150 (50 in each of three classes):Number of Attributes: 4 numeric, predictive attributes and the class:Attribute Information:- sepal length in cm- sepal width in cm- petal length in cm- petal width in cm- class:- Iris-Setosa- Iris-Versicolour- Iris-Virginica:Summary Statistics:============== ==== ==== ======= ===== ====================Min  Max   Mean    SD   Class Correlation============== ==== ==== ======= ===== ====================sepal length:   4.3  7.9   5.84   0.83    0.7826sepal width:    2.0  4.4   3.05   0.43   -0.4194petal length:   1.0  6.9   3.76   1.76    0.9490  (high!)petal width:    0.1  2.5   1.20  0.76     0.9565  (high!)============== ==== ==== ======= ===== ====================
.....
.....

DESCR详细的描述了鸢尾花这个数据集一共有150组数据,每组数据有4个特征,分别是萼片的长度和厚度、花瓣的长度和厚度,还有3种鸢尾花的类别以及这些数据的统计信息和详细的解释说明。

# 再来看看data
iris.data
# 结果
array([[ 5.1,  3.5,  1.4,  0.2],[ 4.9,  3. ,  1.4,  0.2],[ 4.7,  3.2,  1.3,  0.2],[ 4.6,  3.1,  1.5,  0.2],[ 5. ,  3.6,  1.4,  0.2],[ 5.4,  3.9,  1.7,  0.4],[ 4.6,  3.4,  1.4,  0.3],[ 5. ,  3.4,  1.5,  0.2],[ 4.4,  2.9,  1.4,  0.2],[ 4.9,  3.1,  1.5,  0.1],[ 5.4,  3.7,  1.5,  0.2],[ 4.8,  3.4,  1.6,  0.2],......[ 5.9,  3. ,  5.1,  1.8]])# 看看data这个数组的行列情况
iris.data.shape
# 结果
(150, 4)

可以看到data中的数据就是萼片长度、厚度,花瓣长度、厚度的值。是一个150行,4列的矩阵。

# feature_names的值就是4个特征的说明
iris.feature_names
# 结果
['sepal length (cm)','sepal width (cm)','petal length (cm)','petal width (cm)']# target描述了每一行鸢尾花的数据是哪个类别的iris.target# 结果array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])# target是一个一维数组
iris.target.shape
# 结果
(150,)# target_names就是类别名称
iris.target_names
# 结果
array(['setosa', 'versicolor', 'virginica'],dtype='<U10')

下面我们用Matplotlib,用图将鸢尾花的数据展示出来,这样就能更直观的来分析这些数据。

# 因为matplotlib只能绘制二维图像,所以我们先来看看鸢尾花萼片的数据,取data的所有行,前2列
X = iris.data[:, :2]
# 将鸢尾花萼片的长度和宽度用散点图绘制出来
plt.scatter(X[:, :1], X[:, 1:2])
plt.show()

# 我们再来看看这150组鸢尾花数据从萼片维度的类别分类情况
y = iris.target
plt.scatter(X[y == 0, 0], X[y == 0, 1], color="red")
plt.scatter(X[y == 1, 0], X[y == 1, 1], color="blue")
plt.scatter(X[y == 2, 0], X[y == 2, 1], color="green")
plt.show()

# 取data矩阵的所有行,后2列的数据,既鸢尾花的花瓣长度和宽度信息
X1 = iris.data[:, 2:]
plt.scatter(X1[:, :1], X1[:, 1:2])
plt.show()

# 我们再来看看这150组鸢尾花数据从花瓣维度的类别分类情况
plt.scatter(X1[y == 0, 0], X1[y == 0, 1], color="red")
plt.scatter(X1[y == 1, 0], X1[y == 1, 1], color="blue")
plt.scatter(X1[y == 2, 0], X1[y == 2, 1], color="green")
plt.show()

kNN算法

kNN算法又称k近邻算法,是k-Nearest Neighbors的简称,该算法是监督学习中解决分类问题的算法,也是需要数据知识最少的一个算法,但是效果往往不差,能较好的解释机器学习算法使用过程中的很多细节问题,并且能很好的刻画机器学习应用的流程。

 kNN算法解释

上图描述了肿瘤大小和时间的二维关系图,圆点的颜色表示肿瘤的性质是良性还是恶性。

此时又有一个病人的数据采集到,那么我们如何判断这个病人的肿瘤是良性还是恶性呢?

首先我们必须取一个k值,至于这个k值是该如何取后续会讲,这里比如我们取k=3,这个k值的作用就是基于新来的这个点,找到离它最近的k个点,这里也就是找到离绿色点最近的三个点:

然后根据这三个代表的特征进行投票,票数最多的特征就是这个绿色点的特征,这个示例中离绿色点最近的三个点都是蓝色点,既恶性肿瘤,那么可判定绿色点代表的肿瘤性质有很高的概率也是恶性。

 欧拉距离

kNN算法中唯一用到的数学知识就是如何求点与点之间的距离,在这里我们先使用最普遍的欧拉距离来进行计算,欧拉距离的公式如下:

二维:

三维:

N维(N个特征):

=

用大白话解释就是两个点的所有相同维度之差求平方,然后全部相加再开方。有兴趣的话大家可以再深入研究一下点与点间距离的计算。

 编码实现kNN算法

首先我们来准备一下数据:

import numpy as np
import matplotlib.pyplot as plt
raw_data_X = [[3.393533211, 2.331273381],[3.110073483, 1.781539638],[1.343808831, 3.368360954],[3.582294042, 4.679179110],[2.280362439, 2.866990263],[7.423436942, 4.696522875],[5.745051997, 3.533989803],[9.172168622, 2.511101045],[7.792783481, 3.424088941],[7.939820817, 0.791637231]]
raw_data_y = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]

我们将这些样本数据绘制出来看看:

# 和绘制鸢尾花的方式一样
X_train = np.array(raw_data_X)
y_train = np.array(raw_data_y)
plt.scatter(X_train[y_train == 0, 0], X_train[y_train == 0, 1], color="red")
plt.scatter(X_train[y_train == 1, 0], X_train[y_train == 1, 1], color="blue")
plt.show()

下面再创建一组数据,来模拟需要被分类的数据:

x = np.array([8.093607318, 3.365731514])
plt.scatter(X_train[y_train == 0, 0], X_train[y_train == 0, 1], color="red")
plt.scatter(X_train[y_train == 1, 0], X_train[y_train == 1, 1], color="blue")
plt.scatter(x[0], x[1], color="green")
plt.show()

我们现在就要通过kNN算法来分析这个绿点属于哪个类别,虽然从图上我们已经可以看得出。

我们先来通过欧拉距离公式求出所有点与绿点的距离:

# 导入开方的类库
from math import sqrt
distances = []
# 通过for循环求出每一个点与绿点的距离
for x_train in X_train:d = sqrt(np.sum((x_train - x) ** 2))distances.append(d)distances
# 结果
[4.812566907609877,5.229270827235305,6.749798999160064,4.6986266144110695,5.83460014556857,1.4900114024329525,2.354574897431513,1.3761132675144652,0.3064319992975,2.5786840957478887]# 其实还可以更简洁的使用行内表达式distances = [sqrt(np.sum((x_train - x) ** 2)) for x_train in X_train]distances# 结果是一样的[4.812566907609877,5.229270827235305,6.749798999160064,4.6986266144110695,5.83460014556857,1.4900114024329525,2.354574897431513,1.3761132675144652,0.3064319992975,2.5786840957478887]

现在我们就求出了所有点与绿点的距离,但是求出距离并不能表示出每个距离对应点的类别,所以还需要知道这每个距离对应的是哪个点:

# 此时上文中说过的argsort方法就派上用场了
# argsort方法可以将数组排序,但是返回数组中元素的索引
nearest = np.argsort(distances)
nearest
# 结果
array([8, 7, 5, 6, 9, 3, 0, 1, 4, 2])
In [ ]:

从上面的结果可以看到,距离绿点最近的点是X_train中的第8行样本。那么接下来我们看看如何通过k值获取这些点的类别:

# 首先定义k的值为6
k = 6
# 离绿点距离最近的前6个点的类别
topK_y = [y_train[i] for i in nearest[:k]]
topK_y
# 结果
[1, 1, 1, 1, 1, 0]# 导入collections的Counter类库用于计数计算
from collections import Counter
votes = Counter(topK_y)
votes
# 结果
Counter({0: 1, 1: 5})# 通过most_common方法获取最大的若干个结果,传入的参数为想要获取最大的几个结果,这里我们只需要最大的一个结果,既投票票数最多的那个结果
votes.most_common(1)
# 结果
[(1, 5)]# 更精准的获取投票票数最多的结果
predict_y = votes.most_common(1)[0][0]
predict_y
# 结果
1

到目前位置,我们就判断出了绿点有很大概率类别属于1,这个过程就是kNN算法的核心过程。

总结

在下一篇笔记中,将会介绍Scikit Learn中是如何封装kNN算法的,以及我们会自己封装一个kNN算法,以及对分类准确度评定,超参数,数据归一化等知识点的讲解。

END

往期精彩文章回顾

机器学习笔记(二):矩阵、环境搭建、NumPy

机器学习笔记(一):机器的学习定义、导数和最小二乘

Kafka从上手到实践 - 实践真知:搭建Kafka相关的UI工具

Kafka从上手到实践 - Kafka集群:启动Kafka集群

Kafka从上手到实践 - Kafka集群:Kafka Listeners

Kafka从上手到实践 - Kafka集群:配置Broker

Kafka从上手到实践:搭建Zookeeper集群

Kafka从上手到实践-Zookeeper CLI:CRUD zNode

Kafka从上手到实践 - 初步认知:Zookeeper

Kafka从上手到实践:Kafka Java Consumer

长按扫描二维码关注凌云时刻

每日收获前沿技术与科技洞见

机器学习笔记(三):NumPy、Matplotlib、kNN算法 | 凌云时刻相关推荐

  1. 算法图解第十章笔记与习题(KNN算法)

    算法图解第十章笔记与习题(KNN算法) 文章目录 算法图解第十章笔记与习题(KNN算法) 10.1 KNN算法 10.2特征提取 10.3 回归 10.4 小结 练习 习题10.1: 习题10.2: ...

  2. 神经网络与机器学习 笔记—LMS(最小均方算法)和学习率退火

    神经网络与机器学习 笔记-LMS(最小均方算法)和学习率退火 LMS算法和Rosenblatt感知器算法非常想,唯独就是去掉了神经元的压制函数,Rosenblatt用的Sgn压制函数,LMS不需要压制 ...

  3. 机器学习笔记三—卷积神经网络与循环神经网络

    系列文章目录 机器学习笔记一-机器学习基本知识 机器学习笔记二-梯度下降和反向传播 机器学习笔记三-卷积神经网络与循环神经网络 机器学习笔记四-机器学习可解释性 机器学习笔记五-机器学习攻击与防御 机 ...

  4. 机器学习入门 —— 超级详细的KNN算法学习笔记、KNN算法的三要素、KNN算法的优缺点

    文章目录 KNN(K nearest neighbors) K值的选择和影响 k取值偏小 k取值偏大 样本点距离的计算方式 闵可夫斯基距离 曼哈顿距离 欧几里得距离 切比雪夫距离 余弦距离 决策函数的 ...

  5. 机器学习小组系列笔记---(一)knn算法

    一.本周学习内容 知识点描述:用代码打开AI的大门 本周是机器学习小组第一期的第一周,我们这周从最简单的机器学习算法"K-近邻算法"开始,通过代码走进机器学习的大门,搞定传统机器学 ...

  6. 机器学习笔记(一) KNN K-最近邻

    零.摘要 本篇文章主要讲述KNN算法(K-nearest neighbor)的原理与技术细节,并简单提及了数据预处理的方法. 主要参考资料: 斯坦福CS231n课程笔记:分类 <机器学习> ...

  7. 机器学习与深度学习——通过knn算法分类鸢尾花数据集iris求出错误率并进行可视化

    什么是knn算法? KNN算法是一种基于实例的机器学习算法,其全称为K-最近邻算法(K-Nearest Neighbors Algorithm).它是一种简单但非常有效的分类和回归算法. 该算法的基本 ...

  8. 阿旭机器学习实战【2】KNN算法进行人体动作识别

    本系列文章为机器学习实战内容,旨在通过实战的方式学习各种机器学习算法的知识,更易于掌握和学习,更过干货内容持续更新- 目录 人类动作识别问题描述 导入相关数据并查看数据信息 构建KNN算法模型并查看准 ...

  9. 机器学习笔记(十)---- KNN(K Nearst Neighbor)

    KNN是一种常见的监督学习算法,工作机制很好理解:给定测试样本,基于某种距离度量找出训练集中与其最靠近的k个训练样本,然后基于这k个"邻居"的信息来进行预测.总结一句话就是&quo ...

  10. 机器学习之手写字识别(Knn算法应用)

    手写字识别 对手写字体图片进行识别最重要的一点就是要将其转化为二值化(就是就是将图像上的像素点或灰度值设置为0或1,其呈现就是非黑即白)后的数据,然后再进行处理,在手写体处理中,二值化就是有手写笔画的 ...

最新文章

  1. 中国电子学会图形化四级编程题:程序优化
  2. Xamarin提示Build-tools版本过老
  3. AOP原理-创建AOP代理--AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】的作用
  4. 【Python】调用百度云API驾驶行为分析 Driver Behavior
  5. 数学之美 系列十三 信息指纹及其应用
  6. VTK:Qt之ShareCameraQt
  7. 树的宽度 递归法和非递归法
  8. Vue全家桶实战03_【从入门到放弃系列】
  9. python面向对象属性_Python面向对象属性
  10. 关闭layui的弹出层,还会遮罩后面的界面
  11. mysql 数值型注入_slq-注入总结(中)
  12. 如何在 Mac 上使用“备忘录”管理扫描文稿?
  13. 标准时间标准Time Zone: GMT,UTC,DST,CST
  14. ssm启动不报错_搭建ssm+maven环境,启动报错,说spring监听无法实例化,求解?
  15. [android 游戏源码]-体育游戏-疯狂足球源码
  16. #IB TWS编程手记——03-关于IB合约的一二三
  17. python在直方图上画折线图_Python 中 plt 画柱状图和折线图
  18. 每日学点python之六(列表与元组)
  19. webStrom找回删除的文件
  20. (转)日期插件layDate的使用

热门文章

  1. 查询SQLServer 服务器,执行过的SQL语句耗时!
  2. maven错误相关(整理中)
  3. linux 各文件夹的作用
  4. Mybatis-学习笔记(N)mybatis-generator 生成DAO、Mapper、entity
  5. spring-第十八篇之spring AOP基于XML配置文件的管理方式
  6. 如何用maven tycho构建自己的Eclipse RCP应用
  7. destoon 支付异步接口文件 notify.php 调试方式
  8. poj3159 Candies 2012-09-07
  9. Noip2013错误避免
  10. Java自带的keytool命令