代码来自于《Python数据科学手册》的代码复现。
来自和鲸科技(科赛)的K-lab项目
先介绍几类基本的数组操作
- 数组的属性
确定数组的大小、形状、存储大小,数据类型 - 数据的索引
获取和设置组各个元素的值 - 数组的切分
在大的数组中获取或设置更小的子数组。 - 数组的变形
改变给定数组的形状。 - 数组的拼接和分裂
将多个数组合并为一个,以及讲一个数组分裂成多个
NumPy数组的属性
先介绍一些有用的数组,定义三个随机的数组:一个一维数组、一个二维数组和一个三维数组。将用NumPy的随机生成器设置一组种子值,以确保每次程序执行时都可以生成同样的随机数组:
import numpy as np
np.random.seed(0) # 设置随机数种子
x1 = np.random.randint(10, size = 6) # 一维数组
x2 = np.random.randint(10, size = (3, 4)) # 二维数组
x3 = np.random.randint(10, size = (3, 4, 5)) # 三维数组
每个数组都有ndim(数组的维度): shape(数组每个维度的大小): size(数组的总大小): 数组的数据类型: 每个数组元素字节大小的itemsize: 数组总字节大小:
print("x3 ndim:", x3.ndim)
print("x3 shape:", x3.shape)
print("x3 size:", x3.size)
print("x3 dtype:", x3.dtype)
print("x3 itemsize:", x3.itemsize, "bytes")
print("x3 nbytes:", x3.nbytes, "bytes")
x3 ndim: 3
x3 shape: (3, 4, 5)
x3 size: 60
x3 dtype: int64
x3 itemsize: 8 bytes
x3 nbytes: 480 bytes
一般来说可以认为: nbytes = itemsize * size
数组索引
和Python的列表一样,在一维数组中,可以使用括号指定索引第i个值(从0开始计数)
x1
array([5, 0, 3, 3, 7, 9])
x1[1]
0
x1[4]
7
为了获取数组的末尾索引,可以用负值索引:
x1[-1]
9
x1[-4]
3
在多维数组中,可以使用分隔的索引元素获取元素:
x2
array([[3, 5, 2, 4],
[7, 6, 8, 8],
[1, 6, 7, 7]])
x2[0, 0]
3
x2[2, 0]
1
x2[2, -3]
6
也可以用来修改元素值:
x2[0, 0] = 12
x2
array([[12, 5, 2, 4],
[ 7, 6, 8, 8],
[ 1, 6, 7, 7]])
和python列表不同,NumPy数组是固定类型的
x1[0] = 3.1234 # 将会自动截断成整型,而且不会有任何的提醒
x1
array([3, 0, 3, 3, 7, 9])
数组切片
之前一个是获取单个的数组元素,也可以利用切片(slice)来获取数组的子数组,切片用冒号(:)表示,NumPy切片的语法和Python列表的标准切片语法相同。 语法: X = [start : stop : step]
如果三个参数都没有指定,则默认值start = 0, stop = 维度的大小(size of dimension), step = 1
.
一维子数组
x = np.arange(10)
x
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
截取前五个元素
x[: 5]
array([0, 1, 2, 3, 4])
后五个元素
x[5 :]
array([5, 6, 7, 8, 9])
中间的子数组
x[4 : 7]
array([4, 5, 6])
每隔一个元素
x[: : 2]
array([0, 2, 4, 6, 8])
每隔一个元素,索引从0开始
x[1 : : 2]
array([1, 3, 5, 7, 9])
所有元素逆序
x[: : -1]
array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])
从索引5开始,每隔一个元素逆序
x[5 : : -2]
array([5, 3, 1])
多维数组
x2
array([[12, 5, 2, 4],
[ 7, 6, 8, 8],
[ 1, 6, 7, 7]])
显示两行,三列
x2[: 2, : 3]
array([[12, 5, 2],
[ 7, 6, 8]])
所有行,每隔一列
x2[: 3, :: 2]
array([[12, 2],
[ 7, 8],
[ 1, 7]])
子数组的维度也是可以逆序的:
x2[:: -1, ::-1]
array([[ 7, 7, 6, 1],
[ 8, 8, 6, 7],
[ 4, 2, 5, 12]])
获取数组行和列
将索引和切片结合起来可以实现获取单行或者单列。
x2的第一列
print(x2[:, 0])
[12 7 1]
x2[:, 0]
array([12, 7, 1])
x2的第一行
print(x2[0, :])
[12 5 2 4]
在获取行时,可以省略空的切片:
print(x2[0]) # 等同于x2[0, :]
[12 5 2 4]
非副本视图的子数组
print(x2)
[[12 5 2 4]
[ 7 6 8 8]
[ 1 6 7 7]]
从中抽取一个 2 X 2 的子数组:
print(x2[: 2, : 2])
[[12 5]
[ 7 6]]
x2_sub = x2[: 2, : 2]
print(x2_sub)
[[12 5]
[ 7 6]]
x2_sub[0, 0] = 99
print(x2)
[[99 5 2 4]
[ 7 6 8 8]
[ 1 6 7 7]]
这种方式是非常有用的,在处理非常大的数据是, 可以获取或者处理这些数据集的片段,而不是复制底层的数据缓存
创建数组的副本
x2_sub_copy = x2[: 2, :2].copy()
print(x2_sub_copy)
[[99 5]
[ 7 6]]
这个和视图不同的是修改这个子数组,原始的数组也不会发生改变
x2_sub_copy[0, 0] = 10
print(x2_sub_copy)
[[10 5]
[ 7 6]]
print(x2)
[[99 5 2 4]
[ 7 6 8 8]
[ 1 6 7 7]]
数组的变形
数组变换最灵活的方式是通过reshape()函数来实现的。 例如,如果你希望将数字1-9放入一个3X3的矩阵中,可以采用如下方法:
grid = np.arange(1, 10).reshape((3, 3))
print(grid)
[[1 2 3]
[4 5 6]
[7 8 9]]
另一个常见的变形模式是将一个以为数组转变为二维的行或列的矩阵。也可以通过reshape
方法来实现,或者更简单的在一个切片操作中利用newaxis
关键字:
x = np.array([1, 2, 3])
# 通过变形获得行向量
x.reshape((1, 3))
array([[1, 2, 3]])
# 通过newaxis获得行向量
x[np.newaxis, :]
array([[1, 2, 3]])
# 通过变形获得的列向量
x.reshape((3, 1))
array([[1],
[2],
[3]])
# 通过newaxis获得列向量
x[:, np.newaxis]
array([[1],
[2],
[3]])
数组拼接和分裂
数组的拼接
拼接和连接NumPy中的两个数组主要由np.concatenate
、np.vstack
和np.hstack
x = np.array([1, 2, 3])
y = np.array([3, 2, 1])
np.concatenate([x, y])
array([1, 2, 3, 3, 2, 1])
可以同时拼接多个数组:
z = [55, 99, 299]
print(np.concatenate([x, y, z]))
[ 1 2 3 3 2 1 55 99 299]
也可以拼接二维数组:
grid = np.array([[1, 2, 3], [4, 5, 6]])
沿第一个轴拼接
np.concatenate([grid, grid])
array([[1, 2, 3],
[4, 5, 6],
[1, 2, 3],
[4, 5, 6]])
沿第二个轴拼接
np.concatenate([grid, grid], axis = 1)
array([[1, 2, 3, 1, 2, 3],
[4, 5, 6, 4, 5, 6]])
沿着固定维度处理数组时,使用np.vstack(垂直栈)和np.hstack(水平栈)函数会更简洁:
x = np.array([1, 2, 3])
grid = np.array([[9, 8, 7], [6, 5, 4]])
# 垂直栈数组
np.vstack([x, grid])
array([[1, 2, 3],
[9, 8, 7],
[6, 5, 4]])
# 水平栈数组
y = np.array([[99], [99]])
np.hstack([grid, y])
array([[ 9, 8, 7, 99],
[ 6, 5, 4, 99]])
np.dstack
将沿第三个维度拼接数组。
数组的分裂
分裂可以通过np.split
、np.hsplit
和np.vsplit
函数来实现。
x = [2, 3, 4, 5, 6, 8]
x1, x2, x3 = np.split(x, [3, 5])
print(x1, x2, x3)
[2 3 4] [5 6] [8]
grid = np.arange(16).reshape((4, 4))
grid
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
upper, lower = np.vsplit(grid, [2])
print(upper)
print(lower)
[[0 1 2 3]
[4 5 6 7]]
[[ 8 9 10 11]
[12 13 14 15]]
left, right = np.hsplit(grid, [2])
print(left)
print(right)
[[ 0 1]
[ 4 5]
[ 8 9]
[12 13]]
[[ 2 3]
[ 6 7]
[10 11]
[14 15]]
np.dsplit
分割三维数组