NumPy(Numerical Python)是一个开源Python科学计算库,是Python生态圈最重要的底层支持库,支持快速的数组运算,比纯Python代码快得多,因为它底层使用到c来写。NumPy是在Python中进行数据分析、机器学习、人工智能开发的必备工具,是理解学习很多Python工具包的基础。
安装 : pip install numpy # Anaconda已包含np,无需安装。
numpy数组和 list列表运算时间对比示例
import numpy as np # 引入惯例, 后续都用 np
lst = list(range(1000000)) # 列表
ar = np.arange(1000000) # np数组
In: %timeit lst2 = [x*2 for x in lst]
80.8 ms ± 438 µs per loop # 列表耗时
In: %timeit ar2 = ar * 2
1.96 ms ± 34.7 µs per loop # np数组耗时
注:%魔术命令只能在IPython对话窗口运行,不能放入.py程序中。
数组对象特性
x = np.array((90, 85, 80)) # 成绩
w = np.array((0.4, 0.4, 0.2)) # 权重
np.mean(x) #求平均值
np.average(x, weights=w) # 按权重求均值
np.sum(x * w) / np.sum(w) # 自行按权重求均值,(x*w)会对应的元素相乘
In: b = np.array((10, 9.5, 10.2, 11, 10.8, 10.6, 11.2)) # 收盘价
In: np.diff(b) # 差值, 也可写为 b[1:] - b[:-1] ,这样写就是后面一个元素减去前面一个元素
Out: array([-0.5, 0.7, 0.8, -0.2, -0.2, 0.6])
In: ret = np.diff(b) / b[:-1] # 涨跌率
Out: array([-0.05, 0.07, 0.08, -0.02, -0.02, 0.06])
In: np.where(ret>0) # 正收益是哪几天
Out:(array([1, 2, 5], dtype=int64),)
In: np.__version__ # 版本号'1.16.5',一般的软件包都有
# arange产生6个数,reshape重设为 2x3 数组
In: b = np.arange(6).reshape(2, 3)
In: type(b) # numpy.ndarray
# 数组对象的各种特性
In: b.ndim # 数组维度 2
In: b.size # 数组中的元素个数 6, 和len(b) 不同,len(b)显示的是数组行数
In: b.shape # 数组形状 (2, 3)
In: b.dtype # 数组元素的数据类型 dtype('int32')
In: b.itemsize # 单个元素所需存储字节数 4
In: b.nbytes # 整个数组所需存储字节数 24 , 即 size * itemsize
生成数组的五种方法
#使用array()/arange()生成数组
In:b = np.array((1, 2, 3, 4)) # 一维
Out: array([1, 2, 3, 4])
In:b = np.arange(1, 5) # np.arange(1,5,0.5) 小数步长
Out: array([1, 2, 3, 4])
In: np.repeat(b,2) # 将元素重复2次
Out:array([1, 1, 2, 2, 3, 3, 4, 4])
In: np.tile(b, 3) # 将数组重复3次
Out: array([1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4])
In:b = np.array([[1, 2] , [3, 4], [5, 6]]) # 二维
In:b=np.arange(4000).reshape(4, 1000) # 二维
#可以使用ones()/zeros()/linspace()生成数组。
np.ones(5) # 生成含有5个元素的一维全1数组
np.ones((2, 3)) # 生成2x3的二维全1数组
np.zeros((2, 3)) # 生成2x3的二维全0数组
np.ones_like(b) # 生成维数和b相同的全1数组
np.eye(3) # 单位矩阵数组
np.linspace(1, 2, 5) # [1, 2] 间均匀分布5个数
Out: array([1. , 1.25, 1.5 , 1.75, 2. ]) # 默认含终值
np.linspace(1, 2, 5, endpoint=False) # 不含终值
Out:array([1. , 1.2, 1.4, 1.6, 1.8])
x = np.linspace(0, np.pi, 7) # [0, π] 间均匀分布7个数, 即间隔 30度
np.sin(x) # np.round(np.sin(x), 2)
#使用随机函数生成数组。
ar = np.random.rand(3,4) # 3x4数组, [0, 1)内均匀分布小数
np.set_printoptions(precision=3) # 小数部分显示3位
np.random.randint(1,100,5) # [1,100)内5个随机整数
np.random.randint(1,100,(2,3)) # 2x3 数组
np.random.randn(10,20) # 10x20的标准正太 N(0,1)数组
# 符合正太分布N(1, 4) 的 3x4 数组
x = np.random.normal(loc=1, scale=2, size=(3, 4))
x.std() , x.var() , x.mean() # 标准差, 方差, 均值
在创建数组时可以用dtype参数指定数据类型。已有的数组可以用astype()方法转换类型。数组元素的类型一般应相同,列表的元素类型允许不同。
In:b=np.arange(5) # 默认 'int32' 类型
In: b.dtype
Out: dtype('int32')
In: b[0] = 2**40 # 将报错,整数超范围
In:b=np.arange(5, dtype='float') # 指定为 'float'
In: b.dtype
Out: dtype('float64')
In: c=b.astype('int') # 转为int,b不变,得到新的c
In: c.dtype
Out: dtype('int32')
In: c.astype('bool') # 转为bool型,非0对应True
Out: array([False, True, True, True, True])
基本索引和切片操作
np数组可按类似列表访问的语法,单个索引或切片访问。
b=np.arange(5,10) # array([5,6,7,8,9])
b[1], b[-1], b[:2], b[1:3] # (6, 9, array([5, 6]), array([6, 7]))
b[[0,1,3]] # 花式索引 array([5,6,8]), 这里的索引结果是复制
重要:数组切片和列表切片不同,前者是视图,后者是复制。
b=np.arange(5,10) # array([5,6,7,8,9])
c=b[0:3] # 产生视图,b,c指向同一内存块
c[0]=100 # 此修改将同时影响 b 和 c
c # array([100,6,7])
b # array([100,6,7,8,9])
c = b[0:3].copy() # 复制数组,这样 b, c分离,不会再互相影响
二维数组
b=np.arange(12).reshape(3,4)
Out:array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
b[1, 2] # 6,也可写为 b[1][2]
b[1, 1:3] # array([5,6])
b[1] # array([4,5,6,7])
b[:,1] # array([1,5,9])
b[1:3, 1:4] # array([5,6,7],
[9,10,11])
b[ [0,1,2], [3,2,1] ] # array([3, 6, 9]), 将行/列坐标分别排列,花式索引,花式索引是复制操作
布尔索引
筛选出True值对应位置的数据。
np.random.seed(7)
b = np.random.randint(40, 100, size=10) # 生成10个的随机整数
Out: array([87, 44, 65, 94, 43, 59, 63, 79, 68, 97])
c = b < 60 # 生成一个布尔数组c
Out: array([False, True, False, False, True, True, False, False, False, False])
b[c] # 利用布尔索引取数据,得到<60的数据
Out: array([44, 43, 59])
b[(b>=60) & (b<=80)] # 显示60~80间的数据, 此处用&, 不能用and
b[(b<60) | (b>90)] # 显示<60 或 >90的数据,此处用|, 不能用or
b[~(b<60)] # ~非, 显示 >=60的数据
b=np.array((60, 70, 75, 90, 68, 89)) # 成绩
c=np.array((1 , 1 , 1, 2 , 2, 2)) # 班级
b[c==1].mean() # 1班平均成绩
x=np.array((70, 75, 92, 68, 89)) # 评分1
y=np.array((75, 69, 93, 70, 85)) # 评分2
np.where(x>y, x, y) # 取x/y两两比较的较大值
#where函数是当x>y时取x,不然取y,作用效果和np.maximum(x,y)一样
np.where(x>y, 1, 0) # x>y则取1, 否则取0
数组和单个数据的运算
数组和单个数据可直接运算,不需要编写循环程序处理。
b=np.arange(5) # array([0, 1, 2, 3, 4])
b+2 # array([2, 3, 4, 5, 6])
数组和数组的运算
a1=np.arange(5) # array([0, 1, 2, 3, 4])
a2=np.arange(5,10) # array([5, 6, 7, 8, 9])
a1+a2 # array([5, 7, 9, 11, 13])
a1=np.arange(4) # 一维 (4,) array([0, 1, 2, 3])
a2=np.arange(12).reshape(3,4) # 二维(3,4)
a1+a2
array([[ 0, 2, 4, 6],
[ 4, 6, 8, 10],
[ 8, 10, 12, 14]])
上例中的a1是一维数组(4,),a2是3x4的二维数组,两者的维数不同。
a1的维数较小,NumPy在a1的维数前面加1补齐,可近似认为a1的维数变为(1,4)。
现在a1的第0维是1,a2的第0维是3,NumPy将把a1的第0维扩充为3,
相当于将a1的维数变为(3,4),可认为a1在内存中被调整为如下形状:
array([[0, 1, 2, 3],
[0, 1, 2, 3],
[0, 1, 2, 3]]
这样,a1和a2的形状一致,就可以完成 a1+a2的运算。
广播规则
a1=np.array((1,2,3)) a2=np.array((3,4)).reshape(2,1)
形状为(3,) 和2x1的两个数组可运算,结果数组形状为2x3。
先是(1,3)和(2,1),然后变成(2,3)和(2,3)
形状为3x1 和1x2的两个数组可运算,结果数组形状为3x2。
形状为2x3 和3x2的两个数组不满足规则,无法计算。2x3和4x3的数组也无法广播计算。
结果数组是(3,3),但是(2,3)和(3,2)都无法变成(3,3)
数组自带sort()排序方法,按从小到大排列。
np.random.seed(7)
b=np.random.randint(1, 20, size=10)
Out: array([16, 5, 4, 8, 15, 9, 15, 11, 9, 8])
c=b.copy() # 备份
b.sort() # 排序后将改变数组b
b=c.copy() # 从备份中恢复b
np.sort(b) # np.sort不改变数组本身,返回新的有序数组
x=np.argsort(b) # 将返回一个代表原数据顺序的有序下标数组
Out: array([2, 1, 3, 9, 5, 8, 7, 4, 6, 0], dtype=int64)
b[x] # array([ 4, 5, 8, 8, 9, 9, 11, 15, 15, 16]),花式索引
数组排序不支持列表的reverse=True参数,要从大到小排如下所示:
b[np.argsort(-b)] # 注意 -b
Out: array([16, 15, 15, 11, 9, 9, 8, 8, 5, 4])
多维数组排序
多维数组排序时可指定 axis=0(列) / 1(行),排序时默认按最大轴排序。
np.random.seed(7)
b=np.random.randint(1, 50, size=15).reshape(3, 5)
Out:array([[48, 5, 26, 4, 20],
[24, 40, 29, 15, 24],
[ 9, 26, 47, 43, 27]])
np.sort(b) # 默认按axis=1 ,在水平方向上排序
Out:array([[ 4, 5, 20, 26, 48],
[15, 24, 24, 29, 40],
[ 9, 26, 27, 43, 47]])
np.sort(b, axis=0) # 在竖直方向上排序
Out:array([[ 9, 5, 26, 4, 20],
[24, 26, 29, 15, 24],
[48, 40, 47, 43, 27]])
numpy提供了很多ufunc通用函数,可一次性对整个数组进行计算,无须编写循环处理。格式:np.函数名(数组)
常用函数
有max()、min()、ptp()、sum()、var()、std()等
np.random.seed(7)
b = np.random.randint(1, 20, size=8) # array([16, 5, 4, 8, 15, 9, 15, 11])
np.max(b), np.min(b) ,np.mean(b) # 最大值、最小值、平均值
np.ptp(b) # ptp返回数据极差12,即最大值-最小值
np.quantile(b,[0.25,0.5,0.75])
# 返回分位点对应的分位数,array([ 7.25, 10. , 15. ]),
#这个的意思是占了这个数组的0.25的前面数字是比7.25小的数字,
#占了0.5的是数组比10小的数字
一些函数也可写为"数组.函数名()"的形式
b.max(), b.min(), b.ptp(), b.sum(), b.mean()
b.var(), b.std() , np.median(b) # 方差、标准差、中位数
b.argmax(), b.argmin() # 最大值、最小值所对应的索引下标
二维数组
对于二维数组,函数计算时还可指定计算的轴axis=0/1。
b = np.arange(10).reshape(2, 5)
Out[7]:
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
np.sum(b) # 未指定轴,计算所有数据的和
Out: 45
np.sum(b, axis=0) # 指定轴axis=0,行, 沿竖直方向求和
Out: array([ 5, 7, 9, 11, 13])
np.sum(b, axis=1) # 指定轴axis=1,列, 沿水平方向求和
Out: array([10, 35])
缺失值
nan表示缺失值,如数组中含有nan ,则函数运算结果为nan。
b = np.array([1, 2, np.nan, np.nan, 3]) # 构造含有nan值的数组
np.sum(b) # 含有nan值的数组,运算后返回nan
np.isnan(b) # isnan()测试是否nan值
Out: array([False, False, True, True, False])
np.isnan(b).sum() # 计算nan值个数,True被视为1 , 结果 2
b[~np.isnan(b)] # 取出非nan的值
np.nan == np.nan # nan值很特殊,结果为False
c = np.arange(1, 6) # array([1,2,3,4,5])
np.prod(c) # 累乘 120
np.cumprod(c) # 累乘并给出中间结果 array([1,2,6,24,120])
np.cumsum(c) # 累加并给出中间结果
Out: array([1,3,6,10,15], dtype=int32)
b=np.array([1,3,3,5,5,7,8])
np.unique(b) # 返回不重复的元素值 array([1, 3, 5, 7, 8])
np.all(b) # b所有元素都为非0值则返回True
np.any(b) # b有任意元素为非0值则返回True
NumPy还提供实现矩阵乘法运算的dot()函数。
a=np.arange(6).reshape(2, 3)
b=np.arange(6, 12).reshape(3, 2)
np.dot(a, b) # 2x3和3x2数组相乘,结果为2x2数组。写为 a @ b 亦可
Out:
array([[ 28, 31],
[100, 112]])
np.random.seed(7) # 设置随机数种子
np.random.rand(2,3) # 2x3小数数组
b=np.random.randint(1,100, 20) # 返回 [1,100)内20个随机整数
np.random.choice(b, 5) # 从b中随机抽5个数据
card=np.arange(1, 55) # 一副牌
np.random.shuffle(card) # 乱序 (随机洗牌)
np.argwhere(card==54) # 查找数据54的位置,array([[2]]
x = ['A', 'B', 'C']
np.random.permutation(x) # 返回一个排列
Out: array(['C', 'A', 'B'], dtype=')
< : 小端对齐(intelCPU), U :Unicode编码 1:1个字符长
# 补充 Python中的排列和组合
import itertools
list(itertools.permutations(x)) # 返回所有可能的排列
list(itertools.combinations(x, 2)) # x中选2个,返回所有可能组合