Python系列16-数据可视化之生成数据

一.生成数据

数据可视化 指的是通过可视化表示来探索数据,它与数据挖掘 紧密相关,而数据挖掘指的是使用代码来探索数据集的规律和关联。数据集可以是用一行代码就能表示的小型数字列表,也可以是数以吉字节的数据。

漂亮地呈现数据关乎的并非仅仅是漂亮的图片。以引人注目的简洁方式呈现数据,让观看者能够明白其含义,发现数据集中原本未意识到的规律和意义。

所幸即便没有超级计算机,也能够可视化复杂的数据。鉴于Python的高效性,使用它在笔记本电脑上就能快速地探索由数百万个数据点组成的数据集。

在基因研究、天气研究、政治经济分析等众多领域,大家都使用Python来完成数据密集型工作。数据科学家使用Python编写了一系列令人印象深刻的可视化和分析工具,其中很多也可供你使用。最流行的工具之一是matplotlib,它是一个数学绘图库,我们将使用它来制作简单的图表,如折线图和散点图。然后,我们将基于随机漫步概念生成一个更有趣的数据集——根据一系列随机决策生成的图表。

我们还将使用Pygal包,它专注于生成适合在数字设备上显示的图表。通过使用Pygal,可在用户与图表交互时突出元素以及调整其大小,还可轻松地调整整个图表的尺寸,使其适合在微型智能手表或巨型显示器上显示。我们将使用Pygal以各种方式探索掷骰子的结果。

1.1 安装matplotlib

直接使用pip安装即可

C:\>pip install matplotlib
Collecting matplotlib
  Downloading matplotlib-3.3.4-cp36-cp36m-win_amd64.whl (8.5 MB)
     |████████████████████████████████| 8.5 MB 2.2 MB/s
Collecting pillow>=6.2.0
  Downloading Pillow-8.1.2-cp36-cp36m-win_amd64.whl (2.1 MB)
     |████████████████████████████████| 2.1 MB 3.3 MB/s
Requirement already satisfied: python-dateutil>=2.1 in c:\users\administrator\appdata\local\programs\python\python36\lib\site-packages (from matplotlib) (2.7.5)

Collecting cycler>=0.10
  Downloading cycler-0.10.0-py2.py3-none-any.whl (6.5 kB)
Collecting kiwisolver>=1.0.1
  Downloading kiwisolver-1.3.1-cp36-cp36m-win_amd64.whl (51 kB)
     |████████████████████████████████| 51 kB 4.1 MB/s
Requirement already satisfied: numpy>=1.15 in c:\users\administrator\appdata\local\programs\python\python36\lib\site-packages (from matplotlib) (1.15.4)
Collecting pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.3
  Downloading pyparsing-2.4.7-py2.py3-none-any.whl (67 kB)
     |████████████████████████████████| 67 kB 1.8 MB/s
Requirement already satisfied: six in c:\users\administrator\appdata\local\programs\python\python36\lib\site-packages (from cycler>=0.10->matplotlib) (1.12.0)
Installing collected packages: pyparsing, pillow, kiwisolver, cycler, matplotlib
  Attempting uninstall: pillow
    Found existing installation: Pillow 6.0.0
    Uninstalling Pillow-6.0.0:
      Successfully uninstalled Pillow-6.0.0
Successfully installed cycler-0.10.0 kiwisolver-1.3.1 matplotlib-3.3.4 pillow-8.1.2 pyparsing-2.4.7

C:\>

1.2 绘制简单的折线图

下面来使用matplotlib绘制一个简单的折线图,再对其进行定制,以实现信息更丰富的数据可视化。我们将使用平方数序列1、4、9、16和25来绘制这个图表。

只需向matplotlib提供如下数字,matplotlib就能完成其他的工作。

代码:
mpl_squares.py

import matplotlib.pyplot as plt

squares = [1, 4, 9, 16, 25]
plt.plot(squares)
plt.show()

测试记录:

image.png

我们首先导入了模块pyplot ,并给它指定了别名plt ,以免反复输入pyplot 。在线示例大都这样做,因此这里也这样做。模块pyplot 包含很多用于生成图表的函数。

我们创建了一个列表,在其中存储了前述平方数,再将这个列表传递给函数plot() ,这个函数尝试根据这些数字绘制出有意义的图形。plt.show() 打开matplotlib查看器,并显示绘制的图形,如图15-1所示。查看器让你能够缩放和导航图形,另外,单击磁盘图标可将图形保存起来。

1.2.1 修改标签文字和线条粗细

上面图形表明数字是越来越大的,但标签文字太小,线条太细。所幸matplotlib让你能够调整可视化的各个方面。

下面通过一些定制来改善这个图形的可读性。

代码:
mpl_squares.py

import matplotlib.pyplot as plt

squares = [1, 4, 9, 16, 25]
plt.plot(squares, linewidth = 5)

# 设置图表标题,并给坐标轴加上标签
plt.title("Square Numbers", fontsize=24)
plt.xlabel("Value", fontsize=4)
plt.ylabel("Square of Value", fontsize=14)

# 设置刻度标记的大小
plt.tick_params(axis='both', labelsize=14)

plt.show()

测试记录:

image.png

1.2.2 校正图形

图形更容易阅读后,我们发现没有正确地绘制数据:折线图的终点指出4.0的平方为25!下面来修复这个问题。

当你向plot() 提供一系列数字时,它假设第一个数据点对应的 x 坐标值为0,但我们的第一个点对应的 x 值为1。为改变这种默认行为,我们可以给plot() 同时提供输入值和输出值.

代码:
mpl_squares.py

import matplotlib.pyplot as plt

input_values = [1, 2, 3, 4, 5]
squares = [1, 4, 9, 16, 25]
plt.plot(input_values, squares, linewidth=5)

# 设置图表标题,并给坐标轴加上标签
plt.title("Square Numbers", fontsize=24)
plt.xlabel("Value", fontsize=4)
plt.ylabel("Square of Value", fontsize=14)

# 设置刻度标记的大小
plt.tick_params(axis='both', labelsize=14)

plt.show()

测试记录:

image.png

1.2.3 使用scatter() 绘制散点图并设置其样式

有时候,需要绘制散点图并设置各个数据点的样式。例如,你可能想以一种颜色显示较小的值,而用另一种颜色显示较大的值。绘制大型数据集时,你还可以对每个点都设置同样的样式,再使用不同的样式选项重新绘制某些点,以突出它们。

要绘制单个点,可使用函数scatter() ,并向它传递一对 x 和 y 坐标,它将在指定位置绘制一个点.

代码:
scatter_squares.py

import matplotlib.pyplot as plt

plt.scatter(2, 4, s=200)

# 设置图表标题,并给坐标轴加上标签
plt.title("Square Numbers", fontsize=24)
plt.xlabel("Value", fontsize=4)
plt.ylabel("Square of Value", fontsize=14)

# 设置刻度标记的大小
plt.tick_params(axis='both', which='major', labelsize=14)

plt.show()

测试记录:

image.png

1.2.4 使用scatter() 绘制一系列点

要绘制一系列的点,可向scatter() 传递两个分别包含x 值和y 值的列表。

代码:
scatter_squares.py

import matplotlib.pyplot as plt

x_values = [1, 2, 3, 4, 5]
y_values = [1, 4, 9, 16, 25]

plt.scatter(x_values, y_values, s=200)

# 设置图表标题,并给坐标轴加上标签
plt.title("Square Numbers", fontsize=24)
plt.xlabel("Value", fontsize=4)
plt.ylabel("Square of Value", fontsize=14)

# 设置刻度标记的大小
plt.tick_params(axis='both', which='major', labelsize=14)

plt.show()

测试记录:

image.png

1.2.5 自动计算数据

手工计算列表要包含的值可能效率低下,需要绘制的点很多时尤其如此。可以不必手工计算包含点坐标的列表,而让Python循环来替我们完成这种计算。
代码:
scatter_squares.py

import matplotlib.pyplot as plt

x_values = list(range(1, 1001))
y_values = [x**2 for x in x_values]

plt.scatter(x_values, y_values, s=40)

# 设置图表标题,并给坐标轴加上标签
plt.title("Square Numbers", fontsize=24)
plt.xlabel("Value", fontsize=4)
plt.ylabel("Square of Value", fontsize=14)

# 设置刻度标记的大小
plt.tick_params(axis='both', which='major', labelsize=14)

# 设置每个坐标轴的取值范围
plt.axis([0, 1100, 0, 1100000])

plt.show()

测试记录:

image.png

1.2.6 删除数据点的轮廓

matplotlib允许你给散点图中的各个点指定颜色。默认为蓝色点和黑色轮廓,在散点图包含的数据点不多时效果很好。但绘制很多点时,黑色轮廓可能会粘连在一起。要删除数据点的轮廓,可在调用scatter() 时传递实参edgecolor='none'

plt.scatter(x_values, y_values, edgecolor='none', s=40)

将相应调用修改为上述代码后,如果再运行scatter_squares.py,在图表中看到的将是蓝色实心点。

1.2.7 自定义颜色

要修改数据点的颜色,可向scatter() 传递参数c ,并将其设置为要使用的颜色的名称,如下所示:

plt.scatter(x_values, y_values, c='red', edgecolor='none', s=40)

你还可以使用RGB颜色模式自定义颜色。要指定自定义颜色,可传递参数c ,并将其设置为一个元组,其中包含三个0~1之间的小数值,它们分别表示红色、绿色和蓝色分量。例如,下面的代码行创建一个由淡蓝色点组成的散点图:

plt.scatter(x_values, y_values, c=(0, 0, 0.8), edgecolor='none', s=40)

值越接近0,指定的颜色越深,值越接近1,指定的颜色越浅。

1.2.8 使用颜色映射

颜色映射 (colormap)是一系列颜色,它们从起始颜色渐变到结束颜色。在可视化中,颜色映射用于突出数据的规律,例如,你可能用较浅的颜色来显示较小的值,并使用较深的颜色来显示较大的值。

模块pyplot 内置了一组颜色映射。要使用这些颜色映射,你需要告诉pyplot 该如何设置数据集中每个点的颜色。下面演示了如何根据每个点的 y 值来设置其颜色.

代码:
scatter_squares.py

import matplotlib.pyplot as plt

x_values = list(range(1, 1001))
y_values = [x**2 for x in x_values]

plt.scatter(x_values, y_values, c = y_values, cmap=plt.cm.Blues,
            edgecolor='none', s=40)


# 设置图表标题,并给坐标轴加上标签
plt.title("Square Numbers", fontsize=24)
plt.xlabel("Value", fontsize=4)
plt.ylabel("Square of Value", fontsize=14)

# 设置刻度标记的大小
plt.tick_params(axis='both', which='major', labelsize=14)

# 设置每个坐标轴的取值范围
plt.axis([0, 1100, 0, 1100000])

plt.show()

测试记录:

image.png

1.2.9 自动保存图表

要让程序自动将图表保存到文件中,可将对plt.show() 的调用替换为对plt.savefig() 的调用

plt.savefig('squares_plot.png', bbox_inches='tight')

1.3 随机漫步

我们将使用Python来生成随机漫步数据,再使用matplotlib以引人瞩目的方式将这些数据呈现出来。随机漫步 是这样行走得到的路径:每次行走都完全是随机的,没有明确的方向,结果是由一系列随机决策决定的。你可以这样认为,随机漫步就是蚂蚁在晕头转向的情况下,每次都沿随机的方向前行所经过的路径。

在自然界、物理学、生物学、化学和经济领域,随机漫步都有其实际用途。例如,漂浮在水滴上的花粉因不断受到水分子的挤压而在水面上移动。水滴中的分子运动是随机的,因此花粉在水面上的运动路径犹如随机漫步。我们稍后将编写的代码模拟了现实世界的很多情形。

1.3.1 创建RandomWalk() 类

为模拟随机漫步,我们将创建一个名为RandomWalk 的类,它随机地选择前进方向。这个类需要三个属性,其中一个是存储随机漫步次数的变量,其他两个是列表,分别存储随机漫步经过的每个点的 x 和 y 坐标。

RandomWalk 类只包含两个方法:init() 和fill_walk() ,其中后者计算随机漫步经过的所有点。下面先来看看init() ,如下所示
代码:

from random import choice

class RandomWalk():
    """ 初始化随机漫步的属性 """

    def __init__(self, num_points=5000):
        """ 初始化随机漫步的属性 """
        self.num_points = num_points

        # 所有随机漫步都始于(0,0)
        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

            # 计算下一个点的x和y值
            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)

我们建立了一个循环,这个循环不断运行,直到漫步包含所需数量的点。这个方法的主要部分告诉Python如何模拟四种漫步决定:向右走还是向左走?沿指定的方向走多远?向上走还是向下走?沿选定的方向走多远?

我们使用choice([1, -1]) 给x_direction 选择一个值,结果要么是表示向右走的1,要么是表示向左走的-1。接下来,choice([0, 1, 2, 3, 4]) 随机地选择一个0~4之间的整数,告诉Python 沿指定的方向走多远(x_distance )。(通过包含0,我们不仅能够沿两个轴移动,还能够沿y 轴移动。)

我们将移动方向乘以移动距离,以确定沿 x 和 y 轴移动的距离。如果x_step 为正,将向右移动,为负将向左移动,而为零将垂直移动;如果y_step 为正,就意味着向上移动,为负意味着向下移动,而为零意味着水平移动。如果x_step 和y_step 都为零,则意味着原地踏步,我们拒绝这样的情况,接着执行下一次循环。

为获取漫步中下一个点的 x 值,我们将x_step 与x_values 中的最后一个值相加,对于 y 值也做相同的处理。获得下一个点的 x 值和 y 值后,我们将它们分别附加到列表x_values 和y_values 的末尾。

1.3.3 绘制随机漫步图

下面的代码将随机漫步的所有点都绘制出来
代码:
rw_visual.py

import matplotlib.pyplot as plt

from random_walk import RandomWalk

# 创建一个RandomWalk实例,并将其包含的点都绘制出来
rw = RandomWalk()
rw.fill_walk()
plt.scatter(rw.x_values, rw.y_values, s=15)
plt.show()

测试记录:

image.png

1.3.4 模拟多次随机漫步

每次随机漫步都不同,因此探索可能生成的各种模式很有趣。要在不多次运行程序的情况下使用前面的代码模拟多次随机漫步,一种办法是将这些代码放在一个while 循环中.

代码:
rw_visual.py

import matplotlib.pyplot as plt

from random_walk import RandomWalk

# 只要程序处于活动状态,就不断地模拟随机漫步

while True:
    # 创建一个RandomWalk实例,并将其包含的点都绘制出来
    rw = RandomWalk()
    rw.fill_walk()
    plt.scatter(rw.x_values, rw.y_values, s=15)
    plt.show()

    keep_running = input("Make another walk? (y/n): ")
    if keep_running == 'n':
        break

这些代码模拟一次随机漫步,在matplotlib查看器中显示结果,再在不关闭查看器的情况下暂停。如果你关闭查看器,程序将询问你是否要再模拟一次随机漫步。如果你输入y ,可模拟多次随机漫步:这些随机漫步都在起点附近进行,大多沿特定方向偏离起点,漫步点分布不均匀等。要结束程序,请输入n 。

1.3.5 设置随机漫步图的样式

我们将定制图表,以突出每次漫步的重要特征,并让分散注意力的元素不那么显眼。为此,我们确定要突出的元素,如漫步的起点、终点和经过的路径。接下来确定要使其不那么显眼的元素,如刻度标记和标签。最终的结果是简单的可视化表示,清楚地指出了每次漫步经过的路径。

1.3.6 给点着色

我们将使用颜色映射来指出漫步中各点的先后顺序,并删除每个点的黑色轮廓,让它们的颜色更明显。为根据漫步中各点的先后顺序进行着色,我们传递参数c ,并将其设置为一个列表,其中包含各点的先后顺序。由于这些点是按顺序绘制的,因此给参数c 指定的列表只需包含数字1~5000.
代码:
rw_visual.py

import matplotlib.pyplot as plt

from random_walk import RandomWalk

# 只要程序处于活动状态,就不断地模拟随机漫步

while True:
    # 创建一个RandomWalk实例,并将其包含的点都绘制出来
    rw = RandomWalk()
    rw.fill_walk()

    point_numbers = list(range(rw.num_points))
    plt.scatter(rw.x_values, rw.y_values, c = point_numbers ,
                cmap=plt.cm.Blues,edgecolor='none',s=15)

    plt.show()

    keep_running = input("Make another walk? (y/n): ")
    if keep_running == 'n':
        break

测试记录:

image.png

我们使用了range() 生成了一个数字列表,其中包含的数字个数与漫步包含的点数相同。接下来,我们将这个列表存储在point_numbers 中,以便后面使用它来设置每个漫步点的颜色。我们将参数c 设置为point_numbers ,指定使用颜色映射Blues ,并传递实参edgecolor=none 以删除每个点周围的轮廓。最终的随机漫步图从浅蓝色渐变为深蓝色.

1.3.7 重新绘制起点和终点

除了给随机漫步的各个点着色,以指出它们的先后顺序外,如果还能呈现随机漫步的起点和终点就更好了。为此,可在绘制随机漫步图后重新绘制起点和终点。我们让起点和终点变得更大,并显示为不同的颜色,以突出它们.

代码:
rw_visual.py

import matplotlib.pyplot as plt

from random_walk import RandomWalk

# 只要程序处于活动状态,就不断地模拟随机漫步

while True:
    # 创建一个RandomWalk实例,并将其包含的点都绘制出来
    rw = RandomWalk()
    rw.fill_walk()

    point_numbers = list(range(rw.num_points))
    plt.scatter(rw.x_values, rw.y_values, c = point_numbers,
                cmap=plt.cm.Blues,edgecolor='none',s=15)

    # 突出起点和终点
    plt.scatter(0, 0, c= 'green', edgecolor='none',s=100)
    plt.scatter(rw.x_values[-1], rw.y_values[-1], c='red',
                edgecolors='none',s = 100)


    plt.show()

    keep_running = input("Make another walk? (y/n): ")
    if keep_running == 'n':
        break

测试记录:

image.png

1.3.8 隐藏坐标轴

下面来隐藏这个图表中的坐标轴,以免我们注意的是坐标轴而不是随机漫步路径,需要隐藏坐标轴

代码:
rw_visual.py

import matplotlib.pyplot as plt

from random_walk import RandomWalk

# 只要程序处于活动状态,就不断地模拟随机漫步

while True:
    # 创建一个RandomWalk实例,并将其包含的点都绘制出来
    rw = RandomWalk()
    rw.fill_walk()

    point_numbers = list(range(rw.num_points))
    plt.scatter(rw.x_values, rw.y_values, c = point_numbers,
                cmap=plt.cm.Blues,edgecolor='none',s=15)

    # 突出起点和终点
    plt.scatter(0, 0, c= 'green', edgecolor='none',s=100)
    plt.scatter(rw.x_values[-1], rw.y_values[-1], c='red',
                edgecolors='none',s = 100)

    # 隐藏坐标轴
    plt.axes().get_xaxis().set_visible(False)
    plt.axes().get_yaxis().set_visible(False)

    plt.show()

    keep_running = input("Make another walk? (y/n): ")
    if keep_running == 'n':
        break

测试记录:

image.png

1.3.9 增加点数

面来增加点数,以提供更多的数据。为此,我们在创建RandomWalk 实例时增大num_points 的值,并在绘图时调整每个点的大小.

代码:
rw_visual.py

import matplotlib.pyplot as plt

from random_walk import RandomWalk

# 只要程序处于活动状态,就不断地模拟随机漫步

while True:
    # 创建一个RandomWalk实例,并将其包含的点都绘制出来
    rw = RandomWalk(50000)
    rw.fill_walk()

    point_numbers = list(range(rw.num_points))
    plt.scatter(rw.x_values, rw.y_values, c = point_numbers,
                cmap=plt.cm.Blues,edgecolor='none',s=1)

    # 突出起点和终点
    plt.scatter(0, 0, c= 'green', edgecolor='none',s=100)
    plt.scatter(rw.x_values[-1], rw.y_values[-1], c='red',
                edgecolors='none',s = 100)

    # 隐藏坐标轴
    plt.axes().get_xaxis().set_visible(False)
    plt.axes().get_yaxis().set_visible(False)

    plt.show()

    keep_running = input("Make another walk? (y/n): ")
    if keep_running == 'n':
        break

测试记录:

image.png

这个示例模拟了一次包含50 000个点的随机漫步(以模拟现实情况),并将每个点的大小都设置为1。最终的随机漫步图更纤细,犹如云朵,如上图所示。正如你看到的,我们使用简单的散点图制作出了一件艺术品!

请尝试修改上述代码,看看将漫步包含的点数增加到多少后,程序的运行速度变得极其缓慢或绘制出的图形变得很难看。

1.3.10 调整尺寸以适合屏幕

图表适合屏幕大小时,更能有效地将数据中的规律呈现出来。为让绘图窗口更适合屏幕大小,可像下面这样调整matplotlib输出的尺寸。

代码:
rw_visual.py

import matplotlib.pyplot as plt

from random_walk import RandomWalk

# 只要程序处于活动状态,就不断地模拟随机漫步

while True:
    # 创建一个RandomWalk实例,并将其包含的点都绘制出来
    rw = RandomWalk()
    rw.fill_walk()

    # 设置绘图窗口的尺寸
    plt.figure(figsize=(10, 6))

    point_numbers = list(range(rw.num_points))
    plt.scatter(rw.x_values, rw.y_values, c = point_numbers,
                cmap=plt.cm.Blues,edgecolor='none',s=1)

    # 突出起点和终点
    plt.scatter(0, 0, c= 'green', edgecolor='none',s=100)
    plt.scatter(rw.x_values[-1], rw.y_values[-1], c='red',
                edgecolors='none',s = 100)

    # 隐藏坐标轴
    plt.axes().get_xaxis().set_visible(False)
    plt.axes().get_yaxis().set_visible(False)

    plt.show()

    keep_running = input("Make another walk? (y/n): ")
    if keep_running == 'n':
        break

测试记录:

image.png

函数figure() 用于指定图表的宽度、高度、分辨率和背景色。你需要给形参figsize 指定一个元组,向matplotlib指出绘图窗口的尺寸,单位为英寸。

Python假定屏幕分辨率为80像素/英寸,如果上述代码指定的图表尺寸不合适,可根据需要调整其中的数字。如果你知道自己的系统的分辨率,可使用形参dpi 向figure() 传递该分辨率,以有效地利用可用的屏幕空间,如下所示:

plt.figure(dpi=128, figsize=(10, 6))

1.4 使用Pygal模拟掷骰子

我们将使用Python可视化包Pygal来生成可缩放的矢量图形文件。对于需要在尺寸不同的屏幕上显示的图表,这很有用,因为它们将自动缩放,以适合观看者的屏幕。如果你打算以在线方式使用图表,请考虑使用Pygal来生成它们,这样它们在任何设备上显示时都会很美观。

在这个项目中,我们将对掷骰子的结果进行分析。掷6面的常规骰子时,可能出现的结果为1~6点,且出现每种结果的可能性相同。然而,如果同时掷两个骰子,某些点数出现的可能性将比其他点数大。为确定哪些点数出现的可能性最大,我们将生成一个表示掷骰子结果的数据集,并根据结果绘制出一个图形。

在数学领域,常常利用掷骰子来解释各种数据分析,但它在赌场和其他博弈场景中也得到了实际应用,在游戏《大富翁》以及众多角色扮演游戏中亦如此。

1.4.1 安装Pygal

window平台直接使用pip进行安装

C:\>pip install pygal
Collecting pygal
  Downloading pygal-2.4.0-py2.py3-none-any.whl (127 kB)
     |████████████████████████████████| 127 kB 273 kB/s
Installing collected packages: pygal
Successfully installed pygal-2.4.0

C:\>

1.4.2 Pygal画廊

要了解使用Pygal可创建什么样的图表,请查看图表类型画廊:访问http://www.pygal.org/ ,单击Documentation,再单击Chart types。每个示例都包含源代码,让你知道这些图表是如何
生成的。

1.4.3 创建Die 类

下面的类模拟掷一个骰子
代码:
die.py

from random import randint

class Die():
    """ 表示一个骰子的类 """

    def __init__(self, num_sides=6):
        """ 骰子默认为6面 """
        self.num_sides = num_sides

    def roll(self):
        """ 返回一个位于1和骰子面熟之间的随机数 """
        return randint(1, self.num_sides)

方法init() 接受一个可选参数。创建这个类的实例时,如果没有指定任何实参,面数默认为6;如果指定了实参,这个值将用于设置骰子的面数。骰子是根据面数命名的,6面的骰子名为D6,8面的骰子名为D8,以此类推。

方法roll() 使用函数randint() 来返回一个1和面数之间的随机数。这个函数可能返回起始值1、终止值num_sides 或这两个值之间的任何整数。

1.4.4 掷骰子

使用这个类来创建图表前,先来掷D6骰子,将结果打印出来,并检查结果是否合理

代码:
die_visual.py

from die import Die

# 创建一个D6
die = Die()

# 掷几次骰子,并将结果存储在一个列表中
results = []
for roll_num in range(100):
    result = die.roll()
    results.append(result)

print(results)

测试记录:

E:\python\learn_python1\venv\Scripts\python.exe E:/python/learn_python1/数据可视化/die_visual.py
[6, 2, 4, 3, 1, 4, 1, 4, 4, 1, 3, 3, 3, 5, 2, 5, 2, 6, 3, 6, 5, 6, 2, 3, 3, 1, 2, 6, 3, 1, 5, 5, 3, 3, 1, 3, 3, 6, 1, 1, 3, 6, 1, 6, 4, 5, 5, 2, 5, 4, 6, 5, 5, 2, 4, 4, 3, 1, 4, 5, 3, 5, 2, 4, 4, 3, 1, 1, 1, 5, 3, 4, 5, 5, 1, 3, 3, 1, 5, 6, 1, 6, 6, 3, 6, 2, 2, 5, 3, 6, 3, 4, 4, 6, 1, 4, 6, 1, 1, 2]

Process finished with exit code 0

1.4.5 分析结果

为分析掷一个D6骰子的结果,我们计算每个点数出现的次数

代码:
die_visual.py

from die import Die

# 创建一个D6
die = Die()

# 掷几次骰子,并将结果存储在一个列表中
results = []
for roll_num in range(1000):
    result = die.roll()
    results.append(result)

# 分析结果
frequencies = []
for value in range(1, die.num_sides + 1):
    frequency = results.count(value)
    frequencies.append(frequency)

print(frequencies)

测试记录:

E:\python\learn_python1\venv\Scripts\python.exe E:/python/learn_python1/数据可视化/die_visual.py
[189, 153, 157, 162, 154, 185]

Process finished with exit code 0

结果看起来是合理的:我们看到了6个值——掷D6骰子时可能出现的每个点数对应一个;我们还发现,没有任何点数出现的频率比其他点数高很多。下面来可视化这些结果。

1.4.6 绘制直方图

有了频率列表后,我们就可以绘制一个表示结果的直方图。直方图 是一种条形图,指出了各种结果出现的频率。

代码:

import pygal
from die import Die

# 创建一个D6
die = Die()

# 掷几次骰子,并将结果存储在一个列表中
results = []
for roll_num in range(1000):
    result = die.roll()
    results.append(result)

# 分析结果
frequencies = []
for value in range(1, die.num_sides + 1):
    frequency = results.count(value)
    frequencies.append(frequency)

# 对比结果进行可视化
hist = pygal.Bar()

hist.title = "Results of rolling one D6 1000 times."
hist.x_labels = ['1', '2', '3', '4', '5', '6']
hist.x_title = "Result"
hist.y_title = "Frequency of Result"

hist.add('D6', frequencies)
hist.render_to_file('die_visual.svg')

测试记录:

image.png

1.4.7 同时掷两个骰子

同时掷两个骰子时,得到的点数更多,结果分布情况也不同。下面来修改前面的代码,创建两个D6骰子,以模拟同时掷两个骰子的情况。每次掷两个骰子时,我们都将两个骰子的点数相加,并将结果存储在results 中。请复制die_visual.py并将其保存为dice_visual.py,再做如下修改

代码:
dice_visual.py

import pygal
from die import Die

# 创建两个D6
die_1 = Die()
die_2 = Die()

# 掷几次骰子,并将结果存储在一个列表中
results = []
for roll_num in range(1000):
    result = die_1.roll() + die_2.roll()
    results.append(result)

# 分析结果
frequencies = []
max_result = die_1.num_sides + die_2.num_sides
for value in range(2, max_result + 1):
    frequency = results.count(value)
    frequencies.append(frequency)

# 对比结果进行可视化
hist = pygal.Bar()

hist.title = "Results of rolling one D6 1000 times."
hist.x_labels = [ '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12']
hist.x_title = "Result"
hist.y_title = "Frequency of Result"

hist.add('D6 + D6', frequencies)
hist.render_to_file('die_visual.svg')

测试记录:

image.png

这个图表显示了掷两个D6骰子时得到的大致结果。正如你看到的,总点数为2或12的可能性最小,而总点数为7的可能性最大,这是因为在6种情况下得到的总点数都为7。这6种情况如下:1和6、2和5、3和4、4和3、5和2、6和1。

参考

1.Python编程:从入门到实践

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,968评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,682评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,254评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,074评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,964评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,055评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,484评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,170评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,433评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,512评论 2 308
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,296评论 1 325
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,184评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,545评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,150评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,437评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,630评论 2 335

推荐阅读更多精彩内容