读书笔记四、基本的索引和切片

一维数组的切片:

arr=np.arange(10)

arr
Out[29]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

arr[5]
Out[30]: 5

arr[5:8]
Out[31]: array([5, 6, 7])

arr[5:8]=12

arr
Out[33]: array([ 0,  1,  2,  3,  4, 12, 12, 12,  8,  9])
将一个标量值赋给一个切片时,该值会自动传播到整个选区。
跟列表最重要的区别在于,数组的切片是原始数组的视图。这意味着数据不会被复制,视图上的任何修改都会直接反映到源数组上。
arr_slice=arr[5:8]

arr_slice
Out[35]: array([12, 12, 12])

arr_slice[1]=12345

arr
Out[37]: 
array([    0,     1,     2,     3,     4,    12, 12345,    12,     8,
           9])

arr_slice[:]=64

arr
Out[39]: array([ 0,  1,  2,  3,  4, 64, 64, 64,  8,  9])

对于高维数组,能做的事情更多。在一个二维数组中,各索引位置上的元素不再是标量而是一维数组:

arr2d=np.array([[1,2,3],[4,5,6],[7,8,9]])

arr2d
Out[41]: 
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

arr2d[2]
Out[42]: array([7, 8, 9])

因此,可以对各个元素进行递归访问。可以传入一个以逗号隔开的索引列表来选取单个元素。下面2种方式等价:

arr2d[0][2]
Out[43]: 3

arr2d[0,2]
Out[44]: 3

在多维数组中,如果省略了后面的索引,则返回对象会是一个维度第一点的ndarray:

arr3d=np.array([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]])

arr3d
Out[46]: 
array([[[ 1,  2,  3],
        [ 4,  5,  6]],

       [[ 7,  8,  9],
        [10, 11, 12]]])

arr3d[0]是一个2×3数组:

arr3d[0]
Out[47]: 
array([[1, 2, 3],
       [4, 5, 6]])

标量值和数组都可以被赋值给arr3d[0]:

old_values=arr3d[0].copy()

#将标量值赋给arr3d[0]
arr3d[0]=42

arr3d
Out[50]: 
array([[[42, 42, 42],
        [42, 42, 42]],

       [[ 7,  8,  9],
        [10, 11, 12]]])

#将数组赋给arr3d[0]
arr3d[0]=old_values

arr3d
Out[52]: 
array([[[ 1,  2,  3],
        [ 4,  5,  6]],

       [[ 7,  8,  9],
        [10, 11, 12]]])

以此类推,arr3d[1,0]可以访问索引以(1,0)开头的那些值:

arr3d[1,0]
Out[53]: array([7, 8, 9])

注意,在上面这些所有选取数组子集的例子中,返回的数组都是视图:

arr3d[1,0]=8

arr3d
Out[55]: 
array([[[ 1,  2,  3],
        [ 4,  5,  6]],

       [[ 8,  8,  8],
        [10, 11, 12]]])
切片索引
ndarray的切片语法跟Python列表这样的一维对象差不多:
import numpy as np
arr=np.arange(10)

arr[1:6]
Out[3]: array([1, 2, 3, 4, 5])

高维度对象的花样更多,可以在一个或者多个轴上切片,也可以跟整数索引混合使用。对于上面那个二维数组arr2d,其切片方式稍显不同:

arr2d=np.array([[1,2,3],[4,5,6],[7,8,9]])

arr2d
Out[5]: 
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

arr2d[:2]
Out[6]: 
array([[1, 2, 3],
       [4, 5, 6]])

可以看出,它是沿着第0轴(即第一个轴)切片的。也就是说,切片是沿着一个轴向选取元素的。可以一次传入多个切片,像传入多个索引那样:

arr2d[:2,:1]
Out[7]: 
array([[1],
       [4]])

这样进行切片时,只能得到相同维数的数组视图。通过将整数索引和切片混合,可得到低纬度的切片:

arr2d[1,:2]
Out[8]: array([4, 5])

arr2d[2,:1]
Out[9]: array([7])

#“只有冒号”表示选取整个轴:
arr2d[:,:1]
Out[10]: 
array([[1],
       [4],
       [7]])

对切片表达式的赋值操作也会被扩散到整个选区:

arr2d[:2,1:]=0

arr2d
Out[12]: 
array([[1, 0, 0],
       [4, 0, 0],
       [7, 8, 9]])

 

布尔型索引
使用numpy.random 中的randn函数生成一些正态分布的随机数
names=np.array(['Bob','Joe','Will','Bob','Will','Joe','Joe'])

data=np.random.randn(7,4)

names
Out[15]: array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'], dtype='<U4')

data
Out[16]: 
array([[-1.91071805,  0.12663092, -1.53889879,  0.87588762],
       [-0.39801907, -1.51346505,  1.31478407,  0.76033884],
       [ 0.5674499 ,  0.11646338,  0.66960379,  0.33064397],
       [-0.14963993, -0.15050284,  1.37466165, -2.25447743],
       [ 0.06028391,  0.06226708,  0.08291613, -0.19584573],
       [-0.32005106,  1.13986837, -0.33484255, -0.53863811],
       [ 0.46754402,  0.2592858 , -0.76507756,  0.45506666]])

假设每个名字都对应data数组中的一行,选出对应于名字“Bob”的所有行。和算术运算一样,数组的比较运算也是矢量化的。因此,对names和字符串“Bob”的比较运算将会产生一个布尔型数组:

names=='Bob'
Out[17]: array([ True, False, False,  True, False, False, False])

#这个布尔型数组可用于数组索引:
data[names=='Bob']
Out[18]: 
array([[-1.91071805,  0.12663092, -1.53889879,  0.87588762],
       [-0.14963993, -0.15050284,  1.37466165, -2.25447743]])

布尔型数组的长度必须跟被索引的轴长度一致。此外,还可以将布尔型数组跟切片、整数混合使用:

data[names=='Bob',2:]
Out[20]: 
array([[-1.53889879,  0.87588762],
       [ 1.37466165, -2.25447743]])

data[names=='Bob',3]
Out[21]: array([ 0.87588762, -2.25447743])

要选择除“Bob”以外的其他值,既可以使用不等于符号(!=),也可以通过取反符号(~)对条件进行否定:

names!='Bob'
Out[22]: array([False,  True,  True, False,  True,  True,  True])
    
data[~(names=='Bob')]
Out[25]: 
array([[-0.39801907, -1.51346505,  1.31478407,  0.76033884],
       [ 0.5674499 ,  0.11646338,  0.66960379,  0.33064397],
       [ 0.06028391,  0.06226708,  0.08291613, -0.19584573],
       [-0.32005106,  1.13986837, -0.33484255, -0.53863811],
       [ 0.46754402,  0.2592858 , -0.76507756,  0.45506666]])

选取三个名字中的两个需要组合应用多个布尔条件,使用&(和)、|(或)之类的布尔运算符即可:

mask=(names=='Bob')|(names=='Will')

mask
Out[27]: array([ True, False,  True,  True,  True, False, False])

data[mask]
Out[28]: 
array([[-1.91071805,  0.12663092, -1.53889879,  0.87588762],
       [ 0.5674499 ,  0.11646338,  0.66960379,  0.33064397],
       [-0.14963993, -0.15050284,  1.37466165, -2.25447743],
       [ 0.06028391,  0.06226708,  0.08291613, -0.19584573]])
通过布尔型索引选取数组中的数据,将总是创建数据的副本。
通过布尔型数组设置值:
data[data<0]=0

data
Out[31]: 
array([[0.        , 0.12663092, 0.        , 0.87588762],
       [0.        , 0.        , 1.31478407, 0.76033884],
       [0.5674499 , 0.11646338, 0.66960379, 0.33064397],
       [0.        , 0.        , 1.37466165, 0.        ],
       [0.06028391, 0.06226708, 0.08291613, 0.        ],
       [0.        , 1.13986837, 0.        , 0.        ],
       [0.46754402, 0.2592858 , 0.        , 0.45506666]])

通过一位布尔数组设置整行或者整列的值:

data[names!='Joe']=7

data
Out[33]: 
array([[7.        , 7.        , 7.        , 7.        ],
       [0.        , 0.        , 1.31478407, 0.76033884],
       [7.        , 7.        , 7.        , 7.        ],
       [7.        , 7.        , 7.        , 7.        ],
       [7.        , 7.        , 7.        , 7.        ],
       [0.        , 1.13986837, 0.        , 0.        ],
       [0.46754402, 0.2592858 , 0.        , 0.45506666]])

 

花式索引
花式索引是一个numpy术语,指的是利用整数数组进行索引。
arr=np.empty((8,4))

for i in range(8):
    arr[i]=i
    

arr
Out[36]: 
array([[0., 0., 0., 0.],
       [1., 1., 1., 1.],
       [2., 2., 2., 2.],
       [3., 3., 3., 3.],
       [4., 4., 4., 4.],
       [5., 5., 5., 5.],
       [6., 6., 6., 6.],
       [7., 7., 7., 7.]])

为了以特定顺序选取行子集,只需传入一个用于指定顺序的整数列表或ndarray:

arr[[4,3,0,6]]
Out[37]: 
array([[4., 4., 4., 4.],
       [3., 3., 3., 3.],
       [0., 0., 0., 0.],
       [6., 6., 6., 6.]])

#使用负整数索引将会从末尾开始选取行:
arr[[-3,-5,-7]]
Out[38]: 
array([[5., 5., 5., 5.],
       [3., 3., 3., 3.],
       [1., 1., 1., 1.]])

一次传入多个索引数组会有点特别。返回的是一个一维数组,其中的元素对应各个索引元组:

arr=np.arange(32).reshape((8,4))

arr
Out[40]: 
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, 27],
       [28, 29, 30, 31]])

arr[[1,5,7,2],[0,3,1,2]]
Out[42]: array([ 4, 23, 29, 10])
仔细观察,最终选出的元素是(1,0)、(5,3)、(7,1)、(2,2)。
选取行列子集:
arr[[1,5,7,2]][:,[0,3,1,2]]
Out[43]: 
array([[ 4,  7,  5,  6],
       [20, 23, 21, 22],
       [28, 31, 29, 30],
       [ 8, 11,  9, 10]])

另一个办法是使用np.ix_函数,它将两个一维整数数组转换为一个用于选取方形区域的索引器:

arr[np.ix_([1,5,7,2],[0,3,1,2])]
Out[45]: 
array([[ 4,  7,  5,  6],
       [20, 23, 21, 22],
       [28, 31, 29, 30],
       [ 8, 11,  9, 10]])

记住,花式索引和切片不一样,总是将数据复制到新数组中。

posted @ 2018-07-13 16:36  平淡才是真~~  阅读(411)  评论(0编辑  收藏  举报