c语言sscanf函数的用法是什么
233
2022-09-17
数据分析 -- NumPy②
目录
多维数组及其创建多维数组的属性二维数组的加减乘除
广播规则
可以正常进行的广播不能正常进行的广播
二维数组的通用方法二维数组的索引和分片布尔索引NumPy中的使用方法
arange() 方法随机方法genfromtxt() 方法
用NumPy计算均方误差易错点反思①
多维数组及其创建
一维数组只有行,二维数组相比一维数组多了列这个维度,而三维数组则类似多个二维数组堆叠在一起,形如一个立方体。理论上可以往更多的维度延伸,但最常用的还是二维数组。
还是和列表进行类别,二维数组相当于单层的嵌套列表。并且我们可以将单层嵌套列表传入 np.array() 方法创建一个二维数组。
# 单层嵌套列表nested_list = [[1, 2], [3, 4]]print(nested_list)# 输出:[[1, 2], [3, 4]]# 二维数组data = np.array(nested_list)print(data)# 输出:# [[1 2]# [3 4]]
嵌套列表就是列表中的元素也是列表的列表。可以看到,通过嵌套列表创建的二维数组也是用空格分隔的,并且分成了两行。列表中的第一个元素 [1, 2] 在第一行,第二个元素 [3, 4] 在第二行(也就是说一个列表元素占一行)。
同样,上一关学过的 ones() 和 zeros() 方法同样也能快速创建元素全为 1 和 0 的二维数组。与之前的区别在于,创建二维数组要传入一个包含行和列信息的元组。比如:np.ones((m, n)) 表示创建一个 m 行 n 列且元素全为 1 的二维数组。
ones = np.ones((3, 2))print(ones)# 输出:# [[1. 1.]# [1. 1.]# [1. 1.]]zeros = np.zeros((3, 2))print(zeros)# 输出:# [[0. 0.]# [0. 0.]# [0. 0.]]
更多维的数组的创建,只要传入嵌套层数更多的列表即可。创建三维数组的方式如下:
同理,ones() 方法和 zeros() 方法也是如此。创建三维数组只需传入一个长度为 3 的元组,分别指定了每个维度上的元素个数。
多维数组的属性
ndim:多维数组维度的个数。例如:二维数组的 ndim 为 2;shape:多维数组的形状。它是一个元组,每个元素分别表示每个维度中数组的长度。对于 m 行和 n 列的的数组,它的 shape 将是 (m, n)。因此,shape 元组的长度(元素个数)就是 ndim 的值;size:多维数组中所有元素的个数。shape 元组中每个元素的乘积就是 size 的值;dtype:多维数组中元素的类型。
例如:
data = np.array([[1, 2, 3], [4, 5, 6]])print('ndim:', data.ndim)print('shape:', data.shape)print('size:', data.size)print('dtype:', data.dtype)# 输出:# ndim: 2# shape: (2, 3)# size: 6# dtype: int64
小贴士:int64 是 numpy 提供的类型,表示 64 位的整数。
注意: 对于 [1 2 3] 这样的一维数组,它的 shape 是 (3,),表示有三个元素。在元组中只有一个元素时,元素后面的逗号是不能省略的,否则会被认为是加了括号的 3,而不是元组。
二维数组的加减乘除
二维数组的加减乘除与一维数组差不多
二维数组和数学中的 矩阵 很相似,常被用于进行矩阵间的运算。但二维数组间直接用 * 进行计算的方式和矩阵乘法计算的方式并不相同,应该用 @ 符号进行矩阵间的乘法计算。
广播规则
维度相同的数组间可以计算(条件是形状要相同)。那么维度不相同的呢?其实也是可以的。 这得益于 numpy 中的 广播规则,我们知道它是指较小维度的数组在较大维度的数组上进行”广播“,以便它们具有兼容的形状。 当运算中的 2 个数组的形状不同时,numpy 将自动触发广播机制,它具体的规则是:在较小维度数组的 shape 元组前补 1,直到两个数组的 shape 元组长度相同。接着将元素在值为 1 的维度上进行复制,直到两个数组的形状相同。 如果无法使两个数组的形状相同,则会抛出 ValueError: operands could not be broadcast together 的异常。
可以正常进行的广播
例如:
data = np.array([[1, 2], [3, 4], [5, 6]])ones = np.ones(2)print(data.shape)print(ones.shape)# 输出:# (3, 2)# (2,)
data 的形状是 (3, 2),ones 的形状是 (2,)。根据规则在较小维度的 shape 元组前补 1,直到和较大维度的 shape 元组长度相同,补 1 后 ones 的形状是 (1, 2)。接着将元素在值为 1 的维度上进行复制,最后 ones 的形状变成了 (3, 2),和 data 的形状相同,因此可以进行计算。
浅色方块为复制出来的元素
不能正常进行的广播
例如:
data = np.array([[1, 2], [3, 4], [5, 6]])ones = np.ones(3)print(data.shape)print(ones.shape)# 输出:# (3, 2)# (3,)
当 ones 的形状是 (3,) 时,补 1 后变成 (1, 3),最后变成 (3, 3)。形状与 data 不相同,无法进行计算。如果尝试执行 data + ones,会产出如下的报错:
ValueError: operands could not be broadcast together with shapes (3,2) (3,)
二维数组的通用方法
二维数组的通用方法和一维数组的通用方法的基本用法类似,只是多了一个维度的数据而已。
因为二维数组多了个维度,所以它的通用方法可以更加的灵活。不仅可以对所有数据进行计算,还可以针对某个维度上的数据进行计算。
这里就要引入一个概念——轴(axis)。轴和维度的概念是类似的,一维数组有 1 个轴,二维数组有 2 个轴,三维数组有 3 个轴等等。
在 numpy 中,我们可以用 axis 参数来指定轴,从 0 开始依次增加递增的数分别对应着不同的轴。
在一维数组中,axis=0 就代表着它唯一的轴;二维数组中 axis=0 和 axis=1 代表其中的行轴和列轴;在三维数组中,axis=0、axis=1 和 axis=2 分别代表对应的三条轴。下图清晰的展示了 axis 和对应轴的关系:
在一维数组中因为只有一个轴,一般用不到 axis。需要注意的是,在二维数组中 axis=0 的轴是向下的,和一维数组中有所不同,千万不要混淆。 其实按照列表中的一个元素相当于数组中的一行,我们可以把一维数组就想想成为一个竖着的,这样就统一了axis = 0,都代表行轴,向下指。
在通用方法中,通过 axis 参数可以指定计算方向。以二维数组中的 max() 方法为例,指定 axis=0 将会在行轴方向求最大值,指定 axis=1 将会在列轴方向求最大值。
data = np.array([[1, 2], [5, 3], [4, 6]])# 不指定 axisprint(data.max())# 输出:6# axis=0print(data.max(axis=0))# 输出:[5 6]# axis=1print(data.max(axis=1))# 输出:[2 5 6]
二维数组的索引和分片
二维数组的索引和分片同样和一维数组类似,只是在行索引的基础上再加上列索引。形如 data[m, n],其中 data 是二维数组,m 是行索引或分片,n 是列索引或分片。
那么,data[0, 1] 就表示获取 data 中第一行第二列的元素。如果省略第二个参数 n 的话表示获取所有列,data[0] 就表示获取整个第一行,相当于 data[0, :]。
如果想要获取第一列则可以写成 data[:,0];如果想获取 2、3 两行可以写成 data[1:3],相当于 data[1:3, :]。你可以将索引和分片结合起来,以获取二维数组中你想要的任意数据。(此处可以用指针去理解,m告诉我们指针指向哪两行,而n告诉我们具体的列数)
例如:
data = np.array([[1, 2], [3, 4], [5, 6]])print(data[0, 1])# 输出:2print(data[:, 0])# 输出:[1 3 5]print(data[1:3])# 输出:# [[3 4]# [5 6]]
可以看到,在二维数组中,当行和列都是索引时,结果是具体的元素;当行和列中一个是索引,一个是分片时,结果是一维列表;当行和列都是分片时,结果为二维数组。
例如,data[0:2, 0] 和 data[0:2, 0:1] 获取的都是 1 和 3 这两个元素,但其结果一个是 [1 3],一个是 [[1] [3]],实际上并不相同。
以上的都是numpy的基本索引,numpy 中的高级索引分为:
布尔索引花式索引
布尔索引
布尔索引,顾名思义就是用布尔值作为索引去获取需要的元素。 首先我们要明确一个事,numpy在进行数值运算时(也就是加减乘除)会将多维数组中的每个元素进行计算,在进行逻辑运算时也会这样(也就是大于,等于,小于,不等于·····) 例如:
data = np.array([[1, 2], [3, 4], [5, 6]])print(data > 3)#结果是:#[[False False]# [False True]# [ True True]]
大于 3 的元素位置值为 True,小于等于 3 的元素位置值为 False。而这个布尔类型的数组就是布尔索引,通过它可以筛选出值为 True 位置的元素(既然是索引那就要用xx[ ]的形式来进行元素的筛选)。因此,获取数组中所有大于 3 的元素的代码可以这样写:
ata = np.array([[1, 2], [3, 4], [5, 6]])print(data[data > 3])# 输出:[4 5 6]
注意:如果有多个布尔表达式,and 改用 &,or 改用 |,not 改用 ~,并且每个条件要用括号括起来, == 和 != 不变也可用。
例如:
data = np.array([[1, 2], [3, 4], [5, 6]])# 大于 3 或者小于 2print(data[(data > 3) | (data < 2)])# 输出:[1 4 5 6]# 大于 3 或者不小于 2(即大于等于 2)print(data[(data > 3) | ~(data < 2)])# 输出:[2 3 4 5 6]data = np.array([[1, 2], [3, 4], [5, 6]])# 等于 3print(data[data == 3])# 输出:[3]# 不等于 3print(data[data != 3])# 输出:[1 2 4 5 6]
NumPy中的使用方法
arange() 方法
numpy 中的 arange() 方法和 Python 中的 range() 用法类似,不同之处在于 arange() 方法生成的是数组,而 range() 方法生成的是 range 类型的序列。 例如:
# 生成 1-9 的数组print(np.arange(1, 10))# 输出:[1 2 3 4 5 6 7 8 9]# 生成 0-9 的数组print(np.arange(10))# 输出:[0 1 2 3 4 5 6 7 8 9]# 生成 1-9 的数组,步长为 2print(np.arange(1, 10, 2))# 输出:[1 3 5 7 9]
随机方法
np.random.rand(x) 生成一个随机数组(生成 [0, 1) 之间的随机小数)并且可以指定形状 x为生成的随机数组的形状(shape)
例如:
# 不传参数时print(np.random.rand())# 输出:0.1392571183916036# 传入一个参数时print(np.random.rand(3))# 输出:[0.7987698 0.52115291 0.70452156]# 传入多个参数时print(np.random.rand(2, 3))# 输出:# [[0.08539006 0.97878203 0.23976172]# [0.34301963 0.48388704 0.63304024]]
np.random.randint(m,n,shape) numpy 中的 np.random.randint() 方法和 Python 中的 random.randint() 类似,不同之处在于,random.randint(m, n) 生成的是 [m, n] 之间的整数,而 np.random.randint(m, n) 生成的是 [m, n) 之间的整数,这点一定注意要区分。
例如:
# 不传入形状时print(np.random.randint(0, 5))# 输出:3# 形状为一维数组时print(np.random.randint(0, 5, 3))# 输出:[4 0 1]# 形状为二维数组时print(np.random.randint(0, 5, (2, 3)))# 输出:# [[0 2 1]# [4 2 0]]
genfromtxt() 方法
genfromtxt() 方法用于文件的读取。我们学习 numpy 是要到实际生活中应用的,而生活中我们的数据来源通常是一个文件,例如 CSV 文件。
genfromtxt() 方法常用的参数有两个,分别是数据源和分隔符。假设我们要用 numpy 读取一个以逗号分隔的 CSV 文件,可以这样写:
data = np.genfromtxt('data.csv', delimiter=',')
第一个参数是数据源,可以是本地文件的路径,也可以是网络文件的地址。delimiter 参数用于指定分隔符,CSV 文件一般是用逗号作为分隔符,当遇到其他符号分隔的文件时,用 delimiter 参数进行指定即可。
genfromtxt() 方法的返回值是一个多维数组,这样你就可以根据前面所学的知识对其进行处理了。
用NumPy计算均方误差
numpy 在科学计算、神经网络、机器学习等领域深受欢迎。这需要我们拥有较好数学功底,配合 numpy 中数学计算的方法,可以快捷方便地完成这些领域的计算工作。
在机器学习中,均方误差常被作为模型的损失函数,用来预测和回归,它的公式如下:
n 是数据集的个数,Y_prediction 是模型预测的结果集,Y 是实际的数据集。将预测的结果和实际的值作差后进行平方求和,最后除以数据集的个数,得到的就是均方误差。均方误差越小,说明模型预测的越准确,反之则越不准确。
np.sum() 是对数组求和的方法 np.square() 是对数组计算平方的方法
predictions 是预测结果的多维数组,labels 是实际值的多维数组:
predictions = np.array([1, 1, 1])labels = np.array([1, 2, 3])error = (1 / labels.size) * np.sum(np.square(predictions - labels))print(error)# 输出:1.6666666666666665
易错点反思①
题目:现在要求将二维数组[[1 2] [3 4]]的第一列平方,得到结果二维数组[[1 2] [9 4]]
刚开始我做的时候,我想的是将第一列切片切出来,再直接使用square()函数就可以了(因为多维数组的切片是视图形式,直接对本体进行操作) 但是出错了:
b = np.array([[1,2],[3,4]])np.square(b[:,0])print(b)#[[1 2] [3 4]]
还是没有改变,其实问题就出现在square()方法上,你只是将切片的引用传递给了形参,而这个形参在方法体内无论怎么变,都不会影响到传递给方法的实参,也就对切片没有影响,所以结果没有发生改变。
要想达到目的,可以考虑使用for循环:
import numpy as npa = np.array([[1,2],[3,4]])for i in range(a.shape[0]): a[i,0] = a[i,0] * a[i,0]print(a)#结果[[1 2] [9 4]]
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~