python数组列表操作简记三之numpy广播机制
一、numpy数组常用的两种类型转换
1.1numpy数组与图片文件互转
再进行图像处理时,首先需要将图片文件转换为numpy数组,最后将输出结果保存为图片,这个功能可以使用Opencv完成,也可以使用PIL软件包的Image类完成,代码如下:
from PIL import Image
import numpy as np
#图片文件转换为numpy数组
image = Image.open('*.jpg')
image_data = np.array(image)
#图像处理过程
new_image_data = deal_image_Func(image_data)
#numpy数组转换为图片文件
new_image = Image.fromarray(new_image_data)
new_image.save('*_new.jpg', quality = 95)
1.2numpy数组与pytorch中tensor类型互转
pytorch架构使用的变量类型为tensor,因此在数据读取和读出的时候通常需要与numpy数组互转,代码如下:
import numpy as np
import torch
#numpy数组转换为tensor
a01 = np.array([[[1,2,3,6],[4,5,6,15],[7,8,9,24]],[[10,20,30,60],[40,50,60,150],[70,80,90,240]]])
print(type(a01))#输出<class 'numpy.ndarray'>
t01 = torch.tensor(a01)
print(type(t01))#输出<class 'torch.Tensor'>
#tensor转换为numpy数组
a02 = t01.numpy()
print(type(a02))#输出<class 'numpy.ndarray'>
二、numpy多维数组排序
以numpy三维数组为例,展示如何对数组不同维度排序及获取数组排序后对应的索引,代码如下:
import numpy as np
a01 = np.random.randint(0,10,size=(2,3,4))
print(a01)
array([[[8, 7, 4, 8],
[8, 6, 2, 1],
[4, 1, 5, 1]],
[[0, 0, 7, 4],
[2, 7, 5, 7],
[3, 9, 3, 9]]])
print(np.sort(a01,axis=0))
[[[0 0 4 4]
[2 6 2 1]
[3 1 3 1]]
[[8 7 7 8]
[8 7 5 7]
[4 9 5 9]]]
print(np.argsort(a01,axis=0))
[[[1 1 0 1]
[1 0 0 0]
[1 0 1 0]]
[[0 0 1 0]
[0 1 1 1]
[0 1 0 1]]]
print(np.sort(a01,axis=1))
[[[4 1 2 1]
[8 6 4 1]
[8 7 5 8]]
[[0 0 3 4]
[2 7 5 7]
[3 9 7 9]]]
print(np.argsort(a01,axis=1))
[[[2 2 1 1]
[0 1 0 2]
[1 0 2 0]]
[[0 0 2 0]
[1 1 1 1]
[2 2 0 2]]]
print(np.sort(a01,axis=2))
[[[4 7 8 8]
[1 2 6 8]
[1 1 4 5]]
[[0 0 4 7]
[2 5 7 7]
[3 3 9 9]]]
print(np.argsort(a01,axis=2))
[[[2 1 0 3]
[3 2 1 0]
[1 3 0 2]]
[[0 1 3 2]
[0 2 1 3]
[0 2 1 3]]]
三、numpy数组指定区域赋值
有时需要对numpy数组指定区域赋值,这个区域可以是事先指定的形状,也可以是对数据筛选后的不规则区域,如何筛选可参考博客另一篇博文:python数组列表操作简记二。
区域赋值操作主要是使用布尔类型数组示例代码如下:
import numpy as np
a01 = np.random.randint(0,10,size=(2,3,4))
print(a01)
array([[[8, 7, 4, 8],
[8, 6, 2, 1],
[4, 1, 5, 1]],
[[0, 0, 7, 4],
[2, 7, 5, 7],
[3, 9, 3, 9]]])
a02 = np.random.randint(10,20,size=(2,3,4))
print(a02)
array([[[17, 18, 11, 16],
[17, 14, 12, 15],
[10, 14, 11, 19]],
[[12, 17, 12, 14],
[17, 16, 19, 10],
[14, 14, 10, 15]]])
a03 = a01<5
print(a03)
array([[[False, False, True, False],
[False, False, True, True],
[ True, True, False, True]],
[[ True, True, False, True],
[ True, False, False, False],
[ True, False, True, False]]])
a01[a03] = a02[a03]
print(a01)
array([[[ 8, 7, 11, 8],
[ 8, 6, 12, 15],
[10, 14, 5, 19]],
[[12, 17, 7, 14],
[17, 7, 5, 7],
[14, 9, 10, 9]]])
四、numpy广播机制的使用
4.1广播规则和步骤总结
两个相同shape的numpy数组进行加减乘除操作,是对应位置的元素进行运算,但当一个numpy数组与一个常数进行加减乘除,结果是数组的所有元素都与这个常数进行运算,这就是numpy的广播机制,即numpy可以将参与运算的两个数组调整为具有相同形状然后进行计算。
广播的规则和步骤可以总结为:
(1)如果两个数组的维度数不相同,则扩充小维度数的数组的维度,向外一层层扩充维度,直到维度数相同(自动扩充按照此规则,但也可手动扩充)。例如形状(4, 2, 2)与形状(2, )进行运算,会将第二个数组扩充为形状(1, 1, 2);
(2)扩充后,除形状为1外的维度形状不一致,则报错,无法进行计算。例如(4, 2, 2)与形状(4, )进行运算,会将第二个数组扩充为形状(1, 1, 4),此时第一个数组最后一个维度的形状为2,第二个数组最后一个维度的形状为4,二者不一致,则报错;
(3)扩充后,除形状为1外维度形状一致,则进行计算,此时广播机制会沿着形状为1的维度复制内层元素,复制的次数即为对齐的数组形状。例如(4, 2, 2)与扩充后的形状(1, 1, 2)进行运算,将第二个数组最内层元素复制2次变为(1, 2, 2),然后将倒数一二层元素复制四次变为(4, 2, 2),然后进行计算。
上述规则和步骤可结合示例进行理解。
4.2使用示例
4.2.1形状(4, 2)-形状(2, )=形状(4, 2)
例如形状为(4, 2)的二维数组a01减去一个(2, )的一维数组a02,广播机制会将a02扩充为(1, 2),即在现有维度之外增加维度,示例代码如下:
import numpy as np
a01 = np.random.randint(10, size=[4,2])#shape:(4, 2)
print(a01)
array([[8, 6],
[7, 8],
[3, 2],
[0, 6]])
a02 = np.array([1,2])#shape:(2, )
print(a01-a02)
array([[ 7, 4],
[ 6, 6],
[ 2, 0],
[-1, 4]])
a03 = np.array([[1,2]])#shape:(1, 2)
print(a01-a03)
array([[ 7, 4],
[ 6, 6],
[ 2, 0],
[-1, 4]])
4.2.2形状(4, 2)-形状(4, )=形状(4, 2)
注意广播机制无法在现有维度之内增加维度,例如一维数组a02形状为(4, ),那么广播机制无法将a02扩充为(4, 1),然后与a01进行减法计算,但可以使用np.newaxis手动增加维度,示例代码如下:
import numpy as np
a01 = np.random.randint(10, size=[4,2])#shape:(4, 2)
print(a01)
array([[8, 6],
[7, 8],
[3, 2],
[0, 6]])
a02 = np.array([1,2,3,4])#shape:(4, )
print(a01-a02)#报错:ValueError: operands could not be broadcast together with shapes (4,2) (4,)
print(a01-a02[:,np.newaxis])#正常运行
array([[ 7, 5],
[ 5, 6],
[ 0, -1],
[-4, 2]])
4.2.3形状(4, 2)-形状(3, 2)=形状(4, 3, 2)
是指,要使第一个数组的每行分别减去第二个数组的每行,最终得到数组形状为(4, 3, 2),示例代码如下:
import numpy as np
a01 = np.random.randint(10, size=[4,2])#shape:(4, 2)
print(a01)
array([[8, 8],
[1, 4],
[7, 0],
[2, 2]])
a02 = np.random.randint(10, size=[3,2])#shape:(3, 2)
print(a02)
array([[5, 5],
[5, 3],
[2, 4]])
print(a01-a02)#报错:ValueError: operands could not be broadcast together with shapes (4,2) (3,2)
print(a01[:,np.newaxis,:]-a02)#正常运行
array([[[ 3, 3],
[ 3, 5],
[ 6, 4]],
[[-4, -1],
[-4, 1],
[-1, 0]],
[[ 2, -5],
[ 2, -3],
[ 5, -4]],
[[-3, -3],
[-3, -1],
[ 0, -2]]])
4.2.4形状(4, 2)-形状(4, 2)=形状(4, 2, 2)
是指,要使第一个数组的每行中的每列元素分别减去本行的每列元素,最终得到数组形状为(4, 2, 2),示例代码如下:
import numpy as np
a01 = np.random.randint(10, size=[4,2])#shape:(4, 2)
print(a01)
array([[8, 8],
[1, 4],
[7, 0],
[2, 2]])
print(a01[:,:,np.newaxis]-a01[:,np.newaxis,:])
array([[[ 0, 0],
[ 0, 0]],
[[ 0, -3],
[ 3, 0]],
[[ 0, 7],
[-7, 0]],
[[ 0, 0],
[ 0, 0]]])