一、简介
Numpy,全称Numerical Python,是Python的一个强大的数据分析包。相比于list数据结构,Numpy的ndarray数据结构,支持矢量计算、无需遍历即可完成对整组数据的运算。此外,还提供了诸如转置、点积等线代操作。提供了随机数生成、傅里叶变换等功能。最后,Numpy还可以作为Python和C(C++,Fortran等)进行数据交流的基础。
dt = [[1, 2], [3, 4]]
arr = np.array(dt)
# 数组乘数组——对应位置元素相乘
print(arr*arr)
# 数组乘标量——数组每个元素都乘该标量
print(arr*2)
# 输出:[[ 1 4]
# [ 9 16]]
# [[2 4]
# [6 8]]
二、创建ndarray
方法 | 说明 |
array | 将序列数据转换为ndarray,指定类型或自动分析类型 |
asarray | 同上,区别在于输入为ndarray时,不进行复制 |
arange | 使用同range |
ones,ones_like | 生成指定形状值为1的数组 |
zeros,zeros_like | 生成指定形状值为0的数组 |
empty,empty_like | 生成指定形状值不赋初值的数组 |
eye,identity | 根据参数N生成单位矩阵 |
1.np.array创建数组
import numpy as np
dt = [[2, 1], (1, 2)]
arr = np.array(dt)
print(arr)
# 没有指定类型,自动识别
print(arr.dtype)
# 输出:[[2 1]
# [1 2]]
# int32
2.np.array和np.asarray的区别
dt = [[2, 1], (1, 2)]
arr = np.array(dt)
# 不管什么类型序列,都会新建一个数组
arr2 = np.array(arr)
# 传入是数组,则不会再新建
as_arr = np.asarray(arr)
print('原始数组id:{},使用np.array创建数组的id:{},使用np.asarray创建数组的id:{}'.format(id(arr), id(arr2), id(as_arr)))
# 输出:原始数组id:1913281185152,使用np.array创建数组的id:1913281155680,
# 使用np.asarray创建数组的id:1913281185152
3.np.empty创建的并不是空数组
这个方法只是分配了2*3的一个数组空间,未赋值。但并不代表值为空。这些地址空间的值为上一个程序使用之后的结果,存在未知随机风险。
arr = np.empty((2, 3))
print(arr)
# 输出:[[6.23042070e-307 1.89146896e-307 1.42417221e-306]
# [1.37961641e-306 6.23039354e-307 1.69115935e-306]]
4.np.ones_like生成一个和指定数组一样形状的数组,可指定类型
print(arr.shape)
arr = np.ones_like(arr, dtype='f4')
print(arr)
# 输出:(2, 2)
# [[1. 1.]
# [1. 1.]]
类型 | 简写 | 说明 |
int32,uint32 | i4,u4 | 有符号和无符号32位整数(4字节) |
float64 | f8或d | 对应python的float和C的double |
complex128 | c16 | 128位浮点数表示的复数 |
bool | ? | 真假 |
object | O | Python的对象 |
string_ | S | 每个字符占一个字节 |
unicode_ | U | 字节数由平台决定 |
5.类型转换
注意:无论转换前后,数据类型是否一致,都会创建新的对象。
dt = [[2, 1], (1, 2)]
arr = np.array(dt)
print(arr.dtype)
# 自动对应float64
new_arr = arr.astype(float)
print(new_arr.dtype)
# 输出:int32
# float64
三、索引和切片
1.基本索引和切片
(1) 两种方式访问某个元素
dt = [[1, 2], [3, 4]]
arr = np.array(dt)
# 这种访问方式和二维列表一样
print('第一个元素是:', arr[0][0])
# 还可以这样访问
print('还可以这样访问第一个元素:', arr[0, 0])
(2)简单切片
dt = [[1, 2, 3], [4, 5, 6]]
# 列表切片
print(dt[0:1])
arr = np.array(dt)
# 数组切片
print(arr[0:1])
# 输出:[[1, 2, 3]]
# [[1 2 3]]
(3)切片赋值
list的切片会拷贝数据,即修改切片不会改变原列表。同时,赋值号前后应是同样结构的数据。数组的切片则不同。
dt = [1, 2, 3, 4, 5, 6]
arr = np.array(dt)
# 切片所得是两个元素,可以通过广播,赋值为同一个值
arr_slice = arr[1:3]
# 修改切片,原数组也会改变
arr_slice[:] = 0
print(arr)
# 数组:[1 0 0 4 5 6]
2.花式索引
(1)混合索引
dt = [[1, 2, 3], [4, 5, 6]]
arr = np.array(dt)
print('取每一行的第一个元素:', arr[:, 0])
# 输出:取每一行的第一列: [1 4]
(2)布尔型索引
dt = [1, 2, 3, 4, 5, 6]
arr = np.array(dt)
bl = [True, True, False, True, True, True]
print(arr[bl])
# 输出:[1 2 4 5 6]
实际不会这样手动构造包含布尔值的列表,而是采用条件表达式。
dt = [1, 2, 3, 4, 5, 6]
arr = np.array(dt)
print(arr[arr != 3])
(3)花式索引
dt = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
arr = np.array(dt)
print('原数组:\n', arr)
# 取第一行和最后一行
print('取第一行和最后一行:\n', arr[[0, -1]])
# 取对角线元素,相当于取(0,0)(1,1)(2,2)
print('取对角线元素:', arr[[0, 1, 2], [0, 1, 2]])
# 输出:原数组:
# [[1 2 3]
# [4 5 6]
# [7 8 9]]
# 取第一行和最后一行:
# [[1 2 3]
# [7 8 9]]
# 取对角线元素: [1 5 9]
如果想取原数组一个矩形区域,可以使用np.ix_
dt = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
arr = np.array(dt)
print('原数组:\n', arr)
print('取中间2*2的数组:\n', arr[np.ix_([1, 2], [1, 2])])
# 输出:原数组:
# [[ 1 2 3 4]
# [ 5 6 7 8]
# [ 9 10 11 12]
# [13 14 15 16]]
# 取中间2*2的数组:
# [[ 6 7]
# [10 11]]
四、数组函数
数组函数和一般的函数没有什么区别,只是它作用在数组上,是简单函数的矢量化包装。
1.一元函数
函数 | 说明 |
abs,fabs | 绝对值,对于非复数,可使用更高效的fabs |
square | 各元素求平方 |
sigh | 各元素正负号,1:正数,0:零,-1:负数 |
ceil,floor | 向上,向下取整 |
rint | 四舍五入到整数,同时保留dtype |
modf | 将整数和小数部分分开返回 |
isnan | 数组元素要么是数字要么是np.nan |
cos,sin,tan,arcsin | 三角函数 |
(1)向上取整
不小于原数的最小整数,输出数据类型为float
dt = [1.4, 2.6, 3.7, 4.8]
arr = np.array(dt)
# 向上取整,并非四舍五入
print(np.ceil(arr))
# 输出:[2. 3. 4. 5.]
(2)分割整数和小数部分
dt = [1.5, 2.6, 3.7, 4.8]
arr = np.array(dt)
arr_dcm, arr_int = np.modf(arr)
print(arr_dcm, arr_int)
# 输出:[0.5 0.6 0.7 0.8] [1. 2. 3. 4.]
(3)判断是否是数字
元素只能是数字和np.nan,如传入字符串会报错。
dt = [1.5, 2.6, np.nan, 4.8]
arr = np.array(dt)
print(np.isnan(arr))
# 输出:[False False True False]
np.nan并不是“是否为空”的判断,而是一个特殊的float对象。
dt = [1.5, 2.6, None, 4.8]
arr = np.array(dt)
print(np.isnan(arr))
# 输出:TypeError: ufunc 'isnan' not supported for the input types
print(np.nan is None)
print(type(np.nan))
# 输出:False
# <class 'float'>
2.二元函数
函数 | 说明 |
add,subtract,multiply | 元素间加,减,乘 |
divide,floor_divide | 除,向下整除 |
power | a^b |
maximum,fmax | 逐个比较取大值,fmax会忽略nan |
mod | 元素间求模 |
copysign | 将第二组元素符号复制给第一组 |
greater,less_equal | >,<=,返回布尔型数组 |
logical_and,logical_xor | 逻辑与,逻辑异或 |
(1)向下整除
arr = np.array([1, 2, 3, 4])
arr2 = np.array([5, 6, 7, 8])
print(np.floor_divide(arr2, arr))
# 输出:[5 3 2 2]
(2)相同位置元素取大值
arr = np.array([1, 2, 3, np.nan])
arr2 = np.array([5, 6, 7, 8])
print(np.maximum(arr2, arr))
# 输出:[ 5. 6. 7. nan]
(3)复制正负号
arr = np.array([1, 2, 3, 4])
arr2 = np.array([1, -1, 1, -1])
print(np.copysign(arr, arr2))
# 输出:[ 1. -2. 3. -4.]
五、综合使用
1.条件表达np.where
arr = np.array([1, -2, 3, -4])
# 根据条件筛选取数
print(np.where(arr < 0))
# 输出:(array([1, 3], dtype=int64),)
arr = np.array([1, -2, 3, -4])
# 数组中小于0的赋值为0,否则赋值为1
print(np.where(arr < 0, 0, 1))
# 输出:[1 0 1 0]
arr = np.array([1, -2, 3, -4])
# 小于0的元素赋值为0,否则保留原数
print(np.where(arr < 0, 0, arr))
# 输出:[1 0 3 0]
2.数组统计方法
方法 | 说明 |
sum,mean | 求和,求均值 |
std,var | 标准差,方差 |
min,max | 最值 |
argmin,argmax | 最值索引 |
cumsum | 所有元素累计求和 |
cumprod | 所有元素累计求积 |
(1)求和
arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
print(np.sum(arr, axis=0))
# 不指定轴,则求全部元素和
print(arr.sum())
# 输出:[ 6 8 10 12]
# 36
(2)最值索引
arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
# 注意,即使是多维数组,返回的也是数字,而不是位置元组
print(np.argmax(arr))
# 输出:7
3.用于布尔型数组的方法
arr.any():是否存在真元素,arr.all()是否全部为真。注意,0和False为假,非0数字和True为真。
空字符串、空列表、None等不能判断。
arr = np.array([1, False, 3, 4])
# 是否全为真
print(arr.all())
# 输出:False
4.排序
arr = np.array([[1, 5, 3, 9], [6, 4, 7, 8]])
print(np.sort(arr, axis=0))
# 输出:[[1 4 3 8]
# [6 5 7 9]]
5.数组的集合操作
方法 | 说明 |
unique(x) | 返回x中只出现一次的元素数组 |
intersect1d(x,y) | 交集 |
union1d(x,y) | 并集 |
in1d(x,y) | x的元素是否在y中的布尔数组 |
setdiff1d(x,y) | 在x中不在y中的元素 |
setxor1d(x,y) | 在x或y中,但不同时存在于两个数组 |
以上方法的说明已经很明确,就仅以最后一个做示例
arr = np.array([1, 5, 3, 9])
arr2 = np.array([2, 5, 3, 10])
print(np.setxor1d(arr, arr2))
# 输出:[ 1 2 9 10]
6.保存和读取文件
(1)保存文件
arr = np.array([1, 5, 3, 9])
np.save('保存数组.npy', arr)
记事本打开如下图,数据是以原始二进制格式保存的。
(2)保存成压缩文件
arr = np.array([1, 2, 3, 4])
arr2 = np.array([5, 6, 7, 8])
# 关键字key1,key2是自定义的,用于读取数据
np.savez('保存数组.npz', key1=arr, key2=arr2)
(3)保存到文本文件
arr = np.array([[1, 2, 3, 4], [1, 2, 3, 4]], dtype=int)
# 参数分别表示保存文件名,数组,十进制数格式,逗号分割
np.savetxt('保存数组.txt', arr, fmt="%d", delimiter=',')
(3)读取数据
data = np.load('保存数组.npz')
print('数组一的数据为:', data['key1'])
# 输出:数组一的数据为: [1 2 3 4]
# 打开文本文件就是np.loadtxt
data = np.loadtxt('保存数组.txt', delimiter=',')
7.矩阵运算
方法 | 说明 |
dot | 矩阵乘法 |
diagonal | 返回方阵对角线或将一维数组转成方阵 |
trace | 对角线元素之和 |
det | 矩阵行列式 |
inv | 方阵的逆 |
qr | QR分解 |
svd | 奇异值分解 |
solve | 解线性方程组Ax=b,A为方阵 |
(1)矩阵乘法
arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8]], dtype=int)
print('矩阵乘矩阵的转置:\n', arr.dot(arr.T))
# 输出:矩阵乘矩阵的转置:
# [[ 30 70]
# [ 70 174]]
(2)矩阵对角线
arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [1, 2, 3, 4], [5, 6, 7, 8]], dtype=int)
print(arr)
print('矩阵的对角线:\n', arr.diagonal())
# 输出:[[1 2 3 4]
# [5 6 7 8]
# [1 2 3 4]
# [5 6 7 8]]
# 矩阵乘矩阵的转置:
# [1 6 3 8]
8.产生随机数
方法 | 说明 |
permutation | 传入序列则打乱,传入整数则返回打乱的range |
shuffle | 打乱序列 |
rand | [0,1)随机产生一个数,满足均匀分布 |
randint | 给定的范围内随机选取整数 |
randn | 均值0,方差1的正态分布 |
binomail | 二项分布 |
normal | 高斯分布 |
beta | Beta分布 |
chisquare | 卡方分布 |
gamma | Gamma分布 |
uniform | [0, 1)中均匀分布 |
seed | 随机种子 |
(1)打乱序列permutation
data = [1, 2, 3, 4, 5, 6]
arr = np.random.permutation(data)
arr2 = np.random.permutation(6)
print(arr, arr2)
# 输出:[4 3 6 1 2 5] [1 3 2 0 5 4]
(2)给定范围产生一定量随机数
# 1-10之间随机产生2个数
arr = np.random.randint(1, 10, 2)
print(arr)
# 输出:[5 3]
(3)产生一组符合正态分布的数
可以看到,随机产生的这一组数基本满足均值为0,方差为1的正态分布。(数量越大,越接近标准分布)
arr = np.random.randn(1000)
plt.figure(figsize=(10, 8))
plt.hist(arr, bins=20, density=True)
print('均值:{},方差:{}'.format(arr.mean(), arr.std()))
plt.show()
# 输出:均值:-0.0033979715972624175,方差:0.9870680514692056
(4)关于随机数的一个补充
for i in range(3):
np.random.seed(666)
arr = np.random.random()
print('第{}产生的随机数:{}'.format(i+1, arr))
# 输出:第1产生的随机数:0.7004371218578347
# 第2产生的随机数:0.7004371218578347
# 第3产生的随机数:0.7004371218578347
通过上例可以看出,确定了随机种子,那么每次生成的随机数其实是确定的。也就是说,这里的随机不是真的随机。这个特性在某些过程复现中有用。