Numpy的索引和ufunc

一、基本的索引和切片:

1、数组索引

    返回来的都是视图,并不会对数据进行复制,如果想复制可以使用copy()

arr=np.arange(10)
print(arr)
arr[5]=8
print(arr)
arr[2:5]=6
print(arr)

输出结果:
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
array([0, 1, 2, 3, 4, 8, 6, 7, 8, 9])
array([0, 1, 6, 6, 6, 8, 6, 7, 8, 9])

    numpy 设计的目的是处理大数据,如果每次操作都把数据进行复制,那会产生很大的性能浪费。如果需要得到副本而不是视图,则需要进行显式地复制, arr[5:8].copy()

2、切片索引

    ndarry 地切片语法和 python 列表地一维对象差不多,如果是多维度地 ndarray 还可以在多个轴进行切片,也可以跟整数索引混合使用。如果是只用:的切片,那会得到和 array 相同维度地视图

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

输出结果:
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
array([[2, 3],
[5, 6]])
array([[2],
[5]])
array([[7, 8, 9]])

    可以看到,使用了非整数的切片,得到的都还是二维数组。这是因为如果只有一个冒号(:)是对整个轴进行选取的,冒号两边的数字只不过是对轴数量限制,但还是轴。

# 如果使用了整数的切片,就会降维
arra2d[1:,2]
arra2d[2,2]

输出结果:
array([6, 9])
9

注意:切片索引和数组索引返回来的都是视图(布尔索引返回来的是副本),所以对切片的操作也会影响到原数据。

# 使用视图对原数据进行修改
arra2d[:,:]=2

输出结果:
array([[2, 2, 2],
[2, 2, 2],
[2, 2, 2]])

3、布尔型索引

    布尔型索引的长度必须跟被索引的轴一致(先写就是横轴,后写就是纵轴),还可以将布尔型数组和切片、整数 或 整数序列进行混合使用

names=np.array(['bob','joe','ang','will'])
data=np.random.randn(4,3)
data[names=='bob']

输出结果:
array(['bob', 'joe', 'ang', 'will'],
dtype='<U4')
array([[-0.09280068, 0.58965549, 0.00679073],
[ 0.64898363, 0.28057725, -0.31246341],
[ 0.76704748, 1.56162526, -1.27835386],
[ 1.70463106, -1.6897867 , -1.31967934]])
array([[-0.09280068, 0.58965549, 0.00679073]])

# 布尔索引和整数一起混合使用
data[names=='bob',2:]

输出结果:
array([[ 0.00679073]])

如果有多个条件,可以用 & | ! (与或非),并且非还可以通过加负号的形式实现

# 使用整数索引,会降维
data[(names=='bob')|(names=='ang'),2]

输出结果:
array([ 0.00679073, -1.27835386])

应用,可以批量修改数据,例如把所有负数变成0
data[data<0]=0

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

4、花式索引

    花式索引是Numpy 得一个术语,它指的是利用整数数组进行索引。如果想以特定顺序选取行子集,只需传入一个用于指定顺序的整数列表或者 ndarray 就可以了,甚至还可以用负数。

arr = np.empty((8,4))
for i in range(8):
    arr[i]=i
arr[[4,3,0,6]]
arr[[-1,2,4]]

输出结果:
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.]])
array([[ 4., 4., 4., 4.],
[ 3., 3., 3., 3.],
[ 0., 0., 0., 0.],
[ 6., 6., 6., 6.]])
array([[ 7., 7., 7., 7.],
[ 2., 2., 2., 2.],
[ 4., 4., 4., 4.]])

多轴花式索引: 会返回交叉位置的数据
arr[[1,5,7,2],[0,3,1,2]]
输出结果:
array([ 1., 5., 7., 2.])

如果不想选取对应位置的数,可以使用 np.ix_ 函数

index = np.ix_([1,5,7,2],[0,3,1,2])
arr[index]

输出结果: 是一个元组,元组里面
(array([[1],
[5],
[7],
[2]]), array([[0, 3, 1, 2]]))
array([[ 1., 1., 1., 1.],
[ 5., 5., 5., 5.],
[ 7., 7., 7., 7.],
[ 2., 2., 2., 2.]])

注意:花式索引会复制数据

二、通用函数:快速的元素级数组函数

    通用函数(即 ufunc)是一种对 ndarray 种的数据执行元素级运算的函数。可以将其看作是简单函数的矢量化包装器。(接受一个可变参数,给任意个标量数据都可以返回任意个值)

arr=np.arange(10).reshape(2,5) 
np.sqrt(arr) 
np.exp(arr)

输出结果:
array([[ 0. , 1. , 1.41421356, 1.73205081, 2. ], [ 2.23606798, 2.44948974, 2.64575131, 2.82842712, 3. ]])

array([[ 1.00000000e+00, 2.71828183e+00, 7.38905610e+00, 2.00855369e+01, 5.45981500e+01], [ 1.48413159e+02, 4.03428793e+02, 1.09663316e+03, 2.98095799e+03, 8.10308393e+03]])

    sqrt、exp 都是一元的 ufunc,另外一些(如 add 或者 maximum) 则可以接受2个参数,叫做二元通用函数。

123.png
x = np.random.randn(8) 
y = np.random.randn(8) 
# 对比返回大的值 
np.maximum(x,y)

输出结果:
array([-0.10488341, -1.18010624, 0.46492552, 0.58947382, -0.01015324, 2.08139131, 0.35234464, -0.82338906])
array([-1.30925346, -0.14092102, 0.56775544, 1.46416221, 0.67749745, -1.22717291, -0.50538792, 0.95247714])
array([-0.10488341, -0.14092102, 0.56775544, 1.46416221, 0.67749745, 2.08139131, 0.35234464, 0.95247714])

    有些 ufunc 可以返回多个数组,但是比较少见,modf 就是一个例子,它是 Python 内置函数,它是Python内置函数 divmod 的矢量化版本,用于浮点数组的小数和整数部分。

# 得到两个数组组成的元组,一个是小数部分数据,一个是整数部分的数据 
np.modf(y)

输出结果: 前面是小数部分,后面是整数部分
(array([-0.30925346, -0.14092102, 0.56775544, 0.46416221, 0.67749745, -0.22717291, -0.50538792, 0.95247714]), array([-1., -0., 0., 1., 0., -1., -0., 0.]))

根据通用函数的参数个数,可以划分为 一元ufunc 和 二元ufunc

一元 ufunc :
sqrt、cos、floor、modf

二元 ufunc :
add、maximum、mod

推荐阅读更多精彩内容