NumPy (Numerical Python)
是一个用于数值运算的 Python 库,专门对数值运算进行优化,最大的优势是运行高效。
Numpy学习网站
-
数组解惑: 专项理解 数组维度 与 降维处理;
-
https://flat2010.github.io/2017/05/31/Numpy%E6%95%B0%E7%BB%84%E8%A7%A3%E6%83%91/
-
DeepAge 网站:
-
https://deepage.net/features/numpy-axis.html
ndarry 基本概念
- axes : 数组维度(dimensions)称为轴
- ndim : 数组的维数
- size:数组的元素个数
- shape:数组的形状
- stype:数组中元素的类型
- itemsize:数组中元素占用的字节数
创建数组的常用方法
-
np.array([x, y, z], dtype=int)
-
np.arrange(x, y, i)
-
numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0) # 返回等差数列
start:返回样本数据开始点 stop:返回样本数据结束点 num:生成的样本数据量,默认为50 endpoint:True(默认)则包含stop;False则不包含stop retstep:If True, return (samples, step), where step is the spacing between samples.(即如果为True则结果会给出数据间隔) dtype:输出数组类型 axis:0(默认)或-1 >>> np.linspace(2.0, 3.0, num=5) array([ 2. , 2.25, 2.5 , 2.75, 3. ]) >>> np.linspace(2.0, 3.0, num=5, endpoint=False) array([ 2. , 2.2, 2.4, 2.6, 2.8]) >>> np.linspace(2.0, 3.0, num=5, retstep=True) (array([ 2. , 2.25, 2.5 , 2.75, 3. ]), 0.25)
-
统一记忆创建:np.zeros(), np.ones(), np.full()
np.zeros函数跟np.ones函数用法类似相互类比即可: 用法:np.zeros(shape, dtype=float, order=‘C’) 返回:返回来一个给定形状和类型的用0填充的数组; 参数:shape:形状(可以为一个元组,或者数字两者有区别,元组返回的是一个矩阵数字返回的是一个一维数组) dtype:数据类型:可选参数,默认numpy.float64 order:可选参数:c代表与c语言类似,行优先;F代表列优先(这个用法不是很理解) np.zeros(5) array([ 0., 0., 0., 0., 0.]) np.zeros((5,), dtype=np.int) array([0, 0, 0, 0, 0]) np.zeros((2, 1)) array([[ 0.], [ 0.]]) s = (2,2) np.zeros(s) array([[ 0., 0.], [ 0., 0.]]) np.zeros((2,), dtype=[('x', 'i4'), ('y', 'i4')]) # custom dtype array([(0, 0), (0, 0)], dtype=[('x', '<i4'), ('y', '<i4')]) np.full(shape, fill_value, dtype=None, order=‘C’)返回一个指定形状、类型和数值的数组. np.full((2,2),np.inf) array([[ inf, inf], [ inf, inf]]) np.full((2, 2), 10) array([[10, 10], [10, 10]]
-
np.identity(n) # 返回n阶单位阵
-
numpy.eye(N,M=None,k=0,dtype=<class ‘float’>,order='C)
(1)N:int型,表示的是输出的行数 (2)M:int型,可选项,输出的列数,如果没有就默认为N (3)k:int型,可选项,对角线的下标,默认为0表示的是主对角线,负数表示的是低对角,正数表示的是高对角。 (4)dtype:数据的类型,可选项,返回的数据的数据类型 (5)order:{‘C’,‘F'},可选项,也就是输出的数组的形式是按照C语言的行优先’C',还是按照Fortran形式的列优先‘F'存储在内存中 # 案例:(普通的用法) import numpy as np a=np.eye(3) print(a) a=np.eye(4,k=1) print(a) a=np.eye(4,k=-1) print(a) a=np.eye(4,k=-3) print(a) # 结果展示 [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] [[0. 1. 0. 0.] [0. 0. 1. 0.] [0. 0. 0. 1.] [0. 0. 0. 0.]] [[0. 0. 0. 0.] [1. 0. 0. 0.] [0. 1. 0. 0.] [0. 0. 1. 0.]] [[0. 0. 0. 0.] [0. 0. 0. 0.] [0. 0. 0. 0.] [1. 0. 0. 0.]]
案例:(深度学习中的高级用法,将数组转成one-hot形式)
import numpy as np labels=np.array([[1],[2],[0],[1]]) print("labels的大小:",labels.shape,"\n") #因为我们的类别是从0-2,所以这里是3个类 a=np.eye(3)[1] print("如果对应的类别号是1,那么转成one-hot的形式",a,"\n") a=np.eye(3)[2] print("如果对应的类别号是2,那么转成one-hot的形式",a,"\n") a=np.eye(3)[1,0] print("1转成one-hot的数组的第一个数字是:",a,"\n") #这里和上面的结果的区别,注意!!! a=np.eye(3)[[1,2,0,1]] print("如果对应的类别号是1,2,0,1,那么转成one-hot的形式\n",a) res=np.eye(3)[labels.reshape(-1)] print("labels转成one-hot形式的结果:\n",res,"\n") print("labels转化成one-hot后的大小:",res.shape) labels的大小: (4, 1) 如果对应的类别号是1,那么转成one-hot的形式 [0. 1. 0.] 如果对应的类别号是2,那么转成one-hot的形式 [0. 0. 1.] 1转成one-hot的数组的第一个数字是: 0.0 如果对应的类别号是1,2,0,1,那么转成one-hot的形式 [[0. 1. 0.] [0. 0. 1.] [1. 0. 0.] [0. 1. 0.]] labels转成one-hot形式的结果: [[0. 1. 0.] [0. 0. 1.] [1. 0. 0.] [0. 1. 0.]] labels转化成one-hot后的大小: (4, 3
-
np.random.rand(m,n) # 返回一个或一组服从“0~1”[均匀分布]的随机样本值。随机样本取值范围是[0,1),不包括1。返回m行n列的随机数组
ndarray 的形态变换操作
-
reshape(shape) 函数改变数组的形状,返回一个指定形状的数组
即 reshape() 函数不会改变原数组
-
resize(new_shape) 函数把原数组直接变为指定的新形状,返回空值
-
transpose() 函数可以实现对数组转置, 返回新数组
也可用 array.T 操作实现
-
flatten()函数,展平数组,把高纬度数组变为一维,返回新数组
numpy (Numerical Python) 帮助处理数值型数组的
1 维数组
-
数组是一系列 相同类型 数据的集合,它其实和列表很相似,只是列表中的元素类型可以是任意的
# 要创建一个多维数组很简单,将一个列表作为参数传入 numpy 中的 array() 方法即可: import numpy as np data = np.array([1, 2, 3]) print(data) # 输出:[1 2 3] print(type(data)) # 输出:<class 'numpy.ndarray'> #除了使用 np.array() 方法来创建一个多维数组,numpy 还提供了两个实用的方法——np.ones() 和 np.zeros()。 ones = np.ones(3) print(ones) # 输出:[1. 1. 1.] zeros = np.zeros(3) print(zeros) # 输出:[0. 0. 0.] # np.ones() 和 np.zeros() 的参数用于指定生成的多维数组里有多少个元素。上述代码中都传入了 3,因此生成了包含 3 个 1 和 0 的多维数组 # 你应该发现了,生成出来的不是 1 和 0,而是 1. 和 0.。这是因为默认生成的是浮点数,numpy 会省略小数点后的 0,因此 1.0 和 0.0 变成了 1. 和 0.。 # 如果我们想要生成整数的话,可以传入 dtype 参数来指定类型。 ones = np.ones(3, dtype='int') print(ones) # 输出:[1 1 1] zeros = np.zeros(3, dtype='int') print(zeros) # 输出:[0 0 0]
数组的加减乘除操作
data = np.array([1, 2])
ones = np.ones(2)
print(data + ones)
# 输出:[2. 3.]
#data 是 [1 2],ones 是 [1. 1.],第一个元素和第一个元素相加,第二个元素和第二个元素相加,最终便得到 [2. 3.]。
data = np.array([1, 2])
data1 = np.array([2, 4])
print(data * data1) # [2 8]
data = np.array([1, 2])
print(data + 1)
# 输出:[2 3]
np.square() 是计算平方的方法
NumPy广播的规则
-
规则1. 在广播对象数组中,当维数(ndim)不同时,通过将1放在shape的开头来调整。
-
规则2. 可用于计算处理数组是各维的要素数与最大值相等或正好为1的数组
-
多维数组和数字的四则运算会作用在数组中的每个元素上,这在 numpy 中被称为 Broadcasting。Broadcasting 的本义为“大范围传播、投射”。用在媒体中,就是我们熟知的“广播”(广泛将信息传播给大众),用在数组运算中,就是“大范围地将运算应用到全部元素”的意思。下一关中,我们会更加深入地了解 Broadcasting 规则。
因为 Broadcasting 规则,
[1 2] + 1
相当于[1 2] + [1 1]
,因此结果为[2 3]
。# 上述效果用列表来实现也是可以的,一般我们会这样写: data = [] for i in [1, 2]: data.append(i + 1) print(data) # 输出:[2, 3] # 用列表就必须用到循环,而 numpy 中这种不用编写循环就可以对数据进行批量运算的方式叫做 矢量化。numpy 中的矢量化操作把内部循环委托给高度优化的 C 和 Fortran 函数,从而实现更清晰,更快速的 Python 代码。
索引,切片操作
与列表相同
-
这里需要注意的是,列表分片是将分片后的数据复制了一份,而多维数组的分片则是返回原数据其中的一块,并没有复制数据。
-
因此,对列表分片后的数据进行更改不会影响原数据,但对多维数组分片后的数据进行更改会影响到原数据。
# 列表 lst_data = [1, 2, 3] lst_data2 = lst_data[:] lst_data2[0] = 6 print(lst_data) # 输出:[1, 2, 3] # 多维数组 arr_data = np.array([1, 2, 3]) arr_data2 = arr_data[:] arr_data2[0] = 6 print(arr_data) # 输出:[6 2 3]
-
你可能会好奇:numpy 为什么会这样?答案是:这一切都是为了性能。
numpy 设计的目的是处理大数据,所以你可以想象一下,在处理几百万甚至几千万条数据时,每进行一次分片操作就将数据复制一遍将会产生何等的性能和内存问题。
-
所以,numpy 中的切片默认不会复制一份副本,而是返回原数据中的一块,被称为视图(View)。如果你确实想要得到一份副本,则需要手动调用
copy()
方法进行复制,例如:arr_data[:].copy()
。
通用方法
# mean() 求平均值
player1 = np.array([7, 9, 10, 9, 11, 13, 10, 10, 11, 10])
player2 = np.array([7, 9, 8, 9, 11, 10, 11, 12, 10, 13])
player3 = np.array([3, 7, 10, 3, 6, 30, 10, 7, 11, 13])
print('球员1的平均得分为', player1.mean())
print('球员2的平均得分为', player2.mean())
print('球员3的平均得分为', player3.mean())
# 最大值的 max() 方法、求最小值的 min() 方法、求和的 sum() 方法
描述统计学
- 在描述统计学中有很多数据指标,主要分为两类:集中趋势 和 离中趋势。
集中趋势
- 集中趋势所反映的是一组数据所具有的共同趋势,它代表了一组数据的总体水平。其常用指标有 平均数、中位数 和 众数。
- 求平均数很简单,就是把所有数据加起来,再除以这些数据的个数。但平均数对异常数值并不敏感,容易得到误导性的结论。比如,你和马云的身价求平均,你也是亿万富翁。
- 所以不能只看平均数,还有中位数和众数。中位数是指数据排序后处于中间的那个数。众数是指一组数据中出现次数最多的数。
离中趋势
- 离中趋势是指一组数据中各数据值以不同程度的距离偏离其中心(平均数)的趋势。其常用指标有 极差、方差 和标准差。
- 极差是一组数据的最大值减去最小值得到的,反应了数据变动的最大范围。
- 方差的计算方式是:将一组数据中的每个数减去这组数据的平均数,然后将得到的结果进行平方求和,最后再除以数据的个数。
- 而方差的平方根则是标准差。因为方差是对数据进行平方得到的,所以量纲(单位)和原数据不一致。对方差进行开根号后得到的标准差量纲和原数据一致,使用起来更方便。
- 方差和标准差都能反映数据的离散程度,也就是数据的波动程度。方差和标准差的值越小,说明数据越稳定。
- 除了众数则需要我们自己动手写代码计算,numpy 中没有直接计算的方法之外,其他的指标在 numpy 中都有对应的方法进行计算
# 集中趋势:
mean() 求平均数
median() 求中位数
# 离中趋势
min() 求最小值
max() 求最大值
ptp() 求极差
std() 求标准差
var() 求方差
# 这些方法有两种使用方式,一种是直接在多维数组上调用,如 data.std();另一种是在 numpy 上调用并传入数据,如:np.std(data)。
# 需要注意的是,求中位数的 median() 方法只有 numpy 上有,只能使用 np.median(data) 来求中位数。
多维数组
学过的 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.]]
-
接下来我们来认识几个描述多维数组的属性:
-
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 位的整数。
-
二维数组的加减乘除
-
二维数组间的加减乘除和一维数组间的并无大致,也是对应位置的元素进行计算
-
法、乘法和除法也都是如此。二维数组和数学中的 矩阵 很相似,常被用于进行矩阵间的运算。但二维数组间直接用
*
进行计算的方式和矩阵乘法计算的方式并不相同,应该用@
符号进行矩阵间的乘法计算。
-
维度一样的数组间可以进行计算的条件是形状(shape)一样,形状不一样的数组元素无法一一对应,因此无法计算,导致报错。
-
除了维度相同的数组间的计算,如果将二维数组和一维数组放到一起进行计算,这样可以吗?
当然也是可以的,这得益于 numpy 中的 广播规则。上一关中一维数组和数字进行计算时就提到过‘’广播’‘,它是指较小维度的数组在较大维度的数组上进行”广播“,以便它们具有兼容的形状。
-
因为二维数组多了个维度,所以它的通用方法可以更加的灵活。不仅可以对所有数据进行计算,还可以针对某个维度上的数据进行计算。
轴
这里就要引入一个概念——轴(axis)。轴和维度的概念是类似的,一维数组有 1 个轴,二维数组有 2 个轴,三维数组有 3 个轴等等。
-
在一维数组中,
axis=0
就代表着它唯一的轴;二维数组中axis=0
和axis=1
代表其中的行轴和列轴;在三维数组中,axis=0
、axis=1
和axis=2
分别代表对应的三条轴。 -
在二维数组中
axis=0
的轴是向下的,和一维数组中有所不同# 在通用方法中,通过 axis 参数可以指定计算方向。以二维数组中的 max() 方法为例,指定 axis=0 将会在行轴方向求最大值,指定 axis=1 将会在列轴方向求最大值。 data = np.array([[1, 2], [5, 3], [4, 6]]) # 不指定 axis print(data.max()) # 输出:6 # axis=0 print(data.max(axis=0)) # 输出:[5 6] # axis=1 print(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, :]
。你可以将索引和分片结合起来,以获取二维数组中你想要的任意数据。data = np.array([[1, 2], [3, 4], [5, 6]]) print(data[0, 1]) # 输出:2 print(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 位置的元素。因此,获取数组中所有大于 3 的元素的代码可以这样写: data = np.array([[1, 2], [3, 4], [5, 6]]) print(data[data > 3]) # 输出:[4 5 6]
# 那么想要获取大于 3 且小于 5 的元素该怎么写呢? # 你可能会不加思索的写出如下的代码: data = np.array([[1, 2], [3, 4], [5, 6]]) print(data[data > 3 and data < 5]) # 正确方法: data = np.array([[1, 2], [3, 4], [5, 6]]) print(data[(data > 3) & (data < 5)]) # 输出:[4] # 区别在于:and 改用 &,or 改用 |,not 改用 ~,并且每个条件要用括号括起来。 data = np.array([[1, 2], [3, 4], [5, 6]]) # 大于 3 或者小于 2 print(data[(data > 3) | (data < 2)]) # 输出:[1 4 5 6] # 大于 3 或者不小于 2(即大于等于 2) print(data[(data > 3) | ~(data < 2)]) # 输出:[2 3 4 5 6]
genfromtxt() 方法
-
genfromtxt()
方法用于文件的读取。我们学习 numpy 是要到实际生活中应用的,而生活中我们的数据来源通常是一个文件,例如 CSV 文件。genfromtxt()
方法常用的参数有两个,分别是数据源和分隔符。假设我们要用 numpy 读取一个以逗号分隔的 CSV 文件,可以这样写:data = np.genfromtxt('data.csv', delimiter=',')
-
第一个参数是数据源,可以是本地文件的路径,也可以是网络文件的地址。
delimiter
参数用于指定分隔符,CSV 文件一般是用逗号作为分隔符,当遇到其他符号分隔的文件时,用delimiter
参数进行指定即可。