07 Python入门 Lesson7 NumPy
文章目录
本节好多内容参考了
Alien大大
的新版笔记,衷心感谢。
2.NumPy简介
NumPy 是 Numerical Python 的简称,它是 Python 中的科学计算基本软件包。NumPy 为 Python 提供了大量数学库,使我们能够高效地进行数字计算。这些课程将简要讲解 NumPy 基本概念,并介绍一些最重要的 NumPy 功能。
NumPy 手册
NumPy 用户指南
NumPy 参考资料
Scipy 讲座
3.为何要使用NumPy
因为快啊!底层用的C语言,而且做了优化,在例子中的速度是Python的100倍!
4.创建和保存 ndarray
重点提示
这一节的课后说明十分详细,重点如下:
- NumPy 的核心是 ndarray,其中 nd 表示 n维。ndarray 是一个多维数组,其中的所有元素类型都一样。换句话说,ndarray 是一个形状可以多样,并且可以存储数字或字符串的网格。(务必注意,这个维和三维空间的维并不完全相同,将在7节之后的Plus讲解。
- NumPy 的维数对应的是序,在数组中每一行对应一维。使用.shape观察,比如这段代码:
# We create a rank 2 ndarray that only contains integers
Y = np.array([[1,2,3],[4,5,6],[7,8,9], [10,11,12]])
# We print Y
print()
print('Y = \n', Y)
print()
# We print information about Y
print('Y has dimensions:', Y.shape)
print('Y has a total of', Y.size, 'elements')
print('Y is an object of type:', type(Y))
print('The elements in Y are of type:', Y.dtype)
>>> 输出如下:
Y =
[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[10 11 12]]
Y has dimensions: (4, 3)
Y has a total of 12 elements
Y is an object of type: class 'numpy.ndarray'
The elements in Y are of type: int64
- NumPy 数组在创建时有固定的大小,不同于Python列表(可以动态增长)。更改ndarray的大小将创建一个新的数组并删除原始数据。
- NumPy 数组中的元素都需要具有相同的数据类型,如果混搭,会按照最通用的元素进行转换,比如有字符也有数字时,会都存储字符;有整数也有小数时,会都存成小数(浮点型)。
创建ndarray
方法1:使用np.array从python list转换:
(注意可以通过slice的方式选中其中的元素,最后一个例子)
a = np.array([1, 2, 3])
# 1维数组
print(type(a), a.shape, a[0], a[1], a[2])
# out: <class 'numpy.ndarray'> (3,) 1 2 3
a[0] = 5
# 重新赋值 print(a)
>>> 输出如下:
[5 2 3]
b = np.array([[1,2,3],[4,5,6]])
# 2维数组 print(b)
>>> 输出如下:
out: [[1 2 3]
[4 5 6]]
print(b[0, 0], b[0, 1], b[1, 0])
>>> 输出如下:
out: 1 2 4
方法2:直接使用np.array(本是下一节内容,放在一起对比):
a = np.zeros((2,2))
# 创建2x2的全0数组
print(a)
>>> 输出如下:
[[ 0. 0.]
[ 0. 0.]]
b = np.ones((1,2))
# 创建1x2的全1数组
print(b)
>>> 输出如下:
[[ 1. 1.]]
c = np.full((2,2), 7)
# 创建2x2定值为7的数组
print(c)
>>> 输出如下:
[[7 7]
[7 7]]
d = np.eye(2)
# 创建2x2的单位矩阵(对角元素为1)
print(d)
>>> 输出如下:
[[ 1. 0.]
[ 0. 1.]]
d_1 = np.diag([10,20,30,50])
#创建一个对角线为10,20,30,50的对角矩阵
print(d_1)
>>> 输出如下:
[[10 0 0 0]
[ 0 20 0 0]
[ 0 0 30 0]
[ 0 0 0 50]]
e = np.arange(15)
#创建一个一维的0-14的数组
print(e)
>>> 输出如下:
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14]
e_1 = np.arange(4,10)
#创建一个一维的4-9的数组
print(e_1)
>>> 输出如下:
[4 5 6 7 8 9]
e_2 = np.arange(1,14,3)
#创建一个一维的1-13且以间隔为3的数组
print(e_2)
>>> 输出如下:
[ 1 4 7 10 13]
f = np.linspace(0,10,6)
#创建一个一维的范围在0-10,长度为6的数组
print(f)
>>> 输出如下:
[ 0., 2., 4., 6., 8., 10.]
#各个元素的间隔相等,为(10-0)/(6-1) = 2,若不想包含末尾的10,可以添加参数endpoint = False
g = np.arange(12).reshape(3,4)
#把arange创建的一维数组转换为3行4列的二维数组
print(g)
#同样方法也适用于linspace等
#注意:使用reshape转换前后的数据量应该相同,12 = 3x4
>>> 输出如下:
[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]]
h = np.random.random((2,2))
# 2x2的随机数组(矩阵),取值范围在[0.0,1.0)(包含0,不包含1)
print(e)
>>> 输出如下:
[[ 0.72776966 0.94164821]
[ 0.04652655 0.2316599 ]]
i = np.random.randint(4,15,size = (2,2))
#创建一个取值范围在[4,15),2行2列的随机整数矩阵
print(i)
>>> 输出如下:
[[6, 5],
[5, 9]]
j = np.random.normal(0,0.1,size = (3,3))
#创建一个从均值为0,标准差为0.1的正态分布中随机抽样的3x3矩阵
print(j)
>>> 输出如下:
[[-0.20783767, -0.12406401, -0.11775284],
[ 0.02037018, 0.02898423, -0.02548213],
[-0.0149878 , 0.05277648, 0.08332239]]
存储和读取
# We create a rank 1 ndarray
x = np.array([1, 2, 3, 4, 5])
# We save x into the current directory as
np.save('my_array', x)
# We load the saved array from our current directory into variable y
y = np.load('my_array.npy')
# We print y
print()
print('y = ', y)
print()
# We print information about the ndarray we loaded
print('y is an object of type:', type(y))
print('The elements in y are of type:', y.dtype)
7.访问和删除 ndarray 中的元素及向其中插入元素
这里主要是提供了一些访问、更改或增加ndarray中某一元素的基础方法。
访问&更改
类似于访问python list中元素的方式,按照元素的index进行访问或更改。
访问某一元素:
print(np.arange(6)[3])
# 访问一维数组的某一元素,中括号内填写index
>>>
3
print(np.arange(6).reshape(3,2)[1,1])
# 访问二维数组的某一元素,中括号内填写[行,列]
>>>
3
print(np.arange(12).reshape(2,3,2)[0,1,1]) #访问三位数组中的某一元素,中括号内[组,行,列]
>>>
3
更改某一元素
用 = 进行赋值和替换即可。
a = np.arange(6)
a[3] = 7
#先访问,再重新赋值
print(a)
[0 1 2 7 4 5]
删除
可使用np.delete(ndarray, elements, axis)函数进行删除操作。这里需要注意的是axis这个参数,课程中只讲到了2维数据中,axis = 0表示选择行,axis = 1表示选择列,但不能机械的认为0就表示行,1就表示列。在三维数据中,axis = 0表示组,1表示行,2表示列。这是为什么呢?提示一下,三位数组的shape中组、行和列是怎样排序的?
也就是说:axis的赋值一定要考虑数组的shape(将在后面小节扩展)。
a = np.arange(12).reshape(2,2,3)
print(a)
>>>
[[[ 0 1 2]
[ 3 4 5]]
[[ 6 7 8]
[ 9 10 11]]]
print(np.delete(a,[0],axis = 0))
# 思考下,这里删除axis = 0下的第0个,会是什么结果呢?
# 再有一点需要注意的是,如果你想让原数据保留删除后的结果,需要重新替换一下才可以。
# 结果如下,是将第一个维度的,第一个元素删除了:
>>>
[[[ 6 7 8]
[ 9 10 11]]]
a = np.arange(6).reshape(2,3)
print(a)
>>>
[[0 1 2]
[3 4 5]]
print(np.delete(a,[0],axis = 0))
>>>
[[3 4 5]]
array([[0, 1, 2],
[3, 4, 5]])
# 但是这里a其实是没有更改的
# 需要以下重新赋值的过程才能更新到a中
a = np.delete(a,[0],axis = 0)
#重新替换
print(a)
>>>
array([[3, 4, 5]])
#原数据已更改
增加
往ndarray中增加元素的办法跟python list也很类似,常用的有两种:
- 一种是添加(append),就是将新增的元素添加到ndarray的尾部
语法为:np.append(ndarray, elements, axis)
参数和delete函数一致,用法也一致,这里不再赘述 - 一种是插入(insert),可以让新增元素插入到指定位置
语法为:np.insert(ndarray, index, elements, axis)
参数中就多了一个index,指示的是插入新元素的位置。 - 这里值得注意的是,不论是append还是insert,在往多维数组中插入元素时,一定要注意对应axis上的shape要一致。再一个就是,和delete一样,如果你想要更改原数据,需要用
a = np.append(a,elements,axis)
将变化以后的内容写回到变量 a 中。
Plus.秩与轴
对于维度来讲,我们比较熟悉的是3维空间,如果定义了一个坐标原点,我们可以把三维空间中的任意一点,用 (x,y,z)
来表示。
那么对于这3个坐标我们来看下:
b = np.arange(3).reshape(3,1)
print(b,b.shape)
>>>
[[0]
[1]
[2]] (3, 1)
shape的输出 (3, 1)
说明数据是个2维数组,第一个维度是行数,对应了3个数据分别是x,y,z轴。第二个维度是值,分别对应的0,1,2值。但是我们也可以用以下的方式记录坐标:
a = np.arange(3)
print(a,a.shape)
>>>
[0 1 2] (3,)
这个数组就是一维的了,因为把x,y,z三个坐标的值同时写到一个列表里了。那么这么写有什么好处呢?我们假设这是一个球轨迹点,我们们可以通过增加第二个时间维度,来记录球的运动轨迹:
[[0 1 2]
[3 4 5]
[6 7 8]] (3, 3)
于是我们就得到了一个记录了小球运动轨迹的矩阵。其中标记了3个点(1,2,3秒的位置),分别是 [0 1 2]
, [3 4 5]
, [6 7 8]
。
那么再扩展,三维数据的 ndarray 是什么样的呢(和现实有些不同了,需要抽象理解):
[[[ 0 1]
[ 2 3]
[ 4 5]]
[[ 6 7]
[ 8 9]
[10 11]]
[[12 13]
[14 15]
[16 17]]] (3, 3, 2)
在 ndarray 中这个数据维度称为 轴,就是 shape 输出的结果的元素数量,定义如下:
NumPy’s main object is the homogeneous multidimensional array. It is a table of elements (usually numbers), all of the same type, indexed by a tuple of positive integers. In NumPy dimensions are called axes. The number of axes is rank.
For example, the coordinates of a point in 3D space [1, 2, 1] is an array of rank 1, because it has one axis. That axis has a length of 3. In the example pictured below, the array has rank 2 (it is 2-dimensional). The first dimension (axis) has a length of 2, the second dimension has a length of 3.
- 也可以函数直接得出:
- 使用
print(d.ndim)
来输出轴数。 - 使用
print(np.linalg.matrix_rank(d))
来输出轴数(与上面不同,还会显示下层的轴数,请自行对比)
- 使用
- 这个轴,和线性代数中的 秩 是一样的。对于 秩 的定义是:
- 定义2.1 在矩阵A中,任取k行与k列(k≤m,k≤n),位于这些行列交叉处的k2个元素,不改变它们在矩阵中所处的位置次序而得的k阶行列式,称为矩阵A的k阶子式。
- 定义2.1 设在矩阵A中有一个不等于0的r阶子式D,且所有r+1阶子式(如果存在的话)全等于0,那末D称为矩阵A的最高阶非零子式,数r称为矩阵A的秩,记作R(A)。
- 扩展资源1
- 扩展资源2
8.ndarray 切片
ndarray切片
前面学了选择ndarray中的某个元素的方法,这里我们学习选择ndarray子集的方法——切片。对于切片大家并不陌生,在list里面我们也接触过切片,一维的ndarray切片与list无异。需要注意的是,就是理解2维及多维ndarray切片。
2维矩阵切片:
a = np.arange(4*4).reshape(4,4)
print(a)
>>>
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
a[:,:-1]
>>>
array([[ 0, 1, 2],
[ 4, 5, 6],
[ 8, 9, 10],
[12, 13, 14]])
这里可以看出,我们筛选了a矩阵中前三列的所有行,这是如何实现的呢?切片的第一个元素:
表示的是选择所有行,第二个元素:-1
表示的是从第0列至最后一列(不包含),所以结果如上所示。
再看一个例子,筛选的是第2-3行的所有列:
a[1:3,:]
>>>
array([[ 4, 5, 6, 7], [ 8, 9, 10, 11]])
以列的形式获取最后一列数据:
a[:,3:]
>>>
array([[ 3], [ 7], [11], [15]])
以一维数组的形式获取最后一列数据:
a[:,-1]
>>>
array([ 3, 7, 11, 15])
上面两种方法经常会用到,前者的shape为(4,1),后者为(4,)。
ndarray筛选
选择ndarray的对角线:
所用函数为np.diag(ndarray, k=N)
,其中参数k的取值决定了按照哪一条对角线选择数据。
默认k = 0,取主对角线;
k = 1时,取主对角线上面1行的元素;
k = -1时,取主对角线下面1行的元素。
思考:这个函数只能选择主对角线上的元素,那如果想要获取副对角线上的元素呢?
尝试自己搜索一下关键词numpy opposite diagonal
寻找答案。不建议你直接点/getting the opposite diagonal of a numpy array/。
提取ndarray中的唯一值:
所用函数为np.unique(ndarray)
,注意unique也可以添加参数axis来控制评判唯一值的轴方向,不好理解可以看示例:
a = [[0,1,2], [3,4,5], [0,1,2]] print(np.unique(a))
#查看二维数组a中的唯一值 array([0, 1, 2, 3, 4, 5])
print(np.unique(a,axis = 0))
#查看a中的唯一行(也就是没有重复的行)
array([[0, 1, 2], [3, 4, 5]]) print(np.unique(a,axis = 1))
#查看a中的唯一列 array([[0, 1, 2], [3, 4, 5], [0, 1, 2]])
print(np.unique(a[0]))
#查看a中第一行的唯一值
array([0, 1, 2])
通过布尔运算筛选:
这里在中括号中添加筛选条件,当该条件的结果为True时(即满足条件时),返回该值。
X[X > 10] #筛选数组X中大于10的数据
这里需要注意的是,当输入多个筛选条件时,&
表示与,|
表示或,~
表示非。
9.布尔型索引、集合运算和排序
ndarray运算
- 集合运算
np.intersect1d(x,y)
#取x与y的交集 np.setdiff1d(x,y)
#取x与y的差集,返回的是在x中且没在y中的元素 np.union1d(x,y)
#取x与y的并集
- 算术运算
我们可以通过+
、-
、*
、/
或np.add
、np.substract
、np.multiply
、np.divide
来对两个矩阵进行元素级的加减乘除运算,因为是元素级的运算,所以两个矩阵的shape必须要严格一致。
上面涉及到的乘法是元素对应相乘,也就是点乘,那矩阵的叉乘呢?可以了解下/numpy.matmul/函数。
ndarray排序
我们使用np.sort()
和ndarray.sort()
来对ndarray进行排序。
- 相同的是:二者都可以使用参数
axis
来决定依照哪个轴进行排序,axis = 0时按照列排序,axis = 1时按照行排序; - 不同的是:
np.sort()
不会更改原数组;ndarray.sort()
会更改原数组。 - 当 np.sort() 当做函数使用时,它不会对ndarray进行就地排序,即不更改被排序的原始 ndarray。但是,如果将 sort 当做方法,ndarray.sort() 会就地排序 ndarray,即原始数组会变成排序后的数组。
14.迷你项目:均值标准化和数据分离
这里涉及到一个概念,叫做数据标准化。数据的标准化(normalization)是将数据按比例缩放,使之落入一个小的特定区间。在某些比较和评价的指标处理中经常会用到,去除数据的单位限制,将其转化为无量纲的纯数值,便于不同单位或量级的指标能够进行比较和加权。
有很多种对数据进行标准化处理的方法,我们课程中选择的是利用数据均值和标准差进行标准化:
对某一列中某一值进行标准化就是将该值减去该列的平均值,然后除以该列的标准差。标准化后的序列,均值为0,标准差为1,且无量纲。标准化后的数据,没有量纲,方便计算和比较,在机器学习中的很多算法都需要将数据进行标准化。
但是基于本章的要求,我们主要是学习numpy的基本操作即可,具体的数据标准化还有算法可以之后在机器学习课程中学习。
这里需要注意的是:
np.random.permutation()
:
np.random.permutation(N)
函数会创建一个从 0 到 N - 1
的随机排列的整数集。这个整数集也是ndarray类型。
np.random.permutation(5)
>>>
array([3, 1, 2, 4, 0])
- 将数据集切分为训练集、测试集和交叉集。
这里的切分有两点隐形要求:
- 随机性,三个数据集中的数据必须是随机分配的;
- 三个数据集的合集必须为数据集。
考虑到上面学到的np.random.permutation()
函数,所以我们的思路可以是这样的:
- 使用
permutation()
函数,将数据集的行数当作N,这样就可以得到一个随机排列的行索引序列; - 使用切片,将刚才的随机行索引序列,按照训练集、测试集和交叉集的比例
6:2:2
进行切分; - 使用索引访问,获取切分后的数据,即
ndarray[index]
的方式。