Python编程:从入门到实践 第15章课后习题
我看的是本书的第二版。本章节关于“隐藏坐标轴”的小节中,对坐标轴的隐藏是这么写的:
plt.axes().get_xaxis().set_visible(False)
plt.axes().get_yaxis().set_visible(False)
但是我即便照抄、复制-粘贴教材上的这句话,在pycharm的虚拟环境及vs code的环境中执行之后均无plot内容而是一个很奇怪的坐标图。
同样的代码在jupyter notebook可以运行(显示为无边框的图)但是提示warning:
/srv/conda/envs/notebook/lib/python3.6/site-packages/ipykernel_launcher.py:45: MatplotlibDeprecationWarning: Adding an axes using the same arguments as a previous axes currently reuses the earlier instance. In a future version, a new instance will always be created and returned. Meanwhile, this warning can be suppressed, and the future behavior ensured, by passing a unique label to each axes instance.
在StackOverflow上查了一下,貌似有人说这么写:
plt.gca().axes.get_xaxis().set_visible(False)
plt.gca().axes.get_yaxis().set_visible(False)
百度之后,发现也有人有同样的问题。不知道是不是版本更新后的原因。
15-1 立方:数字的三次方被称为其立方。请绘制一个图形,显示前5个整数的立方值,再绘制一个图形,显示前5000个整数的立方值。
之前就看到lambda
和map
函数,正好用一下。
num = list(map(lambda x: x**3, list(range(4))))
plt.plot(num)
plt.show()
15-2 彩色立方:给你前面绘制的立方图指定颜色映射。
教材用的是列表解析,正好好久没用了,回忆一下。
num = list(range(1,5001))
cubic = [x**3 for x in num]
plt.scatter(num, cubic, c=cubic, cmap = plt.cm.Reds, s=10)
plt.title("15-2")
plt.xlabel("value")
plt.ylabel("cubic value")
plt.show()
15-3 分子运动:修改rw_visual.py,将其中的plt.scatter() 替换为plt.plot() 。为模拟花粉在水滴表面的运动路径,向plt.plot() 传递rw.x_values 和rw.y_values ,并指定实参值linewidth 。使用5000个点而不是50000个点。
import matplotlib.pyplot as plt
from random import choice
class RandomWalk():
def __init__(self, num_points=5000):
self.num_points = num_points
self.x_values = [0]
self.y_values = [0]
def fill_walk(self):
while len(self.x_values) < self.num_points:
x_direction = choice([-1, 1])
x_distance = choice([0, 1, 2, 3, 4])
x_step = x_direction * x_distance
y_direction = choice([-1, 1])
y_distance = choice([0, 1, 2, 3, 4])
y_step = y_direction * y_distance
if x_step == 0 and y_step == 0:
continue
next_x = self.x_values[-1] + x_step
next_y = self.y_values[-1] + y_step
self.x_values.append(next_x)
self.y_values.append(next_y)
rw = RandomWalk()
rw.fill_walk()
point_numbers = list(range(rw.num_points))
plt.plot(rw.x_values, rw.y_values, linewidth=1)
plt.scatter(0, 0, c='green', edgecolors='none', s=100)
plt.scatter(rw.x_values[-1], rw.y_values[-1], c='red', edgecolors='none',
s=100)
plt.gca().axes.get_xaxis().set_visible(False)
plt.gca().axes.get_yaxis().set_visible(False)
plt.show()
15-4 改进的随机漫步 :在类RandomWalk 中,x_step 和y_step 是根据相同的条件生成的:从列表[1, -1]中随机地选择方向,并从列表[0, 1, 2, 3, 4] 中随机地选择距离。请修改这些列表中的值,看看对随机漫步路径有何影响。尝试使用更长的距离选择列表,如0~8;或者将-1从 x 或 y 方向列表中删除。
略
15-5 重构 :方法fill_walk() 很长。请新建一个名为get_step() 的方法,用于确定每次漫步的距离和方向,并计算这次漫步将如何移动。然后, 在fill_walk() 中调用get_step() 两次:
x_step = get_step()
y_step = get_step()
通过这样的重构,可缩小fill_walk() 的规模,让这个方法阅读和理解起来更容易。
import matplotlib.pyplot as plt
from random import choice
class RandomWalk():
def __init__(self, num_points=5000):
self.num_points = num_points
self.x_values = [0]
self.y_values = [0]
def get_step(self):
direction = choice([-1, 1])
distance = choice([0, 1, 2, 3, 4])
step = distance * direction
return step
def fill_walk(self):
while len(self.x_values) < self.num_points:
x_step = self.get_step()
y_step = self.get_step()
if x_step == 0 and y_step == 0:
continue
next_x = self.x_values[-1] + x_step
next_y = self.y_values[-1] + y_step
self.x_values.append(next_x)
self.y_values.append(next_y)
rw = RandomWalk()
rw.fill_walk()
point_numbers = list(range(rw.num_points))
plt.plot(rw.x_values, rw.y_values, linewidth=1)
plt.scatter(0, 0, c='green', edgecolors='none', s=100)
plt.scatter(rw.x_values[-1], rw.y_values[-1], c='red', edgecolors='none',
s=100)
plt.gca().axes.get_xaxis().set_visible(False)
plt.gca().axes.get_yaxis().set_visible(False)
plt.show()
15-6 自动生成标签 :请修改die.py和dice_visual.py,将用来设置hist.x_labels 值的列表替换为一个自动生成这种列表的循环。如果你熟悉列表解析,可尝试将 die_visual.py和dice_visual.py中的其他for 循环也替换为列表解析。
原来的列表:
hist.x_labels = ['2', '3', '4', '5', '6','7', '8', '9', '10', '11', '12']
继续练习lambda
:
hist.x_labels = list(map(lambda x : str(x), list(range(2,13))))
如果是列表解析:
hist.x_labels = [str(x) for x in range(2,13)]
15-7 两个D8骰子: 请模拟同时掷两个8面骰子1000次的结果。逐渐增加掷骰子的次数,直到系统不堪重负为止。
from random import randint
import matplotlib.pyplot as plt
class Die():
def __init__(self, num_sides):
self.num_sides = num_sides
def roll(self):
return randint(1, self.num_sides)
d8_1 = Die(8)
d8_2 = Die(8)
results = []
for roll_num in range(1000):
result = d8_1.roll() + d8_2.roll()
results.append(result)
print(results)
plt.hist(results,
bins=[x for x in range(2, d8_1.num_sides + d8_2.num_sides + 1 + 1)],
rwidth=0.8)
plt.title("Results of rolling 2 d8 dice")
plt.xlabel("Value")
plt.ylabel("frequency")
plt.show()
以前就记得似乎plt.hist能直接出直方图。但这次bins这个地方还真绕了一下,去看了documentation,如下:
bins: int or sequence or str, default: rcParams["hist.bins"] (default: 10)
If bins is an integer, it defines the number of equal-width bins in the range.
If bins is a sequence, it defines the bin edges, including the left edge of the first bin and the right edge of the last bin; in this case, bins may be unequally spaced. All but the last (righthand-most) bin is half-open. In other words, if bins is:
[1, 2, 3, 4]
then the first bin is[1, 2)
(including 1, but excluding 2) and the second[2, 3)
. The last bin, however, is[3, 4]
, which includes 4.
如果bins是一个list [1, 2, 3, 4]
,则含前不含后:第一个bin含1不含2,第二个含2不含3,但最后一个bin即包括前面的数值,也包括后面的数值,即最后一个bin含3也含4。
文中range()括号内应该是(2,16)。但range本来就不包括括号内右边的数字,所以要先加1把右边的数字(16)含进去,还要再加1,让最后一个bin分开否则最后一个bin既含15,也含16。
如果用plt.bar():
from random import randint
import matplotlib.pyplot as plt
class Die():
def __init__(self, num_sides):
self.num_sides = num_sides
def roll(self):
return randint(1, self.num_sides)
d8_1 = Die(8)
d8_2 = Die(8)
results = []
for roll_num in range(1000):
result = d8_1.roll() + d8_2.roll()
results.append(result)
plt.hist(results,
bins=[x for x in range(2, d8_1.num_sides + d8_2.num_sides + 1 + 1)],
rwidth=0.8)
plt.title("plt.hist plot")
plt.xlabel("Value")
plt.ylabel("frequency")
plt.savefig("hist.png")
frequencies = []
max_results = d8_1.num_sides + d8_2.num_sides
for value in range(2, max_results + 1):
frequency = results.count(value)
frequencies.append(frequency)
plt.figure(figsize=(8, 6)) # width:20, height:3
plt.bar(list(range(2, max_results+1)), frequencies, width = 0.7 )
plt.title("plt.bar plot")
plt.xlabel("Value")
plt.ylabel("frequency")
plt.savefig("bar.png")
plt.show()
15-8 同时掷三个骰子 :如果你同时掷三个D6骰子,可能得到的最小点数为3,而最大点数为18。请通过可视化展示同时掷三个D6骰子的结果。
略
15-9 将点数相乘 :同时掷两个骰子时,通常将它们的点数相加。请通过可视化展示将两个骰子的点数相乘的结果。
略
15-10 练习使用本章介绍的两个库 :尝试使用matplotlib通过可视化来模拟掷骰子的情况,并尝试使用Pygal通过可视化来模拟随机漫步的情况。
from random import choice
import pygal
class RandomWalk():
def __init__(self, num_points=5000):
self.num_points = num_points
self.x_values = [0]
self.y_values = [0]
def get_step(self):
direction = choice([1, -1])
distance = choice([0, 1, 2, 3, 4])
step = direction * distance
return step
def fill_walk(self):
while len(self.x_values) < self.num_points:
x_step = self.get_step()
y_step = self.get_step()
if x_step == 0 and y_step == 0:
continue
next_x = self.x_values[-1] + x_step
next_y = self.y_values[-1] + y_step
self.x_values.append(next_x)
self.y_values.append(next_y)
rw = RandomWalk()
rw.fill_walk()
xy_chart = pygal.XY()
xy_chart.title = 'Random Walk'
#通过zip打包成由tuple组成的list
rwValues = list(zip(rw.x_values, rw.y_values))
xy_chart.add('rw', rwValues)
xy_chart.render_to_file('rw_visual.svg')