本文是根据
DataWhale
『巨硬的numpy
』文档进行学习, 辅之以天池平台, 之前学习过, 此时进行进一步的整理, 着重学习未完全熟悉的知识点.详细的代码我也放入了天池学习的AI训练营合集中
安装和导库
首先是安装 numpy
.
pip install numpy
然后是导入库, 通常简写为 np
.
import numpy as np
# 后续的画图工具
import matplotlib.pyplot as plt
数组的创建和生成
数组 ndarry
是 numpy
内最基本的数据结构, 本意是: n-dim array
, 即 n
维的数组. 判断数组维度, 一般是根据轴 (axis)
来判断, 几个轴就是几维, 这个参数后续再提, 很多方法中会使用. 更简单的, 可以直接数括号 😏.
首先创建和生成 numpy
数组对象 array
, 通常我们有几种常见的创建方式:
- 使用列表或元组
- 使用
arange
- 使用
linspace
/logspace
(常用于绘制坐标轴) - 使用
ones
/zeros
- 使用
random
(常用于生成一些模拟数据) - 从文件读取
使用列表创建
# 使用一个 list
np.array([1,2,3])
# 输出 array([1, 2, 3])
其中存在一个小数 float
, 那么 numpy
就会为了保持数据类型相同, 都转换为 float
.
# 使用二维 list (多维类似)
np.array([[1, 2., 3], [4, 5, 6]])
如果指定了 dtype
, 输入的值都会被转为对应的类型, 而且不会四舍五入.
# 也可以直接指定数据类型
lst = [
[1, 2, 3],
[4, 5, 6.8]
]
np.array(lst, dtype=np.int32)
# array([[1, 2, 3],
# [4, 5, 6]])
使用 arange 生成
arange
的原理类似于 python 内置的整数序列生成器 range
, 通常在需要创建连续一维向量时使用.
# 创建0-11的一维向量
np.arange(12)
# array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
# 一般使用 reshape 直接转换为其他形状
np.arange(12).reshape(3, 4)
# array([[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11]])
需要注意的是, reshape
前后的元素个数必须相同, 否则 reshape
会出现错误.
# arange也可以是浮点数, reshape也可以有多个维度
np.arange(10, 40, 2.5).reshape(3, 2, 2)
# array([[[10. , 12.5],
# [15. , 17.5]],
# [[20. , 22.5],
# [25. , 27.5]],
# [[30. , 32.5],
# [35. , 37.5]]])
使用 linspace/logspace 生成
linspace
是一个线性序列, 需要三个参数 (开始, 结尾, 数量), 而不是步长, 这里需要注意.logspace
是linspace
的扩展, 是指数序列, 需要的参数多了一个base
, 默认是10
.
# 线性
np.linspace(0, 9, 10).reshape(2, 5)
# array([[0., 1., 2., 3., 4.],
# [5., 6., 7., 8., 9.]])
# 指数 base 默认为 10
np.logspace(0, 9, 6, base=np.e).reshape(2, 3)
# array([[1.00000000e+00, 6.04964746e+00, 3.65982344e+01],
# [2.21406416e+02, 1.33943076e+03, 8.10308393e+03]])
画图观察一下(黑暗主题可能看不见坐标轴)
N = 20
x = np.arange(N)
y1 = np.linspace(0, 10, N) * 100
y2 = np.logspace(0, 10, N, base=2)
plt.plot(x, y2, '*');
plt.plot(x, y1, 'o');
补充: 关于数组的比较
比较的结果依旧是一个由布尔值组成的 array
.
# 不能直接用 if 判断 array 是否符合某个条件 cond1
arr = np.array([1, 2, 3])
cond1 = arr > 2
cond1
# array([False, False, True])
如果需要进行 if
判断一些是否存在 true
, 或者全部为 true
的, 则需要使用 any()
或 all()
来使用
if cond1.any():
print("只要有一个为True就可以,所以——我可以")
if cond1.all():
print("所有值为True才可以,我正好这样")
使用 ones/zeros/full 创建
需要常数值的矩阵时使用, 注意是有 s
的.
- 零数组
- zeros()函数: 返回给定形状和类型的零数组.
- zeros_like()函数: 返回与给定数组形状和类型相同的零数组.
# zeros 同理
np.ones(shape=(2, 3))
# array([[1., 1., 1.],
# [1., 1., 1.]])
np.full(shape=(2,3), fill_value=666)
# array([[666, 666, 666],
# [666, 666, 666]])
# 像给定向量那样的 0 向量(ones_like 是 1 向量)
np.zeros_like(np.ones((2,3,3)))
使用 eye/diag 创建
- eye()函数: 返回一个对角线上为1, 其它地方为零的单位数组.
- diag(v, k=0)函数: 提取对角线或构造对角数组, k代表向左上偏移几个单位.
x = np.eye(4) # 同 np.identity(4)
print(x)
# [[1. 0. 0. 0.]
# [0. 1. 0. 0.]
# [0. 0. 1. 0.]
# [0. 0. 0. 1.]]
x = np.eye(2, 3)
print(x)
# [[1. 0. 0.]
# [0. 1. 0.]]
x = np.arange(9).reshape((3, 3))
print(x)
# [[0 1 2]
# [3 4 5]
# [6 7 8]]
print(np.diag(x)) # [0 4 8]
print(np.diag(x, k=1)) # [1 5]
print(np.diag(x, k=-1)) # [3 7]
v = [1, 3, 5, 7]
x = np.diag(v,k=1)
print(x)
# [[1 0 0 0]
# [0 3 0 0]
# [0 0 5 0]
# [0 0 0 7]]
使用 random 生成
random
是本章相对重要且常用的内容, 一般用来生成用于测试的随机数组.
但是在1.17
版本后, 更推荐使用新的API来生成, 即使用rng = np.random.default_rng(seed)
来获得 Generator
, 在此基础上再进行各种分布的随机数生成.
连续均匀分布
产生符合 0-1 分布的随机数
# 单个元素
np.random.rand()
# 指定数组大小
np.random.rand(2, 3)
# 与上一个同样, 但传入的是一个list或tuple表示数组形状
np.random.random(size=(3, 2))
指定上下限的随机数.
np.random.uniform(-100, 100, size=(2, 3))
使用推荐的 default_rng
的方法.
rng = np.random.default_rng(10086)
# 连续均匀分布用法
rng.random(size=(2, 3))
# array([[0.52249719, 0.59701344, 0.29232232],
# [0.28906326, 0.63037747, 0.73431908]])
# 推荐的连续均匀分布
rng.uniform(0, 1, size=(2, 3))
离散均匀分布(整数)
生成随机整数
# 生成不超过10的2个随机整数
np.random.randint(10, size=2)
np.random.randint(20, 30, size=(2, 3))
使用推荐的 default_rng
的方法.
# 离散均匀分布
rng.integers(10, size=2)
rng.integers(0, 10, size=(2, 3))
标准正态分布
# 标准正态分布
np.random.randn(2, 4)
使用推荐的 default_rng
的方法.
# 上面推荐的标准正态分布用法
rng.standard_normal(size=(2, 4))
高斯分布
# 高斯分布
np.random.normal(loc=0, scale=1, size=(3, 5))
使用推荐的 default_rng
的方法.
rng.normal(loc=0, scale=1, size=(3, 5))
以上新旧版本的写法其实很类似, 简单来说就是使用了一个生成器 rng
来代替原本的 np.random
, 习惯就好.
从文件中读写矩阵
主要是用于加载已经进行预处理的数组或保存好的权重参数, 便于一些耗时项目直接存储, 不需要重复训练.
注意: 此处保存的时候不需要增加后缀, 会自动添加
保存矩阵
# 直接将给定矩阵存为 a.npy
np.save('./data/a', np.array([[1, 2, 3], [4, 5, 6]]))
# 可以将多个矩阵存在一起,名为 `b.npz`
np.savez("./data/b", a=np.arange(12).reshape(3, 4), b=np.arange(12.).reshape(4, 3))
# 和上一个一样,不过进行了压缩
np.savez_compressed("./data/c", a=np.arange(12).reshape(3, 4), b=np.arange(12.).reshape(4, 3))
读取矩阵
# 加载单个 array
np.load("data/a.npy")
是否压缩并不影响读取文件.
# 加载多个,可以像字典那样取出对应的 array
arr = np.load("data/b.npz")
arr["a"]
其他创建方法
除了常见的np.array
创建新数组, 还可以使用其他方法进行创建.
使用 asarray() 进行创建
与 array
相同, 都可以将普通的结构数据转化为 ndarray
但是当数据源就是 ndarray时
, 且 dtype
不变时, asarray
创建的数组将不会开辟一个新的内存地址, 也就是, 而 array
则都会开辟一个新的内存空间来创建.
x = np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]])
y = np.array(x)
z = np.asarray(x)
x[1][2] = 2
print(x,type(x),x.dtype)
# [[1 1 1]
# [1 1 2]
# [1 1 1]] <class 'numpy.ndarray'> int32
print(y,type(y),y.dtype)
# [[1 1 1]
# [1 1 1]
# [1 1 1]] <class 'numpy.ndarray'> int32
print(z,type(z),z.dtype)
# [[1 1 1]
# [1 1 2]
# [1 1 1]] <class 'numpy.ndarray'> int32
使用 fromfunction() 进行创建
该方法是根据函数来创建数组, 常在绘图时使用. 该函数的参数 x, y
分别代表数组的坐标位置, 所以在绘图时非常方便.
def func(x, y):
return 10 * x + y
x = np.fromfunction(func, (5, 4), dtype=int)
print(x)
# [[ 0 1 2 3]
# [10 11 12 13]
# [20 21 22 23]
# [30 31 32 33]
# [40 41 42 43]]
x = np.fromfunction(lambda i, j: i == j, (3, 3), dtype=int)
print(x)
# [[ True False False]
# [False True False]
# [False False True]]
个人收获
这里将原本的 numpy
知识重新回归了一下, 并且学到了一个新的创建数组的方法, 既然是新方法就应该多多适应, 在以后的使用中, 对比其中的优劣.