1.数组的索引和切片(可类比于list的索引和切片,注意区别)
数组
选取数据子集或单个元素的方式有很多。一维数组跟list很相似,多维数组的花样更多, 下面来看。
1. 1基本的索引和切片
先来看一下例子,体会一下一维数组与list的区别:
import numpy as np
temp_array = np.arange(15) # array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
temp_list = list(range(15)) #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
# 索引比较
temp_array[10] #返回值: 10
temp_list[10] #返回值: 10
# 切片比较
temp_array[1:5] #返回值 array([1, 2, 3, 4])
temp_list[1:5] #返回值 [1, 2, 3, 4]
# 对单个值赋值比较
temp_list[1] = 6
temp_list # 返回值 [0, 6, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
temp_array[1] = 6
temp_array # 返回值 array([ 0, 6, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
# 对切片赋值比较
temp_array[1:5] = 6
temp_array # 返回值 array([ 0, 6, 6, 6, 6, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
# temp_list[1:5] = 6 # 报错 TypeError: can only assign an iterable
# 切片之后索引赋值比较
#先来看list
temp_list2 = temp_list[1:5]
temp_list2 # [6, 2, 3, 4]
temp_list2[2] = 1234
temp_list #返回值: [0, 6, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
temp_list2 # 返回值 [6, 2, 1234, 4]
temp_list[3] = 12341234 #对原始list修改
temp_list2 # 返回值:[6, 2, 1234, 4]
#再来看array
temp_array2 = temp_array[2:8]
temp_array2 # array([6, 6, 6, 5, 6, 7])
temp_array2[2] = 1234
temp_array2 #返回值:array([ 6, 6, 1234, 5, 6, 7])
temp_array # 返回值 array([ 0, 6, 6, 6, 1234, 5, 6, 7, 8, 9, 10,11, 12, 13, 14])
temp_array[5] = 123123 # 对原始数组的修改
temp_array2 # 返回值: array([ 6, 6, 1234, 123123, 6, 7])
通过看上边的例子, 不难发现ndarray和list的索引、切片、对单个值赋值基本都一样, 区别在于对切片直接赋值、切片单个值的赋值,list的切片赋值直接报错,数组的切片修改,该值会自动广播到整个选区的元素。
ndarray切片是原始数组的视图
(可以理解为地址指向,类比于浅拷贝.),视图上的任何修改都会直接反应到原数组上,源数据的修改也会影响视图。Numpy的这种做法其实不难理解,Numpy是用来处理大数据的,如果将其中的数据多次复制的话那得是多大的内存和性能的消耗。
当然,如果想要的得到的数组的切片是一个副本而不是视图的话,需要进行复制操作,即 temp_array[1:5].copy()
,如果非必须,还是不要辣么做了!
来看一下多维数组的例子(拿二维为例):
# 二维:
temp_array5 = np.eye(4)
temp_array5
#返回值: array([[1., 0., 0., 0.],
# [0., 1., 0., 0.],
# [0., 0., 1., 0.],
# [0., 0., 0., 1.]])
temp_array5[2]# 返回值:array([0., 0., 1., 0.])
temp_array5[2][2] # 返回值 1.0
temp_array5[:3] # [:3]的切片
# 返回值: array([[1., 0., 0., 0.],
# [0., 1., 0., 0.],
# [0., 0., 1., 0.]])
temp_array5[:3:2] # 类比于list,这就是[:3]的切片, 步长为2
# 返回值: array([[1., 0., 0., 0.],
# [0., 0., 1., 0.]])
temp_array5[:3] = 42 # 视图赋值
temp_array5
#返回值: array([[42., 42., 42., 42.],
# [42., 42., 42., 42.],
# [42., 42., 42., 42.],
# [ 0., 0., 0., 1.]])
temp_array5[1,2] = 88 #与 temp_array5[1][2] 一样
temp_array5
# 返回值:array([[42., 42., 42., 42.],
# [42., 42., 88., 42.],
# [42., 42., 42., 42.],
# [ 0., 0., 0., 1.]])
1.2切片索引
低维度的切片索引其实在1.1已经用到过了, 现在咱们直接用多维的来为例:
temp_list = [[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]
temp_array = np.array(temp_list)
temp_array.shape # 返回值: (2, 2, 3)
temp_array[1, :1] # 返回值 array([[7, 8, 9]])
temp_array[:2, :1] # array([[[1, 2, 3]],[[7, 8, 9]]])
# 注:相当于temp_array[0][:1]与temp_array[1][:1] 的结合,不是相加,相加会广播到元素级。
# 这里需要注意
temp_array[1,0,1] # 返回值 8
# temp_array[1,0,1]的元素和 temp_array[1,1,1]的元素组成的列表
temp_array[1,:,1] #array([ 8, 11])
# 请参考上一种请情况
temp_array[:,:,1]
# 返回值:array([[ 2, 5],
# [ 8, 11]])
temp_array[:,:,1] = 110
temp_array
# array([[[ 1, 110, 3],
# [ 4, 110, 6]],
# [[ 7, 110, 9],
# [ 10, 110, 12]]])
# 注:只写一个冒号, 表示选取的对应的整个维度。
1.3 布尔型索引
值的注意的是布尔型索引返回的值是副本而不是视图,不是视图!!!
#数据准备
temp_array1 = np.array('a b c d e f'.split(' '))
temp_array1 #array(['a', 'b', 'c', 'd', 'e', 'f'], dtype='<U1')
temp_array2 = np.random.randn(6,3)
temp_array2
# array([[-1.75405219, -0.33932199, 0.16753136],
# [-0.40832969, 0.28943606, -0.29946825],
# [-1.15116563, -1.93324644, 0.04890539],
# [-2.55618941, -0.55516265, 0.13617437],
# [ 2.11503851, 1.84211619, 0.35172968],
# [ 0.75279809, 0.63056373, -1.1371636 ]])
# 单独的判断可以广播到元素级,这不是中点, 布尔切片才是重点
temp_array1 == 'a' # 返回值 array([ True, False, False, False, False, False])
temp_array2>0
# array([[ True, True, True],
# [False, True, False],
# [False, False, True],
# [False, False, True],
# [ True, True, True],
# [ True, True, False]])
temp_array2[temp_array1 == 'a'] #返回值:array([[-1.75405219, -0.33932199, 0.16753136]])
temp_array3 = temp_array2[temp_array1 == 'a']
temp_array2[0] = 5
temp_array3 #返回值:array([[-1.75405219, -0.33932199, 0.16753136]])
temp_array2
# array([[ 5. , 5. , 5. ],
# [-0.40832969, 0.28943606, -0.29946825],
# [-1.15116563, -1.93324644, 0.04890539],
# [-2.55618941, -0.55516265, 0.13617437],
# [ 2.11503851, 1.84211619, 0.35172968],
# [ 0.75279809, 0.63056373, -1.1371636 ]])
# 注: 这里返回的并不是视图, 而是副本,是副本不是视图 不是视图。
# 可以写为并列形式
temp_array2[(temp_array1 == 'a') | (temp_array1 == 'b')]
# 注:and 和or在布尔型数组中无效
# 返回值:array([[ 5. , 5. , 5. ],
# [-0.40832969, 0.28943606, -0.29946825]])
# 当然还有这种形式
temp_array2[(temp_array1 == 'a'),:2] # array([[5., 5.]])
1.4 花式索引
花式索引是Numpy术语,指的是利用整数数组进行索引。返回的并不是视图, 也是副本。
temp_array = np.eye(6)
temp_array[[1,2]]
# 返回值: array([[0., 1., 0., 0., 0., 0.],
# [0., 0., 1., 0., 0., 0.]])
temp_array[[1,-2]]
# array([[0., 1., 0., 0., 0., 0.],
# [0., 0., 0., 0., 1., 0.]])
temp_array[[1,-2],[1,2]] # 返回值 array([1., 0.]), 我擦, 这种看不出来啊, 咱们换一种
temp_array = np.arange(24).reshape((6,4))
temp_array
# array([[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11],
# [12, 13, 14, 15],
# [16, 17, 18, 19],
# [20, 21, 22, 23]])
temp_array[[1,2,3]]
# array([[ 4, 5, 6, 7],
# [ 8, 9, 10, 11],
# [12, 13, 14, 15]])
temp_array[[1,2,3],[2,3,1]] #最终选取的元素是(1,2),(2,3), (3,1)返回值: array([ 6, 11, 13])
temp_array[[1,2,3]][:,[2,3,1]] # 这个是选取的方形区域, 注意以上两种的写法区别
# 返回值 array([[ 6, 7, 5],
# [10, 11, 9],
# [14, 15, 13]])
#注:花式索引返回的是副本, 而不是视图。
2.数组的转置和轴的对换
转置
是重塑的一种特殊形式,它返回的是源数据的视图, 转置最少要两维。
#两维数组
temp_array = np.arange(9).reshape((3,3))
# 两维数组的转置, 这两种方式等价
temp_array.transpose(1,0)
# 返回值:array([[0, 3, 6],
# [1, 4, 7],
# [2, 5, 8]])
temp_array.T
# 返回值:array([[0, 3, 6],
# [1, 4, 7],
# [2, 5, 8]])
# 更高维度的数组转置
temp_array2 = np.arange(27).reshape((3,3,3))
temp_array2
# 返回值:array([[[ 0, 1, 2],
# [ 3, 4, 5],
# [ 6, 7, 8]],
# [[ 9, 10, 11],
# [12, 13, 14],
# [15, 16, 17]],
# [[18, 19, 20],
# [21, 22, 23],
# [24, 25, 26]]])
temp_array2.transpose((1,0,2))
# 返回值:array([[[ 0, 1, 2],
# [ 9, 10, 11],
# [18, 19, 20]],
# [[ 3, 4, 5],
# [12, 13, 14],
# [21, 22, 23]],
# [[ 6, 7, 8],
# [15, 16, 17],
# [24, 25, 26]]])
temp_array2.transpose((1,2,0))
# 返回值:array([[[ 0, 9, 18],
# [ 1, 10, 19],
# [ 2, 11, 20]],
# [[ 3, 12, 21],
# [ 4, 13, 22],
# [ 5, 14, 23]],
# [[ 6, 15, 24],
# [ 7, 16, 25],
# [ 8, 17, 26]]])
temp_array2.T # 等价于 temp_array2.transpose((2,1,0))
#返回值: array([[[ 0, 9, 18],
# [ 3, 12, 21],
# [ 6, 15, 24]],
# [[ 1, 10, 19],
# [ 4, 13, 22],
# [ 7, 16, 25]],
# [[ 2, 11, 20],
# [ 5, 14, 23],
# [ 8, 17, 26]]])
是不是看的恍恍惚惚,迷迷瞪瞪.... 哈哈,我当时看的时候也是如此,那下边再来看一个例子(拿三维来讲):
temp_array = np.arange(24).reshape((2,3,4))
temp_array # 原始数组
# 返回值:array([[[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11]],
# [[12, 13, 14, 15],
# [16, 17, 18, 19],
# [20, 21, 22, 23]]])
temp_array.transpose(1,0,2).shape # 返回值:(3, 2, 4)
temp_array.transpose(1,0,2)
# 返回值:array([[[ 0, 1, 2, 3],
# [12, 13, 14, 15]],
# [[ 4, 5, 6, 7],
# [16, 17, 18, 19]],
# [[ 8, 9, 10, 11],
# [20, 21, 22, 23]]])
temp_array.transpose(1,2,0) # 其形状为(3,4,2)
# 返回值:array([[[ 0, 12],
# [ 1, 13],
# [ 2, 14],
# [ 3, 15]],
# [[ 4, 16],
# [ 5, 17],
# [ 6, 18],
# [ 7, 19]],
# [[ 8, 20],
# [ 9, 21],
# [10, 22],
# [11, 23]]])
temp_array.transpose(2,1,0)# 其形状为(4,3,2)
#返回值: array([[[ 0, 12],
# [ 4, 16],
# [ 8, 20]],
# [[ 1, 13],
# [ 5, 17],
# [ 9, 21]],
# [[ 2, 14],
# [ 6, 18],
# [10, 22]],
# [[ 3, 15],
# [ 7, 19],
# [11, 23]]])
从例子可以看出,数组的转置是按照轴来转换的,轴的编号即对应维度的下标,比如原始数据temp_array的形状(2,3,4),则0轴对应的维度是2,即第一层中括号里的数据,1号周对应的维度是3,即第二层括号里的数据,3轴对应的维度是4,即第三层里的数据,即元素。那么轴的转换,比如原始数据(0,1,2),转换为(1,0,2),也就是1号轴沿着0轴拼数据, 然后组成形状为 (3,2,4)的数据, 咱们依次看图:


注:temp_array.transpose(1,2,0)的数据可在temp_array.transpose(1,0,2)的基础上求得在此不再展示。

网友评论