0%

Numpy基础

Numpy是Python中用于科学计算的核心库。它提供了高性能的多维数组对象,以及相关工具

绘制雪花

转自博客上看到的一位大牛的程序,利用Numpy + matplotlib 绘制雪花,效果如下:

snow

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
51
52
53
54
55
56
57
58
59
60
61
import numpy as np
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['FangSong'] # 指定默认字体
plt.rcParams['axes.unicode_minus'] = False # 解决中文显示为方块的问题


def rotate(p, d):
"""返回点p绕原点逆时针旋转d度的坐标"""

a = np.radians(d)
m = np.array([[np.cos(a), np.sin(a)], [-np.sin(a), np.cos(a)]])
return np.dot(p, m)


def koch_curve(p, q):
"""将线段pq生成科赫曲线,返回uvw三个点"""

p, q = np.array(p), np.array(q)
u = p + (q - p) / 3 # 三等分点u的坐标
v = q - (q - p) / 3 # 三等分点V的坐标
w = rotate(v - u, 60) + u # 线段uv绕u点逆时针旋转60°得到点w的坐标

return u.tolist(), v.tolist(), w.tolist()


def snow(triangle, k):
"""给定三角形,生成封闭的科赫雪花"""

for i in range(k):
result = list()
t_len = len(triangle)
for j in range(t_len):
p = triangle[j]
q = triangle[(j + 1) % t_len]
u, v, w = koch_curve(p, q)
result.extend([p, u, w, v])
triangle = result.copy()

triangle.append(triangle[0])
return triangle


def plot_snow(snow_list):
"""绘制雪花"""

for triangle, k in snow_list:
data = np.array(snow(triangle, k))
x, y = np.split(data, 2, axis=1)
plt.plot(x, y)

plt.axis('equal')
plt.show()


snow_list = [
([(0, 0), (0.5, 0.8660254), (1, 0)], 5),
([(1.1, 0.4), (1.35, 0.8330127), (1.6, 0.4)], 4),
([(1.1, -0.1), (1.25, 0.15980761), (1.4, -0.1)], 3)
]
plot_snow(snow_list)

数组Arrays

numpy数组是一个由不同数值组成的网格。网格中的数据都是同一种数据类型,可以通过非负整型数的元组来访问。

我们可以从列表创建数组,然后利用方括号访问其中的元素。

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

a = np.array([1,2,3]) #创建一个一维数组
print(type(a))
print(a[0],a[1],a[2])
a[0] = 5
print(a)

<class 'numpy.ndarray'>
1 2 3
[5 2 3]

函数shape()用法

shape函数功能室查看矩阵或者数组的维度

例如:建立一个3✖3的单位矩阵e,e.shape()为(3,3),表示三行三列,第一维的长度是3,第二维的长度也是3。

1
2
3
4
5
6
7
8
9
10
e = np.eye(3)
print(e)
print(e.shape)
print(e[0,0],e[1,1])

[[ 1. 0. 0.]
[ 0. 1. 0.]
[ 0. 0. 1.]]
(3, 3)
1.0 1.0

建立一个一维矩阵b,b.shape为矩阵的长度。

1
2
3
4
b = np.array([1,2,3,4])
print(b.shape)

(4,)

建立一个4✖2的矩阵c,c.shape[1]为第一维的长度,c.shape[0]为第二维的长度,c.shape[1]为第一维的长度。

1
2
3
4
5
6
c = np.array([[1,2],[3,5],[9,5],[4,0]])
print(c.shape[0])
print(c.shape[1])

4
2

一个单独的数值,返回值为空。

1
2
3
np.shape(3)

()

其他创建数组的方法

Numpy还提供了很多其他创建数组的方法:

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

a = np.zeros((2,3)) #创建一个2✖️3全0数组
print(a)
print("\n")

b = np.ones((1,2)) #创建一个1✖️2全1数组
print(b)
print("\n")

c = np.full((4,2),7) #创建一个4✖️2的全7数组
print(c)
print("\n")

d = np.eye(2) #创建一个二维单位矩阵
print(d)
print("\n")

e = np.random.random((2,2))#创建一个2✖️2由随机数字组成的数组
print(e)

[[ 0. 0. 0.]
[ 0. 0. 0.]]


[[ 1. 1.]]


[[7 7]
[7 7]
[7 7]
[7 7]]


[[ 1. 0.]
[ 0. 1.]]


[[ 0.82662589 0.93604335]
[ 0.20764393 0.53211415]]

访问数组

切片

切片:和Python列表类似,numpy数组可以使用切片语法。因为数组是多维的,所以必须为每个维度指定好切片。

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

#创建一个3✖️4的数组
a = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
print(a)
print("\n")

#利用切片来截取前两行,第1到2列。注意这里“:2”是第0,1行,不包含第二行,与range()相同
b = a[:2,1:3]
print(b)
print("\n")

#切片是原数组的一部分,所以改变切片,原数组也会改变
print(a[0,1])
b[0,0] = 77
print(a)


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


[[2 3]
[6 7]]


2
[[ 1 77 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]

你可以同时使用整型和切片语法来访问数组。但是,这样会产生一个比原数组低阶的新数组。

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

#3✖️4
a = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
print(a)
print(a.shape)
print("\n")

row_r1 = a[1,:]
row_r2 = a[1:2,:]

print(row_r1)
print(row_r1.shape)
print("\n")

print(row_r2)
print(row_r2.shape)
print("\n")

[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
(3, 4)


[5 6 7 8]
(4,)


[[5 6 7 8]]
(1, 4)

整型数组访问

当我们使用切片语法访问数组时,得到的总是原数组的一个子集。整型数组访问允许我们利用其他数组的数据构建一个新的数组。

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

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

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

#上面的print等价于
print([a[0,0],a[1,1],a[2,0]])

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


[[1 4 5]]
[1, 4, 5]

整型数组访问语法还有一个有用的技巧,可以用来选择或者更改矩阵中每行中的一个元素:

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

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

b = np.array([0,2,0,1])

#利用b中的索引在a的每行选择一个元素
print(a[np.arange(4),b])
print("\n")

a[np.arange(4),b] += 10
print(a)

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


[ 1 6 7 11]

[[11 2 3]
[ 4 5 16]
[17 8 9]
[10 21 12]]

布尔型数组访问

布尔数组访问可以让你选择数组中任意元素。通常,这种访问方式用于选取数组中满足某些条件的元素。

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

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

#返回大于2的元素,这返回一个与原数组同型的布尔数组
bool_idx = (a > 2)

print(bool_idx)

#利用生成的布尔数组构建一个大于2的一维数组
print(a[bool_idx])

print(a[a > 2])

[[False False]
[ True True]
[ True True]]
[3 4 5 6]
[3 4 5 6]

数组计算

既可利用操作符重载,也可以使用函数方式:

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

x = np.array([[1,2],[3,4]],dtype=np.float64)
y = np.array([[5,6],[7,8]],dtype=np.float64)

#加
print(x+y)
print(np.add(x,y))

#减
print(x-y)
print(np.subtract(x,y))

#乘,对应元素乘法
print(x*y)
print(np.multiply(x,y))

#除,对应元素除法
print(x/y)
print(np.divide(x,y))

#开方
print(np.sqrt(x))

#矩阵乘法
a = np.array([[1,2],[3,4]])
b = np.array([[5,6],[7,8]])

v = np.array([9,10])
w = np.array([11,12])

print(v.dot(w))
print(np.dot(v,w))
print(a.dot(v))
print(np.dot(a,v))

Numpy提供了很多计算数组的函数,其中最常用的一个是sum:

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

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

#Compute sum of all elements
print(np.sum(x))

#Compute sum of each column
print(np.sum(x,axis=0))

#Compute sum of each row
print(np.sum(x,axis=1))

查看更多函数,查看SciPy.org

1
2
3
4
5
import numpy as np

x = np.array([[1,2],[3,4]])
#转置
print(x.T)

查看更多操作数组的方法

广播Broadcasting

广播是一种强有力的机制,它让Numpy可以让不同大小的矩阵在一起进行数学计算。我们常会有一个小的矩阵和一个大的矩阵,然后需要用小的矩阵对大的矩阵做一些计算。

例如,我们想要把一个向量加到矩阵的每一行,我们可以这样做:

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

x = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])
v = np.array([1,0,1])
#Create an empty matrix with the same shape as x
y = np.empty_like(x)

for i in range(4):
y[i,:] = x[i,:] + v

print(y)

这样是行的通的,但是当x矩阵非常大,利用循环开计算就会变得很慢很慢。我们可以换一种思路。

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

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

#stack 4 copies of v on top of each other
vv = np.tile(v,(4,1))
print(vv)

y = x + vv
print(y)

[[1 0 1]
[1 0 1]
[1 0 1]
[1 0 1]]

[[ 2 2 4]
[ 5 5 7]
[ 8 8 10]
[11 11 13]]

Numpy广播机制可以让我们不用创建vv,就能直接运算,看看下面的例子。

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

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

y = x + v
print(y)

[[ 2 2 4]
[ 5 5 7]
[ 8 8 10]
[11 11 13]]

对两个数组使用广播机制要遵守下列规则:

1.如果数组的秩不同,使用1来将秩小的数组进行扩展,直到两个数组的尺寸长度都一样。

2.如果两个数组在某个维度上的长度是一样的,或者其中一个数组在该维度上的长度为1,那么我们说这两个数组在该维度上是相容的。

3.如果两个数组在所有维度上都是相容的,它们就能使用广播。

4.如果两个输入数组的尺寸不同,那么注意其中较大的那个尺寸。因为广播之后,两个数组的尺寸将和较大的尺寸一样。

5.在任何一个维度上,如果一个数组的长度为1,另一个数组的长度大于1,那么在该维度上,就好像是对第一个数组进行了复制。

参阅文档注释。支持广播机制的函数是全局函数。哪些是全局函数可以在文档中查找。

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

#v is [1,2,3],w is [4,5]
v = np.array([1,2,3])
w = np.array([4,5])

print(np.reshape(v,(3,1)) * w)

#x is [[1,2,3]
# [4,5,6]]
x = np.array([[1,2,3],[4,5,6]])
print(x + v)

print(x.T + w)

print(x + np.reshape(w,(2,1)))

print(x * 2)

[[ 4 5]
[ 8 10]
[12 15]]

[[2 4 6]
[5 7 9]]

[[ 5 9]
[ 6 10]
[ 7 11]]

[[ 5 6 7]
[ 9 10 11]]

[[ 2 4 6]
[ 8 10 12]]

Numpy文档

此文介绍了numpy中的一些重要内容,但是numpy远不止如此,可以查阅numpy文献来学习更多。

SciPy

1
$ pip3 install Pillow

Numpy提供了高性能的多维数组,以及计算和操作数组的基本工具。Scipy基于Numpy,提供了大量的计算和操作数组的函数,这些函数对于不同类型的科学和工程计算非常有用。

SciPy文档

点之间的距离

SciPy定义了一些有用的函数,可以计算集合中点之间的距离。

函数scipy.spatial.distance.pdist能够计算集合中所有两点之间的距离。

1
2
3
4
5
6
7
8
import numpy as np
from scipy.spatial.distance import pdist, squareform

x = np.array([[0,1],[1,0],[2,0]])
print(x)

d = squareform(pdist(x,'euclidean'))
print(d)

具体细节请阅读文档

函数scipy.spatial.distance.cdist可以计算不同集合中点的距离,具体请查看文档