Numpy:索引与切片

时间:2023-03-08 16:43:13

numpy基本的索引和切片

import numpy as np
arr = np.array([1,2,3,555,666,888,10])
arr
array([ 1, 2, 3, 555, 666, 888, 10]) # 数组的切片是不会复制,任何视图上的修改都会直接修改源数组
arr[1:5]
array([ 2, 3, 555, 666]) # 广播 将一个标量赋值给一个切片时,自动传播到整个选区
arr[1:5] = 12
# 源数据改变
arr
array([ 1, 12, 12, 12, 12, 888, 10])

注意:由于Numpy的设计目的是处理大数据,所以如果坚持要讲数据复制来复制去的话会产生严重的性能和内存问题。 警告:如果你想得到的是一份副本而非视图,就需要显示复制操作

arr =
array([ 1, 12, 12, 12, 12, 888, 10]) arr_slice = arr[5:8]
array([888, 10])
arr_slice[1] = 123456
# 源数据改变
arr
array([ 1, 12, 12, 12, 12, 888, 123456]) arr_slice[:] = 64
# 源数据改变
arr
array([ 1, 12, 12, 12, 12, 64, 64])

多维数组的操作

arr2d = np.array([[1,2,3],[4,5,6],[7,8,9]])
# 索引是一维数组,不再是标量
arr2d[2]
array([7, 8, 9]) arr2d[2,0]
7

Numpy数组中的元素索引

arr3d = np.array([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]])
arr3d array([[[ 1, 2, 3],
[ 4, 5, 6]], [[ 7, 8, 9],
[10, 11, 12]]]) arr3d[0]
array([[1, 2, 3],
[4, 5, 6]]) # 复制一份副本
old_values = arr3d[0].copy()
old_values
array([[1, 2, 3],
[4, 5, 6]])
# 赋值标量
arr3d[0] = 42
arr3d
# 源数据改变
array([[[42, 42, 42],
[42, 42, 42]], [[ 7, 8, 9],
[10, 11, 12]]]) # 赋值上面的保存的副本
arr3d[0] = old_values
arr3d
# 源数据恢复之前的状态
array([[[ 1, 2, 3],
[ 4, 5, 6]], [[ 7, 8, 9],
[10, 11, 12]]]) arr3d[1,0]
array([7, 8, 9])

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

切片索引 跟Python列表这样的一维对象差不多

arr2d = np.array([[1,2,3],[4,5,6],[7,8,9]])
arr2d array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]) arr2d[:2]
array([[1, 2, 3],
[4, 5, 6]]) arr2d[:2,:1]
array([[1],
[4]]) arr2d[2,:1]
array([7]) arr2d[:,:1]
array([[1],
[4],
[7]]) # 对切片赋值也会被扩散到整个选区
arr2d[:2,1:] = 0
arr2d
array([[1, 0, 0],
[4, 0, 0],
[7, 8, 9]])

布尔型索引

names = np.array(['Bob','Joe','Will','Bob','Will','Joe','Joe'])
# 随机生成数字
data = np.random.randn(7,4)
names array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'], dtype='<U4') data
array([[-0.61531969, 0.09937418, -1.15678643, -0.17431226],
[-1.72274669, -0.88961153, -1.21114235, 0.53001087],
[ 0.43352234, 0.32843525, 0.4930513 , -1.78710182],
[ 0.43997916, 0.4744386 , -2.27592661, -0.41136012],
[-0.63605234, -1.31366781, -1.5077842 , -0.98485074],
[-0.62922743, -0.62193719, -0.42214742, 0.94249323],
[ 0.38367682, 0.67807875, 0.32268169, 0.30776196]]) # 假设每个名字都对应data数组中的一行,我们想要选出对应于名字'Bob'的所有行
names == 'Bob'
array([ True, False, False, True, False, False, False]) # 数组的比较运算也是矢量化的
data[names == 'Bob']
array([[-0.61531969, 0.09937418, -1.15678643, -0.17431226],
[ 0.43997916, 0.4744386 , -2.27592661, -0.41136012]]) # 数组的长度必须跟被索引的轴长度一致,此外还可以将布尔型数组跟切片、整数、混合使用
data[names == 'Bob', 2:]
array([[-1.15678643, -0.17431226],
[-2.27592661, -0.41136012]]) data[names == 'Bob', 3]
array([-0.17431226, -0.41136012]) # 要选择'Bob'以外的其他值,既可以使用!=,也可以通过负号(-)对条件进行否定
names != 'Bob'
array([False, True, True, False, True, True, True]) data[names != 'Bob']
array([[-1.72274669, -0.88961153, -1.21114235, 0.53001087],
[ 0.43352234, 0.32843525, 0.4930513 , -1.78710182],
[-0.63605234, -1.31366781, -1.5077842 , -0.98485074],
[-0.62922743, -0.62193719, -0.42214742, 0.94249323],
[ 0.38367682, 0.67807875, 0.32268169, 0.30776196]])

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

Python关键字and和or在布尔型数组中无效

mask = (names == 'Bob')|(names == 'Will')
mask
array([ True, False, True, True, True, False, False]) data[mask]
array([[-0.61531969, 0.09937418, -1.15678643, -0.17431226],
[ 0.43352234, 0.32843525, 0.4930513 , -1.78710182],
[ 0.43997916, 0.4744386 , -2.27592661, -0.41136012],
[-0.63605234, -1.31366781, -1.5077842 , -0.98485074]])

注意:通过布尔型索引选择数组数据,将总是创建数据的副本,即使返回一模一样的数组也是如此

# 将data中的所有负值都设置为 0
data[data<0] = 0
data
array([[0. , 0.09937418, 0. , 0. ],
[0. , 0. , 0. , 0.53001087],
[0.43352234, 0.32843525, 0.4930513 , 0. ],
[0.43997916, 0.4744386 , 0. , 0. ],
[0. , 0. , 0. , 0. ],
[0. , 0. , 0. , 0.94249323],
[0.38367682, 0.67807875, 0.32268169, 0.30776196]]) data[ names != 'Joe'] = 7
data
array([[7. , 7. , 7. , 7. ],
[0. , 0. , 0. , 0.53001087],
[7. , 7. , 7. , 7. ],
[7. , 7. , 7. , 7. ],
[7. , 7. , 7. , 7. ],
[0. , 0. , 0. , 0.94249323],
[0.38367682, 0.67807875, 0.32268169, 0.30776196]])

花式索引

利用整数数组进行索引

arr = np.empty((8,4))
arr
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]]) for i in range(8):
arr[i] = i
arr 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]] array([[4., 4., 4., 4.],
[3., 3., 3., 3.],
[0., 0., 0., 0.],
[6., 6., 6., 6.]]) # 使用负数索引
arr[[-3,-5,-7]]
array([[5., 5., 5., 5.],
[3., 3., 3., 3.],
[1., 1., 1., 1.]]) # 一次传入多个索引数组
arr = np.arange(32).reshape((8,4))
arr
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]]
array([ 4, 23, 29, 10])
# 选取方形区域的索引器
arr[np.ix_([1,5,7,2],[0,3,1,2])]
array([[ 4, 7, 5, 6],
[20, 23, 21, 22],
[28, 31, 29, 30],
[ 8, 11, 9, 10]])

注意:花式索引跟切片不一样,它总是将数据复制到新数组中

数组转置和轴对换

  • 转置(transpose方法):重塑的一种特殊形式,它返回的是源数据的视图(不会进行任何复制操作)。
  • T属性
arr1 = np.arange(15).reshape((3,5))
arr1 # 三行五列
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
arr1.T # 转置为五行三列 适用于二维数组
array([[ 0, 5, 10],
[ 1, 6, 11],
[ 2, 7, 12],
[ 3, 8, 13],
[ 4, 9, 14]])
# 在进行矩阵计算时,经常需要用到该操作,内积计算,行行向量乘以列向量
np.dot(arr1.T,arr1)
array([[125, 140, 155, 170, 185],
[140, 158, 176, 194, 212],
[155, 176, 197, 218, 239],
[170, 194, 218, 242, 266],
[185, 212, 239, 266, 293]])

Numpy:索引与切片

  • 计算方法:计算第几行第几列的数字,就把arr1.T的行取出来 和 arr1的列去乘,例如:

    • 125位于第一行第一列,则计算过程 0 * 0 + 5 * 5 + 10 * 10 = 125
    • 197位于第三行第三列,则计算过程 2 * 2 + 7 * 7 + 12 * 12 = 197
    • 218位于第四行第三列,则计算过程 3 * 2 + 8 * 7 + 13 * 12 = 218