JS的事以后有机会再搞吧,近期由于学习的需要,于是重新搞起了python,简单地学习了一下Matplotlib库,还大概了解了一下爬虫和OpenCV库。先来简单聊聊Matplotlib库吧。

一、快速上手Matplotlib库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 导入包
import matplotlib.pyplot as plt
import numpy as np

plt.figure(num=1, figsize=(10, 5)) # 定义窗口 num图片数量,figsize图片大小

#以下是绘制不同类型图像的函数
plt.plot(x, y, color="r", linewidth=2, linestyle="--", label=" ") # 折线图 可以设置具体的值和线条的呈现 颜色
plt.scatter(x, y, s=75, alpha=0.5, maker="o") # 绘制散点图,maker默认为'o',也可设置其他类型,比如星形。
plt.bar([1, 2, 3, 4], [4, 6, 3, 6], ) # 直方图
plt.hist(list, bins, histype="bar", rwidth=0.8, label=) # 条形图

plt.xlim(-1, 2) #
plt.ylim(-1, 2) # 设置x,y轴的坐标范围

plt.show() # 展示

基本的使用流程就是:引入包 —>创建一个画板—>使用对应绘图函数进行绘图—>展示图像

当然,里面还可以设置很多属性的,比如设置图像的标题、xy轴的注记,也有一些进阶的技巧,比如绘制多个子图等等。这个就需要后续继续探索了

另外matplotlib库经常会和numpy库一起使用,numpy 是一个运行速度非常快的数学库,主要用于数组计算,绘图过程中不可避免地要传入图像的各种坐标数据,这时候用numpy就非常方便了,具体提供了怎样的便利,使用过程中就可以渐渐体会到了。

二、绘制太阳系行星的运转图

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
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np


class Planet:
# 参数分别为 初始坐标:x,y ,颜色:c,面积大小s,轨道半径R,转速w
# 同时将x作为轨道半径
def __init__(self, center=(0, 0), s=10, c='b', R=1, w=1):
self.x = center[0]
self.y = center[1]
self.R = R
self.s = s
self.c = c
self.w = w

def draw_planet(self):
plt.scatter(self.x, self.y, self.s, self.c)

def draw_orbit(self):
draw_circle = plt.Circle((0, 0), self.R, fill=False, alpha=0.2, color='white')
ax.add_artist(draw_circle)

def set_location(self, t):
self.x = self.R * np.cos(2 * np.pi * self.w * t)
self.y = self.R * np.sin(2 * np.pi * self.w * t)

def get_loc(self):
return self.x, self.y

def get_color(self):
return self.c


# 水 金 地 火 木 土 天王 海王 共八大行星
Mercury = Planet((10, 0), 5, '#D9AA74', R=10, w=60)
Venus = Planet((15, 0), 5, '#E4A66D', R=15, w=30)
Earth = Planet((20, 0), 5, '#69D821', R=20, w=20)
Mars = Planet((25, 0), 5, '#ED713D', R=25, w=10)
Jupiter = Planet((30, 0), 5, '#C58347', R=30, w=1.5)
Saturn = Planet((35, 0), 5, '#95633E', R=35, w=0.6)
Uranus = Planet((40, 0), 5, '#3DBBF6', R=40, w=0.2)
Neptune = Planet((45, 0), 5, '#0753A1', R=45, w=0.1)

# 将八大行星放进列表中,可进行遍历,方便调用
eight_planets = [Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune]

# 创建画布
fig = plt.figure(figsize=(7, 7))
ax = fig.add_subplot(facecolor='black')
plt.xlim(-50, 50)
plt.ylim(-50, 50)

# 绘制星星
for i in range(500):
x = np.random.uniform(-50, 50)
y = np.random.uniform(-50, 50)
size = np.random.random(1) / 1.2
# s指大小,c指颜色,marker指符号形状
plt.plot(x, y, 'wo', markersize=size)
# 绘制太阳
sun = plt.scatter(0, 0, s=1000, c='r')
# 绘制八大行星
scat = plt.scatter([x.get_loc()[0] for x in eight_planets], [x.get_loc()[1] for x in eight_planets],
c=[x.get_color() for x in eight_planets])

for i in eight_planets:
i.draw_orbit()

# 随时间变化,设置新的行星位置坐标
def update(t):
for planet in eight_planets:
planet.set_location(t)
scat.set_offsets([[x.get_loc()[0], x.get_loc()[1]] for x in eight_planets])
return scat,

ani = FuncAnimation(fig, func=update, frames=np.linspace(0, 1, 2000), interval=60)

plt.show()

# 用于将图像保存为gif动图
# ani.save('solar_system.gif', writer='pillow', fps=60, dpi=100)

2、代码说明

第一部分,我们创建了一个类,可以用来实例化各个行星,也方便后续操作。

第二部分,我们就可以利用Matplotlib库进行绘图了。

这里稍稍说一下scatter函数吧

1
2
3
4
5
# 省略引入库与创建画板
x=[1,3]
y=[2,4]
plt.scatter(x,y,c='r',s=100) #绘制两个面积为100的点(1,2)和(3,4)
plt.scatter(1,5,c='b',s=10)

在向scatter函数里面传入坐标时,可以传入一个值,也可以传入一个列表,传入列表的话就代表的是绘制多个点了。

接着再来谈一谈FuncAnimation函数的几个参数

  • fig:画布对象,由创建画布时的返回得到,即fig = plt.figure()

  • frames:指定动图的帧数,但这个参数类型必须是可迭代的列表等,可自行设置。每次调用func函数对图像进行更新时,接口将自动向func函数提供此时的帧数frame,这使得更新数据十分方便

  • func:用于更新图片从而产生动态效果的调用函数,在编写时通常会用到set_data等类似的方法,其返回值是一个元素为被更新的图形对象的列表。同时,func可以接受帧数参数frame,用来更新每帧图像

    简单地说就是可以给frames指定一个列表,动画函数会一直执行,次数与frames列表中元素个数相同,每次调用时,会将frames中的一个元素传递给func函数作为参数(第几次调用,就传递列表中第几个元素作为参数),然后利用这个参数就可以更新图像的点。

  • interval:更新频率,单位是毫秒

三、丧尸病毒传播

说明:编写一个函数,初始条件有若干丧尸与健康人,丧尸会追逐最近的人,追上则感染对方。人会检测与之最近的丧尸,朝其反方向奔跑。且丧尸速度大于人的速度

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
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
from sklearn.neighbors import NearestNeighbors

n = 1 # 初始丧尸数量
N = 30 # 总人数


class Person: # 点类,用来模拟人
def __init__(self):
self.x = np.random.uniform(-10, 10)
self.y = np.random.uniform(-10, 10)
self.color = 'g'
self.dire = 0 # 方向
self.speed = 0.1

def get_loc(self):
# 这里返回的是一个元组
return self.x, self.y

def get_color(self):
return self.color

def set_color(self, color):
self.color = color

def set_speed(self, speed):
self.speed = speed

# 以猎人为视角的追逐算法,传入最近的对象,然后追逐。锁定两个目标
def chase(self, person):
# 丧尸距离健康者的x,y距离
delta_x = self.x - person.get_loc()[0]
delta_y = self.y - person.get_loc()[1]
# 丧尸追逐
if delta_y > 0:
self.y -= self.speed
elif delta_y < 0:
self.y += self.speed
if delta_x > 0:
self.x -= self.speed
elif delta_x < 0:
self.x += self.speed

# 逃跑函数
def run(self, zombie):
# 健康者距离丧尸的x,y距离
delta_x = self.x - zombie.get_loc()[0]
delta_y = self.y - zombie.get_loc()[1]
if delta_y < 0:
self.y -= self.speed
elif delta_y > 0:
self.y += self.speed
if delta_x < 0:
self.x -= self.speed
elif delta_x > 0:
self.x += self.speed

def border(self):
# 边界判定,超出边界后,从另一边出来
if self.x > 10:
self.x = self.x - 20 + 1
elif self.x < -10:
self.x = self.x + 20 - 1
if self.y > 10:
self.y = self.y - 20 + 1
elif self.y < -10:
self.y = self.y + 20 - 1

# 存储所有对象(包括正常人与丧尸)
people = []
for i in range(N): # 人数
s = Person()
people.append(s)

zombies = []
for i in np.random.random(n): # 选出n个病人,改变颜色
people[int(len(people) * i)].set_color('r')
zombies.append(people[int(len(people) * i)])

fig = plt.figure()
scat = plt.scatter([x.get_loc()[0] for x in people], [x.get_loc()[1] for x in people],
c=[x.get_color() for x in people])
plt.xlim(-10, 10)
plt.ylim(-10, 10)


def animate(i):
# 表示健康人群
g_people = []
for j in people:
if j.get_color() == 'g':
g_people.append(j)

# 存储被感染的人群
l2 = []
for j in set(g_people):
if not g_people:
break
j.set_speed(0.15)
nnarray = NearestNeighbors(n_neighbors=1).fit(np.array([x.get_loc() for x in zombies])).kneighbors(
np.array(j.get_loc()).reshape(1, -1), return_distance=False)[0]
# nn为最近丧尸在zoobies中的索引
nn = nnarray[0]
j.run(people[nn])
j.border() # 边界检测

# 丧尸追逐,追逐最近的人类
for zombie in set(zombies):
if not g_people:
break
zombie.set_speed(0.3)
nnarray = NearestNeighbors(n_neighbors=1).fit(np.array([x.get_loc() for x in g_people])).kneighbors(
np.array(zombie.get_loc()).reshape(1, -1), return_distance=False)[0]
# nn为最近正常人在g_people中的索引
nn = nnarray[0]
zombie.chase(g_people[nn])
zombie.border() # 边界检测
# 追上则感染
if abs(zombie.get_loc()[0] - g_people[nn].get_loc()[0]) < 0.4 and abs(
zombie.get_loc()[1] - g_people[nn].get_loc()[1]) < 0.4:
g_people[nn].set_color('r')
l2.append(g_people[nn])
del g_people[nn] #将被感染的人从健康人群中删除

for j in l2:
zombies.append(j)
scat.set_offsets([[x.get_loc()[0], x.get_loc()[1]] for x in people])
scat.set_color([x.get_color() for x in people])
return scat,


anim = animation.FuncAnimation(fig, animate, frames=100, interval=100, blit=False)
plt.show()

# anim.save('丧尸.gif', writer='pillow')

2、代码思路

在有了matplotlib库的一些基础后,这个题目的难点基本就剩两个了,一、如何获取最近距离的点,二、如何实现追逐与逃跑的函数。

对于第一个问题,我们可以引入sklearn.neighbors,里面有方法可以帮助我们解决。

对于第二个问题,这里提供一个思路:追逐函数可以这样定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 以猎人为视角的追逐算法,传入最近的对象,然后追逐。锁定两个目标
def chase(self, person):
# 丧尸距离健康者的x,y距离
delta_x = self.x - person.get_loc()[0]
delta_y = self.y - person.get_loc()[1]
# 丧尸追逐
if delta_y > 0:
self.y -= self.speed
elif delta_y < 0:
self.y += self.speed
if delta_x > 0:
self.x -= self.speed
elif delta_x < 0:
self.x += self.speed

既然是追逐,那肯定就要让两个点越来越近,以丧尸与人横坐标为例,二者之差大于零,说明丧尸偏右,那就让丧尸向左移,其他的就与之类似。实际上,逃跑函数和追逐函数也差不多。

3、总结与改进

代码运行中,其实还有有一些问题的。比如,最后有可能会出现丧尸追不上人的情况,也挺鬼畜的,挺好玩的,哈哈。这是人合理利用了机制嘛,这是特性,绝对不是bug,当然也可以对追逐函数再进行补充,使其能够 ”跨边界追踪”。

另一个就是,有时候,某个人会突然不动,杵在原地,这个暂时还没有解决,期待大佬的解答。也欢迎大家提出其他优化建议啊。

四、心得与总结

刚开始接触新东西,确实挺懵的,但凡是都要循序渐进嘛,而且精通一门技术是很难,但入门其实还是挺容易的。主要还是从题目出发,需要什么就去学习什么,初期也可以参考别人的代码,起初可能看不明白每一行的含义,但最后一定是要弄懂的,在这个过程中,可以在别人代码上进行增删修改,或者去看某个函数伙方法的具体含义、运转机制。在多次尝试以后,一定可以掌握入门技巧的。

以上就是最近学习成果中的一部分了,其实还简单地了解了一下爬虫和OpenCV部分,感觉还挺有意思的,不过目前刚刚算是入门中的入门,在这这里就不丢人了。主要还是任务驱动嘛,学习以及做项目的需要,所以最近学习的确实挺杂的。另外也确实有点迷茫,不知道该往哪个方向去钻研。走一步看一步吧,暂时缺失还没有想好。