Numpy用法(一)

文章发布时间:

最后更新时间:

文章总字数:
2.4k

预计阅读时间:
10 分钟

一.数组切片

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

a[x , y],x 为行索引,y 为列索引。x,y的格式都是 a : b : c ,其中 a,b,c 为整数,a 为起始索引,b 为结束索引,c 为步长。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy

a = numpy.array([[1,2,3],[4, 5, 6]])

# 查询第一、二列(其中:可以用 ... 代替)
print(a[:,0:2])

# 查询第一行的第一列和第二列
print(a[0,0:2])

# 更改提取的切片会改变原数组
b = a[0,0:2]
b[:] = 1
print(b)
print(a)

结果:

1
2
3
4
5
6
[[1 2]
[4 5]]
[1 2]
[1 1]
[[1 1 3]
[4 5 6]]

二.高级索引

2.1 整数数组索引

  • 使用一个数组来访问另一个数组的元素。

  • 这个数组中的每个元素都是目标数组中某个维度上的索引值。

就是把数组中元素的横纵坐标取出来,所有要取的元素的横坐标放在一个列表中,纵坐标放在另一个列表中,然后将两个列表整合成一个数组,用这个数组作为下标访问就可以得到想要的值了。

看到这里是不是觉得有点懵,没事,举个例子就明白了:

1
2
3
4
5
6
7
8
9
# 示例
import numpy as np

# 创建一个二维数组
a = numpy.array([[1,2,3],[4, 5, 6]])

# 把元素'1'和'5'的横坐标取出来[0,1],纵坐标取出来[0,1],合并成数组就是[[0,1],[0,1]]

print(a[[0,0],[0,1]])

结果:

1
[1 2]

  也就是说,先根据数组形状,在脑子里构建一个坐标系。你要取什么元素,就把这个元素的横坐标单独取出来,纵坐标单独取出来,然后把这两个列表组合起来当成数组下标就能拿到了你想要的元素了。

还有一个特殊情况,可以用简便方法:

1
2
3
4
5
6
7
# 取出 4 * 3 数组四个角的数据
import numpy as np

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

# 倒数第一行、列可以用 -1 表示
print(a[[0,0,-1,-1],[0,-1,0,-1]])

结果:

1
[ 1  3 10 12]

2.2 布尔索引

通过布尔运算,来获取符合指定条件的元素的数组。

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

a = np.array([1,2,3,5,6,7])

# 也就是一个包含a的表达式,把数组中每个元素都用这个表达式进行处理并返回一个布尔值
bool_idx = a > 2
print(bool_idx)

# 将表达式作为下标进行访问,会返回满足条件的元素
print(a[bool_idx])

结果:

1
2
[False False  True  True  True  True]
[3 5 6 7]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 高维数组

import numpy as np

# 一个二维数组
a = np.array([[1,2,3],[4,5,6],[7,8,9]])

print(a)


# 换个行

print()

# 取出满足条件的元素
print(a[a>4])

结果:

1
2
3
4
5
[[1 2 3]
[4 5 6]
[7 8 9]]

[5 6 7 8 9]

大的来了——:

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

# 切片布尔索引
# 这里的关键点是被布尔处理后的每个元素返回一个布尔值,为True的返回该元素所在的行或列


# 一个二维数组
a = np.array([[1,2,3],[4,5,6],[7,8,9]])


# 布尔处理第三列中的值大于3的元素,将结果作为下标索引行
# (因为这里的表达式是写在行这边的,所以索引的是行)
print(a[a[...,2] > 3]) # 这里其实默认是(a[a[...,2] > 3],...)


# 换个行
print()


# 布尔处理第三行的值大于7的元素,将结果作为下标索引行
#(这里是把第三行被布尔处理后的值当作是行的索引)
print(a[a[2,...] > 7])



######
# 比如这里索引的结果是[False,True,True],而表达式在行这边,所以就取下标为1和2的行
# 也就是把布尔处理后True的顺序[0(为假),1,2]作为下标,返回用这个下标的行
######

结果:

1
2
3
4
5
[[4 5 6]
[7 8 9]]

[[4 5 6]
[7 8 9]]
1
2
3
4
5
6
7
8
# 布尔处理第2行 值大于5 的元素,将其作为下标索引所在列(因为这里的表达式是写在列这边的,所以索引的是列)
print(a[:, a[1] > 5])

# 换个行
print()

# 布尔处理第2列 值大于4 的元素,然后将结果作为下标索引最前面取出来的一、二行中的列
print(a[0:2,a[...,1] > 4])

结果:

1
2
3
4
5
6
[[3]
[6]
[9]]

[[2 3]
[5 6]]

这里有一个简单的理解方法:

a [] 的括号中是要放表达式的,可能既有行也有列,当其中一个地方(行或列)填表达式时,可以先解读表达式的结果,然后把结果代入另一个列(或行)的切片去找到满足表达式的值。

同时,布尔运算也能加上逻辑运算符,各个条件要加上():

  • &:与运算,组合多个条件。

  • |:或运算,组合多个条件。

  • ~:非运算,取反条件。

2.3 提取数组主对角线元素

使用 diag() 方法

1
2
3
4
import numpy as np

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

结果:

1
[1 5 9]

三.广播

  是 numpy 对不同形状(shape)的数组进行数值计算的方式,要求维数相同,且各维度的长度相同,如果不相同,可以通过广播机制,这种机制的核心是对形状较小的数组,在横向或纵向上进行一定次数的重复,使其与形状较大的数组拥有相同的维度。

1
2
3
4
5
6
7
8
# 维度不同,但某一维度上长度(元素个数)相同

import numpy

a = numpy.array([1,2,3])
b = numpy.array([[4,5,6],[7,8,9]])

print(a + b)

结果:

1
2
[[ 5  7  9]
[ 8 10 12]]

可以看到,a的形状是 (3,) ,b的形状是 (2,3) ,a会通过广播机机制复制自身增加维度让自己的形状变成(2,3),这样a和b的形状就匹配了。

1
2
3
4
5
6
7
8
# 维度不同,没有一个维度的长度相同,但其中一个数组在该维度上的长度为 1

import numpy

a = numpy.array([1,2,3])
b = numpy.array([[1],[2]])

print(a+b)

结果:

1
2
[[2 3 4]
[3 4 5]]

维度不匹配的时候,先看看其中一个数组的每个维度中是否只有一个元素或没有元素,如果是的话,那么就自动扩展这个维度长度;如果没有元素就向高维扩展(形状(2,2)往前面扩展为(1,2,2))依次扩展其他维度。这里就是把b数组中的[1]扩展成[1 1 1],[2]扩展成[2 2 2]。然后就变成了长度匹配的做法了。

1
2
3
4
5
6
7
8
# 与标量相加

import numpy as np

a = np.array([1, 2, 3, 4])

# 1 会自动扩展为[1 1 1 1]
print(a + 1)

结果:

1
[2 3 4 5]

其余大多数情况,都是不能进行广播的,比如:

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

# 形状为(3,)
a = np.array([1, 2, 3])


# 形状为(2,2)
b = np.array([[1, 2], [4, 5]])
print(a+b)

结果:

1
2
3
4
5
6
7
8
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[26], line 6
3 a = np.array([1, 2, 3])
5 b = np.array([[1, 2], [4, 5]])
----> 6 print(a+b)

ValueError: operands could not be broadcast together with shapes (3,) (2,2)

也就是两个数组行列至少有一边长度相同,另一边长度为1;或俩都有一边长度为1;或一个数组的几个维度长度全为1,这样才可以进行广播相加。

四.数组遍历

4.1 遍历数组的第一维度

  • for i in arr:

遍历数组的第一维度,即按行或列的顺序逐个访问元素,返回的是数组的子数组(如行或列),而不是单个元素。

1
2
3
4
5
6
7
import numpy as np

a = np.array([[1, 2, 3],[4,5,6]])

# 这里就是通过行来进行遍历,一维的话只需要[]就可以了,而不是[][]两个下标才能得到值
for i in a:
print(i)

结果:

1
2
[1 2 3]
[4 5 6]

4.2 nditer逐个访问元素

是 NumPy 中的一个强大的迭代器对象,其中有两个重要参数:orderflags

  1. order 参数用于指定数组的遍历顺序。默认情况下,nditer 按照 C 风格(行优先)遍历数组。
  • C 风格(行优先): order=’C’

  • Fortran 风格(列优先): order=’F’

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

a = np.array([[1, 2, 3],[4, 5, 6]])

# 按列优先
for i in np.nditer(a,order='F'):
print(i)

# 换个行
print()

# 按行优先
for i in np.nditer(a,order='C'):
print(i)

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
1
4
2
5
3
6

1
2
3
4
5
6
  1. flags 参数用于指定迭代器的额外行为,一般配合新建变量来使用
  • multi_index: 返回每个元素的多维索引,这是一个存储数据的东西,需要引用

  • external_loop: 返回一维数组而不是单个元素,减少函数调用的次数,从而提高性能。

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

a = np.array([[1, 2, 3],[4, 5, 6]])

# 返回多维索引(坐标)
x = np.nditer(a,flags = ['multi_index'])
for i in x:
print(i,x.multi_index)

结果:

1
2
3
4
5
6
1 (0, 0)
2 (0, 1)
3 (0, 2)
4 (1, 0)
5 (1, 1)
6 (1, 2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 返回一维数组

# external_loop 和 order='C' 一起使用时,会将所有元素打包成一个数组返回,而不是按行打包。
x = np.nditer(a,flags = ['external_loop'],order='C')

# external_loop 和 order='F' 一起使用时,会将每列的元素打包成一个数组返回。
y = np.nditer(a,flags = ['external_loop'],order='F')


for i in x:
print(i)

# 换个行
print()

for i in y:
print(i)

结果:

1
2
3
4
5
[1 2 3 4 5 6]

[1 4]
[2 5]
[3 6]