【Numpy】数值计算工具Numpy的基本操作
- 2020 年 2 月 21 日
- 筆記
1、Numpy简介
(1)Scipy库简介 Scipy库是基于Python生态的一款开源数值计算、科学与工程应用的开源软件,包括常用的Numpy、pandas、matplotlib等库。 Scipy库官网:http://www.scipy.org/。 (2)Numpy 官网参考文档:https://docs.scipy.org/doc/numpy-dev/user/index.html。 Numpy:Numerical Python,即数值Python包,是Python进行科学计算的一个基础包,因此要更好理解和掌握Python科学计算包,尤其是pandas,需要先行掌握Numpy库的用法。 (3)Numpy包括的内容 Numpy系统是Python的一种开源的数值计算扩展,是一个用python实现的科学计算包。包括: 1)一个具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组,称为ndarray(N-dimensional array object)。 2)用于对整组数据进行 快速运算的标准数学函数, ufunc (universal function object)。 3)用于整合C/C++和Fortran代码的工具包。 4)实用的线性代数、傅里叶变换和随机数生成函数。numpy和稀疏矩阵运算包scipy配合使用更加方便。 (4)Numpy发展历史 1)1995年 Jim HugUNin开发了Numeric。 2)随后,Numarray包诞生。 3)Travis Oliphants整合Numeric和Numarray,开发Numpy,于2006年发布第一个版本。 4)Numpy(Numeric Python)提供了许多高级的数值编程工具,如:矩阵数据类型、矢量处理,以及精密的运算库。专为进行严格的数字处理而产生。多为很多大型金融公司使用,以及核心的科学计算组织如:Lawrence Livermore,NASA用其处理一些本来使用C++,Fortran或Matlab等所做的任务。 5)使用Anaconda发行版的Python,已经帮我们事先安装好了Numpy模块,因此无需另外安装。 6)依照标准的Numpy约定,习惯使用 import numpy as np
方式导入该模块。
2、Numpy的数据结构:ndarray,一种多维数组对象
(1)ndarray介绍 ndarray:N-dimensional array, N维数组。一种由相同类型的元素组成的多维数组,元素数量是事先指定好的。元素的数据类型由dtype(data-type)对象来指定,每个ndarray只有一种dtype类型。ndarray大小固定,创建好数组时一旦指定好大小,就不会再发生改变。 (2)ndarray属性 ndim:维度数量。 shape:一个表示各维度大小的元组,即数组的形状。 dtype:一个用于说明数组元素数据类型的对象。 size:元素总个数,即shape中各数组相乘。
>> import numpy as np >> a = np.array([ [ [3.4, 5, 6, 8], [3, 2.4, 5, 7] ], [ [2.3, 4, 5, 6], [0.9, 5, 6, 1] ], [ [9, 6.7, 3, 2], [1, 3, 4, 5] ] ]) >> a.ndim # ndarray数组维数 3 >> a.dtype # ndarray数组元素数据类型 dtype('float64') >> a.shape # ndarray数组形状 (3L, 2L, 4L) >> a.size # ndarray数组元素总个数 24
ndarray的shape属性计算:先规范显示出数组,然后层层去中括号对,直到无中括号对,每去一层,一个维度,去掉的一层 [ ]中的元素个数(逗号隔开)即该维度的元素个数。 (3)ndarray的常见创建方式 array函数:接收一个普通的Python序列,转成ndarray。
>> a = np.array([ [ [3.4, 5, 6, 8], [3, 2.4, 5, 7] ], [ [2.3, 4, 5, 6], [0.9, 5, 6, 1] ], [ [9, 6.7, 3, 2], [1, 3, 4, 5] ] ]) >> b = np.array(( ( (1, 2, 3, 4), (4, 5, 6, 7), (9, 3, 4, 5) ), ( (0, 2, 3, 4), (4, 5, 6, 7), (4, 5, 2, 3) ) )) >> c = np.array([ ( (3.4, 4.5, 6, 1), (2.0, 3, 4, 1), (3, 5.6, 3, 1), (0.9, 2, 3, 1) ), ( (1.2, 3, 4, 5), (4.5, 6, 7, 2), (9.8, 2, 3, 1), (2.3, 4, 5, 7) ) ])
zeros函数:创建指定长度或形状的全零数组。
>> np.zeros((3, 4)) array([[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]])
ones函数:创建指定长度或形状的全1数组。
>> np.ones((4, 6)) array([[1., 1., 1., 1., 1., 1.], [1., 1., 1., 1., 1., 1.], [1., 1., 1., 1., 1., 1.], [1., 1., 1., 1., 1., 1.]])
empty函数:创建一个没有任何具体值的数组(准确地说是一些未初始化的垃圾值)。
>> np.empty((2, 3, 4)) array([[[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]], [[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]]])
arange函数:类似于python的range函数,通过指定开始值、终值和步长来创建一维数组,注意数组不包括终值。
>> np.arange(20) array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]) >> np.arange(0, 20, 1) array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]) >> np.arange(0, 20, 2) array([0, 2, 4, 6, 8, 10, 12, 14, 16, 18]) >> np.arange(0, 12).reshape(3, 4) array([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]])
linspace函数:通过指定开始值、终值和元素个数来创建一维数组,可以通过endpoint关键字指定是否包括终值,缺省设置是包括终值。
>> np.linspace(0, 10, 5) array([0., 2.5, 5., 7.5, 10.])
logspace函数:和linspace类似,不过它创建等比数列。
>> np.logspace(0, 2, 5) array([1., 3.16227766, 10., 31.6227766, 100.])
使用随机数填充数组,即使用numpy.random模块的random()函数,数组所包含的元素数量由参数决定。
>> np.random.random((2, 2, 4)) array([[[0.37103481, 0.20852992, 0.06803338, 0.01894039], [0.39276509, 0.72441318, 0.94296218, 0.37620134]], [[0.01425183, 0.64108487, 0.38092613, 0.969736], [0.09067193, 0.26279249, 0.20117111, 0.79920612]]])
(4)Numpy中的数据类型 创建Numpy数组时可以通过dtype属性显式指定数据类型,如果不指定,Numpy会自己推断出合适的数据类型,所以一般无需显式指定。astype方法可以转换数组的元素数据类型,得到一个新数组。
>> ndarray01 = np.array([1, 2, 3, 4]) >> ndarray01.dtype dtype('int32') >> ndarray02 = ndarray01.astype(float) >> ndarray02.dtype dtype('float64')
数值型dtype的命名方式:一个类型名(比如int、float),后面接着一个用于表示各元素位长的数字,比如表中的双精度浮点值,即Python中的float对象,需要占用8个字节(即64位),因此该类型在Numpy中就记为float64。 每种类型有一个相对应的类型代码,即简写方式,参照下面的表格。
数据类型 |
类型代码 |
说明 |
---|---|---|
int_ |
|
默认整型(与C中的long相同,通常为int64或者int32) |
intc |
|
完全等同于C中的long(通常为int64或者int32) |
intp |
|
表示索引的整型,与C中的size_t相同,通常为int64或者int32 |
int8 |
i1 |
字节(-128 ~ 127),1个字节 |
int16 |
i2 |
整型(-32768 ~ 32767),2个字节 |
int32 |
i4 |
整型(-2147483648 ~ 2147483647),4个字节 |
int64 |
i8 |
整型(- 9223372036854775808~9223372036854775807),8字节 |
uint8 |
u1 |
无符号整型(0~255) |
uint16 |
u2 |
无符号整型(0~65535) |
uint32 |
u4 |
无符号整型(0~4294967295) |
uint64 |
u8 |
无符号整型(0 ~ 18446744073709551615) |
float_ |
|
float64的简写形式 |
float16 |
f2 |
半精度浮点型:符号位,5位指数,10位小数部分 |
float32 |
f4或者f |
单精度浮点型:符号位,8位指数,23位小数部分 |
float64 |
f8或者d |
双精度浮点型:符号位,11位指数,52位小数部分 |
float128 |
f16或者g |
扩展精度浮点数 |
complex_ |
c16 |
complex128的简写形式 |
complex64 |
c8 |
复数,由两个32位的浮点数来表示(实数部分和虚数部分) |
complex128 |
c16 |
复数,由两个64位的浮点数来表示(实数部分和虚数部分) |
bool_ |
|
以一个字节形式存储的布尔值(True或者False) |
bool |
? |
存储True和False值的布尔类型 |
object |
O |
Python对象类型 |
String_ |
S |
固定长度的字符串类型(每个字符1个字节)。例如要创建一个字符长度为8的字符串,应使用S8 |
Unicode_ |
U |
固定长度的unicode类型(字节数由平台决定),跟字符串的定义方式一样,比如U20 |
>> d = np.array(["Python", "Scala", "Java", "C#"]) >> d array(['Python', 'Scala', 'Java', 'C#'], dtype='|S6') >> d.dtype dtype('S6') >> e = np.array(["Python", "Scala", "Java", "C++"], dtype=np.string_) >> e array(['Python', 'Scala', 'Java', 'C++'], dtype='|S6') >> e = np.array(["Python", "Scala", "Java", "C++"], dtype='S8') >> e array(['Python', 'Scala', 'Java', 'C++'], dtype='|S8')
(5)改变ndarray的形状 直接修改ndarray的shape值可以改变ndarray的形状。 使用reshape函数也改变ndarray的形状,可以创建一个改变了尺寸的新数组,原数组的shape保持不变,但注意他们共享内存空间,因此修改任何一个也对另一个产生影响,因此注意新数组的元素个数必须与原数组一样。 当指定新数组某个轴的元素为-1时,将根据数组元素的个数自动计算此轴的长度。
>> a = np.arange(0, 20, 2) >> print a >> print a.size [0 2 4 6 8 10 12 14 16 18] 10 >> a.reshape(2, 5) array([[0, 2, 4, 6, 8], [10, 12, 14, 16, 18]]) >> a.reshape(-1, 5) array([[0, 2, 4, 6, 8], [10, 12, 14, 16, 18]]) >> b = a.reshape(2, -1) >> b array([[0, 2, 4, 6, 8], [10, 12, 14, 16, 18]]) >> b.shape() (2L, 5L) >> b.shape = 5, -1 >> b array([[0, 2], [4, 6], [8, 10], [12, 14], [16, 18]])
3、Numpy基本操作
(1)数组与标量的运算、数组之间的运算 数组不用循环即可对每个元素执行批量运算,这通常就叫做矢量化,即用数组表达式代替循环的做法。 矢量化数组运算性能要比纯Python方式快上一两个数量级。 大小相等的数组之间的任何算术运算都会将运算应用到元素级。 数组与标量之间的运算:
>> import numpy as np >> arr1 = np.array([1, 2, 3, 4, 5]) >> arr1 + 2 array([3, 4, 5, 6, 7]) >> arr1 - 2 array([-1, 0, 1, 2, 3]) >> arr1 * 2 array([2, 4, 6, 8, 10]) >> 1/arr1 array([1, 0, 0, 0, 0]) >> from __future__ import division >> 1/arr1 array([1., 0.5, 0.33333333, 0.25, 0.2]) >> arr1 ** 2 array([1, 4, 9, 16, 25])
大小相等的数组之间的运算:
>> arr1 = np.array([[1, 2.0], [1.9, 3.4]]) >> arr2 = np.array([[3.6, 1.2], [2.0, 1.2]]) >> arr1 + arr2 array([[4.6, 3.2], [3.9, 4.6]]) >> arr1 - arr2 array([[-2.6, 0.8], [-0.1, 2.2]]) >> arr1 * arr2 array([[3.6, 2.4], [3.8, 4.08]]) >> arr1 / arr2 array([[0.27777778, 1.66666667], [0.95, 2.83333333]])
像上面例子展现出来的,加、减、乘、除、幂运算等,可以用于数组与标量、大小相等数组之间。在Numpy中,大小相等的数组之间运算为元素级运算,即只用于位置相同的元素之间,所得到的运算结果组成一个新的数组,运算结果的位置跟操作数位置相同。 (2)数组的矩阵积(matrix product) 两个二维矩阵(多维数组即矩阵)满足第一个矩阵的列数与第二个矩阵的行数相同,那么可以进行矩阵乘法,即矩阵积,矩阵积不是元素级的运算。 两个矩阵相乘结果所得到的的数组中每个元素为第一个矩阵中与该元素行号相同的元素与第二个矩阵中与该元素列号相同的元素,两两相乘后求和。
>> arr1 = np.array([ [120, 60, 220], [115, 23, 201], [132, 48, 230] ]) >> arr2 = np.array([ [12.34, 0.04], [204.56, 2.34], [9.89, 0.45] ]) >> arr1.dot(arr2) array([[15930.2, 244.2], [8111.87, 148.87], [13722.46, 221.1]]) >> np.dot(arr1, arr2) array([[15930.2, 244.2], [8111.87, 148.87], [13722.46, 221.1]])
(3)多维数组的索引
>> arr = np.array([ [ [2, 3, 4, 5], [1, 3, 4, 9] ], [ [0, 3, 4, 8], [2, 4, 9, 4] ], [ [1, 4, 5, 8], [2, 5, 6, 8] ], [ [2, 3, 6, 8], [3, 4, 8, 9] ] ]) >> arr.shape (4L, 2L, 4L) >> arr[3] array([[2, 3, 6, 8], [3, 4, 8, 9]]) >> arr[3][1] array([3, 4, 8, 9]) >> arr[3][1][2] 8 >> arr[3, 1, 2] 8
(4)Numpy中数组的切片 在各维度上单独切片,如果某维度都保留,则直接使用 : 冒号,不指定起始值和终止值。
>> arr array([[[2, 3, 4, 5], [1, 3, 4, 9]], [[0, 3, 4, 8], [2, 4, 9, 4]], [[1, 4, 5, 8], [2, 5, 6, 8]], [[2, 3, 6, 8], [3, 4, 8, 9]]]) >> arr[1] array([[0, 3, 4, 8], [2, 4, 9, 4]]) >> arr[1][0] array([0, 3, 4, 8]) >> arr[1][0][1:3] array([3, 4]) >> arr[1][1][1:3] array([4, 9]) >> arr[1, :, 1:3] array([[3, 4], [4, 9]])
注意Numpy中通过切片得到的新数组,只是原来数组的一个视图,因此对新数组进行操作也会影响原数组。 (5)布尔型索引
>> A = np.random.random((2, 2)) >> A array([[0.42349554, 0.52963875], [0.86297436, 0.12893687]]) >> A < 0.5 array([[True, False], [False, True]]) >> A[A < 0.5] # True的位置上的元素取出组成一个新的数组(一维) array([0.42349554, 0.12893687]) >> names = np.array(['Merry', 'Tom', 'John']) >> names array(['Merry', 'Tom', 'John'], dtype='|S5') >> names = np.array('Tom', 'Merry') >> names == 'Tom' array([True, False], dtype=bool) >> scores = np.array([ [98, 87, 45, 34], [88, 45, 23, 98] ]) >> scores[names == 'Tom'] array([[98, 87, 45, 34]]) >> scores[names == 'Tom', 2] array([45]) >> names array(['Tom', 'John', 'Merry', 'Jill'], dtype='|S5') >> scores array([[98, 97, 67, 99], [96, 87, 78, 65], [88, 87, 67, 66], [86, 84, 82, 78]]) >> scores[~((names == 'Tom')|(names == 'Jill'))] array([[96, 87, 78, 65], [88, 87, 67, 66]]) >> scores[(names != 'Tom')&(names != 'Jill')] array([[96, 87, 78, 65], [88, 87, 67, 66]])
(6)花式索引 花式索引(Fancy indexing)指的是利用整数数组进行索引。
>> arr = np.arange(32).reshape(8, 4) >> arr array([[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, 27], [28, 29, 30, 31]]) >> arr[[0, 3, 5]] array([[0, 1, 2, 3], [12, 13, 14, 15], [20, 21, 22, 23]]) >> arr[[0, 3, 5], [0, 3, 2]] array([0, 15, 22]) >> arr[np.ix_([0, 3, 5], [0, 2, 3])] # ix_函数产生一个索引器 array([[0, 2, 3], [12, 14, 15], [20, 22, 23]])
(7)数组转置与轴对换 transpose函数用于数组转置,对于二维数组来说就是行列互换。 数组的T属性,也是转置。
>> arr array([[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, 27], [28, 29, 30, 31]]) >> arr.transpose() array([[0, 4, 8, 12, 16, 20, 24, 28], [1, 5, 9, 13, 17, 21, 25, 29], [2, 6, 10, 14, 18, 22, 26, 30], [3, 7, 11, 15, 19, 23, 27, 31]]) >> arr.T array([[0, 4, 8, 12, 16, 20, 24, 28], [1, 5, 9, 13, 17, 21, 25, 29], [2, 6, 10, 14, 18, 22, 26, 30], [3, 7, 11, 15, 19, 23, 27, 31]])
(8)通用函数:快速的元素级数组函数 ufunc:一种对ndarray中的数据执行元素级运算的函数,也可以看做是简单函数(接受一个或多个标量值,并产生一个或多个标量值)的矢量化包装器。
>> arr = np.arange(10).reshape(2, -1) >> arr array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]) >> np.sqrt(arr) array([[0., 1., 1.41421356, 1.73205081, 2.], [2.23606798, 2.44948974, 2.64575131, 2.82842712, 3.]])
常见的一元通用函数如下:
一元ufunc |
说明 |
---|---|
abs,fabs |
计算整数、浮点数或复数的绝对值。对于非复数值,可以使用更快的fabs |
sqrt |
计算各元素的平方根,相当于 arr ** 0.5 |
square |
计算各元素的平方,相当于arr ** 2 |
exp |
计算各元素的指数e的x次方 |
log, log10, log2, log1p |
分别为自然对数、底数是10的log,底数为2的log,log(1+x) |
sign |
计算各元素的正负号:1 正数,0 零 , -1 负数 |
cell |
计算各元素的ceiling值,即大于等于该值的最小整数 |
floor |
计算各元素的floor值,即小于等于该值的最大整数 |
rint |
将各元素值四舍五入到最接近的整数,保留dtype |
modf |
将数组的小数位和整数部分以两个独立数组的形式返回 |
isnan |
返回一个表示“哪些值是NaN(不是一个数字)”的布尔类型数组 |
isfinite,isinf |
分别返回一个表示“哪些元素是有穷的(非inf,非NaN)”或“哪些元素是无穷的”的布尔型数组 |
cos、cosh、sin、sinh、tan、tanh |
普通型和双曲型三角函数 |
arccos,arccosh,arcsin、arctan、arctanh |
反三角函数 |
logical_not |
计算各元素not x的真值,相当于 ~ 和 -arr |
常见的二元通用函数如下:
二元ufunc |
说明 |
---|---|
add |
将数组中相同位置对应的元素相加 |
substract |
从第一个数组中减去第二个数组中的元素 |
multiply |
数组元素相乘 |
divide、floor_divive |
除法或者向下圆整除法(丢弃余数) |
pow |
对第一个数组中的元素A,根据第二个数组中的相应元素B,计算A的B次方 |
maximum、fmax |
元素级别的最大值,fmax会忽略NaN |
minimum、fmin |
元素级别的最小值,fmin会忽略NaN |
mod |
元素级的求模(除法的余数) |
copysign |
将第二个数组中的值的符号复制给第一个数组中对应位置的值 |
greater、greater_equal、less、less_equal、equal、not_equal |
执行元素级别的比较运算,最终产生布尔型数组 |
logical_and、logical_or、logical_xor |
执行元素级别的布尔逻辑运算,相当于中缀运算符&、|、^ |
(9)聚合函数 聚合函数是对一组值(比如一个数组)进行操作,返回一个单一值作为结果的函数。因此求数组所有元素之和、求所有元素的最大最小值以及标准差的函数就是聚合函数。
>> arr = np.array([1.0, 2.0, 3.0, 4.0]) >> arr.max() 4.0 >> arr.min() 1.0 >> arr.mean() 2.5 >> arr.std() 1.1180339887498949 >> np.sqrt(np.power(arr - arr.mean(), 2).sum() / arr.size) # 计算标准差 1.1180339887498949
聚合函数可以指定对数值的某个轴元素进行操作。
>> arr array([[1, 2, 3, 4], [3, 4, 5, 6]]) >> arr.mean(axis=0) # 对同一列上的元素进行聚合 array([2., 3., 4., 5.]) >> arr.mean(axis=1) # 对同一行上的元素进行聚合 array([2.5, 4.5]) >> arr.sum(axis=0) array([4, 6, 8, 10]) >> arr.max(axis=1) array([4, 6]) >> arr.std(axis=0) array([1., 1., 1., 1.])
(10)np.where函数 np.where函数是三元表达式 x if condition else y
的矢量化版本。
>> xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5]) >> yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5]) >> condition = np.array([True, False, True, True, False]) >> result = [(x if c else y) for x, y, c in zip(xarr, yarr, condition)] >> result [1.1000000000000001, 2.2000000000000002, 1.3, 1.3999999999999999, 2.5] >> result = np.where(condition, xarr, yarr) >> result [1.1000000000000001, 2.2000000000000002, 1.3, 1.3999999999999999, 2.5]
将数组中所有NaN缺失值替换为0。
>> arr = np.array([[1, 2, np.NaN, 4], [3, 4, 5, np.NaN]]) >> arr array([[1., 2., nan, 4.], [3., 4., 5., nan]]) >> np.isnan(arr) array([[False, False, True, False], [False, False, False, True]], dtype=bool) >> np.where(np.isnan(arr), 0, arr) array([[1., 2., 0., 4.], [3., 4., 5., 0.]])
(11)np.unique函数 求数组中不重复的元素。
>> productTypes = np.array(['图书', '数码', '小吃', '美食', '男装', '美食', '女装', '小吃']) >> productTypes array(['xe5x9bxbexe4xb9xa6', 'xe6x95xb0xe7xa0x81', 'xe5xb0x8fxe5x90x83', 'xe7xbex8exe9xa3x9f', 'xe7x94xb7xe8xa3x85', 'xe7xbex8exe9xa3x9f', 'xe5xa5xb3xe8xa3x85', 'xe5xb0x8fxe5x90x83'], dtype='|S6') >> np.unique(productTypes) array(['xe5x9bxbexe4xb9xa6', 'xe6x95xb0xe7xa0x81', 'xe5xb0x8fxe5x90x83', 'xe7xbex8exe9xa3x9f', 'xe7x94xb7xe8xa3x85', 'xe5xa5xb3xe8xa3x85'], dtype='|S6') >> for pt in np.unique(productTypes): print pt 图书 数码 小吃 美食 男装 女装
4、数组数据文件读写
(1)将数组以二进制格式保存到磁盘
>> data array([[1, 2, 3, 4], [2, 3, 4, 5], [6, 7, 8, 9], [2, 4, 5, 6]]) >> np.save('data', data) >> np.load('data.npy') array([[1, 2, 3, 4], [2, 3, 4, 5], [6, 7, 8, 9], [2, 4, 5, 6]])
(2)存取文本文件
>> x = np.arange(0.0, 5.0, 1.0) >> np.savetxt('data.csv', x, delimiter=',') >> np.loadtxt('data.csv', delimiter=',') array([0., 1., 2., 3., 4.]) >> np.genfromtxt('data.csv', delimiter=',') array([0., 1., 2., 3., 4.])