本人pygal库的安装使用了 pip3 install pygal 安装成功,使用 conda install pygal 提示没有这个库,我在学习过程中安装别的库的时候也有出现这种情况,所以使用Anaconda 的用户如果安装不上某个库时,建议使用 pip 安装,而且这两种安装是并不冲突的。
原文的摘录如下:
本章我们将使用 Python 可视化包 Pygal 来生成可缩放的矢量图形文件。对于需要在尺寸不同的屏幕上显示的图表,这很有用,因为它们将自动缩放,以适合观看者的屏幕。
如果你打算以在线方式使用图表,请考虑使用 Pygal 来生成它们,这样它们在任何设备上显示时都会很美观。
代码摘录如下,略有改动便于学习记录,日后查看:

1 from random import randint 2 3 # 创建Die类 4 class Die(): 5 # 表示一个骰子的类 6 def __init__(self, num_sides = 6): 7 # 骰子默认为6面 8 self.num_sides = num_sides 9 10 def roll(self): 11 # 返回一个位于1和骰子面数之间的随机值 12 return randint(1, self.num_sides)

1 # 掷骰子 2 from die import Die 3 4 # 创建一个D6 5 die = Die() 6 7 # 掷几次骰子,并将结果存储在一个列表中 8 results = [] 9 10 # 掷骰子100次 11 for roll_num in range(100): 12 result = die.roll() 13 results.append(result) 14 15 print(results)

1 # 掷骰子 2 from die import Die 3 4 # 创建一个D6 5 die = Die() 6 7 # 掷几次骰子,并将结果存储在一个列表中 8 results = [] 9 10 # 掷骰子1000次 11 for roll_num in range(1000): 12 result = die.roll() 13 results.append(result) 14 # 分析结果 15 frequencies = [] 16 for value in range(1, die.num_sides+1): 17 frequency = results.count(value) 18 frequencies.append(frequency) 19 20 print(frequencies)

1 import pygal 2 from die import Die 3 4 # 创建一个D6 5 die = Die() 6 7 # 掷几次骰子,并将结果存储在一个列表中 8 results = [] 9 10 # 掷骰子1000次 11 for roll_num in range(1000): 12 result = die.roll() 13 results.append(result) 14 15 # 分析结果 16 frequencies = [] 17 for value in range(1, die.num_sides+1): 18 frequency = results.count(value) 19 frequencies.append(frequency) 20 21 # 对结果进行可视化 22 # pygal.Bar() 实例 用于创建条形图,存储到hist中 23 hist = pygal.Bar() 24 25 hist.title = 'Results of rolling one D6 1000 times.' 26 hist.x_labels = ['1', '2', '3', '4', '5', '6'] 27 hist.x_title = 'Result' 28 hist.y_title = 'Frequency of Result' 29 30 # add传递到图标,这种文件的扩展名必须为.svg 31 hist.add('D6', frequencies) 32 hist.render_to_file('die_visual.svg') 33 34 # 使用浏览器打开这个图表
个人提示:不要用系统自带的图像查看器查看,你很有可能看到的是一堆黑的,SVG文件用浏览器打开能够看到如图的效果,并且将鼠标放置在直方图上时会显示出,该点数一共掷到了几次,实现与用户之间的一个简单交互,我想你可能会与我一样对此感兴趣,接下来和随机漫步一样,会进行多次操作,对这个有趣的小东西进行升级。
投掷两个骰子的代码如下:

1 import pygal 2 3 from die import Die 4 5 # 创建两个D6骰子 6 die_1 = Die() 7 die_2 = Die() 8 9 # 掷1000次骰子,并将结果存储在一个列表中 10 results = [] 11 for roll_num in range(1000): 12 result = die_1.roll() + die_2.roll() 13 results.append(result) 14 15 # 分析结果 16 frequencies = [] 17 max_result = die_1.num_sides + die_2.num_sides 18 for value in range(2, max_result+1): 19 frequency = results.count(value) 20 frequencies.append(frequency) 21 22 # 对结果进行可视化 23 hist = pygal.Bar() 24 25 hist.title = 'Results of rolling two D6 1000 times.' 26 hist.x_labels = ['2', '3', '4', '5', '6', '7', '8', 27 '9', '10', '11', '12'] 28 hist.x_title = 'Result' 29 hist.y_title = 'Frequency of Result' 30 31 # add传递到图标,这种文件的扩展名必须为.svg 32 hist.add('D6 + D6', frequencies) 33 hist.render_to_file('dice_visual.svg')
这些图片不是我给它更改了大小,而是pygal可以根据窗口自动调节尺寸的,这也是它神奇的一个地方,我使用的浏览器是 Ubuntu 中的 Chromium 浏览器,无论我怎么缩放,浏览器中的图片都保持尺寸不变。
而且我们还发现了什么?正如我们想的一样,同时掷两颗骰子,掷出2点和12点的概率是最小的,因为只有两个都是1或者6时才能够出现。关于这些概率问题,通过修改书本中所提供的代码,我们是不是可以把现实中的例子放入进来,做一个概率分析呢?
下面是书籍中所提到的练习,如果我已经写出了这个代码,则会在下面贴出来,如果还没能写出来的,作业就暂时留着,等我学得更好了再回头来做这些我现在所不能完成的练习,也欢迎看到本文章的各位给我提供源码+适当的注释。
动手试一试:
1.同时掷 6 面骰子和 10 面骰子 50 000 次的结果:
首先需要把原来建立的类(die.py)依旧保存到你新建项目的文件夹中,然后根据 die.py 的建立方法,把骰子从 6 面改成 10 面,文件命名为 die1.py 。本人感觉用这样的方法虽然会多一个文件,但是在分析的时候会看得更加清楚,逻辑比较清晰,相关注释已经在代码中给出。具体的代码如下(根据原先代码修改得到结果):

1 # Author:Canvas 2 # -*- coding:utf-8 -*- 3 from random import randint 4 5 # 创建Die1类 6 class Die1(): 7 # 表示一个骰子的类 8 def __init__(self, num_sides = 10): 9 # 此处将骰子默认为10面 10 self.num_sides = num_sides 11 12 def roll(self): 13 # 返回一个位于1和骰子面数之间的随机值 14 return randint(1, self.num_sides)

1 # Author:Canvas 2 # -*- coding:utf-8 -*- 3 4 # 同时掷 6 面骰子和 10 面骰子 50 000 次的结果 5 6 import pygal 7 8 from die import Die 9 from die1 import Die1 10 11 # 创建两个D6骰子 12 die_1 = Die() 13 die_2 = Die1() 14 15 # 掷5000次骰子,并将结果存储在一个列表中 16 results = [] 17 for roll_num in range(5000): 18 result = die_1.roll() + die_2.roll() 19 results.append(result) 20 21 # 分析结果 22 frequencies = [] 23 max_result = die_1.num_sides + die_2.num_sides 24 for value in range(2, max_result+1): 25 frequency = results.count(value) 26 frequencies.append(frequency) 27 28 # 对结果进行可视化 29 hist = pygal.Bar() 30 31 hist.title = 'Results of rolling D6 and D10 5000 times.' 32 hist.x_labels = ['2', '3', '4', '5', '6', '7', '8', 33 '9', '10', '11', '12', '13', '14', 34 '15', '16'] 35 hist.x_title = 'Result' 36 hist.y_title = 'Frequency of Result' 37 38 # add传递到图标,这种文件的扩展名必须为.svg 39 hist.add('D6 + D10', frequencies) 40 hist.render_to_file('D6D10.svg')
2.自动生成标签 :请修改 die.py 和 dice_visual.py ,将用来设置 hist.x_labels 值的列表替换为一个自动生成这种列表的循环。如果你熟悉列表解析,可尝试将die_visual.py 和 dice_visual.py 中的其他 for 循环也替换为列表解析。
3.两个 D8 骰子: 请模拟同时掷两个 8 面骰子 1000 次的结果。逐渐增加掷骰子的次数,直到系统不堪重负为止。

1 # -*- coding:utf-8 -*- 2 3 from random import randint 4 5 # 创建Die类 6 class Die(): 7 # 表示一个骰子的类 8 def __init__(self, num_sides = 8): 9 self.num_sides = num_sides 10 11 def roll(self): 12 # 返回一个位于1和骰子面数之间的随机值 13 return randint(1, self.num_sides)

1 # -*- coding:utf-8 -*- 2 3 import pygal 4 5 from die import Die 6 7 # 创建两个D8骰子 8 die_1 = Die() 9 die_2 = Die() 10 11 # 掷1000次骰子,并将结果存储在一个列表中 12 results = [] 13 for roll_num in range(1000): 14 result = die_1.roll() + die_2.roll() 15 results.append(result) 16 17 # 分析结果 18 frequencies = [] 19 max_result = die_1.num_sides + die_2.num_sides 20 for value in range(2, max_result+1): 21 frequency = results.count(value) 22 frequencies.append(frequency) 23 24 # 对结果进行可视化 25 hist = pygal.Bar() 26 27 hist.title = 'Results of rolling two D8 1000 times.' 28 hist.x_labels = ['2', '3', '4', '5', '6', '7', '8', '9', 29 '10', '11', '12', '13', '14', '15', '16'] 30 hist.x_title = 'Result' 31 hist.y_title = 'Frequency of Result' 32 33 hist.add('D8 + D8', frequencies) 34 hist.render_to_file('D8_D8.svg')
注:上面我把鼠标放在 9 那边就进行了一个小互动
4.同时掷三个骰子 :如果你同时掷三个 D6 骰子,可能得到的最小点数为 3 ,而最大点数为 18 。请通过可视化展示同时掷三个 D6 骰子的结果。
在这个练习中,我们依然引入本章中第一次引入的代码 Class_Die ,因为这里的骰子仍然是 6 面的,所以导入原来的类,然后修改代码即可,具体代码如下:

1 # -*- coding:utf-8 -*- 2 3 # 同时掷三个 6 面骰子的结果 4 5 import pygal 6 7 from die import Die 8 9 # 创建三个D6骰子 10 die_1 = Die() 11 die_2 = Die() 12 die_3 = Die() 13 14 # 将结果存储在一个列表中 15 results = [] 16 for roll_num in range(3): 17 result = die_1.roll() + die_2.roll() +die_3.roll() 18 results.append(result) 19 20 # 分析结果 21 frequencies = [] 22 max_result = die_1.num_sides + die_2.num_sides + die_3.num_sides 23 for value in range(3, max_result+1): 24 frequency = results.count(value) 25 frequencies.append(frequency) 26 27 # 对结果进行可视化 28 hist = pygal.Bar() 29 30 hist.title = 'Results of rolling three D6 one time.' 31 hist.x_labels = ['3', '4', '5', '6', '7', '8', 32 '9', '10', '11', '12', '13', '14', 33 '15', '16', '17', '18'] 34 hist.x_title = 'Result' 35 hist.y_title = 'Frequency of Result' 36 37 # add传递到图标,这种文件的扩展名必须为.svg 38 hist.add('D6 + D6 +D6', frequencies) 39 hist.render_to_file('D6_D6_D6.svg')
注:结果是随机的,因为pygal的自动适应屏幕原因,这边的Y轴其实最小值就是1,如果选择掷骰子多次则不会出现这样的刻度划分。
5.将点数相乘 :同时掷两个骰子时,通常将它们的点数相加。请通过可视化展示将两个骰子的点数相乘的结果。
关于点数相乘,我遇见了一些小小的问题,在经过几次代码的修改以后,我突然发现,乘法和加法在掷骰子的行为中,有着一些本质的区别,两个骰子相加时,最小点数为 2 ,最大点数是 12 , 这之间的数字是连续不断开的,理论上来讲,只要掷骰子的次数够多,每个点都会掷到。但是乘法不同,乘法的最小值是 1 , 最大值是 36 , 注意了! 乘法从 1 到 36 的得数不是连续的,比如说 两个骰子无论怎么分配,都是不可能掷到 11 点 、17 点 等等的,所以在这里我们需要考虑一个问题,是把 1 - 36 的每个数都写出来作为 X 轴呢,还是把不需要的数值去掉。这个问题是仁者见仁智者见智的,需要注意的一点是,pygal 的 互动 在你取消掉中间不可能的得数时,它是不会根据你横坐标数值的不同而自动对应的,比如说当横坐标为 1, 2, 3 时,如果你去掉了 2 ,程序会把 3 当成 2 的, 这就出现了 X 轴上实际数值对应的问题,对于如何使不可能出现的得数不将它作为横坐标,而不使图表出现对应错误的问题,经过我的尝试,目前还没有想到比较好的办法,因为程序的计数就是很直接的从 1 开始计数,或许在不久的将来,我能够找到解决这种问题的方法,毕竟对于我这样的强迫症患者来说,明明不该出现的东西出现了,还占了一个碍眼的位置是很难受的。我们依旧以投掷骰子 1000 次为例,具体代码如下:

1 import pygal 2 3 from die import Die 4 5 # 创建两个D6骰子 6 die_1 = Die() 7 die_2 = Die() 8 9 # 掷1000次骰子,并将结果存储在一个列表中 10 results = [] 11 for roll_num in range(1000): 12 result = die_1.roll() * die_2.roll() 13 results.append(result) 14 15 # print(results) 16 17 # 分析结果 18 frequencies = [] 19 max_result = die_1.num_sides * die_2.num_sides 20 for value in range(1, max_result+1): 21 frequency = results.count(value) 22 frequencies.append(frequency) 23 # print(frequencies) 24 # 对结果进行可视化 25 hist = pygal.Bar() 26 27 hist.title = 'Results of rolling two D6 1000 times.' 28 hist.x_labels = ['1', '2', '3', '4', '5', '6', '7','8', 29 '9', '10', '11', '12', '13', '14', '15', '16', 30 '17', '18', '19', '20', '21', '22', '23', '24', 31 '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36'] 32 hist.x_title = 'Result' 33 hist.y_title = 'Frequency of Result' 34 35 # add传递到图标,这种文件的扩展名必须为.svg 36 hist.add('D6 * D6', frequencies) 37 hist.render_to_file('D6D6.svg')
注:中间空出来的东西我是看着真的不舒服,不过尝试了几个小时都没能解决,就先让它留着吧,当作日后进步的鞭策。
下面的这段代码是我尝试途中的一部分,问题还是比较大的,先让它留在这里,日后慢慢再来修改:

1 # Author:Canvas 2 # -*- coding:utf-8 -*- 3 4 import pygal 5 6 from die import Die 7 8 # 创建两个D6骰子 9 die_1 = Die() 10 die_2 = Die() 11 12 # 掷1000次骰子,并将结果存储在一个列表中 13 results = [] 14 for roll_num in range(1000): 15 result = die_1.roll() * die_2.roll() 16 results.append(result) 17 18 # print(results) 19 20 # 分析结果 21 frequencies = [] 22 # max_result = die_1.num_sides * die_2.num_sides 23 # 此处不再使用max_result 因为我的X轴的数字总共只有20个 24 # max 有36 个,结果会和X坐标对应不起来 25 for value in range(1, 21): 26 frequency = results.count(value) 27 frequencies.append(frequency) 28 # print(frequencies) 29 # 对结果进行可视化 30 hist = pygal.Bar() 31 32 hist.title = 'Results of rolling two D6 1000 times.' 33 hist.x_labels = ['1', '2', '3', '4', '5', '6', '8', 34 '9', '10', '12', '13', '14', '15', 35 '16', '18', '20', '24', '25', '30', '36'] 36 hist.x_title = 'Result' 37 hist.y_title = 'Frequency of Result' 38 39 # add传递到图标,这种文件的扩展名必须为.svg 40 hist.add('D6 * D6', frequencies) 41 hist.render_to_file('D6D6.svg')
6.尝试使用 matplotlib 通过可视化来模拟掷骰子的情况,并尝试使用 Pygal 通过可视化来模拟随机漫步的情况。
关于代码除了书本上摘录的,个人的学习笔记篇幅有限,主要是为了方便自己日后学习查阅,所以你如果对这个代码感兴趣,对其中有疑问的地方可以问一下我,我会尽我所能提供解疑方法与途径,或者你可以直接查阅这篇文章所查阅的书籍——《python编程:从入门到实践》。