NumPy 是一个 Python 包。 它代表 “Numeric Python”。 它是一个由多维数组对象和用于处理数组的例程集合组成的库。
数据类型 ndarray
全称 N-dimensiona array, N维数组, ndarray 是一个具有矢量算术运算和复杂广播能力的快速且节省空间的多维数据
.
ndarray 是一种由相同元素组成的多维数组,元素数量是事先指定好的
元素的类型由 dtype 对象来指定,每个 ndarray 只有一种 dtype 类型
大小固定,创建好数组时一旦指定好大小,就不会再发生改变
``
ndarray 属性
ndim: 维度数量
shape: 数组的形状, 是一个表示各维度大小的元组
dtype: 表示数组元素类型的对象
size: 数组中元素的总个数,是 shape 中各维度数量相乘得到的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import numpy as nparr = np.array([ [ [1 , 2 , 4 , 9 ], [2 , 5 , 6 , 8 ], ], [ [4 , 2 , 7 , 1 ], [9 , 5 , 6 , 5 ], ], [ [3 , 9 , 6 , 9 ], [2 , 7 , 2 , 8 ], ] ]) arr.ndim arr.dtype arr.shape arr.size
ndarray 常见的创建方式 array 函数 接收一个普通的 python 序列, 转成 ndarray
1 2 arr = np.array([1, 2, 3, 4]) arr = np.array((1, 2, 3, 4))
zeros 函数 创建指定长度或形状的全零数组
1 2 3 4 5 arr = np.zeros((3, 4)) # 创建的数组如下,zeros 的参数其实就是要创建的数组的 shape 值, # array([[0., 0., 0., 0.], # [0., 0., 0., 0.], # [0., 0., 0., 0.]])
ones 函数 创建指定长度或形状的全 1 数组
1 2 3 4 5 arr = np.ones((3, 4)) # array([[1., 1., 1., 1.], # [1., 1., 1., 1.], # [1., 1., 1., 1.]])
empty 函数 创建一个没有任何具体值的数组, 所以创建速度会比 zeros 和 ones 快, 但这种方式需要手动指定所有值,慎用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # 在测试中发现一个有趣的现象,如果在 empty() 中指定的 shape 在之前已经被 zeros 或 ones 指定过,然么 empty 会直接使用 zeros 或 ones 的值 arr = np.empty((2, 2)) # array([[4.9e-324, 9.9e-324], # [1.5e-323, 2.0e-323]]) arr = np.ones((2, 2)) # array([[1., 1.], # [1., 1.]]) arr = np.empty((2, 2)) # array([[1., 1.], # [1., 1.]])
arange 函数 类似于 python 中的 range 函数,通过指定开始值,终值和步长来创建一维数组,注意不包括终值
1 2 3 4 5 6 7 8 9 10 11 12 arr = np.arange(6) # array([0, 1, 2, 3, 4, 5]) # 同 python 的 range 类似, 第三个参数是步长 arr = np.arange(0, 7, 2) # array([0, 2, 4, 6]) # 可以使用 reshape 把一维数组转为多维 arr = np.arange(12).reshape(3, 4) # array([[ 0, 1, 2, 3], # [ 4, 5, 6, 7], # [ 8, 9, 10, 11]])
linespace 函数 通过指定开始值,终值和元素个数来创建一维数组(等差数列),可以通过 endpoint 关键字指定是否包括终值, 默认包括
1 2 3 # 第三个参数表示元素个数 arr = np.linspace(0, 10, 5) # array([ 0. , 2.5, 5. , 7.5, 10. ])
logspace 函数 和 linespace 类似,不过它创建等比数列
1 2 arr = np.logspace(0, 2, 3) # array([ 1., 10., 100.])
random() 函数生成数组 1 2 3 arr = np.random.random((2, 2)) # array([[0.34659492, 0.83549888], # [0.83618173, 0.0771643 ]])
改变 ndarray 的形状的 1. 直接修改 ndarray 的 shape 值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 arr = np.arange(10) # array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) arr.shape # (10,) arr.shape = 2, 5 arr # array([[0, 1, 2, 3, 4], # [5, 6, 7, 8, 9]]) arr.shape = -1, 2 arr # array([[0, 1], # [2, 3], # [4, 5], # [6, 7], # [8, 9]])
2. 使用 reshape 函数 1 2 3 4 5 6 7 8 arr = np.arange(9) # array([0, 1, 2, 3, 4, 5, 6, 7, 8]) arr.shape # (9,) arr.reshape(3, 3) arr # array([[0, 1, 2], # [3, 4, 5], # [6, 7, 8]])
注意: 当指定新数组某个轴的元素为 -1 时, 将根据数组元素的个数自动计算此轴的长度
numpy 中的数据类型 创建 ndarray 数据时,可以通过 dtype 属性显示指定数据类型, 如果不指定, numpy 会自动推断出合适的数据类型,所以一般无需显示指定
astype 方法,可以转换数组的元素数据类型, 得到一个新数组
数据类型
类型代码
说明
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)
unint64
u8
无符号整型 (0 ~ 18446744073709551615)
float_
float64 的简写形式
float16
f2
半精度浮点型: 符号位, 5 位指数, 10 位小数部分
float32
f4 或者 f
单精度浮点数:32位,符号位, 8 位指数, 23 位小数部分
float64
f8 或者 d
双精度浮点数:64位,符号位, 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个字节) eg.创建一个长度为 8 的字符串使用 S8
Unicode_
U
固定长度的 unicode 类型(字节数由平台决定), 跟字符串的定义方式一样, eg U8
1 2 3 4 5 6 7 8 9 10 arr = np.array(["python", "scala", "javascript", "lua"]) arr.dtype # dtype('<U10') arr = np.array(["python", "scala", "javascript", "lua"], dtype='S6') # array([b'python', b'scala', b'javasc', b'lua'], dtype='|S6') 注意有元素被截断 arr.dtype # dtype('S6') ## 在需要指定类型的时候,推荐使用 np 对象内提供的类型 arr = np.array(["python", "scala", "javascript", "lua"], dtype=np.string_) # array([b'python', b'scala', b'javascript', b'lua'], dtype='|S10')
numpy 基本操作 数组与标量, 数组之间的运算
数组不用循环即可对每个元素执行批量运算,这通常就叫做矢量化,即用数组表达式代替循环的做法
矢量化数组运算性能要比纯 python 方式快一两个数量级
大小相等的数组之间的任何算术运算都会将运算应用到元素级
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 32 33 34 35 36 37 38 39 40 import numpy as np ## 数组与标量之间的运算 arr = np.arange(1, 6) # array([1, 2, 3, 4, 5]) arr + 2 # array([3, 4, 5, 6, 7]) arr - 2 # array([-1, 0, 1, 2, 3]) arr * 2 # array([ 2, 4, 6, 8, 10]) arr / 2 # array([0.5, 1. , 1.5, 2. , 2.5]) arr ** 2 # array([ 1, 4, 9, 16, 25]) ## shape 相同的数组之间的运算 arr1 = np.arange(4).reshape(2, 2) # array([[0, 1], # [2, 3]]) arr2 = np.arange(1, 5).reshape(2, 2) # array([[1, 2], # [3, 4]]) arr1 + arr2 # array([[1, 3], # [5, 7]]) arr1 * arr2 # array([[ 0, 2], # [ 6, 12]]) arr1 / arr2 # array([[0. , 0.5 ], # [0.66666667, 0.75 ]])
数组的矩阵积(matrix product)
两个多为矩阵(通常是二维)满足第一个矩阵的列数与第二个矩阵的行数相同,那么可以进行矩阵乘法
两个矩阵相乘结果所得到的数据中每个元素为,第一个矩阵中与该元素行号相同的元素与第二个矩阵中与该元素列号相同的元素,两两相乘后的求和
eg. 假设有一下两张表格,要求计算每个部门购买产品的总价格和总占用空间
\
产品A
产品B
产品C
部门1
2
4
6
部门2
1
3
5
部门3
2
3
4
\
单价
单件体积
产品A
6
5
产品B
4
3
产品C
2
1
分别计算每个部分的用花费和总体积:
部门1 总花费: 2 x 6 + 4 x 4 + 6 x 2 = 40 总体积: 2 x 5 + 4 x 3 + 6 x 1 = 28 部门2 总花费: 1 x 6 + 3 x 4 + 5 x 2 = 28 总体积: 1 x 5 + 3 x 3 + 5 x 1 = 19 部门3 总花费: 2 x 6 + 3 x 4 + 4 x 2 = 32 总体积: 2 x 5 + 3 x 3 + 4 x 1 = 23
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # 用 numpy 的矩阵积计算 arr1 = np.array([ [2, 4, 6], [1, 3, 5], [2, 3, 4] ]) arr2 = np.array([ [6, 5], [4, 3], [2, 1] ]) # 直接就可以计算出满足需求的结果 res = np.dot(arr1, arr2) # array([[40, 28], # [28, 19], # [32, 23]])
数组的索引与切片 多维数组的索引 对于二维数组的索引如表中所示,其他维度的数组索引与此类似 横轴\ 纵轴| 0 | 1 | 2 | 3 —|—|—|—|— 0 | [0, 0] | [0, 1] | [0, 2] | [0, 3] 1 | [1, 0] | [1, 1] | [1, 2] | [1, 3] 2 | [2, 0] | [2, 1] | [2, 2] | [2, 3]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 arr = np.arange(12).reshape(2, 2, 3) # array([[[ 0, 1, 2], # [ 3, 4, 5]], # # [[ 6, 7, 8], # [ 9, 10, 11]]]) arr[1] # array([[ 6, 7, 8], # [ 9, 10, 11]]) arr[1][1] # array([ 9, 10, 11]) arr[1, 1] # array([ 9, 10, 11]) arr[1, 1, 2] # 11
布尔型索引 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 32 33 34 35 36 37 38 39 arr = np.arange(12).reshape(2, 2, 3) # array([[[ 0, 1, 2], # [ 3, 4, 5]], # # [[ 6, 7, 8], # [ 9, 10, 11]]]) arr < 5 # array([[[ True, True, True], # [ True, True, False]], # # [[False, False, False], # [False, False, False]]]) arr[arr < 5] # array([0, 1, 2, 3, 4]) 中 True 位置上取出的元素组成新的数组 names = np.array(['zj', 'zk', 'zl']) scores = np.array([ [98, 97, 96], [89, 79, 69], [77, 66, 55] ]) # 取出 'zj' 对应的分数 scores[names == 'zj'] # array([[98, 97, 96]]) # 取出 'zj' 对应的分数的第二项分数 scores[names=='zj', 1] # array([97]) # numpy 中可以使用 逻辑与 &, 逻辑或 |, 逻辑非 ~ # 取出不是 zj 的分数 scores[~(names == "zj")] # array([[89, 79, 69], # [77, 66, 55]])array([[89, 79, 69], # [77, 66, 55]])
花式索引 指的是利用整数数组进行索引
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 arr = np.arange(12).reshape(6, 2) # array([[ 0, 1], # [ 2, 3], # [ 4, 5], # [ 6, 7], # [ 8, 9], # [10, 11]]) # 取第一维度的第 0, 3, 5 这几个元素 arr[[0, 3, 5]] # array([[ 0, 1], # [ 6, 7], # [10, 11]]) # 取第一维度的第 0 个元素中的 第 1 个元素,第 3 个元素中的第 0 个元素,第 4 个元素中的第 1 个元素 arr[[0, 3, 5], [1, 0 ,1]] # array([ 1, 6, 11]) # ix_函数产生一个索引器 ix = np.ix_([0, 3, 5], [1, 0, 1]) # (array([[0],[3],[5]]), array([[1, 0, 1]])) arr[ix] # array([[ 1, 0, 1], # [ 7, 6, 7], # [11, 10, 11]])
numpy 数组的切片
在各维度上单独切片,如果某维度都要保留,则直接使用 :
冒号, 不指定起始值和终止值
numpy 中通过切片得到的新数组,只是原来数组的一个视图, 因此对新数组进行操作也会影响原数组
1 2 3 4 5 6 7 8 9 10 11 12 13 arr = np.arange(12).reshape(2, 2, 3) # array([[[ 0, 1, 2], # [ 3, 4, 5]], # # [[ 6, 7, 8], # [ 9, 10, 11]]]) arr[1][1][1:] # array([10, 11]) arr[0, : , 1:] # array([[1, 2], # [4, 5]])
数组转置 transpose 函数和数组的 T 属性用于数组转置,对于二维数组就是行列互换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 arr = np.arange(12).reshape(3, 4) # array([[ 0, 1, 2, 3], # [ 4, 5, 6, 7], # [ 8, 9, 10, 11]]) arr.transpose() # array([[ 0, 4, 8], # [ 1, 5, 9], # [ 2, 6, 10], # [ 3, 7, 11]]) arr.T # array([[ 0, 4, 8], # [ 1, 5, 9], # [ 2, 6, 10], # [ 3, 7, 11]])
通用函数: 快速的元素级数组函数 ufunc: 一种对 dnarray 中的数据执行元素级运算的函数,也可以看做是简单函数(接受一个或多个标量值,并产生一个或多个标量值)的矢量化包装器
常见的一元通用函数
一元 ufunc
说明
abs, 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
计算大于等于该值的最小正数
floor
计算小于等于该值的最大整数
rint
将各元素值四舍五入到最接近的整数, 保留 dtype
modf
将数组的小数位和整数部分以两个独立数组的形式返回
isnan
返回一个表示 “哪些值是 NaN” 的布尔类型数组
isfinite, isinf
分别返回一个表示 “哪些元素是有穷的(非 inf, 非 nan)” 或 “哪些元素是无穷的” 的布尔型整数
cos, cosh, sin, sinh, tan, tanh
普通型和双曲型三角函数
arccos, arccosh, arc, sin, 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
执行元素级别的布尔逻辑运算, 相当于 &,
聚合函数 聚合函数是对一组值(比如一个数组)进行操作,返回一个单一值作为结果的函数.
1 2 3 4 5 6 arr = np.array([1, 2, 3, 4]) arr.max() #最大值 4 arr.min() # 最小值 1 arr.mean() # 均值 2.5 arr.std() # 标准差 1.118033988749895 np.sqrt(np.power(arr - arr.mean(), 2).sum()/arr.size) # 标准差 1.118033988749895
聚合函数可以指定对数值的某个轴元素进行操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 arr = np.array([ [1, 2, 3, 4], [5, 6, 7, 8] ]) # 对同一列上的元素计算均值, axis=0 表示横轴, axis=1 表示纵轴 arr.mean(axis=0) # array([3., 4., 5., 6.]) # 对同一行上的元素计算均值 arr.mean(axis=1) # array([2.5, 6.5]) # 同一列求和 arr.sum(axis=0) # array([ 6, 8, 10, 12]) # 同一行求最大值 arr.max(axis=1) # array([4, 8]) # 同一列求标准差 arr.std(axis=0) # array([2., 2., 2., 2.])
np.where 函数 三元表达式 x if condition else y
的矢量化版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 xarr = np.array([1, 2, 3, 4, 5]) yarr = np.array([2, 3, 4, 5, 6]) condition = np.array([True, False, True, False, True]) result = [(x if c else y) for x, y, c in zip(xarr, yarr, condition)] # [1, 3, 3, 5, 5] result = np.where(condition, xarr, yarr) # array([1, 3, 3, 5, 5]) ## 将数组中所有的 NaN 缺失值替换为 0 arr = np.array([ [1, 2, np.NaN, 4], [3, 4, 5, np.NaN] ]) condition = np.isnan(arr) result = np.where(condition, 0, arr) # array([[1., 2., 0., 4.], # [3., 4., 5., 0.]])
np.unique 函数 求数组中去掉重复元素后的一维数组
1 2 3 4 5 6 arr = np.array([ ['a', 'a', 'c', 'd', 'h', 'c', 'f'], ['a', 'a', 'c', 'd', 'h', 'c', 'f'] ]) result = np.unique(arr) # array(['a', 'c', 'd', 'f', 'h'], dtype='<U1')
数组数据文件读写 将数组以二进制格式保存到磁盘 1 2 3 4 5 6 7 8 9 10 11 12 13 arr = np.arange(12).reshape(3, 4) # array([[ 0, 1, 2, 3], # [ 4, 5, 6, 7], # [ 8, 9, 10, 11]]) # 可以存储到本地文件名为 'arr', 后缀名为 '.npy' 的文件中, np.save('arr', arr) # 读取二进制文件 arr = np.load('arr.npy') # array([[ 0, 1, 2, 3], # [ 4, 5, 6, 7], # [ 8, 9, 10, 11]])
存取文本文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 arr = np.arange(12, dtype=np.int).reshape(3, 4) # array([[ 0, 1, 2, 3], # [ 4, 5, 6, 7], # [ 8, 9, 10, 11]]) # 转存为 'csv' 格式文件 np.savetxt('arr.csv', arr, delimiter=',', fmt="%s") # 从 csv 文件中读取 arr = np.loadtxt('arr.csv', delimiter=',', dtype=np.int) # array([[ 0, 1, 2, 3], # [ 4, 5, 6, 7], # [ 8, 9, 10, 11]]) # 从 csv 文件中读取 arr = np.genfromtxt('arr.csv', delimiter=',', dtype=np.int) # array([[ 0, 1, 2, 3], # [ 4, 5, 6, 7], # [ 8, 9, 10, 11]])
使用 genformtxt 还可以指定 header, 假设有文件内容如下的 test.csv 文件:
1 2 3 4 A,B,C,D 0,1,2,3 4,5,6,7 8,9,10,11
可以这样灵活操作:
1 2 3 4 5 6 arr = np.genfromtxt("test.csv", delimiter=",", names=True, dtype=np.int) # array([(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11)], # dtype=[('A', '<i8'), ('B', '<i8'), ('C', '<i8'), ('D', '<i8')]) arr['A'] # array([0, 4, 8]) arr[0] # (0, 1, 2, 3)