NumPy基础¶
————开源的数值编程工具
- 强大的N维数组对象:
ndarray - 对数组结构数据进行运算
- 随机数、线性代数、傅里叶变换等
注解
numpy 提供了两种基本的对象:
ndarray(N-dimensional array object)和ufunc(universal function object)ndarray(下文统一称之为数组)是存储单一数据类型的多维数组ufunc则是能够对数组进行处理的函数!
高级工具 Pandas 也是基于 numpy 来构建的
NumPy介绍¶
numpy构建的数组是一个多维的数组对象,由两部分构成: 1. 实际的数据 2. 描述这些数据的元数据
>>> import numpy as np # 导入numpy并命别名为np,这一般是约定俗称的 >>> arr = np.array([[1,2,3,4,5],[0,3,2,5,2]]) #array()函数的括号内可以是 列表、元组、数组等可迭代对象 >>> print(arr) # 输出中没有逗号 [[1 2 3 4 5] [0 3 2 5 2]] >>> print(arr.ndim) # 求数组的维度的个数,线代中称为'秩',rank 2 >>> print(arr.shape) # 求数组的具体维度(几行几列)返回的形式是tuple。如果返回的结果只有一个元素,那么就是一维数组 (2, 5) >>> print(arr.size) # 数组中元素的总个数 10 >>> print(arr.dtype) # data-type:指数组中数值的类型。记住是数值的类型 int32 >>> print(arr.itemsize) # 数组中每个元素的字节大小。 int32在内存中占4个字节 4 >>> print(arr.data) # 实际包含数组元素的地址位置 <memory at 0x000001EB63459120> >>>创建数组:array()函数的括号内可以是 列表、元组、数组可迭代对象等
>>> import numpy as np >>> ar1 = np.array(range(5)) # 利用可迭代对象创建数组 >>> print(ar1) [0 1 2 3 4] >>> ar2 = np.array([1, 2, 3.1, 4.5, 5.6]) #浮点型 >>> print(ar2) # 注意看输出形式,输入中有float和int,结果全为float,这个叫向上统一 [1. 2. 3.1 4.5 5.6] >>> ar3 = np.array([[1,2,3],['a','b','c']]) >>> print(ar3) # 向上合并,因为ar3中含有字符型,所以数值型也转换为字符型。因为数组中要求元素类型一致 [['1' '2' '3'] ['a' 'b' 'c']] >>> print(ar3.dtype) <U11 # 这里的 U11 就是unicode 表示字符型 >>>
NumPy的索引及切片¶
一维数组的索引及切片¶
>>> import numpy as np # 导入模块
>>>
>>> ar = np.arange(20) # 利用numpy的arange()方法生成一个含有20个元素的一维数组
>>> print(type(ar)) # 查看类型
<class 'numpy.ndarray'>
>>> print(ar)
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19] # 注意没有逗号
>>> print(ar[4]) # 元素索引,和python的序列索引类似
4
>>> print(ar[5:8]) # 切片,也和python的序列切片类似
[5 6 7]
>>> print(ar[:5])
[0 1 2 3 4]
>>> print(ar[:9])
[0 1 2 3 4 5 6 7 8]
>>> print(ar[::2]) # 指定步长
[ 0 2 4 6 8 10 12 14 16 18]
>>>
二维数组的索引及切片¶
>>> import numpy as np # 导入模块 >>> ar = np.arange(20).reshape(4,5) # 通过reshape()指定数组的维度 >>> print(ar) # 输出为4行5列的二维数组 [[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 12 13 14] [15 16 17 18 19]] >>> print(ar.ndim) # 输出数组ar的轴数 2 # 因为是二维,所以轴数为2 >>> print(ar[0][4]) # 索引到第0行第4个元素(从0开始计数) 4 >>> print(ar[0]) # 拿出第一行 [0 1 2 3 4] # 这是第一行的数据 >>> print(ar[0].ndim) 1 # 因为第一行是一维数组,所以轴数为1 >>> print(ar[1:3]) # 二维数组切片,结果为由第1行和第2行组成的新的二维数组(从0开始计数) [[ 5 6 7 8 9] [10 11 12 13 14]] >>> print(ar[0::2]) # 二维数组切片也可以指定步长,输出为第0行开始步长为2,组成的一个多维数组 [[ 0 1 2 3 4] [10 11 12 13 14]] >>>注解
- 二维数组的索引依然为一个元素,只不过需要 二次 索引,即先索引某行再索引某列,最后锁定到一个元素。
- 二维数组的切片可能为一维数组也可能为一个二维数组,取决于切片的长度。
三维数组的索引及切片¶
>>> import numpy as np # 导入模块 >>> ar = np.arange(27).reshape(3,3,3) # 3*3*3 = 27 >>> print(ar) # 这是一个三维数组 [[[ 0 1 2] [ 3 4 5] [ 6 7 8]] [[ 9 10 11] [12 13 14] [15 16 17]] [[18 19 20] [21 22 23] [24 25 26]]] >>> print(ar[0][0][1]) # 三维数组索引,需要三次索引 1 >>> print(ar.ndim) # 三维数组所以轴数为3 3 >>> print(ar[0], ar[0].ndim) # ar[0]为二维数组,故轴数为2 [[0 1 2] [3 4 5] [6 7 8]] 2 >>> print(ar[0][1], ar[0][1].ndim) # ar[0][1]为一维数组,轴数为1 [3 4 5] 1 >>>注解
- 三维数组的索引依然为一个元素,只不过需要 三次 索引,索引一次结果为二维数组,再索引一次结果为一维数组,最后索引一次锁定到某个元素。
- 三维数组的切片可能为一维数组、二维数组、三维数组中的某一个,取决于切片的长度。
- 如果一次切片长度为1,则结果为一个二维数组,一次切片长度大于1,则结果为一个三维数组
- 要想获得一维数组,则需要对三维数组进行二次切片,即先切片一次获得一个二维数组,再对二维数组切片获得一维数组
布尔型的索引和切片¶
>>> import numpy as np
>>> ar = np.arange(12).reshape(3,4)
>>> i = np.array([True, False, True])
>>> j = np.array([True, False, True, False])
>>> print(ar) # 输出数组ar
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
>>> print(ar[i,:]) # 在第一维度做判断,只保留True对应的数据。这里的第一维度是行。
[[ 0 1 2 3]
[ 8 9 10 11]]
>>> print(ar[:, j]) # 在第二维度做判断,与上同理,这里的第二维度是列
[[ 0 2]
[ 4 6]
[ 8 10]]
>>> m = ar > 5 # 输出为布尔型矩阵
>>> print(m)
[[False False False False]
[False False True True]
[True True True True]]
>>> print(ar[m]) # 用m判断矩阵去筛选ar数组中的>5的元素
[ 6 7 8 9 10 11]
>>>
NumPy-指定区间内等分数据¶
>>> import numpy as np
>>> a = np.linspace(1,10,5) # 该方法前2个数指定首位和尾部数字,第三个指定将这部分等分为多少段
>>>
>>> print(a)
[ 1. 3.25 5.5 7.75 10. ]
>>> print(type(a[0])) # 返回的数值默认为float64
<class 'numpy.float64'>
>>> b = np.linspace(1,10,6).reshape((2,3)) # 配合reshape方法
>>> print(b)
[[ 1. 2.8 4.6]
[ 6.4 8.2 10. ]]
>>> print(b.dtype)
float64
>>>
NumPy中的浅拷贝和深拷贝¶
NumPy中如果利用切片的原理进行拷贝,则拷贝后的数据和原数据是共用一套数据,它们之间数据共享,即为浅拷贝。
>>> import numpy as np >>> ar = np.array(range(5)) >>> br = ar[:] # 拷贝数据给对象br,这里其实也是切片,只不过是切片了整个ar给br >>> print(ar, br) [0 1 2 3 4] [0 1 2 3 4] >>> print(id(ar),id(br)) # 你会发现它们的id是不一样的,但是它们的数据是共享的 140088680084224 140088680084384 >>> ar[1] = 100 >>> print(ar, br) # 所以只修改ar中的值,br也会跟着修改 [ 0 100 2 3 4] [ 0 100 2 3 4] >>>如果不想出现上述的现象,那么就用深拷贝,在
numpy中深拷贝是利用array对象中的copy()方法实现的。 这在python中的深浅拷贝有所不同,这里的copy()就等同于python中深拷贝deepcopy()>>> ar = np.array(range(5)) >>> br = ar.copy() # 这里用深拷贝,br对象和ar对象除了数值相等之外 无任何关系 >>> print(ar, br) [0 1 2 3 4] [0 1 2 3 4] >>> print(id(ar),id(br)) # id值依然不相等 140088680084784 140088680084944 >>> ar[1] = 100 >>> print(ar, br) # 修改ar的值不影响br,同理修改br也不会影响ar的值 [ 0 100 2 3 4] [0 1 2 3 4] >>>
NumPy中随机数¶
——由于
numpy.random包含多种概率分布的随机样本,故其是数据分析辅助的重点工具之一>>> import numpy as np # 导入模块 >>> >>> rand_number = np.random.normal(size=(3,3)) # 利用normal()方法生成一个标准正态分布的3*3样本值 >>> print(rand_number) [[-0.82594205 0.50800583 0.39472314] [-0.56514795 0.82111505 -0.22010978] [ 0.50343797 1.1543087 0.21979859]] >>> rand_number = np.random.normal(size=(5)) # size指定它的shape >>> print(rand_number) 3 输出为一维数组 [-0.43982788 0.81971666 0.71810232 -0.93671977 -0.12508982] >>>
rand()-生成[0,1)之间满足均匀分布的随机浮点数(数组)¶
—— 利用
numpy.random.rand()方法,结果满足 均匀分布>>> import numpy as np # 导入模块 >>> rand = np.random.rand() # 括号不添加参数,则输出为1个数 >>> print(rand, type(rand)) 0.8750019466316016 <class 'float'> >>> rand_1 = np.random.rand(4) # 参数为4,则输出4个在[0,1)区间的随机数构成的一维数组 >>> print(rand_1, type(rand_1)) [0.23196631 0.37355104 0.43773787 0.81470703] <class 'numpy.ndarray'> >>> rand_2 = np.random.rand(3,3) # 输出 3*3 的二维数组 >>> print(rand_2) [[0.3232683 0.29482801 0.40134497] [0.36757934 0.20004137 0.4931598 ] [0.32954516 0.30837629 0.21156013]] >>>注解
rand()方法,根据括号中的参数不同,输出为对应的数组。例如:rand = np.random.rand(1000), 就是生成1000个满足均匀分布的样本值,这在计算概率或其他方面有重要意义。
randn()-生成满足正态分布的随机浮点数(数组)¶
—— 利用
numpy.random.randn()方法,结果满足 正态分布>>> import numpy as np # 导入模块 >>> rand = np.random.randn() # 括号不添加参数,则输出为1个数 >>> print(rand, type(rand)) # 注意这时输出的数没有区间,但是由于满足正态分布,所以大概率分布在坐标轴上的0左右 -0.568911571252003 <class 'float'> >>> rand_1 = np.random.randn(4) # 参数为4,则输出4个在[0,1)区间的随机数构成的一维数组 >>> print(rand_1, type(rand_1)) [-0.76996229 -0.48924834 -0.52933874 -0.76477789] <class 'numpy.ndarray'> >>> rand_2 = np.random.randn(3,3) # 输出 3*3 的二维数组 >>> print(rand_2) [[ 1.04398953 0.2627963 0.16515206] [-0.59837973 1.3337921 0.35175933] [-1.57560115 1.79773319 -0.24910758]] >>>警告
注意这里是
numpy.random.randn()方法,与numpy.random.rand()方法的区别仅仅是多了一个字母n
randint()-指定范围生成随机整数(数组)¶
—— 利用
numpy.random.randint()方法,可以生成一个整数或N为整数数组
- 该方法的完整API为
numpy.random.randint(low,high=None,size=None,dtype='I')
- 若high不会None,则取[low,high)之间的随机整数,且high必须大于low,
- 若只给定一个参数low,则取[0,low)之间的随机整数
- size指定生成的个数和维数
- dtype参数只能是int类型
>>> import numpy as np # 导入模块 >>> rand_int = np.random.randint(4) >>> print(rand_int) 3 >>> rand_int = np.random.randint(4,8,size=5) # 记住左闭右开 >>> print(rand_int) [4 7 6 4 7] >>> rand_int = np.random.randint(4,8,size=(3,3)) #这里也可以写作np.random.randint(4,8,(3,3)) >>> print(rand_int) [[7 4 6] [7 5 6] [4 7 7]] >>>
RandomState()-随机种子seed¶
—— 设置随机种子是为了使具备随机性的代码最终结果可复现
对于一个随机数发生器,只要种子不变,则产生的随机数序列就是一样的
>>> import numpy as np # 导入模块 >>> rng = np.random.RandomState(1) # 创建随机种子 >>> x = rng.rand(1) >>> y = rng.rand(1) >>> print('{}\n{}'.format(x, y)) # 在这个随机数发生器中,只要种子不变,不管执行多少次,结果都是固定的随机值 [0.417022] [0.72032449]随机种子的好处,是可以复现随机的结果
NumPy通用函数¶
数组的转置¶
>>> ar = np.arange(5) >>> print(ar,ar.T) # 一维数组的转置,结果是一样的 [0 1 2 3 4] [0 1 2 3 4] >>> br = np.arange(6).reshape(2,3) # 二维数组的转置 >>> print(br) # 2行3列的数组 [[0 1 2] [3 4 5]] >>> print(br.T) # 3行2列的数组 [[0 3] [1 4] [2 5]]注解
数组的转置,由
ar.T来完成,其中ar为一个numpy的数组对象。例如原数组的shape值为(2,3),转置后为(3,2)。
对于一维数组的转置¶
- 由上述数组的转置我们发现一维数组利用
.T转置后结果不变,如果真的需要将一维数组的1行几列转置成几行1列呢?有以下3种方法: 方法一:
np.transpose([a])方法二:
a.reshape(len(a), 1)这里还可以写为a.reshape(-1, 1)-1意味着不知道有多少行,但是想将其转成1列方法三:
a[:, None]上面三种方法的区别在于:
transpose()返回copy(),reshape()和[,None]返回引用上述中
a为np.array对象>>> import numpy as np # 导入模块 >>> a=np.arange(4) >>> print(a) [0 1 2 3] >>> b=np.transpose([a]) # transpose()方法copy了一个新的对象 >>> b[0][0]=-1 # 对b做修改 >>> print(b) [[-1] [ 1] [ 2] [ 3]] >>> print(a) # a没有变 [0 1 2 3]
>>> a=np.arange(4) >>> b=a[:,None] >>> b[0][0]=-1 #对b做修改 >>> print(b) [[-1] [ 1] [ 2] [ 3]] >>> print(a) # a的元素发生了变化,说明两者共用一套数据 [-1 1 2 3]
>>> a=np.arange(4) >>> b=a.reshape(len(a),1) >>> b[0][0]=-1 >>> print(b) [[-1] [ 1] [ 2] [ 3]] >>> print(a) # 这种方法也是共用一套数据 [-1 1 2 3]
reshape()方法¶
———
reshape()返回的新对象和原对象共用一套数据,换句话说reshape()方法和切片类似,只传递了 引用>>> import numpy as np >>> ar = np.arange(10).reshape(2,5) # 用法一:生成数组时改变形状 >>> print(ar) [[0 1 2 3 4] [5 6 7 8 9]] >>> br = np.arange(10) >>> br = br.reshape(5,2) # 用法二:将已有数组改变形状 >>> print(br) [[0 1] [2 3] [4 5] [6 7] [8 9]] >>> cr = np.reshape(np.arange(8),(2,4)) # 用法三:在其参数内添加数组,并给出形状 >>> print(cr) [[0 1 2 3] [4 5 6 7]] >>>
resize()方法¶
—— 没有返回值或称返回值为None,修改原数据
>>> import numpy as np >>> ar = np.resize(np.ones((5,2)),(2,5)) # 例外的是,np.resize() 是返回一个数组,因为它的参数中就加入了array对象 >>> print(ar) [[1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.]] >>> ar = np.arange(6) >>> b = ar.resize(2,3) >>> print(ar) # 修改了ar的值 [[0 1 2] [3 4 5]] >>> print(b) # 返回值为None None上述代码,本来是生成5行2列的接近1的数组,然后由resize改变形状为2行5列,所以最后结果为2行5列的数组
数组类型转换¶
>>> import numpy as np >>> ar1 = np.arange(10,dtype=float) >>> print(ar1,ar1.dtype) [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.] float64 >>> >>> ar2 = ar1.astype(np.int32) # 利用np.astype()返回新的数组对象并对数组类型进行转换 >>> ar1[0] = 100 >>> print(ar1) [100. 1. 2. 3. 4. 5. 6. 7. 8. 9.] >>> print(ar2,ar2.dtype) # ar1的改变并不会影响ar2 [0 1 2 3 4 5 6 7 8 9] int32此时ar2和ar1是完全独立的两个对象
数组的堆叠¶
水平(按列)堆叠数组¶
——
np.hstack()方法>>> import numpy as np >>> ar = np.arange(5) >>> br = np.arange(5,9) >>> print(ar, ar.shape) [0 1 2 3 4] (5,) >>> print(br, br.shape) [5 6 7 8] (4,) >>> cr = np.hstack((ar,br)) # 这里注意hstack()方法中还有一个括号 >>> print(cr,cr.shape) [0 1 2 3 4 5 6 7 8] (9,) >>>
np.hstack()方法中的两个数组参数同为一维数组时,它们的shape可以不同>>> ar_1 = np.arange(6).reshape(2,3) >>> br_1 = np.arange(6,12).reshape(2,3).astype(np.str) >>> print(ar_1, ar_1.shape) # ar_1为 2行3列的数组 [[0 1 2] [3 4 5]] (2, 3) >>> print(br_1, br_1.shape) # br_1也为 2行3列的数组 [['6' '7' '8'] ['9' '10' '11']] (2, 3) >>> cr_1 = np.hstack((ar_1,br_1)) >>> print(cr_1, cr_1.shape) # 因为br_1的数据为str型,所以堆叠之后的结果也为str类型,并且shape为2行6列,按列堆叠 [['0' '1' '2' '6' '7' '8'] ['3' '4' '5' '9' '10' '11']] (2, 6) >>>警告
利用
np.hstack()方法进行按列堆叠时需要注意: 堆叠的数组需满足 行数 相同的原则! 简称 水平按列行相等
垂直(按行)堆叠数组¶
——
np.vstack()方法>>> import numpy as np >>> ar = np.arange(10).reshape(2,5) >>> print(ar, ar.shape) # ar为2行5列的数组 [[0 1 2 3 4] [5 6 7 8 9]] (2, 5) >>> br = np.arange(5,10) >>> print(br, br.shape) # br为1行5列的数组 [5 6 7 8 9] (5,) >>> cr = np.vstack((ar,br)) # 利用np.vstack()方法,注意参数内还有一个括号 >>> print(cr,cr.shape) # 结果为3行5列,列数不变 [[0 1 2 3 4] [5 6 7 8 9] [5 6 7 8 9]] (3, 5)警告
利用
np.vstack()方法进行按行堆叠时需要注意: 堆叠的数组需满足 列数 相同的原则! 简称 垂直按行列相等
stack堆叠¶
—— 需保证数组的shape完全一致
>>> import numpy as np >>> >>> ar_1 = np.arange(6).reshape(2,3) >>> br_1 = np.arange(6,12).reshape(2,3).astype(np.str) >>> print(ar_1, ar_1.shape) [[0 1 2] [3 4 5]] (2, 3) >>> print(br_1, br_1.shape) [['6' '7' '8'] ['9' '10' '11']] (2, 3) >>> cr_1 = np.stack((ar_1,br_1)) # stack默认是按行堆叠,并且二维数组的堆叠的shape必须完全一致,此时结果为三维数组 >>> print(cr_1, cr_1.shape) [[['0' '1' '2'] ['3' '4' '5']] [['6' '7' '8'] ['9' '10' '11']]] (2, 2, 3) >>> >>> cr_1 = np.stack((ar_1,br_1),axis=1) # 添加了参数axis=1 >>> print(cr_1, cr_1.shape) # 注意看和上面结果的区别 [[['0' '1' '2'] ['6' '7' '8']] [['3' '4' '5'] ['9' '10' '11']]] (2, 2, 3)注解
如果给stack()添加参数 axis=1,即np.stack((ar,br),axis=1) ,其中ar、br为数组对象。则为列和列的堆叠。 如果给设置参数axis=0 或 不设置则为行和行的堆叠
数据拆分¶
按列拆分¶
>>> import numpy as np >>> ar = np.arange(16).reshape(4,4) # 4行4列的数组 >>> br = np.hsplit(ar,2) >>> print(ar) [[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11] [12 13 14 15]] >>> print(br, type(br)) # 看清楚返回的是一个列表类型,数组在列表中 [array([[ 0, 1], [ 4, 5], [ 8, 9], [12, 13]]), array([[ 2, 3], [ 6, 7], [10, 11], [14, 15]])] <class 'list'>注解
hsplit()方法:按列分隔分隔后的结果为
hsplit()方法括号内的参数数组ar除以其后的数字的结果为结果的数组的 列数 (必须能够除尽)。 上述就是数组ar的总列数4,除以其后的数字2结果为2,所以结果的数组的 列数 为2列,并且是按 列 分隔的,如上结果所示。
按行拆分¶
>>> import numpy as np >>> ar = np.arange(16).reshape(4,4) # 4行4列的数组 >>> br = np.vsplit(ar,2) >>> print(ar) [[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11] [12 13 14 15]] >>> print(br, type(br)) # 看清楚返回的是一个列表类型,数组在列表中 [array([[0, 1, 2, 3], [4, 5, 6, 7]]), array([[ 8, 9, 10, 11], [12, 13, 14, 15]])] <class 'list'>注解
vsplit()方法:按列分隔分隔后的结果为
vsplit()方法括号内的参数数组ar除以其后的数字的结果为结果的数组的 行数 (必须能够除尽)。 上述就是数组ar的总列数4,除以其后的数字2结果为2,所以结果的数组的 行数 为2行,并且是按 行 分隔的,如上结果所示。
数组简单运算¶
>>> import numpy as np >>> ar = np.arange(8).reshape(4,2) >>> print(ar) [[ 0 2] [ 4 6] [ 8 10] [12 14]] >>> print(ar + 10) # 整体加10 [[10 11] [12 13] [14 15] [16 17]] >>> print(ar * 2) # 整体乘2 [[ 0 2] [ 4 6] [ 8 10] [12 14]] >>> print(1 / (ar+1)) # 因为ar第一个元素为0,所以整体加1 [[1. 0.5 ] [0.33333333 0.25 ] [0.2 0.16666667] [0.14285714 0.125 ]] >>> print(ar ** 2) # 整体求幂 [[ 0 1] [ 4 9] [16 25] [36 49]] >>> print(ar.mean()) # 求平均值,即数组内所有元素的平均值 3.5 >>> print(ar.max()) 7 >>> print(ar.min()) 0 >>> print(ar.std()) # 标准差 2.29128784747792 >>> print(ar.var()) # 方差 5.25 >>> print(ar.sum()) #求所有元素之和 也可写为 np.sum(ar) 28 >>> print(np.sum(ar,axis=0)) # 按列求和,结果为每一列的和。结果还是一个数组 [12 16] >>> print(np.sum(ar,axis=1)) # 按行求和,结果为每一个行的和。结果还是一个数组 [ 1 5 9 13]警告
- 注意:上述除了
sum()方法之外,std() var() max() min() mean()等方法都能用一下几种形式表示:
np.max(ar,axis=1)求每行最大值、np.max(ar,axis=0)求每列最大值、np.max(ar)求数组中的最大值np.min(ar,axis=1)求每行最小值、np.max(ar,axis=0)求每列最小值、np.min(ar)求数组中的最小值np.std(ar,axis=1)求每行数据的标准差、np.std(ar,axis=0)求每列数据的标准差、np.std(ar)求数组的标准差np.var(ar,axis=1)求每行数据的方差、np.var(ar,axis=0)求每列数据的方差、np.var(ar)求数组的方差np.mean(ar,axis=1)求每行数据的平均值、np.mean(ar,axis=0)求每列数据的平均值、np.mean(ar)求数组的平均数