左滑查看目录
形状操纵
我们在创建二维数组的过程中,借助
reshape()
函数,将一维数组转换为矩阵 。
>>> a = np.random.random(12)>>> aarray([ 0.77841574, 0.39654203, 0.38188665, 0.26704305, 0.27519705, 0.78115866, 0.96019214, 0.59328414, 0.52008642, 0.10862692, 0.41894881, 0.73581471])>>> A = a.reshape(3, 4)>>> Aarray([[ 0.77841574, 0.39654203, 0.38188665, 0.26704305], [ 0.27519705, 0.78115866, 0.96019214, 0.59328414], [ 0.52008642, 0.10862692, 0.41894881, 0.73581471]])
>>> a = np.random.random(12)
>>> a
array([ 0.77841574, 0.39654203, 0.38188665, 0.26704305, 0.27519705,
0.78115866, 0.96019214, 0.59328414, 0.52008642, 0.10862692,
0.41894881, 0.73581471])
>>> A = a.reshape(3, 4)
>>> A
array([[ 0.77841574, 0.39654203, 0.38188665, 0.26704305],
[ 0.27519705, 0.78115866, 0.96019214, 0.59328414],
[ 0.52008642, 0.10862692, 0.41894881, 0.73581471]])
reshape()
函数返回一个新的数组,因此可以创建新对象。但是,如果要通过修改形状来修改对象,则必须将包含新尺寸的元组直接分配给它的
形状
attribute。
>>> a.shape = (3, 4)>>> aarray([[ 0.77841574, 0.39654203, 0.38188665, 0.26704305], [ 0.27519705, 0.78115866, 0.96019214, 0.59328414], [ 0.52008642, 0.10862692, 0.41894881, 0.73581471]])
>>> a.shape = (3, 4)
>>> a
array([[ 0.77841574, 0.39654203, 0.38188665, 0.26704305],
[ 0.27519705, 0.78115866, 0.96019214, 0.59328414],
[ 0.52008642, 0.10862692, 0.41894881, 0.73581471]])
这次是改变形状的起始数组,并且没有返回对象。逆运算也是可能的,即,您可以使用
ravel()
函数将二维数组转换为一维数组。
>>> a = a.ravel()array([ 0.77841574, 0.39654203, 0.38188665, 0.26704305, 0.27519705, 0.78115866, 0.96019214, 0.59328414, 0.52008642, 0.10862692, 0.41894881, 0.73581471])
>>> a = a.ravel()
array([ 0.77841574, 0.39654203, 0.38188665, 0.26704305, 0.27519705,
0.78115866, 0.96019214, 0.59328414, 0.52008642, 0.10862692,
0.41894881, 0.73581471])
甚至在这里直接作用于数组本身的
shape
属性。
>>> a.shape = (12)>>> aarray([ 0.77841574, 0.39654203, 0.38188665, 0.26704305, 0.27519705, 0.78115866, 0.96019214, 0.59328414, 0.52008642, 0.10862692, 0.41894881, 0.73581471])
>>> a.shape = (12)
>>> a
array([ 0.77841574, 0.39654203, 0.38188665, 0.26704305, 0.27519705,
0.78115866, 0.96019214, 0.59328414, 0.52008642, 0.10862692,
0.41894881, 0.73581471])
另一个重要的操作是转置矩阵,该矩阵将列与行反转。NumPy启用了
transpose()
函数即可提供此功能 。
>>> A.transpose()array([[ 0.77841574, 0.27519705, 0.52008642], [ 0.39654203, 0.78115866, 0.10862692], [ 0.38188665, 0.96019214, 0.41894881], [ 0.26704305, 0.59328414, 0.73581471]])
>>> A.transpose()
array([[ 0.77841574, 0.27519705, 0.52008642],
[ 0.39654203, 0.78115866, 0.10862692],
[ 0.38188665, 0.96019214, 0.41894881],
[ 0.26704305, 0.59328414, 0.73581471]])
阵列操作
通常,我们需要使用已创建的数组再创建一个数组。
下面,你将看到如何通过联接或拆分已定义的数组来创建新数组。
连接数组
可以合并多个阵列以形成一个包含所有阵列的新阵列。NumPy使用 堆栈
的概念,为此提供了许多功能。
例如,可以使用
vstack()
函数执行垂直堆栈,该函数将第二个数组合并为第一个数组的新行。
在这种情况下,阵列沿垂直方向生长。
相比之下,
hstack()
函数执行水平堆叠。也就是说,第二个数组将添加到第一个数组的列中。
>>> A = np.ones((3, 3))>>> B = np.zeros((3, 3))>>> np.vstack((A, B))array([[ 1., 1., 1.], [ 1., 1., 1.], [ 1., 1., 1.], [ 0., 0., 0.], [ 0., 0., 0.], [ 0., 0., 0.]])>>> np.hstack((A,B))array([[ 1., 1., 1., 0., 0., 0.], [ 1., 1., 1., 0., 0., 0.], [ 1., 1., 1., 0., 0., 0.]])
>>> A = np.ones((3, 3))
>>> B = np.zeros((3, 3))
>>> np.vstack((A, B))
array([[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.],
[ 0., 0., 0.],
[ 0., 0., 0.],
[ 0., 0., 0.]])
>>> np.hstack((A,B))
array([[ 1., 1., 1., 0., 0., 0.],
[ 1., 1., 1., 0., 0., 0.],
[ 1., 1., 1., 0., 0., 0.]])
另外两个函数的多个阵列之间进行堆叠是
column_stack()
一个 第二
row_stack()
。这些功能与之前的两个功能不同。通常,这些功能与一维数组一起使用,这些维数组按列或行堆叠以形成新的二维数组。
>>> a = np.array([0, 1, 2])>>> b = np.array([3, 4, 5])>>> c = np.array([6, 7, 8])>>> np.column_stack((a, b, c))array([[0, 3, 6], [1, 4, 7], [2, 5, 8]])>>> np.row_stack((a, b, c))array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
>>> a = np.array([0, 1, 2])
>>> b = np.array([3, 4, 5])
>>> c = np.array([6, 7, 8])
>>> np.column_stack((a, b, c))
array([[0, 3, 6],
[1, 4, 7],
[2, 5, 8]])
>>> np.row_stack((a, b, c))
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
分割阵列
在上面,我们了解了如何通过堆叠组装多个阵列。现在,我们将学习如何将数组分为几个部分。在NumPy中,我们可以使用拆分来执行此操作。这里也一样,我们有一组功能的工作水平都与
hsplit()
函数和垂直与
vsplit()
函数。
>>> A = np.arange(16).reshape((4, 4))>>> Aarray([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15]])
>>> A = np.arange(16).reshape((4, 4))
>>> A
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
因此,如果要水平拆分数组,这意味着将数组的宽度分为两部分,则4x4矩阵A将被拆分为两个2x4矩阵。
>>> [B,C] = np.hsplit(A, 2)>>> Barray([[ 0, 1], [ 4, 5], [ 8, 9], [12, 13]])>>> Carray([[ 2, 3], [ 6, 7], [10, 11], [14, 15]])
>>> [B,C] = np.hsplit(A, 2)
>>> B
array([[ 0, 1],
[ 4, 5],
[ 8, 9],
[12, 13]])
>>> C
array([[ 2, 3],
[ 6, 7],
[10, 11],
[14, 15]])
相反,如果要垂直拆分数组,这意味着将数组的高度分为两部分,则4x4矩阵A将拆分为两个4x2矩阵。
>>> [B,C] = np.vsplit(A, 2)>>> Barray([[0, 1, 2, 3], [4, 5, 6, 7]])>>> Carray([[ 8, 9, 10, 11], [12, 13, 14, 15]])
>>> [B,C] = np.vsplit(A, 2)
>>> B
array([[0, 1, 2, 3],
[4, 5, 6, 7]])
>>> C
array([[ 8, 9, 10, 11],
[12, 13, 14, 15]])
一个更复杂的命令是
split()
函数,该函数允许您将数组拆分为非对称部分。将数组作为参数传递时,还必须指定要分割的零件的索引。
如果使用option
axis = 1
,则索引将是列;否则,索引将是列。
相反,如果选项是
axis = 0
,那么它们将是行索引。
例如,如果要将矩阵分为三个部分,其中第一部分将包括第一列,第二部分将包括第二和第三列,第三部分将包括最后一列,则必须在中指定三个索引以下方式。
>>> [A1,A2,A3] = np.split(A,[1,3],axis=1)>>> A1array([[ 0], [ 4], [ 8], [12]])>>> A2array([[ 1, 2], [ 5, 6], [ 9, 10], [13, 14]])>>> A3array([[ 3], [ 7], [11], [15]])
>>> [A1,A2,A3] = np.split(A,[1,3],axis=1)
>>> A1
array([[ 0],
[ 4],
[ 8],
[12]])
>>> A2
array([[ 1, 2],
[ 5, 6],
[ 9, 10],
[13, 14]])
>>> A3
array([[ 3],
[ 7],
[11],
[15]])
你也可以逐行执行相同的操作。
>>> [A1,A2,A3] = np.split(A,[1,3],axis=0)>>> A1array([[0, 1, 2, 3]])>>> A2array([[ 4, 5, 6, 7], [ 8, 9, 10, 11]])>>> A3array([[12, 13, 14, 15]])
>>> [A1,A2,A3] = np.split(A,[1,3],axis=0)
>>> A1
array([[0, 1, 2, 3]])
>>> A2
array([[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> A3
array([[12, 13, 14, 15]])
该特征还包括functionaliti 所述的
vsplit()
和
hsplit()
函数。
Numpy的一般概念
下面内容描述了NumPy库的基本概念。副本和视图之间的区别在于它们返回值的时间。本节还介绍了在许多NumPy函数中隐式发生的广播机制。
对象的副本或视图
稍微细心一点,你在使用NumPy时就会发现,在处理数组时,你可以返回数组的副本或视图。
Numpy分配均不产生数组的副本,也不包含其中的任何元素。
>>> a = np.array([1, 2, 3, 4])>>> b = a>>> barray([1, 2, 3, 4])>>> a[2] = 0>>> barray([1, 2, 0, 4])
>>> a = np.array([1, 2, 3, 4])
>>> b = a
>>> b
array([1, 2, 3, 4])
>>> a[2] = 0
>>> b
array([1, 2, 0, 4])
如果将一个数组
a
分配给另一个数组
b
,则不是要复制它。数组
b
只是调用数组
a的
另一种方法。实际上,通过更改第三个元素的值,您也可以更改
b
的第三个值。切片数组时,返回的对象是原始数组的视图。
>>> c = a[0:2]>>> carray([1, 2])>>> a[0] = 0>>> carray([0, 2])
>>> c = a[0:2]
>>> c
array([1, 2])
>>> a[0] = 0
>>> c
array([0, 2])
如上,即使在切片时,实际上也指向同一对象。如果要生成完整且独特的数组,请使用
copy()
函数。
>>> a = np.array([1, 2, 3, 4])>>> c = a.copy()>>> carray([1, 2, 3, 4])>>> a[0] = 0>>> carray([1, 2, 3, 4])
>>> a = np.array([1, 2, 3, 4])
>>> c = a.copy()
>>> c
array([1, 2, 3, 4])
>>> a[0] = 0
>>> c
array([1, 2, 3, 4])
在这种情况下,即使更改 数组
a中的项目
,数组
c
仍保持不变。
向量化
Vectoriz ation和广播是NumPy内部实现的基础。
矢量化是指在开发代码的过程中没有明确的循环。
这些循环实际上是不能省略的,而是在内部实现,然后在代码中被其他构造所取代。
矢量化的应用使得代码更加简洁易读,可以说在外观上会显得更加 "Pythonic"。事实上,得益于向量化,很多操作都有了更加数学化的表达方式。
例如,NumPy允许你表达两个数组的乘法,如下所示:
a * b
甚至两个矩阵:
A * B
在其他语言中,此类操作将通过许多嵌套循环和
for
构造来表达。例如,第一个操作将以以下方式表示:
for (i = 0; i c[i] = a[i]*b[i];}
for (i = 0; i c[i] = a[i]*b[i];}
矩阵的乘积表示如下:
for(i = 0; i for(j = 0; j c [i] [j] = a [i] [j] * b [i] [j];}
for(i = 0; i for(j = 0; j c [i] [j] = a [i] [j] * b [i] [j];}