上一期的学习中,我们简要的复习了一下Python中常用的函数和语法,例如列表推导式/匿名函数/zip方法等。本期我们来重点回顾一下Python的另一个基础,numpy的基本知识。
我们都知道Pandas的很多内置的计算都是基于numpy的,那么这两个库之间到底有什么不同呢?
numpy是以矩阵为基础的数学计算模块,他的基本数据结构是ndarray,可以理解成多维数组。因为底层是用C编写的,所有拥有超快的计算速度。使用numpy可以很轻松的完成矩阵乘法、换位、重塑等操作。另外在计算机图形学和图像处理中也有很多应用,因为图像天然就是一个多维矩阵。从这点方面来说,numpy很多时候可以作为Python的矩阵计算库替代MATLAB
而Pandas是面向数据分析而生的。我们常见的数据除了numpy可以搞定的数值型数据以外,还有大量的符号,字符串(例如文本分析)等,这些就需要Pandas了。Pandas的核心数据结构是Series以及DataFrame。和numpy的ndarray相比,Pandas引入了行列索引index和标签label的概念,更加适合做表格型数据的分析和展示。另外因为pandas一开始是开发出来做金融量化分析的,天生具有时间序列基因,因此,可以简单的把pandas理解成可以理解成表格+时间戳。
下面总结一下numpy常用的知识点
1. 基本数据结构 np.array
numpy里面最基本的数据结构是array(数组),构造也非常简单,np.array即可。下面总结几种特殊的array
- 等差序列
- np.linspace(起始,终止(包含),样本个数): 适用于提前知道需要创建多少个样本的情况
- np.arange(起始,终止(不包含),步长): 适用于提前知道相邻间隔的情况
注意 np.arange和python数组中的range不要混淆了。range只能生成整数数列,而np.arange可以生成小数(float)数列
import numpy as np
a = np.linspace(1,100,10)
b = np.arange(1,10,1.5)
print(a)
print(b)
>>>
[ 1. 12. 23. 34. 45. 56. 67. 78. 89. 100.]
[1. 2.5 4. 5.5 7. 8.5]
- 特殊矩阵,包括zeros/ones/eye/full等
直接上代码参考:
- 特殊矩阵,包括zeros/ones/eye/full等
print('np.zeros(n):全0矩阵')
print(np.zeros((3,4)))
print('*' * 10)
print('np.ones(n):全1矩阵')
print(np.ones((3, 3)))
print('*' * 10)
print('np.eye(m):单位矩阵')
print(np.eye(3))
print('*' * 10)
print('np.full((i,j),k):构造一个指定维度的矩阵,形状为i*j, 数值全为k:指定维度的/数值填充矩阵')
print(np.full((2,3), 6))
>>>
np.zeros(n):全0矩阵
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
**********
np.ones(n):全1矩阵
[[1. 1. 1.]
[1. 1. 1.]
[1. 1. 1.]]
**********
np.eye(m):单位矩阵
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
**********
np.full((i,j),k):构造一个指定维度的矩阵,形状为i*j, 数值全为k:指定维度的/数值填充矩阵
[[6 6 6]
[6 6 6]]
-
- 随机矩阵
常用的几种如下:
- np.random.rand(): 取值0-1之间的随机分布,这里不要传元组,直接指定不同维度的个数即可
- np.random.randn(): 0~1标准正态分布
- np.random.randint(low,high,size) :指定生成随机整数的最小值最大值和维度大小
- np.random.choice(): 可以从给定的列表中,以一定概率和方式抽取结果,当不指定概率时为均匀采样,默认抽取方式为有放回抽样
- np.random.seed() : 设置种子,就相当是设定了随机值,之后每次随机都一样
- 随机矩阵
print('创建0-1之间的随机数组,指定形状: random.rand')
print(np.random.rand(3,4)) # 0-1之间随机取数,直接指定形状,不需要传入元组
print('*' * 10)
print('创建0-1之间的标准正态分布,指定形状: random.randn')
print(np.random.randn(3,3)) # 0-1之间标准正态分布,直接指定形状
print('*' * 10)
print('创建一个指定区间的整数数组,: random.randint')
print(np.random.randint(0,10)) # 创建一个
print('*' * 10)
print('创建指定区间的整数数组,指定形状: random.randint')
print(np.random.randint(0,10,(3,3))) # 指定形状,将形状作为一个tuple传入
print('*' * 10)
>>>
创建0-1之间的随机数组,指定形状: random.rand
[[0.05827317 0.01510242 0.90460899 0.3653948 ]
[0.65032212 0.33339636 0.58233328 0.15782141]
[0.87060376 0.89384448 0.18587953 0.21796723]]
**********
创建0-1之间的标准正态分布,指定形状: random.randn
[[-0.62439785 -1.5957837 -0.46571742]
[-0.51072119 -0.66087807 -0.51630982]
[ 1.9481531 -0.18061413 0.16868743]]
**********
创建一个指定区间的整数数组,: random.randint
1
**********
创建指定区间的整数数组,指定形状: random.randint
[[6 2 4]
[4 3 1]
[4 2 0]]
**********
对于随机种子,它的作用是固定随机数的输出结果。意思是如果我们可以通过设置相同的种子,产生相同的随机数,如果不同,则可以产生的不同的随机数。这个在机器学习中比较有用,可以通过设置相同的种子产生相同的随机数来横向比较算法的准确度。
# 固定相同的种子,获得相同输出结果
np.random.seed(1024)
x1 = np.random.randint(1,100,10)
x1
>>>array([60, 18, 98, 90, 98, 81, 78, 33, 68, 51])
np.random.seed(1024)
x2 = np.random.randint(1,100,10)
x2
>>>array([60, 18, 98, 90, 98, 81, 78, 33, 68, 51])
# 不设定或者设定不同,输出随机
y1 = np.random.randint(1,100,10)
y1
>>>array([97, 24, 89, 44, 74, 10, 66, 19, 60, 70])
y2 = np.random.randint(1,100,10)
y2
>>>array([49, 38, 70, 28, 20, 49, 14, 31, 62, 55])
上面的代码中x1,x2通过固定相同的种子,可以获得相同的随机数。
下面的y1,y2因为没有设置,完全是随机输出,所以不同
2. np数组的常用操作:
- 转置: T
A = np.random.randint(10,30, (3,4))
print(A)
print('*' * 10)
B = A.T
print(B)
>>>
[[27 19 13 23]
[16 26 26 18]
[25 16 14 27]]
**********
[[27 16 25]
[19 26 16]
[13 26 14]
[23 18 27]]
- 合并: r_, c_
对于二维数组而言, r_ 和 c_ 分别表示上下合并和左右合并:
print('r_:上下合并,r是rows行的缩写')
print(np.r_[A,A])
print('*' * 10)
print('c_:左右合并,c是columns的缩写')
print(np.c_[A,A])
>>>
r_:上下合并,r是rows行的缩写
[[27 19 13 23]
[16 26 26 18]
[25 16 14 27]
[27 19 13 23]
[16 26 26 18]
[25 16 14 27]]
**********
c_:左右合并,c是columns的缩写
[[27 19 13 23 27 19 13 23]
[16 26 26 18 16 26 26 18]
[25 16 14 27 25 16 14 27]]
- 维度变换reshape
reshape 可以把原数组按照新的维度重新排列。在使用时有两种模式,分别为 C 模式和 F 模式,分别以逐行和逐列的顺序进行填充。
a = np.arange(10)
print(a)
print('-' * 10)
print('reshape: 维度变化, 默认order = C, 即按行填充')
print(a.reshape(2,5))
print('-' * 10)
print('设置order = F, 即按列填充:')
print(a.reshape(2,5, order = 'F'))
>>>
[0 1 2 3 4 5 6 7 8 9]
----------
reshape: 维度变化, 默认order = C, 即按行填充
[[0 1 2 3 4]
[5 6 7 8 9]]
----------
设置order = F, 即按列填充:
[[0 2 4 6 8]
[1 3 5 7 9]]
另外,reshape在使用时由于被调用数组的大小是确定的, reshape 允许有一个维度存在空缺,此时只需填充-1即可,程序会自动计算另一个维度的大小。
例如一个38的矩阵,更改为46时,可以指定前或者后一个维度为-1,
M = np.arange(1,25).reshape(3,8)
M
>>>
array([[ 1, 2, 3, 4, 5, 6, 7, 8],
[ 9, 10, 11, 12, 13, 14, 15, 16],
[17, 18, 19, 20, 21, 22, 23, 24]])
M.reshape(4,-1, order = 'F')
>>>
array([[ 1, 10, 19, 5, 14, 23],
[ 9, 18, 4, 13, 22, 8],
[17, 3, 12, 21, 7, 16],
[ 2, 11, 20, 6, 15, 24]])
M.reshape(-1).reshape(4,6)
>>>
array([[ 1, 2, 3, 4, 5, 6],
[ 7, 8, 9, 10, 11, 12],
[13, 14, 15, 16, 17, 18],
[19, 20, 21, 22, 23, 24]])
- 数组切片与索引
数组的切片模式支持使用 slice 类型的 start:end:step 切片
先来简单的一维数组
a = np.arange(10)
print(a)
# 索引和切片
print(a[2])
print(a[2:5])
print(a[:5])
>>>
[0 1 2 3 4 5 6 7 8 9]
2
[2 3 4]
[0 1 2 3 4]
对于二维数组,切片的样式较多,常见的有以下几种:
- 二维数组索引中有且仅有一个[]号时,只表示行索引,牢记这一点。
arr_2D = np.arange(1,13).reshape(3,4)
arr_2D
>>>
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
# 1. 二维数组索引中有且仅有一个[]号时,只表示行索引,牢记这一点!
arr_2D[2]
>>>
array([ 9, 10, 11, 12])
- 索引为[] []或[ , ]的形式,这时表示第几行的第几列,两种索引方式等价。
print(arr_2D[1][2])
print('*' * 5)
print(arr_2D[1,2])
>>>
7
*****
7
- 单独:表示所有行或列,:前后有数字表示索引到第几行或者第几列,视情况而定。索引位置包括开头,不包括结尾,和列表索引一致
arr_2D[:2]
>>>
array([[1, 2, 3, 4],
[5, 6, 7, 8]])
上面的代码只有一个[],按照第一种情况肯定取行,行内的索引为':2',表示从0开始到2,包括第0行和第一行,不包括第2行
arr_2D[:-1, 1:-1]
>>>
array([[2, 3],
[6, 7]])
上面的代码首先包含行列索引切片,逗号前面的为行索引切片,从第0行开始到最后一行,但是不包括最后一行;列索引从第1列开始到最后一列,同样不包含最后一列
arr_2D[2, : -1]
>>>
array([ 9, 10, 11])
行索引切片中没有":"号,表示只取第2行,(同样是从0开始数),后面的列表示除去最后一列的所有列
上面的切片均为连续区域,如果需要隔开取,则可以将需要的行或者列索引作为一个列表传入,如下:
arr_2D[:-1, [0,2]]
>>>
array([[1, 3],
[5, 7]])
- where条件函数
where 是一种条件函数,可以指定满足条件与不满足条件位置对应的填充值:
a = np.arange(10)
print('a:', a)
b = np.where(a>a.mean(), 1, 0)
print('b:', b)
>>>
a: [0 1 2 3 4 5 6 7 8 9]
b: [0 0 0 0 0 1 1 1 1 1]
上面的代码中,将a中大于a的平均值的数填充为1,否则为0
- nonzero, argmax, argmin
这三个函数返回的都是索引, nonzero 返回非零数的索引, argmax, argmin 分别返回最大和最小数的索引:
X = np.array([-1,3, -2, 0, 5, 9, 0])
print(np.nonzero(X))
print(X.argmax())
print(X.argmin())
>>>
(array([0, 1, 2, 4, 5], dtype=int64),)
5
2
另外,更多精彩内容也可以微信搜索,并关注公众号:‘Python数据科学家之路“ ,期待您的到来和我交流!