NumPy入门

什么是NumPy?

NumPy是python中科学计算的基础包。提供多维数组对象,各种派生对象(如掩码数组和矩阵),以及用于数组快速操作的各种API,有包括数学、逻辑、形状操作、排序、选择、输入输出、离散傅立叶变换、基本线性代数,基本统计运算和随机模拟等等。

NumPy数组 和 原生Python Array(数组)之间有几个重要的区别:

  • NumPy 数组在创建时具有固定的大小,与Python的原生数组对象(可以动态增长)不同。更改ndarray的大小将创建一个新数组并删除原来的数组。
  • NumPy 数组中的元素都需要具有相同的数据类型,因此在内存中的大小相同。 例外情况:Python的原生数组里包含了NumPy的对象的时候,这种情况下就允许不同大小元素的数组。
  • NumPy 数组有助于对大量数据进行高级数学和其他类型的操作。通常,这些操作的执行效率更高,比使用Python原生数组的代码更少。
  • 越来越多的基于Python的科学和数学软件包使用NumPy数组; 虽然这些工具通常都支持Python的原生数组作为参数,但它们在处理之前会还是会将输入的数组转换为NumPy的数组,而且也通常输出为NumPy数组。换句话说,为了高效地使用当今科学/数学基于Python的工具(大部分的科学计算工具),你只知道如何使用Python的原生数组类型是不够的 - 还需要知道如何使用 NumPy 数组。

快速入门教程

NumPy官网

NumPy中文网站

1
2
# 蹈入包
import numpy as np

1.基础知识

NumPy的数组类被调用为ndarray:一个同质多维数组(同质指所有元素类型相同)

1
2
import numpy as np
a = np.arange(15).reshape(3,5) #生成一个3*5的矩阵
  • ndarray.ndim数组的轴(维度)的个数
  • ndarray.shape数组的维度
  • ndarray.size数组元素的总数
  • ndarray.dtype一个描述数组中元素类型的对象
  • ndarray.itemsize数组中每个元素的字节大小
  • ndarray.data该缓冲区包含数组的实际元素。通常,我们不需要使用此元素,因为我们将使用索引访问数组中的元素。

1.1 数组创建

方法一:通过Python的list或tuple创建ndarray

1
2
3
4
5
6
#具体做法就是传入一个list或tuple作为参数
a = np.array([1,2,3,4])
#一种典型的错误情况:a = np.array(1,2,3,4)

#如果传入又多个参数
b = np.array([(1,5,2,3),(4,5,6)])

方法二:用占位符创建已知尺寸的数组

1
2
3
4
5
6
7
8
9
10
11
d = np.zeros((3,4))
e = np.ones((2,3,4),dtype=np.int16)
f = np.empty((2,3)) #其初始内容是随机的,取决于内存的状态,默认情况下dtype是float64类型

#arrange类似于python内置的range
g = np.arange(10,30,5)
h = np.arange(0,2,0.3) #可以使用浮点数,步长为0.3

#arrange函数由于浮点数精度的原因,可能无法知道数组最后有多少个数
#为了解决这一问题可以使用linspace函数
i = np.linspace(0,2,9) #9个数组从0到2

1.2 打印数组

如果数组太大而无法打印,NumPy会自动跳过数组的中心部分并打印角点,要禁用此行为并强制NumPy打印整个数组,可以使用打印选项set_printoptions

1
np.set_printoptions(threshold=sys.maxsize)

1.3 基本数学操作

加减这里就不需要演示了,乘积运算符*在NumPy数组中按元素进行运算。矩阵乘积可以使用@运算符或dot函数或方法执行:

1
2
3
4
5
6
7
8
9
10
A = np.array([[1,1],[0,1]])
B = np.array([[2,0],[3,4]])
A*B
#结果为
array([[2,0],[0,4]])
A@B
#结果为
array([[5,4],[3,4]])
A.dot(B)
array([[5,4],[3,4]])

+=,*=等运算符进行原地运算(需要加同类型的,如果不强制转换就会报错)

默认情况下,这些操作适用于数组,就像它是一个数字列表一样,无论其形状如何。但是,通过指定axis 参数,您可以沿数组的指定轴应用操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> b = np.arange(12).reshape(3,4)
>>> b
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>>
>>> b.sum(axis=0) # sum of each column
array([12, 15, 18, 21])
>>>
>>> b.min(axis=1) # min of each row
array([0, 4, 8])
>>>
>>> b.cumsum(axis=1) # cumulative sum along each row
array([[ 0, 1, 3, 6],
[ 4, 9, 15, 22],
[ 8, 17, 27, 38]])

1.4 索引,切片和迭代

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
>>> a = np.arange(10)**3
>>> a
array([ 0, 1, 8, 27, 64, 125, 216, 343, 512, 729])
>>> a[2]
8
>>> a[2:5]
array([ 8, 27, 64])
>>> a[:6:2] = -1000 # equivalent to a[0:6:2] = -1000; from start to position 6, exclusive, set every 2nd element to -1000
>>> a
array([-1000, 1, -1000, 27, -1000, 125, 216, 343, 512, 729])
>>> a[ : :-1] # reversed a
array([ 729, 512, 343, 216, 125, -1000, 27, -1000, 1, -1000])
>>> for i in a:
... print(i**(1/3.))
...
nan
1.0
nan
3.0
nan
5.0
6.0
7.0
8.0
9.0

三个点(...)表示产生完整索引元组所需的冒号。例如x是rank为5的数组,则:

  • x[1,2,...]相当于x[1,2,:,:,:]
  • x[...,3]等效于x[:,:,:,:,3]
  • x[4,...,5,:]等效于x[4,:,:,5,:]

对多维数组进行迭代(Iterating)是相对于第一个轴完成的:

1
2
3
4
5
6
7
8
>>> for row in b:
... print(row)
...
[0 1 2 3]
[10 11 12 13]
[20 21 22 23]
[30 31 32 33]
[40 41 42 43]

但是,如果想要对数组中的每个元素执行操作,可以使用flat属性,该属性是数组的所有元素的迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>> for element in b.flat:
... print(element)
...
0
1
2
3
10
11
12
13
20
21
22
23
30
31
32
33
40
41
42
43

2. 形状操纵

###2.1 改变数组的形状

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> a.ravel()  # returns the array, flattened
array([ 2., 8., 0., 6., 4., 5., 1., 1., 8., 9., 3., 6.])
>>> a.reshape(6,2) # returns the array with a modified shape
array([[ 2., 8.],
[ 0., 6.],
[ 4., 5.],
[ 1., 1.],
[ 8., 9.],
[ 3., 6.]])
>>> a.T # returns the array, transposed
array([[ 2., 4., 8.],
[ 8., 5., 9.],
[ 0., 1., 3.],
[ 6., 1., 6.]])
>>> a.T.shape
(4, 3)
>>> a.shape
(3, 4)

reshape函数返回带有修改形状的参数,而该ndarray.resize方法会修改数组本身:

1
2
3
4
5
6
7
8
>>> a
array([[ 2., 8., 0., 6.],
[ 4., 5., 1., 1.],
[ 8., 9., 3., 6.]])
>>> a.resize((2,6))
>>> a
array([[ 2., 8., 0., 6., 4., 5.],
[ 1., 1., 8., 9., 3., 6.]])

如果在 reshape 操作中将 size 指定为-1,则会自动计算其他的 size 大小:

1
2
3
4
>>> a.reshape(3,-1)
array([[ 2., 8., 0., 6.],
[ 4., 5., 1., 1.],
[ 8., 9., 3., 6.]])

2.2 将不同数组推叠在一起

几个数组可以沿不同的轴堆叠在一起,例如:

np.vstack相当于列拼接,np.hstack相当于行拼接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> a = np.floor(10*np.random.random((2,2)))
>>> a
array([[ 8., 8.],
[ 0., 0.]])
>>> b = np.floor(10*np.random.random((2,2)))
>>> b
array([[ 1., 8.],
[ 0., 4.]])
>>> np.vstack((a,b))
array([[ 8., 8.],
[ 0., 0.],
[ 1., 8.],
[ 0., 4.]])
>>> np.hstack((a,b))
array([[ 8., 8., 1., 8.],
[ 0., 0., 0., 4.]])

2.3 将一个数组拆分成几个较小的数组

使用hsplit,可以沿数组的水平轴拆分数组,方法是指定要返回的形状相等的数组的数量,或者指定应该在其之后进行分割的列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> a = np.floor(10*np.random.random((2,12)))
>>> a
array([[ 9., 5., 6., 3., 6., 8., 0., 7., 9., 7., 2., 7.],
[ 1., 4., 9., 2., 2., 1., 0., 6., 2., 2., 4., 0.]])
>>> np.hsplit(a,3) # Split a into 3
[array([[ 9., 5., 6., 3.],
[ 1., 4., 9., 2.]]), array([[ 6., 8., 0., 7.],
[ 2., 1., 0., 6.]]), array([[ 9., 7., 2., 7.],
[ 2., 2., 4., 0.]])]
>>> np.hsplit(a,(3,4)) # Split a after the third and the fourth column
[array([[ 9., 5., 6.],
[ 1., 4., 9.]]), array([[ 3.],
[ 2.]]), array([[ 6., 8., 0., 7., 9., 7., 2., 7.],
[ 2., 1., 0., 6., 2., 2., 4., 0.]])]

vsplit沿垂直轴分割,并array_split允许指定要分割的轴。

3. 拷贝和视图

3.1 完全不复制

简单分配不会复制数组对象或其数据。

b=a

3.2 视图或浅拷贝

不同的数组对象可以共享相同的数据。该view方法创建一个查看相同数据的新数组对象。

改变其数值,原来的数组数值也会发生改变。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> c = a.view()
>>> c is a
False
>>> c.base is a # c is a view of the data owned by a
True
>>> c.flags.owndata
False
>>>
>>> c.shape = 2,6 # a's shape doesn't change
>>> a.shape
(3, 4)
>>> c[0,4] = 1234 # a's data changes
>>> a
array([[ 0, 1, 2, 3],
[1234, 5, 6, 7],
[ 8, 9, 10, 11]])

切片数组会返回一个视图

3.3 深拷贝

copy方法生成数组及其数据的完整副本。

4. Less基础

广播(Broadcasting)规则

广播允许通用功能以有意义的方式处理不具有完全相同形状的输入。

广播的第一个规则是,如果所有输入数组不具有相同数量的维度,则将“1”重复地预先添加到较小数组的形状,直到所有数组具有相同数量的维度。

广播的第二个规则确保沿特定维度的大小为1的数组表现为具有沿该维度具有最大形状的数组的大小。假定数组元素的值沿着“广播”数组的那个维度是相同的。

应用广播规则后,所有数组的大小必须匹配。更多细节可以在广播中找到。

5. 花式索引和索引技巧

使用索引数组进行索引

1
2
3
4
5
6
7
8
9
10
>>> a = np.arange(12)**2                       # the first 12 square numbers
array([ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121])
>>> i = np.array( [ 1,1,3,8,5 ] ) # 显示a中的位置
>>> a[i] # the elements of a at the positions i
array([ 1, 1, 9, 64, 25])
>>>
>>> j = np.array( [ [ 3, 4], [ 9, 7 ] ] ) # a bidimensional array of indices
>>> a[j] # the same shape as j
array([[ 9, 16],
[81, 49]])

使用布尔数组进行索引

我们可以明确地选择我们想要的数组中的哪些项目以及我们不需要的项目。

1
2
3
4
5
6
7
8
>>> a = np.arange(12).reshape(3,4)
>>> b = a > 4
>>> b # b is a boolean with a's shape
array([[False, False, False, False],
[False, True, True, True],
[ True, True, True, True]])
>>> a[b] # 1d array with the selected elements
array([ 5, 6, 7, 8, 9, 10, 11])