0%

numpy学习——01数组创建

本文是根据 DataWhale『巨硬的 numpy』文档进行学习, 辅之以天池平台, 之前学习过, 此时进行进一步的整理, 着重学习未完全熟悉的知识点.

详细的代码我也放入了天池学习的AI训练营合集中

安装和导库

首先是安装 numpy.

1
pip install numpy

然后是导入库, 通常简写为 np.

1
2
3
import numpy as np
# 后续的画图工具
import matplotlib.pyplot as plt

数组的创建和生成

数组 ndarrynumpy 内最基本的数据结构, 本意是: n-dim array, 即 n 维的数组. 判断数组维度, 一般是根据轴 (axis) 来判断, 几个轴就是几维, 这个参数后续再提, 很多方法中会使用. 更简单的, 可以直接数括号 😏.

首先创建和生成 numpy 数组对象 array, 通常我们有几种常见的创建方式:

  • 使用列表或元组
  • 使用 arange
  • 使用 linspace / logspace (常用于绘制坐标轴)
  • 使用 ones / zeros
  • 使用 random (常用于生成一些模拟数据)
  • 从文件读取

使用列表创建

1
2
3
# 使用一个 list
np.array([1,2,3])
# 输出 array([1, 2, 3])

其中存在一个小数 float, 那么 numpy 就会为了保持数据类型相同, 都转换为 float.

1
2
# 使用二维 list (多维类似)
np.array([[1, 2., 3], [4, 5, 6]])

如果指定了 dtype, 输入的值都会被转为对应的类型, 而且不会四舍五入.

1
2
3
4
5
6
7
8
# 也可以直接指定数据类型
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, 通常在需要创建连续一维向量时使用.

1
2
3
4
5
6
7
8
9
# 创建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 会出现错误.

1
2
3
4
5
6
7
8
9
10
# 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 是一个线性序列, 需要三个参数 (开始, 结尾, 数量), 而不是步长, 这里需要注意.
  • logspacelinspace 的扩展, 是指数序列, 需要的参数多了一个 base, 默认是 10.
1
2
3
4
# 线性
np.linspace(0, 9, 10).reshape(2, 5)
# array([[0., 1., 2., 3., 4.],
# [5., 6., 7., 8., 9.]])
1
2
3
4
# 指数 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]])

画图观察一下(黑暗主题可能看不见坐标轴)

1
2
3
4
5
6
7
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.

1
2
3
4
5
# 不能直接用 if 判断 array 是否符合某个条件 cond1
arr = np.array([1, 2, 3])
cond1 = arr > 2
cond1
# array([False, False, True])

如果需要进行 if 判断一些是否存在 true, 或者全部为 true 的, 则需要使用 any()all() 来使用

1
2
3
4
if cond1.any():
print("只要有一个为True就可以,所以——我可以")
if cond1.all():
print("所有值为True才可以,我正好这样")

使用 ones/zeros/full 创建

需要常数值的矩阵时使用, 注意是有 s 的.

  • 零数组
    • zeros()函数: 返回给定形状和类型的零数组.
    • zeros_like()函数: 返回与给定数组形状和类型相同的零数组.
1
2
3
4
5
6
7
8
# 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]])
1
2
# 像给定向量那样的 0 向量(ones_like 是 1 向量)
np.zeros_like(np.ones((2,3,3)))

使用 eye/diag 创建

  • eye()函数: 返回一个对角线上为1, 其它地方为零的单位数组.
  • diag(v, k=0)函数: 提取对角线或构造对角数组, k代表向左上偏移几个单位.
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
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 分布的随机数

1
2
3
4
5
6
# 单个元素
np.random.rand()
# 指定数组大小
np.random.rand(2, 3)
# 与上一个同样, 但传入的是一个list或tuple表示数组形状
np.random.random(size=(3, 2))

指定上下限的随机数.

1
np.random.uniform(-100, 100, size=(2, 3))

使用推荐的 default_rng 的方法.

1
2
3
4
5
6
7
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))

离散均匀分布(整数)

生成随机整数

1
2
3
# 生成不超过10的2个随机整数
np.random.randint(10, size=2)
np.random.randint(20, 30, size=(2, 3))

使用推荐的 default_rng 的方法.

1
2
3
# 离散均匀分布
rng.integers(10, size=2)
rng.integers(0, 10, size=(2, 3))

标准正态分布

1
2
# 标准正态分布
np.random.randn(2, 4)

使用推荐的 default_rng 的方法.

1
2
# 上面推荐的标准正态分布用法
rng.standard_normal(size=(2, 4))

高斯分布

1
2
# 高斯分布
np.random.normal(loc=0, scale=1, size=(3, 5))

使用推荐的 default_rng 的方法.

1
rng.normal(loc=0, scale=1, size=(3, 5))

以上新旧版本的写法其实很类似, 简单来说就是使用了一个生成器 rng 来代替原本的 np.random, 习惯就好.

从文件中读写矩阵

主要是用于加载已经进行预处理的数组或保存好的权重参数, 便于一些耗时项目直接存储, 不需要重复训练.

注意: 此处保存的时候不需要增加后缀, 会自动添加

保存矩阵

1
2
# 直接将给定矩阵存为 a.npy
np.save('./data/a', np.array([[1, 2, 3], [4, 5, 6]]))
1
2
3
4
5
# 可以将多个矩阵存在一起,名为 `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))

读取矩阵

1
2
# 加载单个 array
np.load("data/a.npy")

是否压缩并不影响读取文件.

1
2
3
# 加载多个,可以像字典那样取出对应的 array
arr = np.load("data/b.npz")
arr["a"]

其他创建方法

除了常见的np.array创建新数组, 还可以使用其他方法进行创建.

使用 asarray() 进行创建

array 相同, 都可以将普通的结构数据转化为 ndarray 但是当数据源就是 ndarray时, 且 dtype 不变时, asarray 创建的数组将不会开辟一个新的内存地址, 也就是, 而 array 则都会开辟一个新的内存空间来创建.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 分别代表数组的坐标位置, 所以在绘图时非常方便.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 知识重新回归了一下, 并且学到了一个新的创建数组的方法, 既然是新方法就应该多多适应, 在以后的使用中, 对比其中的优劣.

------ 本文结束------