索引

  • 有三种可用的索引方法类型: 字段访问,基本切片和高级索引
  • 所有的索引方式都是在方括号内。
    • :表示切片
    • ,表示高维度索引
    • [][]表示递归索引,对索引结果再次索引。

字段访问

ndarray对象的内容可以通过索引或切片来访问和修改,与 Python 中 list 的切片操作一样。

ndarray 数组可以基于 0 - n 的下标进行索引。

1
2
3
4
5
import numpy as np

a = np.arange(10)
s = slice(2,7,2) # 从索引 2 开始到索引 7 停止,间隔为2
print (a[s])

基本切片

切片对象可以通过内置的 slice 函数,并设置 start, stop 及 step 参数进行,从原数组中切割出一个新数组。

  • 基本切片语法是 i:j:k,其中 i 是起始索引,j 是停止索引,k 是步骤(k\neq0)。这将选择具有索引值(在相应的维度中)i, i+k, …, i+(m-1) k 的 m 个元素,
1
2
3
4
5
import numpy as np

a = np.arange(10)
b = a[2:7:2] # 从索引 2 开始到索引 7 停止,间隔为 2
print(b)
  • 负 i 和 j 被解释为 n + i 和 n + j ,其中 n 是相应维度中的元素数量。负 k 使得踩踏指向更小的指数。
1
2
3
4
>>> x[-2:10]
array([8, 9])
>>> x[-3:3:-1]
array([7, 6, 5, 4])
  • 切片还可以包括省略号 …,来使选择元组的长度与数组的维度相同。 如果在行位置使用省略号,它将返回包含行中元素的 ndarray。Ellipsis扩展:为选择元组索引所有维度所需的对象数。在大多数情况下,这意味着扩展选择元组的长度是x.ndim。可能只存在一个省略号。
1
2
3
4
5
6
import numpy as np

a = np.array([[1,2,3],[3,4,5],[4,5,6]])
print (a[...,1]) # 第2列元素
print (a[1,...]) # 第2行元素
print (a[...,1:]) # 第2列及剩下的所有元素

高级索引

数组可以由整数数组索引、布尔索引及花式索引。

整数数组索引

当索引包含尽可能多的整数数组时,索引的数组具有维度,索引是直接的,但与切片不同。

  • 高级索引始终作为 一个 整体进行 广播 和迭代:
1
result[i_1, ..., i_M] == x[ind_1[i_1, ..., i_M], ind_2[i_1, ..., i_M],..., ind_N[i_1, ..., i_M]]
  • 应从每一行中选择特定的元素。 行索引只是[0,1,2],列索引指定要为相应行选择的元素,这里是[0,1,0]。 将两者结合使用,可以使用高级索引解决任务
1
2
3
4
5
6
7
import numpy as np 

x = np.array([[1, 2], [3, 4], [5, 6]])
y = x[[0,1,2], [0,1,0]]
print (y)

>>> [1,4,5]

布尔索引

我们可以通过一个布尔数组来索引目标数组。

布尔索引通过布尔运算(如:比较运算符)来获取符合指定条件的元素的数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import numpy as np 

x = np.array([[ 0, 1, 2],[ 3, 4, 5],[ 6, 7, 8],[ 9, 10, 11]])
print ('我们的数组是:')
print (x)
print ('\n')
# 现在我们会打印出大于 5 的元素
print ('大于 5 的元素是:')
print (x[x > 5])

我们的数组是:
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]

大于 5 的元素是:
[ 6 7 8 9 10 11]

花式索引

花式索引指的是利用整数数组进行索引。

  • 花式索引根据索引数组的值作为目标数组的某个轴的下标来取值。对于使用一维整型数组作为索引,如果目标是一维数组,那么索引的结果就是对应位置的元素;如果目标是二维数组,那么就是对应下标的行。花式索引跟切片不一样,它总是将数据复制到新数组中。

  • 传入顺序索引数组

1
2
3
4
5
6
7
8
9
10
import numpy as np 

x=np.arange(32).reshape((8,4))
print (x[[4,2,1,7]])
输出结果为:

[[16 17 18 19]
[ 8 9 10 11]
[ 4 5 6 7]
[28 29 30 31]]
  • 传入倒序索引数组
1
2
3
4
5
6
7
8
9
10
import numpy as np 

x=np.arange(32).reshape((8,4))
print (x[[-4,-2,-1,-7]])
输出结果为:

[[16 17 18 19]
[24 25 26 27]
[28 29 30 31]
[ 4 5 6 7]]

迭代数组

迭代器

NumPy 迭代器对象 numpy.nditer 提供了一种灵活访问一个或者多个数组元素的方式。

1
2
3
4
5
6
7
8
9
10
import numpy as np

a = np.arange(6).reshape(2,3)
print ('原始数组是:')
print (a)
print ('\n')
print ('迭代输出元素:')
for x in np.nditer(a):
print (x, end=", " )
print ('\n')

按顺序迭代

这反映了默认情况下只需访问每个元素,而无需考虑其特定顺序。我们可以通过迭代上述数组的转置来看到这一点,并与以 C 顺序访问数组转置的 copy 方式做对比,如下实例:

1
2
3
4
5
6
7
8
9
10
import numpy as np

a = np.arange(6).reshape(2,3)
for x in np.nditer(a.T):
print (x, end=", " )
print ('\n')

for x in np.nditer(a.T.copy(order='C')):
print (x, end=", " )
print ('\n')

控制遍历顺序

for x in np.nditer(a, order='F'):Fortran order,即是列序优先;
for x in np.nditer(a.T, order='C'):C order,即是行序优先

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import numpy as np

a = np.arange(0,60,5)
a = a.reshape(3,4)
print ('原始数组是:')
print (a)
print ('\n')
print ('原始数组的转置是:')
b = a.T
print (b)
print ('\n')
print ('以 C 风格顺序排序:')
c = b.copy(order='C')
print (c)
for x in np.nditer(c):
print (x, end=", " )
print ('\n')
print ('以 F 风格顺序排序:')
c = b.copy(order='F')
print (c)
for x in np.nditer(c):
print (x, end=", " )
输出结果为:

原始数组是:
[[ 0 5 10 15]
[20 25 30 35]
[40 45 50 55]]


原始数组的转置是:
[[ 0 20 40]
[ 5 25 45]
[10 30 50]
[15 35 55]]


以 C 风格顺序排序:
[[ 0 20 40]
[ 5 25 45]
[10 30 50]
[15 35 55]]
0, 20, 40, 5, 25, 45, 10, 30, 50, 15, 35, 55,

以 F 风格顺序排序:
[[ 0 20 40]
[ 5 25 45]
[10 30 50]
[15 35 55]]
0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55,

修改迭代的值

修改数组中元素的值
nditer 对象有另一个可选参数 op_flags。 默认情况下,nditer 将视待迭代遍历的数组为只读对象(read-only),为了在遍历数组的同时,实现对数组元素值得修改,必须指定 read-write 或者 write-only 的模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import numpy as np

a = np.arange(0,60,5)
a = a.reshape(3,4)
print ('原始数组是:')
print (a)
print ('\n')
for x in np.nditer(a, op_flags=['readwrite']):
x[...]=2*x
print ('修改后的数组是:')
print (a)
输出结果为:

原始数组是:
[[ 0 5 10 15]
[20 25 30 35]
[40 45 50 55]]


修改后的数组是:
[[ 0 10 20 30]
[ 40 50 60 70]
[ 80 90 100 110]]

广播迭代

广播迭代
如果两个数组是可广播的,nditer 组合对象能够同时迭代它们。 假设数组 a 的维度为 3X4,数组 b 的维度为 1X4 ,则使用以下迭代器(数组 b 被广播到 a 的大小)。

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
import numpy as np 

a = np.arange(0,60,5)
a = a.reshape(3,4)
print ('第一个数组为:')
print (a)
print ('\n')
print ('第二个数组为:')
b = np.array([1, 2, 3, 4], dtype = int)
print (b)
print ('\n')
print ('修改后的数组为:')
for x,y in np.nditer([a,b]):
print ("%d:%d" % (x,y), end=", " )
输出结果为:

第一个数组为:
[[ 0 5 10 15]
[20 25 30 35]
[40 45 50 55]]


第二个数组为:
[1 2 3 4]


修改后的数组为:
0:1, 5:2, 10:3, 15:4, 20:1, 25:2, 30:3, 35:4, 40:1, 45:2, 50:3, 55:4,