一、数组的创建
数组简介
数组 (array) 是相同类型的元素 (element) 的集合所组成数据结构 (data structure)。numpy 数组中的元素用的最多是「数值型」元素,平时我们说的一维、二维、三维数组长下面这个样子 (对应着线、面、体)。四维数组很难被可视化。
注意一个关键字 axis,中文叫「轴」,一个数组是多少维度就有多少根轴。由于 Python 计数都是从 0 开始的,那么
第 1 维度 = axis 0
第 2 维度 = axis 1
第 3 维度 = axis 2
二、创建数组
带着上面这个对轴的认识,接下来我们用代码来创建 numpy 数组,有三种方式:
步就班的 np.array() 用在列表和元组上
定隔定点的 np.arange() 和 np.linspace()
一步登天的 np.ones(), np.zeros(), np.eye() 和 np.random.random()
按步就班法
给了「列表」和「元组」原材料,用 np.array() 包装一下便得到 numpy 数组。
import numpy as np
l = [3.5, 5, 2, 7, 9]
print(np.array(l))
[3.5 5. 2. 7. 9. ]
定隔定点法
更常见的两种创建 numpy 数组方法:
定隔的 arange:固定元素大小间隔
定点的 linspace:固定元素个数
函数 arange 的参数为起点 , 终点 , 间隔
print(np.arange(8))
print(np.arange(2, 8))
print(np.arange(2, 8, 2))
[0 1 2 3 4 5 6 7]
[2 3 4 5 6 7]
[2 4 6]
函数 linspace 的参数为起点 , 终点 , 点数
linspace (start , stop , num)
其中 start 和 stop 必须要有,num 没有的话默认为 50。对着这个规则看看上面各种情况的输出。
import numpy as np
print(np.linspace(2, 6, 3)) # 均分为三个
t = np.linspace(3, 8, 11)
print(np.linspace(3, 8, 11)
[2. 4. 6.]
[3. 3.5 4. 4.5 5. 5.5 6. 6.5 7. 7.5 8. ]
一步登天法
NumPy 还提供一次性
用 zeros() 创建全是 0 的 n 维数组
用 ones() 创建全是 1 的 n 维数组
用 random() 创建随机 n 维数组
用 eye() 创建对角矩阵 (二维数组)
对于前三种,由于输出是 n 为数组,它们的参数是一个「标量」或「元组类型的形状」,下面三个例子一看就懂了:
print(np.zeros(5))
print(np.ones((2, 3)))
print(np.random.random((2, 3, 4)))
[0. 0. 0. 0. 0.]
[[1. 1. 1.]
[1. 1. 1.]]
[[[0.22776412 0.72046211 0.29758594 0.0923665 ]
[0.2027479 0.53507183 0.61219788 0.50821071]
[0.62908069 0.6038276 0.53519934 0.10484686]]
[[0.20239382 0.34965616 0.22142288 0.56592552]
[0.32693076 0.09853307 0.49262424 0.96626366]
[0.4547175 0.63994587 0.2033189 0.40908813]]]
对于函数 eye(),它的参数就是一个标量,控制矩阵的行数或列数:
此外还可以设定 eye() 里面的参数 k
默认设置 k = 0 代表 1 落在对角线上
k = 1 代表 1 落在对角线右上方
k = -1 代表 1 落在对角线左下方
np.eye(4)
print(np.eye(4, k=1))
np.eye(4, k=-1)
[[0. 1. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]
[0. 0. 0. 0.]]
rray([[0., 0., 0., 0.],
[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.]])
三、 数组的性质
一维数组
用按步就班的 np.array() 带列表生成数组 arr
现在你应该会用 dir(arr) 来查看数组的属性了吧,看完之后我们对 type, ndim, len(), size, shape, stride, dtype 几个感兴趣,一一打印出来看看:
arr = np.array([3.5, 5, 2, 8, 4.2])
print('The type is:', type(arr))
print('The dimension is:', arr.ndim)
print('The length is:', len(arr))
print('The number of elements is:', arr.size)
print('The shape of array is:', arr.shape)
print('The stride of array is:', arr.strides)
print('The type of elements is:', arr.dtype)
The type is: <class 'numpy.ndarray'>
The dimension is: 1
The length is: 5
The number of elements is: 5
The shape of array is: (5,)
The stride of array is: (8,)
The type of elements is: float64
根据结果我们来看看上面属性到底是啥:
type:数组类型,当然是 numpy.ndarray
ndim:维度个数是 1
len():数组长度为 5 (注意这个说法只对一维数组有意义)
size:数组元素个数为 5
shape:数组形状,即每个维度的元素个数 (用元组来表示),只有一维,元素个数为 5,写成元组形式是 (5,)
strides:跨度,即在某一维度下为了获取到下一个元素需要「跨过」的字节数 (用元组来表示),float64 是 8 个字节数 (bytes),因此跨度为 8
dtype:数组元素类型,是双精度浮点 (注意和 type 区分)
注意我黄色高亮了 strides,这个概念对于解决引言的「转置高维数组」问题很重要。一图胜千言。
四、二维数组
先用按步就班的 np.array() 带二维列表生成二维数组 arr2d按步就班的 np.array() 带二维列表生成二维数组 arr2d
l2 = [[1, 2, 3], [4, 5, 6]]
arr2d = np.array(l2)
arr2d
array([[1, 2, 3],
[4, 5, 6]])
print("The typeis :", type(arr2d))
print("The dimension is:", arr2d.ndim)
print("The length of array is:", len(arr2d))
print("The number of elments is:", arr2d.size)
print("The shape of array is:", arr2d.shape)
print("The stride of array is:", arr2d.strides)
print("The type of elements is:", arr2d.dtype)
The typeis : <class 'numpy.ndarray'>
The dimension is: 2
The length of array is: 2
The number of elments is: 6
The shape of array is: (2, 3)
The stride of array is: (12, 4)
The type of elements is: int32
同样,我们来分析一下上面属性:
type:数组类型 numpy.ndarray
ndim:维度个数是 2
len():数组长度为 2 (严格定义 len 是数组在「轴 0」的元素个数)
size:数组元素个数为 6
shape:数组形状 (2, 3)
strides:跨度 (12, 4) 看完下图再解释
dtype:数组元素类型 int32
对于二维数组,Python 视图」看它和「内存块」存储它的形式是不一样的,如下图所示:
n 维数组
我们使用np.random.random()来生成一个多维数组
arr4d = np.random.random((2, 2, 2, 3))
print("The type is:", type(arr4d))
print("The dimension is:", arr4d.ndim)
print("The length of array is:", len(arr4d))
print("The number of elments is:", arr4d.size)
print("The shape of array is:", arr4d.shape)
print("The stride of array is:", arr4d.strides)
print("The type of elments is:", arr4d.dtype)
arr4d
The type is: <class 'numpy.ndarray'>
The dimension is: 4
The length of array is: 2
The number of elments is: 24
The shape of array is: (2, 2, 2, 3)
The stride of array is: (96, 48, 24, 8)
The type of elments is: float64
array([[[[0.12097602, 0.85898477, 0.16043155],
[0.87946725, 0.96358488, 0.4051294 ]],
[[0.65434255, 0.21199418, 0.68517886],
[0.53783086, 0.93971496, 0.14067768]]],
[[[0.93745553, 0.95048614, 0.81013165],
[0.36328647, 0.16424842, 0.96708327]],
[[0.90332958, 0.30356389, 0.50146022],
[0.05044289, 0.49204122, 0.2607904 ]]]])
数组的获取
获取数组是通过索引 (indexing) 和切片 (slicing) 来完成的,
切片是获取一段特定位置的元素
索引是获取一个特定位置的元素
索引和切片的方式和列表一模一样。对于一维数组 arr,
切片写法是 arr[start : stop : step]
索引写法是 arr[index]
因此,切片的操作是可以用索引操作来实现的 (一个一个总能凑成一段),只是没必要罢了。为了简化,我们在本章三节标题里把切片和索引都叫做索引。
索引数组有三种形式,正规索引 (normal indexing)、布尔索引 (boolean indexing) 和花式索引 (fancy indexing)。
正规索引
虽然切片操作可以由多次索引操作替代,但两者最大的区别在于
切片得到的是原数组的一个视图 (view) ,修改切片中的内容会改变原数组
索引得到的是原数组的一个复制 (copy),修改索引中的内容不会改变原数组
请看下面一维数组的例子来说明上述两者的不同。
一维数组
arr = np.arange(10)
arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
二维数组
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
arr2d
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])