Python Numpy基本的索引和切片
NumPy数组的索引是一个内容丰富的主题,因为选取数据子集或单个元素的方式有很多。
arr = np.arange(10)
ipdb> arr[5]
5
ipdb> arr[5:8]
array([5, 6, 7])
ipdb> arr[5:8] = 12
ipdb> arr
array([ 0, 1, 2, 3, 4, 12, 12, 12, 8, 9])
当将标量值赋值给一个切片时(如arr[5:8] = 12) , 该值会自动传播(广播)到整个选区
跟列表最重要的区别在于,数组切片时原始数组的视图,所以数据不会被复制,视图上的
任何修改都会直接反映到原数组上
ipdb> arr_slice = arr[5:8]
ipdb> arr_slice
array([12, 12, 12])
ipdb> arr_slice[1] = 123
ipdb> arr
array([ 0, 1, 2, 3, 4, 12, 123, 12, 8, 9])
ipdb> arr_slice
array([ 12, 123, 12])
ipdb> arr_slice[:] = 64
ipdb> arr
array([ 0, 1, 2, 3, 4, 64, 64, 64, 8, 9])
由于NumPy的设计目的是处理大数据,假如NumPy将数据复制来复制去会产生性能问题
如果你想要得到的是ndarray切片的一份副本而非视图,就需要显示地进行复制操作
##eg: arr[5:8].copy()
在一个二维数组中,各索引位置上的元素不再是标量而是一维数组:
ipdb> arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
ipdb> arr2d[2]
array([7, 8, 9])
##可以这样选取单个元素
arr2d[0][2] ## or arr2d[0, 2]
在多维数组中,如果省略了后面的索引,则返回对象会是一个维度低一点的ndarray
ipdb> arr3d = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 9, 8], [10, 11, 12]]])
ipdb> arr3d
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 7, 9, 8],
[10, 11, 12]]])
##arr3d[0]是一个2 * 3数组
ipdb> arr3d[0]
array([[1, 2, 3],
[4, 5, 6]])
##标量值和数组都可以被赋值给arr3d[0]:
old_values = arr3d[0].copy()
ipdb> arr3d[0] = 22
ipdb> arr3d
array([[[22, 22, 22],
[22, 22, 22]],
[[ 7, 9, 8],
[10, 11, 12]]])
ipdb> arr3d[0] = old_values
ipdb> arr3d
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 7, 9, 8],
[10, 11, 12]]])
选取数组子集的例子中,返回的数组都是视图
ipdb> arr3d[1, 0]
array([7, 9, 8])
切片索引
##ndarray的切片语法 跟 Python列表这样的以为对象差不多
ipdb> arr
array([ 0, 1, 2, 3, 4, 64, 64, 64, 8, 9])
ipdb> arr[1:6]
array([ 1, 2, 3, 4, 64])
对于高维度对象,还可以在一个或多个轴上进行切片
##还可以跟整数索引 混合使用
ipdb> arr2d
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
ipdb> arr2d[:2] ##它是沿的第0轴切片的,切片时沿着一个轴向选取元素的
array([[1, 2, 3],
[4, 5, 6]])
##一次还可以传入多个切片,就像传入多个索引那样
ipdb> arr2d[:2, 1:]
array([[2, 3],
[5, 6]])
通过将整数索引和切片混合,可以得到低维度的切片
ipdb> arr2d[1, :2]
array([4, 5])
ipdb> arr2d[2, :1]
array([7])
ipdb> arr2d
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
## "只有冒号"表示选取整个轴,
eg:arr2d[:, :1]
ipdb> arr2d[:, :1]
array([[1],
[4],
[7]])
##对切片表达式的赋值操作也会被扩散到整个选区
ipdb> arr2d[:2, 1:]
array([[2, 3],
[5, 6]])
布尔型索引
##如果有一个用于存储 数据的数组,有一个用于存储 姓名的数组
##使用numpy.random中的randn函数生成一些正态分布的随机数据:
ipdb> names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
ipdb> from numpy.random import randn
ipdb> data = randn(7,4)
ipdb> names
array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'], dtype='|S4')
ipdb> data
array([[ 0.68550649, 0.35127609, -1.5233016 , 0.27477923],
[-0.82826478, -0.3227764 , 0.78966993, 0.30686813],
[ 2.18590243, 0.6594865 , 0.06266253, -0.20267557],
[ 0.69611251, 3.07113108, 1.28155528, 0.57559268],
[-1.06859787, -1.76166274, -0.08568517, -0.97292077],
[ 0.39498941, -0.01672193, 0.47766726, 0.83518101],
[-0.3424802 , -0.26958982, 0.61804347, -0.63663972]])
##假如每个名字都对应data数组中一行,如果选出对于名字"Bob"的所有行
##可以使用数组的比较运算(如==)
##对names和字符串 "Bob"的比较运算将会产生一个布尔型数组:
ipdb> names == 'Bob'
array([ True, False, False, True, False, False, False])
##这个布尔型数组可用于数组索引:
ipdb> data[names == 'Bob']
array([[ 0.68550649, 0.35127609, -1.5233016 , 0.27477923],
[ 0.69611251, 3.07113108, 1.28155528, 0.57559268]])
##布尔型数组的长度必须跟被索引的轴长度一致
##还可以将布尔型数组跟切片、整数(整数序列)混合使用
ipdb> data[names == 'Bob', 2:]
array([[-1.5233016 , 0.27477923],
[ 1.28155528, 0.57559268]])
ipdb> data[names == 'Bob', 3]
array([0.27477923, 0.57559268])
##选择出 “Bob” 以外的其他值,可用 != 和 -(负号)对条件进行否定
names != 'Bob'
ipdb> data[names != 'Bob'] ## data[-(names == 'Bob')] 不支持 -
array([[-0.82826478, -0.3227764 , 0.78966993, 0.30686813],
[ 2.18590243, 0.6594865 , 0.06266253, -0.20267557],
[-1.06859787, -1.76166274, -0.08568517, -0.97292077],
[ 0.39498941, -0.01672193, 0.47766726, 0.83518101],
[-0.3424802 , -0.26958982, 0.61804347, -0.63663972]])
## & | 操作
ipdb> mask = (names == 'Bob') | (names == 'Will')
ipdb> mask
array([ True, False, True, True, True, False, False])
ipdb> data[mask]
array([[ 0.68550649, 0.35127609, -1.5233016 , 0.27477923],
[ 2.18590243, 0.6594865 , 0.06266253, -0.20267557],
[ 0.69611251, 3.07113108, 1.28155528, 0.57559268],
[-1.06859787, -1.76166274, -0.08568517, -0.97292077]])
注意:通过布尔型索引选取数组中的数据,将总是创建数据的副本
##将data中的所有负值都设置为0
ipdb> data[data < 0] =0
ipdb> data
array([[0.68550649, 0.35127609, 0. , 0.27477923],
[0. , 0. , 0.78966993, 0.30686813],
[2.18590243, 0.6594865 , 0.06266253, 0. ],
[0.69611251, 3.07113108, 1.28155528, 0.57559268],
[0. , 0. , 0. , 0. ],
[0.39498941, 0. , 0.47766726, 0.83518101],
[0. , 0. , 0.61804347, 0. ]])
##通过一维布尔数组设置整行或列的值
ipdb> data[names != 'Joe'] = 7
ipdb> data
array([[7. , 7. , 7. , 7. ],
[0. , 0. , 0.78966993, 0.30686813],
[7. , 7. , 7. , 7. ],
[7. , 7. , 7. , 7. ],
[7. , 7. , 7. , 7. ],
[0.39498941, 0. , 0.47766726, 0.83518101],
[0. , 0. , 0.61804347, 0. ]])