数据可视化指的是通过可视化表示来探索数据,它与数据挖掘紧密相关,而数据挖掘指的是使用代码来探索数据集的规律和关联。数据集可以是用一行代码来表示的小型数字列表,也可以是海量字节的数据。
鉴于 Python 的高效性,使用它在笔记本电脑上就能快速地探索由数百万个数据点组成的数据集。数据点并非必须是数字,也可以对非数字数据进行分析。
在基因研究、天气预测、政治经济分析等领域,都可以使用 Python 完成数据密集型工作。使用 Python 编写一些了可视化和分析工具,其中最流行的工具之一就是 matplotlib ,这是一个数学绘图库,使用它能够很简单地完成图表的绘制,如折线图、散点图。这里,将基于随机漫步概念生成一个有趣的数据集——根据一系列随机决策生成的图表
此外,还可以使用 pygal 包(它专注于生成适合在数字设备上显示的图表)。通过使用 pygal,可在用户与图表交互时突出元素以及调整其大小,还可以轻松地调整整个图表的尺寸,使其适合在微型智能手表或巨型显示器上正常显示。这里,将使用 pygal 以各种方式探索投掷骰子的结果
本章中用到的项目相关代码已经保存在 GitHub上,链接:代码仓库。
当然也可以通过 CSDN进行下载代码包。CSDN项目包下载
首先,需要安装 matplotlib 包,与之前安装 pygame 包的操作类似,也可以使用 pip 工具进行安装。
在 Linux 系统下,可以在终端通过一系列命令完成 matplotlib 包的下载安装,如下:
$ sudo apt-get install python3-matplotlib
$ sudo apt-get install python3.5-dev python3.5-tk tk-dev
$ sudo apt-get install libfreetype6-dev g++
$ pip install --user matplotlib
访问 matplotlib 包下载网站:matplotlib下载地址 ,找到与所使用的 Python 版本相匹配的 wheel 文件,如何打开命令窗口,输入以下命令完成安装。
$ python -m pip install --user matplotlib-3.2.2-cp38-cp38-win_amd64.whl
如果需要查看使用 matplotlib 可制作的各种图表,可以访问 示例画廊 。单击画廊中的图表就可以查看生成图表的代码。
下面就使用 matplotlib 来绘制一个简单的折线图,然后再对其进行修改以实现信息更丰富的数据可视化。
使用平方数列 1、4、9、16 和 25 来绘制这个图表,具体代码如下:
import matplotlib.pyplot as plt
squares = [1, 4, 9, 16, 25]
plt.plot(squares)
plt.show()
plot()
,这个函数尝试根据这些数字绘制出有意义的图形plt.show()
打开 matplotlib 查看器并显示绘制的图形执行程序显示如下:
由前面的图形可以表明数字是越来越大的,但是标签文字太小、线条太细,展现细节不是很明显。但是 matplotlib 是可以调整这些方面的。
对代码做如下修改:
import matplotlib.pyplot as plt
squares = [1, 4, 9, 16, 25]
# 绘制图形,定义线条宽度为5
plt.plot(squares, linewidth=5)
# 设置图表标题,并为坐标轴加上标签
plt.title("Square Numbers", fontsize = 24)
plt.xlabel("Value", fontsize = 14)
plt.ylabel("Square of Value", fontsize = 14)
# 设置刻度标记大小
plt.tick_params(axis = 'both', labelsize = 14)
# 显示
plt.show()
执行如下:
图形更容易阅读后,发现折线图的显示不是很正确,4 的平方居然指向 25 。
这是由于当向 plot()
提供一系列数字时,它假设第一个数据点对应的 x 坐标值为 0,但是这里我们使用的是 1.为了改变这种默认的行为,需要给 plot()
函数同时提供输入和输出的值,修改如下:
import matplotlib.pyplot as plt
# 输入队列
input_values = [1, 2, 3, 4, 5]
# 输出队列
squares = [1, 4, 9, 16, 25]
# 绘制图形,定义线条宽度为5
plt.plot(input_values, squares, linewidth=5)
# 设置图表标题,并为坐标轴加上标签
plt.title("Square Numbers", fontsize = 24)
plt.xlabel("Value", fontsize = 14)
plt.ylabel("Square of Value", fontsize = 14)
# 设置刻度标记大小
plt.tick_params(axis = 'both', labelsize = 14)
# 显示
plt.show()
此时再执行后,图形显示就正确了。
scatter()
绘制散点图并设置样式有时候需要绘制散点图并设置各个数据点的样式。例如:用一种颜色显示较小的值,用另一种颜色显示较大的值。在绘制大型数据集时,还可以对每个点设置同样的样式,在使用不同的样式选项重新绘制它们用来突出显示。
要绘制单个点,需要用函数 scatter()
,并向它传递一对 x 和 y 坐标,它将在指定位置绘制一个点。
import matplotlib.plot as plt
plt.scatter(2,4)
plt.show()
scatter()
绘制一系列点要绘制一系列的点,可以向 scatter()
函数传递两个分别包含 x 和 y 坐标的列表,如下:
import matplotlib.pyplot as plt
#plt.scatter(2,4)
x_values = [1, 2, 3, 4, 5]
y_values = [1, 4, 9, 16, 25]
plt.scatter(x_values, y_values, s=100)
# 设置图表标题,并为坐标轴加上标签
plt.title("Square Numbers", fontsize = 24)
plt.xlabel("Value", fontsize = 14)
plt.ylabel("Square of Value", fontsize = 14)
# 设置刻度标记大小
plt.tick_params(axis = 'both', labelsize = 14)
plt.show()
手工计算列表要包含的值可能效率低下,尤其是当需要绘制的点很多的情况下。可以不需要手工计算包含点坐标的列表,通过使用 Python 循环来完成这种计算就很方便了。
import matplotlib.pyplot as plt
x_values = lis(range(1, 10001))
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 = 14)
plt.ylabel("Square of Value", fontsize = 14)
# 设置刻度标记大小
plt.tick_params(axis = 'both', labelsize = 14)
# 设置每个坐标轴的取值范围
plt.axis([0, 1100, 0, 1100000])
plt.show()
在调用 scatter()
函数时传递实参 edgecolor='none'
可以删除掉数据点的轮廓,具体调用如下:
plt.scatter(x_values, y_values, edgecolor='none', s=40)
要修改数据点的颜色,可以向 scatter()
传递参数 c ,将其设置为要使用的颜色名称,如下所示:
plt.scatter(x_values, y_values, c='red', edgecolor='none', s=40)
修改后,显示如下:
此外,还可以使用 RGB颜色模式自定义颜色,如:plt.scatter(x_values, y_values, c=(0, 0.8, 0), edgecolor='none', s=40)
颜色映射是一系列颜色,从起始颜色渐变到结束颜色。在可视化中,颜色映射用于突出数据规律,例如:可能使用较浅的颜色来显示较小的值,深颜色显示较大的值。
模块 pyplot
内置了一组颜色映射,使用颜色映射需要告诉 pyplot
该如何设置数据集中每个点的颜色,如下:
import matplotlib.pyplot as plt
x_values = list(range(1, 10001))
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 = 14)
plt.ylabel("Square of Value", fontsize = 14)
# 设置刻度标记大小
plt.tick_params(axis = 'both', labelsize = 14)
# 设置每个坐标轴的取值范围
plt.axis([0, 1100, 0, 1100000])
plt.show()
如果需要让程序自动将图表保存到文件中,可以使用 plt.savefig()
函数进行保存,具体调用方式如下:
plt.savefig('squares_plot.png', bbox_inches='tight')
执行如下;
接下来,将使用 Python 来生成随机漫步数据,如何使用 matplotlib 将这些数据呈现出来。
随机漫步:每次行走都是完全随机的、没有明确的方向,结果是由一系列随机决策决定的。可以认为是一只蚂蚁在晕头转向的情况下沿着随机的方向前行所经过的路径。
为模拟随机漫步,将创建一个名为 RandomWalk 类,它随机选择前进方向。这个类需要三种属性,存储随机漫步的次数、存储随机漫步经过的点的 x 和 y 坐标。具体如下:
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]
向 RandomWalk 类中添加 fill_walk()
来生成漫步包含的点,并决定每次漫步的方向,如下所示:
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)
使用以下代码将随机漫步的点都绘制出来:
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()
生成图片如下:
在不关闭查看器的情况下,暂停程序,关闭查看器后询问是否多次生成显示随机漫步:
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()
'''
# 只有程序处于活动状态,就不断模拟随机漫步
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
接下来将定制图标,以突出每次漫步的重要特征,并让分散注意力的元素不那么明显。为此,先是需要确定要突出的元素,比如漫步的起点、终点以及经过的路径;然后就是确定哪些不显眼的元素,如刻度标记、标签等等。最终结果是简单的可视化表示能够清楚指出每次漫步经过的路径。
使用颜色映射来指出漫步中各点的先后顺序,并删除每一个点的黑色轮廓,让它们的颜色更明显。为方便根据漫步中各点的先后顺序进行着色,需要传递参数c,并将其设置为一个列表,其中包含各点的先后顺序。由于这些点是按顺序绘制的,因此给参数c指定的列表只需包含数字 1-5000 ,修改后的绘图文件如下所示:
这里,使用了 range()
生成了一个数字列表,其中包含的数字个数与漫步包含的点数相同。接下来,将这个列表存储在 point_numbers
,以便后面使用它来设置每个漫步点的颜色,将设置参数 c 设置为 point_numbers
,指定使用颜色映射 Blues,并传递实参 edgecolor=none
以删除每个点周围的轮廓。最终的随机漫步图从浅蓝色渐变成深蓝色。
除了给随机漫步的每个点上渐变的颜色以区分前后,还需要重新绘制起点和终点。具体如下:
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()
'''
# 只有程序处于活动状态,就不断模拟随机漫步
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', edgecolors='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
接下来就需要隐藏这个图表中的坐标轴,以免突出显示图片中的内容,如下:
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()
'''
# 只有程序处于活动状态,就不断模拟随机漫步
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', edgecolors='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
接下来增加绘制点数,从而提供更多数据,只需要在创建 RandomWalk
实例时提供参数即可,代码如下:
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()
'''
# 只有程序处于活动状态,就不断模拟随机漫步
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=15)
# 突出起点和终点
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.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
图表适合屏幕大小时,更能有效地将数据中的规律呈现出来。使得绘图窗口更适合屏幕大小。
函数 figure()
用于指定图表的宽度、高度、分辨率和背景色。需要给形参 figsize
指定一个元组,想 matplotlib
指定绘图窗口的尺寸,单位为英寸。
可以像下面这样调整 matplotlib
输出尺寸:
# 设置绘图窗口的尺寸
plt.figure(figsize=(10, 6))
这里,使用 pygal 可视化包来生成可缩放的矢量图形文件。对于需要在尺寸不同的屏幕上显示的图表,矢量图很有用。如果考虑在线方式显示图表,最好使用 pygal 来生成它们,这样方便图表在任意的设备上都可以显示美观。
接下来这个项目中,将对掷骰子的结果进行分析。掷六面的常规骰子时,可能出现的结果为 1~6 点,且出现每种的可能性都相同。然而,如果同时投掷两颗骰子,某些点数出现的可能性将会比其他点数大。为了确定哪些点数出现的可能性最大,将生成一个表示投掷骰子结果的数据集,并根据结果绘制出一个图形。
在数学领域,常常会用到投掷骰子来解释各种数据分析,在实际游戏中也还能用得到,比如在游戏《大富翁》以及众多角色扮演游戏中亦是如此。
类似之前安装包的方式,如下图:
如果要了解 pygal 能够创建什么样的图表,可以查看 Python 的图表类型画廊,访问图表画廊链接 ,单击 Documentation
,再单击 Chart types
。这里每个示例都包含源代码,可以通过查询这里来进行绘制图表的参考。
创建一个 Die
类来模拟投掷一个骰子,如下:
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)
其中,roll()
方法会返回一个 1~骰子面数的随机值。
使用前面创建的投掷骰子的类,先来投掷 D6 骰子,并将结果打印出来,检查是否合理。如下:
from die import Die
# 创建一个D6
die = Die()
# 投掷几次骰子,并将结果存储到列表中
results = []
for rool_num in range(100):
result = die.roll()
results.append(result)
print(results)
执行程序,显示结果如下:
可以看到,打印出的结果都是位于 1~6 之间的,没有打印出 0 或者 7 之类的,所以结果都在正确的范围内。
为分析前面打印出的骰子结果,可以在代码中计算每个点数出现的次数,如下:
from die import Die
# 创建一个D6
die = Die()
# 投掷几次骰子,并将结果存储到列表中
results = []
for rool_num in range(100):
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)
打印显示结果如下:
前面打印出来随机数字的频率列表后,就可以绘制一个表示结果的直方图。直方图是一种条形图,指出了各种结果出现的频率。创建直方图的代码如下:
from die import Die
# 创建一个D6
die = Die()
# 投掷几次骰子,并将结果存储到列表中
results = []
for rool_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_label = "Frequency of Result"
hist.add('D6', frequencies)
hist.render_to_file('die_visual.svg')
显示结果如下:
同时投掷两个骰子时,得到的点数更多,结果分布情况也不同。下面来修改前面的代码,创建两个 D6 骰子,以模拟同时投掷两个骰子的情况。每次投掷两个骰子时,我们将两个骰子的点数相加,并将结果存储在 results
中。请复制 die_visual.py
并将其保存为 dice_visual.py
,代码做如下修改:
下面就来创建一个6面骰子和10面骰子同时投掷 50000 次的结果:
from die import Die
import pygal
# 创建一个D6 和一个D10
die_1 = Die()
die_2 = Die(10)
# 投掷骰子多次,将结果存储到列表中
results = []
for roll_num in range(50000):
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):
frequency = results.count(value)
frequencies.append(frequency)
# 可视化结果
hist = pygal.Bar()
hist.title = "Results of rolling a D6 and a D10 50000 times"
hist.x_labels = ['2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16']
hist.x_title = "Result"
hist.y_label = "Frequency of Result"
hist.add('D6 + D10', frequencies)
hist.render_to_file('die_visual.svg')