一、形状操作
1.改变数组的形状
注意:下列的命令是返回一个修改过后的数组,但并不会修改原数组
import numpy as np
def f(x, y):
return 5*x+y
a = np.fromfunction(f,(3, 4), dtype=int) #先创建一个新数组
print(a)
print(a.ravel()) #将数组中元素排成一列输出
print(a.reshape(2, 6)) #将数组的形状重塑,不改变原数组
print(a.T) #将矩阵数组转置
print(a) #再次打印原数组,验证其并未被改变
#输出:
[[ 0 1 2 3]
[ 5 6 7 8]
[10 11 12 13]]
[ 0 1 2 3 5 6 7 8 10 11 12 13]
[[ 0 1 2 3 5 6]
[ 7 8 10 11 12 13]]
[[ 0 5 10]
[ 1 6 11]
[ 2 7 12]
[ 3 8 13]]
[[ 0 1 2 3]
[ 5 6 7 8]
[10 11 12 13]]
这里辨析一下reshape函数与resize函数:前者是返回修改后的数组,并不会改变原数组的大小;而后者是直接改变了原数组的大小。
print(a.reshape(2, 6)) #将数组的形状重塑
print(a) #验证原数组未被改变
a.resize(4, 3) #直接改变原数组的大小
print(a) #验证原数组已被改变
#输出:
[[ 0 1 2 3 5 6]
[ 7 8 10 11 12 13]]
[[ 0 1 2 3]
[ 5 6 7 8]
[10 11 12 13]]
[[ 0 1 2]
[ 3 5 6]
[ 7 8 10]
[11 12 13]]
2.数组矩阵的堆叠
堆叠的方式大致分为行堆叠vstack()和列堆叠hstack()两种,个人理解为拼接方式的不同。
另外,column_stack()函数比较特殊,对于二维数组的堆叠,其功能类似于hstack()函数,但对于一维数组的堆叠,会将一维转为二维且其功能与hstack()完全不同了,具体见下方代码示例:
import numpy as np
a = np.array([[4, 8], [2, 3]])
b = np.array([[1, 9], [5, 7]])
print(np.vstack((a, b))) #将a,b数组行堆叠(上下拼接)
print(np.hstack((a, b))) #将a,b数组列堆叠(左右拼接)
print(np.column_stack((a, b))) #将a,b数组列堆叠,对于二维数组的堆叠类似hstack函数
c = np.array([4, 2])
d = np.array([5, 7])
print(np.vstack((c, d))) #行堆叠
print(np.hstack((c, d))) #注意这里堆叠后仍是一维数组
print(np.column_stack((c, d))) #将c,d一维数组转置后列堆叠且一维变二维
#输出:
[[4 8]
[2 3]
[1 9]
[5 7]]
[[4 8 1 9]
[2 3 5 7]]
[[4 8 1 9]
[2 3 5 7]]
[[4 2]
[5 7]]
[4 2 5 7]
[[4 5]
[2 7]]
注意一点:hstack()对于一维数组堆叠后仍为一维数组(不仔细看,很容易以为变成二维)
3.将一个数组拆分成几个较小的数组
运用hsplit函数,可以将数组沿水平轴方向切割成几个较小的数组,方法是指定要返回的形状相等的数组的数量,或者指定应该在其之后进行分割的列。具体见下方代码示例:
import numpy as np
a = np.floor(10*np.random.random((2, 12)))
print(a)
print(np.hsplit(a, 3)) #将a数组分成大小相等的三块数组
print(np.hsplit(a, (3, 5))) #以第四列到第五列为切割线,分隔a数组
#输出:
[[5. 2. 3. 7. 5. 9. 7. 1. 8. 2. 0. 7.]
[2. 6. 3. 4. 2. 5. 6. 9. 3. 3. 5. 4.]]
[array([[5., 2., 3., 7.],
[2., 6., 3., 4.]]), array([[5., 9., 7., 1.],
[2., 5., 6., 9.]]), array([[8., 2., 0., 7.],
[3., 3., 5., 4.]])]
[array([[5., 2., 3.],
[2., 6., 3.]]), array([[7., 5.],
[4., 2.]]), array([[9., 7., 1., 8., 2., 0., 7.],
[5., 6., 9., 3., 3., 5., 4.]])]
另外,除了hsplit(),还有vsplit():沿垂直轴切割,方法类似。
二、拷贝与视图
1.完全不复制
import numpy as np
a = np.arange(12)
b = a #最简单的“完全不复制”的复制
print(b is a) #验证b与a实质上指向同一个数组
b.shape = (3, 4) #改变b的大小
print(a.shape) #验证b的改变会影响到a
print(a)
#输出:
True
(3, 4)
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
我的理解是b就是数组a的别名(类似C语言中的引用)改变b数组的大小或者元素都会影响到a数组
2.视图(浅拷贝)
import numpy as np
a = np.arange(12).reshape(3, 4)
c = a.view() #创建一个a的视图
print(c is a) #验证c与a指向的对象是不同
c.shape = (2, 6)
print(a.shape) #验证改变c的大小不会影响数组a的大小
c[0, 4] = 1234
print(a) #验证改变c中元素会影响数组a中的元素
#输出:
False
(3, 4)
[[ 0 1 2 3]
[1234 5 6 7]
[ 8 9 10 11]]
对于视图,我的理解是改变视图的大小不会影响到原数组的大小,但改变视图中的具体元素则会影响到原数组中的元素。
3.深拷贝
这个很好理解,copy()函数可以生成原数组的副本,且两者完全不相关联,互不干扰。
import numpy as np
a = np.arange(12).reshape(3, 4)
d = a.copy()
print(d is a)
d.shape = (2, 6)
print(a.shape)
d[0, 0] = 999
print(a)
#输出:
False
(3, 4)
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
有时,如果不再需要原始数组,则可在切片后调用 copy
。例如,假设a是一个巨大的中间结果(需要将其删除释放),最终结果b只包含a的一小部分,那么在用切片构造b时应该做一个深拷贝:
import numpy as np
a = np.arange(int(1e8)) #巨大的中间结果a
b = a[:100].copy() #最终结果只取其中一部分,可用copy()
del a #这样删除a就无法影响到b