我心中的王者:Python-第23章 数据图表的设计
本章所叙述的重点是数据图形的绘制,所使用的工具是matplotlib绘图库模块,使用前需先安装:
pip install matplotlib
matplotlib是一个庞大的绘图库模块,本章我们只导入其中的pyplot子模块就可以完成许多图表绘制,如下所示,未来就可以使用plt调用相关的方法。
import matplotlib.pyplot as plt
本章将叙述matplotlib的重点,更完整使用说明可以参考下列网站。
http://matplotlib.org
23-1 绘制简单的折线图
这一节将从最简单的折线图开始解说。
23-1-1 显示绘制的图形show( )
这个show( )方法主要是显示所绘制的图形,当我们绘制图形完成后,可以调用此方法。
23-1-2 画线plot( )
应用方式是将含数据的列表当参数传给plot( ),列表内的数据会被视为y轴的值,x轴的值会依列表值的索引位置自动产生。
程序实例ch23_1.py:绘制折线的应用,数据基本上是1-8的平方值序列。
# ch23_1.py
import matplotlib.pyplot as plt
squares = [1, 4, 9, 16, 25, 36, 49, 64]
plt.plot(squares)
plt.show()
执行结果

从上述执行结果可以看到左下角的轴刻度不是(0,0),我们可以使用axis( )设定x,y轴的最小和最大刻度。
程序实例ch23_1_1.py:重新设计ch23_1.py,将轴刻度x轴设为0,8,y轴刻度设为0,70。
# ch23_1_1.py
import matplotlib.pyplot as plt
squares = [1, 4, 9, 16, 25, 36, 49, 64]
plt.plot(squares)
plt.axis([0, 8, 0, 70])
plt.show()
执行结果

23-1-3 线条宽度linewidth
使用plot( )时,可以多加一个linewidth(缩写是lw)参数设定线条的粗细。
程序实例ch23_2.py:设定线条宽度是3。
# ch23_2.py
import matplotlib.pyplot as plt
squares = [1, 4, 9, 16, 25, 36, 49, 64]
plt.plot(squares, linewidth=3)
plt.show()
执行结果

23-1-4 标题的显示
目前matplotlib模块不支持中文显示,下列是几个图表重要的方法。
title( ):图表标题。
xlabel( ):x轴标题。
ylabel( ):y轴标题。
上述方法可以显示默认大小是12的字体,它的语法如下:
title(标题名称, fontsize=数值大小) # 同时可用在xlabel( )和ylabel( )
程序实例ch23_3.py:使用默认字号为图表与x/y轴建立标题。
# ch23_3.py
import matplotlib.pyplot as plt
squares = [1, 4, 9, 16, 25, 36, 49, 64]
plt.plot(squares, linewidth=3)
plt.title("Test Chart")
plt.xlabel("Value")
plt.ylabel("Square")
plt.show()
执行结果 可参考下方上图。

程序实例ch23_4.py:使用设定字号为图表与x/y轴建立标题。
# ch23_4.py
import matplotlib.pyplot as plt
squares = [1, 4, 9, 16, 25, 36, 49, 64]
plt.plot(squares, linewidth=3)
plt.title("Test Chart", fontsize=24)
plt.xlabel("Value", fontsize=16)
plt.ylabel("Square", fontsize=16)
plt.show()
执行结果 可参考上方下图。
23-1-5 坐标轴刻度的设定
在设计图表时可以使用tick_params( )设计设定坐标轴的刻度大小、颜色以及应用范围。
tick_params(axis=‘xx', labelsize=xx, color=‘xx') # labelsize的xx代表刻
度大小
如果axis的xx是both代表应用到x和y轴,如果xx是x代表应用到x轴,如果xx是y代表应用到y轴。color则是设定刻度的线条颜色,例如,red代表红色。
程序实例ch23_5.py:使用不同刻度与颜色的应用。
# ch23_5.py
import matplotlib.pyplot as plt
squares = [1, 4, 9, 16, 25, 36, 49, 64]
plt.plot(squares, linewidth=3)
plt.title("Test Chart", fontsize=24)
plt.xlabel("Value", fontsize=16)
plt.ylabel("Square", fontsize=16)
plt.tick_params(axis='both', labelsize=12, color='red')
plt.show()
执行结果

23-1-6 修订图表的起始值
从上图可以看到平方列表的值是有8个数据,依照Python语法起始数字是从0开始,所以整个数值到7结束。但是在我们日常生活呈现的报表中,通常数字是从1开始,为了要做这个修订,可以再增加一个列表,这个列表主要是设定数值索引,细节可参考下列实例的第5行。
程序实例ch23_6.py:修订图表的起始值,读者应该注意到x轴标计从1开始。
# ch23_6.py
import matplotlib.pyplot as plt
squares = [1, 4, 9, 16, 25, 36, 49, 64]
seq = [1,2,3,4,5,6,7,8]
plt.plot(seq, squares, linewidth=3)
plt.title("Test Chart", fontsize=24)
plt.xlabel("Value", fontsize=16)
plt.ylabel("Square", fontsize=16)
plt.tick_params(axis='both', labelsize=12, color='red')
plt.show()
执行结果

23-1-7 多组数据的应用
目前所有的图表皆只有一组数据,其实可以扩充多组数据,只要在plot( )内增加数据列表参数即可。此时plot( )的参数如下:
plot(第一组数据, 第二组数据, … )
程序实例ch23_7:设计多组数据图的应用。
# ch23_7.py
import matplotlib.pyplot as plt
data1 = [1, 4, 9, 16, 25, 36, 49, 64] # data1线条
data2 = [1, 3, 6, 10, 15, 21, 28, 36] # data2线条
seq = [1,2,3,4,5,6,7,8]
plt.plot(seq, data1, seq, data2) # data1&2线条
plt.title("Test Chart", fontsize=24)
plt.xlabel("x-Value", fontsize=14)
plt.ylabel("y-Value", fontsize=14)
plt.tick_params(axis='both', labelsize=12, color='red')
plt.show()
执行结果

上述以不同颜色显示线条是系统默认,我们也可以自定义线条色彩。
23-1-8 线条色彩与样式
如果想设定线条色彩,可以在plot( )内增加下列参数设定,下列是常见的色彩表。

下列是常见的样式表。可以混合使用,例如,‘r-.’代表红色虚点线。

程序实例ch23_8.py:采用不同色彩与线条样式绘制图表。
# ch23_8.py
import matplotlib.pyplot as plt
data1 = [1, 2, 3, 4, 5, 6, 7, 8] # data1线条
data2 = [1, 4, 9, 16, 25, 36, 49, 64] # data2线条
data3 = [1, 3, 6, 10, 15, 21, 28, 36] # data3线条
data4 = [1, 7, 15, 26, 40, 57, 77, 100] # data4线条
seq = [1, 2, 3, 4, 5, 6, 7, 8]
plt.plot(seq, data1, 'g--', seq, data2, 'r-.', seq, data3, 'y:', seq, data4, 'k.')
plt.title("Test Chart", fontsize=24)
plt.xlabel("x-Value", fontsize=14)
plt.ylabel("y-Value", fontsize=14)
plt.tick_params(axis='both', labelsize=12, color='red')
plt.show()
执行结果

在上述第10行最右边‘k.’代表绘制黑点而不是绘制线条,由这个观念读者应该可以使用不同颜色绘制散点图(23-2节会介绍另一个方法scatter( )绘制散点图)。上述格式应用是很活的,如果我们使用‘-*’可以绘制线条,同时在指定点加上星星标记。
程序实例ch23_9.py:重新设计ch23_8.py绘制线条,同时为各个点加上标记。

执行结果

23-1-9 刻度设计
目前所有绘制图表x轴和y轴的刻度皆是plot( )方法针对所输入的参数采用默认值设定,请先参考下列实例。
程序实例ch23_10.py:有一个假设3大品牌车辆2018-2020的销售数据如下:
Benz 3367 4120 5539
BMW 4000 3590 4423
Lexus 5200 4930 5350
请使用上述方法将上述数据绘制成图表。
# ch23_10.py
import matplotlib.pyplot as plt
Benz = [3367, 4120, 5539] # Benz线条
BMW = [4000, 3590, 4423] # BMW线条
Lexus = [5200, 4930, 5350] # Lexus线条
seq = [2018, 2019, 2020] # 年度
plt.plot(seq, Benz, '-*', seq, BMW, '-o', seq, Lexus, '-^')
plt.title("Sales Report", fontsize=24)
plt.xlabel("Year", fontsize=14)
plt.ylabel("Number of Sales", fontsize=14)
plt.tick_params(axis='both', labelsize=12, color='red')
plt.show()
执行结果

上述程序最大的遗憾是x轴的刻度,对我们而言,其实只要有2018-2020这3个年度的刻度即可,还好可以使用pyplot模块的xticks( )/yticks( )分别设定x/y轴刻度,可参考下列实例。
程序实例ch23_11.py:重新设计ch23_10.py,自行设定刻度,这个程序的重点是第9行,将seq列表当参数放在plt.xticks( )内。
# ch23_11.py
import matplotlib.pyplot as plt
Benz = [3367, 4120, 5539] # Benz线条
BMW = [4000, 3590, 4423] # BMW线条
Lexus = [5200, 4930, 5350] # Lexus线条
seq = [2018, 2019, 2020] # 年度
plt.xticks(seq) # 设定x轴刻度
plt.plot(seq, Benz, '-*', seq, BMW, '-o', seq, Lexus, '-^')
plt.title("Sales Report", fontsize=24)
plt.xlabel("Year", fontsize=14)
plt.ylabel("Number of Sales", fontsize=14)
plt.tick_params(axis='both', labelsize=12, color='red')
plt.show()
执行结果

23-1-10 图例legend( )
程序实例ch23_11.py所建立的图表,坦白说已经很好了,缺点是缺乏各种线条代表的意义,在Excel中称图例(legend),下列笔者将直接以实例说明。
程序实例ch23_12.py:为ch23_11.py建立图例。
# ch23_12.py
import matplotlib.pyplot as plt
Benz = [3367, 4120, 5539] # Benz线条
BMW = [4000, 3590, 4423] # BMW线条
Lexus = [5200, 4930, 5350] # Lexus线条
seq = [2018, 2019, 2020] # 年度
plt.xticks(seq) # 设定x轴刻度
lineBenz, = plt.plot(seq, Benz, '-*', label='Benz')
lineBMW, = plt.plot(seq, BMW, '-o', label='BMW')
lineLexus, = plt.plot(seq, Lexus, '-^', label='Lexus')
plt.legend(handles=[lineBenz, lineBMW, lineLexus], loc='best')
plt.title("Sales Report", fontsize=24)
plt.xlabel("Year", fontsize=14)
plt.ylabel("Number of Sales", fontsize=14)
plt.tick_params(axis='both', labelsize=12, color='red')
plt.show()
执行结果

这个程序最大不同在第10-12行,以第10行解说。
lineBenz, = plt.plot(seq, Benz, ‘-*', label=‘Benz') # 留意linzBenz,
上述调用plt.plot( )时需同时设定label,注意返回值的使用,变量右边的‘,’,最后使用第13行方式执行legend( )图例的调用。其中参数loc可以设定图例的位置,可以有下列设定方式:
‘best’:0,
‘upper right’:1
‘upper left’:2,
‘lower left’:3,
‘lower right’:4,
‘right’:5, (与‘center right’相同)‘center left’:6,
‘center right’:7,
‘lower center’:8,
‘upper center’:9,
‘center’:10,如果省略loc设定,则使用预设‘best’,在应用时可以使用设定整数值,例如,设定loc=0与上述效果相同。若是顾虑程序可读性建议使用文字符串方式设定,当然也可以直接设定数字,可以小小炫耀或迷惑不懂的人吧!

执行结果 下方右图。

经过上述解说,我们已经可以将图例放在图表内了,如果想将图例放在图表外,笔者先解释坐标,在右图表内左下角位置是(0,0),右上角是(1,1)。
首先须使用bbox_to_anchor( )当作legend( )的一个参数,设定锚点(anchor),也就是图例位置,例如,我们想将图例放在图表右上角外侧,需设定loc=‘upper left’,然后设定bbox_to_anchor(1,1)。
程序实例ch23_12_5.py:将图例放在图表右上角外侧。

上述最大的缺点是由于图表与Figure 1的留白不足,造成无法完整显示图例。Matplotlib模块内有tight_layout( )函数,可利用设定pad参数在图表与Figure 1间设定留白。
程序实例ch23_12_6.py:设定pad=7,重新设计ch23_12_5.py。
执行结果 下方左图。

很明显我们改善了图例显示不完整的问题了。如果将pad改为h_pad/w_pad可以分别设定高度/宽度的留白。
23-1-11 保存图片文件
图表设计完成,可以使用savefig( )保存图片文件,这个方法需放在show( )的前方,表示先存储再显示图表。
程序实例ch23_13.py:扩充ch23_12.py,在屏幕显示图表前,先将图表存入当前文件夹的out23_13.py。执行结果读者可以在ch23文件夹看到out23_13.jpg文件。
# ch23_13.py
import matplotlib.pyplot as plt
Benz = [3367, 4120, 5539] # Benz线条
BMW = [4000, 3590, 4423] # BMW线条
Lexus = [5200, 4930, 5350] # Lexus线条
seq = [2018, 2019, 2020] # 年度
plt.xticks(seq) # 设定x轴刻度
lineBenz, = plt.plot(seq, Benz, '-*', label='Benz')
lineBMW, = plt.plot(seq, BMW, '-o', label='BMW')
lineLexus, = plt.plot(seq, Lexus, '-^', label='Lexus')
plt.legend(handles=[lineBenz, lineBMW, lineLexus])
plt.title("Sales Report", fontsize=24)
plt.xlabel("Year", fontsize=14)
plt.ylabel("Number of Sales", fontsize=14)
plt.tick_params(axis='both', labelsize=12, color='red')
plt.savefig('out23_13.jpg', bbox_inches='tight') # 存档
plt.show()
上述plt.savefig( )第一个参数是所存的文件名,第二个参数代表将图表外多余的空间删除。
23-2 绘制散点图scatter( )
23-2-1 基本散点图的绘制
绘制散点图可以使用scatter( ),最基本语法应用如下:
scatter(x, y, s, c) # 更多参数应用未来几小节会解说
上述相当于可以在(x,y)位置绘图,其中(0,0)位置在左下角,x轴刻度往右增加,y轴刻度往上增加。s是绘图点的大小,预设是20。c是颜色,预设是蓝色。暂时s与c皆用默认值处理,未来将一步一步解说。
程序实例ch23_14.py:在坐标轴(5,5)绘制一个点。
# ch23_14.py
import matplotlib.pyplot as plt
plt.scatter(5, 5)
plt.show()
执行结果

23-2-2 绘制系列点
如果我们想绘制系列点,可以将系列点的x轴值放在一个列表,y轴值放在另一个列表,然后将这2个列表当参数放在scatter( )即可。
程序实例ch23_15.py:绘制系列点的应用。
# ch23_15.py
import matplotlib.pyplot as plt
xpt = [1,2,3,4,5]
ypt = [1,4,9,16,25]
plt.scatter(xpt, ypt)
plt.show()
执行结果

在程序设计时,有些系列点的坐标可能是由程序产生,其实应用方式是一样的。另外,可以在scatter( )内增加color(也可用c)参数,设定点的颜色。
程序实例ch23_16.py:绘制黄色的系列点,这个系列点有100个点,x轴的点由range(1,101)产生,相对应y轴的值则是x的平方值。
# ch23_16.py
import matplotlib.pyplot as plt
xpt = list(range(1,101)) # 建立1-100序列x坐标点
ypt = [x**2 for x in xpt] # 以x平方方式建立y坐标点
plt.scatter(xpt, ypt, color='y')
plt.show()
执行结果

上述程序第6行使用直接的指定色彩,也可以使用RGB(Red, Green, Blue)颜色模式设定色彩,RGB( )内每个参数数值在0.0到1.0之间。
23-2-3 设定绘图区间
可以使用axis( )设定绘图区间,语法格式如下:
axis([xmin, xmax, ymin, ymax]) # 分别代表x和y轴的最小和最大区间
程序实例ch23_17.py:设定绘图区间为[0,100,0,10000]的应用,读者可以将这个执行结果与ch23_16.py作比较。另外,第7行以不同方式建立色彩。
# ch23_17.py
import matplotlib.pyplot as plt
xpt = list(range(1,101)) # 建立1-100序列x坐标点
ypt = [x**2 for x in xpt] # 以x平方方式建立y坐标点
plt.axis([0, 100, 0, 10000]) # 留意参数是列表
plt.scatter(xpt, ypt, c=(0, 1, 0)) # 绿色
plt.show()
执行结果

上述程序第5行是依据xpt列表产生ypt列表值的方式,由于在网络上大部分的文章使用数组方式产生图表列表,所以下一节笔者将对此做说明,期待可为读者建立基础。
23-3 Numpy模块
Numpy是Python的一个扩充模块,主要是可以支持多维度空间的数组与矩阵运算,本节笔者将使用其最简单的产生数组功能做解说,由此可以将这个功能扩充到数据图表的设计。使用前我们需导入Numpy模块,如下所示:
import Numpy as np
23-3-1 建立一个简单的数组linspace( )和arange( )
在Numpy模块中最基本的就是linspace( )方法,使用它可以很方便产生相同等距的数组,它的语法如下:
linspace(start, end, num) # 这是最常用简化的语法
start是起始值,end是结束值,num是设定产生多少个等距的数组值,num的默认值是50。
在网络上阅读他人使用Python设计的图表时,另一个常看到产生数组的方法是arange( ),语法如下:
arange(start, stop, step) # start和step是可以省略
start是起始值如果省略默认值是0,stop是结束值但是所产生的数组通常不包含此值,step是数组相邻元素的间距如果省略默认值是1。
程序实例ch23_18:建立0, 1, …, 9, 10的数组。
# ch23_18.py
import numpy as np
x1 = np.linspace(0, 10, num=11) # 使用linspace()产生数组
print(type(x1), x1)
x2 = np.arange(0,11,1) # 使用arange()产生数组
print(type(x2), x2)
x3 = np.arange(11) # 简化语法产生数组
print(type(x3), x3)
执行结果
<class 'numpy.ndarray'> [ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]
<class 'numpy.ndarray'> [ 0 1 2 3 4 5 6 7 8 9 10]
<class 'numpy.ndarray'> [ 0 1 2 3 4 5 6 7 8 9 10]
23-3-2 绘制波形
在初中数学中我们有学过sin( )和cos( )观念,其实有了数组数据,我们可以很方便绘制sin和cos的波形变化。
程序实例ch23_19.py:绘制sin( )和cos( )的波形,在这个实例中调用plt.scatter( )方法2次,相当于也可以绘制2次波形图表。
# ch23_19.py
import matplotlib.pyplot as plt
import numpy as np
xpt = np.linspace(0, 10, 500) # 建立含500个元素的数组
ypt1 = np.sin(xpt) # y数组的变化
ypt2 = np.cos(xpt)
plt.scatter(xpt, ypt1, color=(0, 1, 0)) # 绿色
plt.scatter(xpt, ypt2) # 预设颜色
plt.show()
执行结果

23-3-3 建立不等宽度的散点图
在scatter( )方法中,(x,y)的数据可以是列表也可以是矩阵,预设所绘制点大小s的值是20,这个s可以是一个值也可以是一个数组数据,当它是一个数组数据时,利用更改数组值的大小,我们就可以建立不同大小的散点图。
在我们使用Python绘制散点图时,如果将2个点之间绘了上百或上千个点,则可以产生绘制线条的视觉,如果再加上每个点的大小不同,且依一定规律变化,则可以有特别效果。
程序实例ch23_20.py:建立一个不等宽度的图形。
# ch23_20.py
import matplotlib.pyplot as plt
import numpy as np
xpt = np.linspace(0, 5, 500) # 建立含500个元素的数组
ypt = 1 - 0.5*np.abs(xpt-2) # y数组的变化
lwidths = (1+xpt)**2 # 宽度数组
plt.scatter(xpt, ypt, s=lwidths, color=(0, 1, 0)) # 绿色
plt.show()
执行结果

23-3-4 色彩映射color mapping
至今我们针对一组数组(或列表)所绘制的图皆是单色,若是以ch23_20.py第8行为例,色彩设定是color=(0,1,0),这是固定颜色的用法。在色彩的使用中是允许色彩也是数组(或列表)随着数据而变化,此时色彩的变化是根据所设定的色彩映射值(color mapping)而定,例如有一个色彩映射值是rainbow,内容如下:

在数组(或列表)中,数值低的值颜色在左边,会随着数值变高颜色往右边移动。当然在程序设计中,我们需在scatter( )中增加color(也可用c)设定,这时color的值就变成一个数组(或列表)。然后我们需增加参数cmap(英文是color map),这个参数主要是指定使用哪一种色彩映射值。
程序实例ch23_21.py:色彩映射的应用。
# ch23_21.py
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(100)
y = x
t = x
plt.scatter(x, y, c=t, cmap='rainbow')
plt.show()
执行结果

有时候我们在程序设计时,色彩映射也可以设定是根据x轴的值变化,或是y轴的值变化,整个效果是不一样的。
程序实例ch23_22.py:重新设计ch23_20.py,主要是设定差别是固定点的宽度为50,将色彩改为依y轴值变化,同时使用hsv色彩映射表。

程序实例ch23_23.py:重新设计ch23_22.py,主要是将将色彩改为依x轴值变化。
执行结果 如下方左图。

执行结果 如下方右图。

目前matplotlib协会所提供的色彩映射内容如下:



数据源matplotlib协会网址如下:
http://matplotlib.org/examples/color/colormaps_reference.html
如果有一天你做大数据研究,当收集了无数的数据后,可以将数据以图表显示,然后用色彩判断整个数据趋势。
23-4 随机数的应用
随机数在统计的应用中是非常重要的知识,这一节笔者试着用随机数方法,了解Python的随机数分布。这一节将介绍下列随机方法:
np.random.random(N) # 传回N个0.0至1.0之间的数字
23-4-1 一个简单的应用
程序实例ch23_24.py:产生100个0.0至1.0之间的随机数,使用brg色彩映射表绘出这个图表。当关闭图表时,会询问是否继续,如果输入n/N则结束。其实因为数据是随机数,所以每次皆可产生不同的效果。
# ch23_24.py
import matplotlib.pyplot as plt
import numpy as np
num = 100
while True:
x = np.random.random(100) # 可以产生num个0.0至1.0之间的数字
y = np.random.random(100)
t = x # 色彩随x轴变化
plt.scatter(x, y, s=100, c=t, cmap='brg')
plt.show()
yORn = input("是否继续 ?(y/n) ") # 询问是否继续
if yORn == 'n' or yORn == 'N': # 输入n或N则程序结束
break
执行结果

上述程序笔者使用第5行的num控制产生随机数的数量,其实读者可以自行修订,增加或减少随机数的数量,以体会本程序的运作。
23-4-2 随机数的移动
其实我们也可以针对随机数的特性,让每个点随着随机数的变化产生有序列的随机移动,经过大量值的运算后,每次均可产生不同但有趣的图形。
程序实例ch23_25.py:随机数移动的程序设计,这个程序在设计时,最初点的起始位置是(0,0),程序第7行可以设定下一个点的x轴是往右移动3或是往左移动3,程序第9行可以设定下一个点的y轴是往上移动1或5或是往下移动1或5。每此执行完10000点的测试后,会询问是否继续。如果继续,先将上一回合的终点坐标当作新回合的起点坐标(27至28行),然后清除列表索引x[0]和y[0]以外的元素(29至30行)。
# ch23_25.py
import matplotlib.pyplot as plt
import random
def loc(index):
''' 处理坐标的移动 '''
x_mov = random.choice([-3, 3]) # 随机x轴移动值
xloc = x[index-1] + x_mov # 计算x轴新位置
y_mov = random.choice([-5, -1, 1, 5]) # 随机y轴移动值
yloc = y[index-1] + y_mov # 计算y轴新位置
x.append(xloc) # x轴新位置加入列表
y.append(yloc) # y轴新位置加入列表
num = 10000 # 设定随机点的数量
x = [0] # 设定第一次执行x坐标
y = [0] # 设定第一次执行y坐标
while True:
for i in range(1, num): # 建立点的坐标
loc(i)
t = x # 色彩随x轴变化
plt.scatter(x, y, s=2, c=t, cmap='brg')
plt.show()
yORn = input("是否继续 ?(y/n) ") # 询问是否继续
if yORn == 'n' or yORn == 'N': # 输入n或N则程序结束
break
else:
x[0] = x[num-1] # 上次结束x坐标成新的起点x坐标
y[0] = y[num-1] # 上次结束y坐标成新的起点y坐标
del x[1:] # 删除旧列表x坐标元素
del y[1:] # 删除旧列表y坐标元素
执行结果

23-4-3 隐藏坐标
有时候我们设计随机数移动建立了美丽的图案后,觉得坐标好像很煞风景,可以使用下列程序实例ch23_26.py内的axes( ).get_xaxis( )、axes( ).get_yaxis( )、set_visible( )方法隐藏坐标。
程序实例ch23_26.py:重新设计ch23_25.py隐藏坐标,这个程序只是增加下列行。

# ch23_26.py
import matplotlib.pyplot as plt
import random
def loc(index):
''' 处理坐标的移动 '''
x_mov = random.choice([-3, 3]) # 随机x轴移动值
xloc = x[index-1] + x_mov # 计算x轴新位置
y_mov = random.choice([-5, -1, 1, 5]) # 随机y轴移动值
yloc = y[index-1] + y_mov # 计算y轴新位置
x.append(xloc) # x轴新位置加入列表
y.append(yloc) # y轴新位置加入列表
num = 10000 # 设定随机点的数量
x = [0] # 设定第一次执行x坐标
y = [0] # 设定第一次执行y坐标
while True:
for i in range(1, num): # 建立点的坐标
loc(i)
t = x # 色彩随x轴变化
plt.scatter(x, y, s=2, c=t, cmap='brg')
plt.axes().get_xaxis().set_visible(False) # 隐藏x轴坐标
plt.axes().get_yaxis().set_visible(False) # 隐藏y轴坐标
plt.show()
yORn = input("是否继续 ?(y/n) ") # 询问是否继续
if yORn == 'n' or yORn == 'N': # 输入n或N则程序结束
break
else:
x[0] = x[num-1] # 上次结束x坐标成新的起点x坐标
y[0] = y[num-1] # 上次结束y坐标成新的起点y坐标
del x[1:] # 删除旧列表x坐标元素
del y[1:] # 删除旧列表y坐标元素
执行结果

23-5 绘制多个图表
23-5-1 一个程序有多个图表
Python允许一个程序绘制多个图表,默认是一个程序绘制一个图表(Figure),如果想要绘制多个图表,可以使用figure(N)设定图表,N是图表的序号。在建立多个图表时,只要将所要绘制的图接在欲放置的图表后面即可。
程序实例ch23_27.py:设计2个图表,将data1线条放在图表Figure 1,将data2线条放在图表Figure 2。同时图表Figure 2将会建立图表标题与x/y轴的标签。
# ch23_27.py
import matplotlib.pyplot as plt
data1 = [1, 2, 3, 4, 5, 6, 7, 8] # data1线条
data2 = [1, 4, 9, 16, 25, 36, 49, 64] # data2线条
seq = [1, 2, 3, 4, 5, 6, 7, 8]
plt.figure(1) # 建立图表1
plt.plot(seq, data1, '-*') # 绘制图表1
plt.figure(2) # 建立图表2
plt.plot(seq, data2, '-o') # 以下皆是绘制图表2
plt.title("Test Chart 2", fontsize=24)
plt.xlabel("x-Value", fontsize=14)
plt.ylabel("y-Value", fontsize=14)
plt.show()
执行结果

上述第8行所绘制的data1图表因为是接在plt.figure(1)后面,所以所绘制的图出现在Figure 1。上述第10-13行所绘制的data2图表因为是接在plt.figure(2)后面,所以所绘制的图出现在Figure 2。
23-5-2 含有子图的图表
要设计含有子图的图表需要使用subplot( )方法,语法如下:
subplot(x1, x2, x3)
x1代表上下(垂直)要绘几张图,x2代表左右(水平)要绘几张图。x3代表这是第几张图。如果规划是一个Figure绘制上下2张图,那么subplot( )的应用如下:

如果规划是一个Figure绘制左右2张图,那么subplot( )的应用如下:

如果规划是一个Figure绘制上下2张图,左右3张图,那么subplot( )的应用如下:

程序实例ch23_28.py:在一个Fugure内绘制上下子图的应用。
# ch23_28.py
import matplotlib.pyplot as plt
data1 = [1, 2, 3, 4, 5, 6, 7, 8] # data1线条
data2 = [1, 4, 9, 16, 25, 36, 49, 64] # data2线条
seq = [1, 2, 3, 4, 5, 6, 7, 8]
plt.subplot(2, 1, 1) # 子图1
plt.plot(seq, data1, '-*')
plt.subplot(2, 1, 2) # 子图2
plt.plot(seq, data2, '-o')
plt.show()
执行结果

程序实例ch23_29.py:在一个Fugure内绘制上下子图的应用。
# ch23_29.py
import matplotlib.pyplot as plt
data1 = [1, 2, 3, 4, 5, 6, 7, 8] # data1线条
data2 = [1, 4, 9, 16, 25, 36, 49, 64] # data2线条
seq = [1, 2, 3, 4, 5, 6, 7, 8]
plt.subplot(1, 2, 1) # 子图1
plt.plot(seq, data1, '-*')
plt.subplot(1, 2, 2) # 子图2
plt.plot(seq, data2, '-o')
plt.show()
执行结果

23-6 直方图的制作bar( )
在直方图的制作中,我们可以使用bar( )方法,常用的语法如下:
bar(x, height, width)
x是一个序列,主要是直方图x轴位置。height是序列数值的大小。width是直方图的宽度,预设是0.85。
程序实例ch23_30:有一个选举,James得票135、Peter得票412、Norton得票397,用直方图表示。
# ch23_30.py
import numpy as np
import matplotlib.pyplot as plt
votes = [135, 412, 397] # 得票数
N = len(votes) # 计算长度
x = np.arange(N) # 直方图x轴坐标
width = 0.35 # 直方图宽度
plt.bar(x, votes, width) # 绘制直方图
plt.ylabel('The number of votes')
plt.title('The election results')
plt.xticks(x, ('James', 'Peter', 'Norton'))
plt.yticks(np.arange(0, 450, 30))
plt.show()
执行结果

上述程序第11行是打印y轴的标签,第12行是打印直方图的标题,第13行则是打印x轴各直方图的标签,第14行是设定y轴刻度。
程序实例ch23_31.py:掷骰子的机率设计,一个骰子有6面分别记载1, 2, 3, 4, 5, 6,我们这个程序会用随机数计算600次,每个数字出现的次数,同时用柱形图表示,为了让读者有不同体验,笔者将图表颜色改为绿色。
# ch23_31.py
import numpy as np
import matplotlib.pyplot as plt
from random import randint
def dice_generator(times, sides):
''' 处理随机数 '''
for i in range(times):
ranNum = randint(1, sides) # 产生1-6随机数
dice.append(ranNum)
def dice_count(sides):
'''计算1-6个出现次数'''
for i in range(1, sides+1):
frequency = dice.count(i) # 计算i出现在dice列表的次数
frequencies.append(frequency)
times = 600 # 掷骰子次数
sides = 6 # 骰子有几面
dice = [] # 建立掷骰子的列表
frequencies = [] # 储存每一面骰子出现次数列表
dice_generator(times, sides) # 产生掷骰子的列表
dice_count(sides) # 将骰子列表转成次数列表
x = np.arange(6) # 直方图x轴坐标
width = 0.35 # 直方图宽度
plt.bar(x, frequencies, width, color='g') # 绘制直方图
plt.ylabel('Frequency')
plt.title('Test 600 times')
plt.xticks(x, ('1', '2', '3', '4', '5', '6'))
plt.yticks(np.arange(0, 150, 15))
plt.show()
执行结果

上述程序最重要的是第11-15行的dice_count( )函数,这个函数主要是将含600个元素的dice列表,分别计算1, 2, 3, 4, 5, 6各出现的次数,然后将结果存储至frequencies列表。如果读者忘记count( )方法的用法可以参考6-6-2小节。
23-7 使用CSV文件绘制图表
其实网络上有许多CSV文件,原始的文件有些复杂,不过我们可以使用Python读取文件,然后筛选我们要的字段,整个工作就变得比较简单了。本节主要是用实例介绍将图表设计应用在CSV文件。
23-7-1 台北2017年1月气象资料
在ch23文件夹内有TaipeiWeatherJan.csv文件,这是2017年1月份台北市的气象资料,这个文件的Excel内容如下:

程序实例ch23_32.py:读取TaipeiWeatherJan.csv文件,然后列出标题栏。
# ch23_32.py
import csv
fn = 'TaipeiWeatherJan.csv'
with open(fn) as csvFile:
csvReader = csv.reader(csvFile)
headerRow = next(csvReader) # 读取文件下一行
print(headerRow)
执行结果
['Date', 'HighTemperature', 'MeanTemperature', 'LowTemperature']
从上图我们可以得到TaipeiWeatherJan.csv有4个字段,分别是记载日期(Date)、当天最高温(HighTemperature)、平均温度(MeanTemperature)、最低温度(LowTemperature)。
23-7-2 列出标题数据
我们可以使用6-12节所介绍的enumerate( )。
程序实例ch23_33.py:列出TaipeiWeatherJan.csv文件的标题与相对应的索引。
# ch23_33.py
import csv
fn = 'TaipeiWeatherJan.csv'
with open(fn) as csvFile:
csvReader = csv.reader(csvFile)
headerRow = next(csvReader) # 读取文件下一行
for i, header in enumerate(headerRow):
print(i, header)
执行结果
0 Date
1 HighTemperature
2 MeanTemperature
3 LowTemperature
23-7-3 读取最高温与最低温
程序实例ch23_34.py:读取TaipeiWeatherJan.csv文件的最高温与最低温。这个程序会将一月份的最高温放在highTemps列表,最低温放在lowTemps列表。
# ch23_34.py
import csv
fn = 'TaipeiWeatherJan.csv'
with open(fn) as csvFile:
csvReader = csv.reader(csvFile)
headerRow = next(csvReader) # 读取文件下一行
highTemps, lowTemps = [], [] # 设定空列表
for row in csvReader:
highTemps.append(row[1]) # 储存最高温
lowTemps.append(row[3]) # 储存最低温
print("最高温 : ", highTemps)
print("最低温 : ", lowTemps)
执行结果
最高温 : ['26', '25', '22', '27', '25', '25', '26', '22', '18', '20', '21', '22', '18', '15', '15', '16', '23', '23', '22', '18', '15', '17', '16', '17', '18', '19', '24', '26', '25', '27', '18']
最低温 : ['20', '18', '19', '20', '19', '20', '20', '18', '17', '16', '18', '18', '14', '12', '13', '13', '16', '18', '18', '12', '12', '12', '13', '14', '13', '13', '13', '16', '17', '14', '14']
23-7-4 绘制最高温
其实这一节内容不复杂,所有绘图方法前面各小节已有说明。
程序实例ch23_35.py:绘制2017年1月份,台北每天气温的最高温。
# ch23_35.py
import csv
import matplotlib.pyplot as plt
fn = 'TaipeiWeatherJan.csv'
with open(fn) as csvFile:
csvReader = csv.reader(csvFile)
headerRow = next(csvReader) # 读取文件下一行
highTemps = [] # 设定空列表
for row in csvReader:
highTemps.append(row[1]) # 储存最高温
plt.plot(highTemps)
plt.title("Weather Report, Jan. 2017", fontsize=24)
plt.xlabel("", fontsize=14)
plt.ylabel("Temperature (C)", fontsize=14)
plt.tick_params(axis='both', labelsize=12, color='red')
plt.show()
执行结果

23-7-5 设定绘图区大小
if gure(dpi=n,figsize=(width, height))
经上述设定后,绘图区的宽将是n×width像素,高是n×width像素。
程序实例ch23_36.py:重新设计ch23_35.py,设定绘图区宽度是960,高度是640,这个程序只是增加下列行。
目前绘图区大小是使用系统默认,不过我们可以使用figure( )设定绘图区大小,设定方式如下:

执行结果

23-7-6 日期格式
天气图表建立过程,我们可能想加上日期在x轴的刻度上,这时我们需要使用Python内置的datetime模块,在使用前请使用下列方式导入模块。
from datetime import datetime
然后可以使用下列方法将日期字符串解析为日期对象:
strptime(string, format)
string是要解析的日期字符串,format是该日期字符串目前格式,下表是日期格式参数的意义。

程序实例ch23_37.py:将字符串转成日期对象。
# ch23_37.py
from datetime import datetime
dateObj = datetime.strptime('2017/1/1', '%Y/%m/%d')
print(dateObj)
执行结果
2017-01-01 00:00:00
有关datetime对象的更进一步使用可以参考30-1节。
23-7-7 在图表增加日期刻度
其实我们可以在plot( )方法内增加日期列表参数时,在图表增加日期刻度。
程序实例ch23_38.py:为图表增加日期刻度。
# ch23_38.py
import csv
import matplotlib.pyplot as plt
from datetime import datetime
fn = 'TaipeiWeatherJan.csv'
with open(fn) as csvFile:
csvReader = csv.reader(csvFile)
headerRow = next(csvReader) # 读取文件下一行
dates, highTemps = [], [] # 设定空列表
for row in csvReader:
highTemps.append(row[1]) # 储存最高温
currentDate = datetime.strptime(row[0], "%Y/%m/%d")
dates.append(currentDate)
plt.figure(dpi=80, figsize=(12, 8)) # 设定绘图区大小
plt.plot(dates, highTemps) # 图标增加日期刻度
plt.title("Weather Report, Jan. 2017", fontsize=24)
plt.xlabel("", fontsize=14)
plt.ylabel("Temperature (C)", fontsize=14)
plt.tick_params(axis='both', labelsize=12, color='red')
plt.show()
执行结果

这个程序的第一个重点是第13行和14行,主要是将日期字符串转成对象,然后存入dates日期列表。第二个重点是第17行,在plot( )方法中第一个参数是放dates日期列表。
23-7-8 日期位置的旋转
上一节的执行结果可以发现日期是水平放置,autofmt_xdate( )设定日期旋转,语法如下:
fig = plt.figure( xxx ) # xxx是相关设定信息
…
if g.autofmt_xdate(rotation=xx) # rotation若省略则系统使用优化默认
程序实例ch23_39.py:重新设计ch23_38.py,增加将日期旋转。

执行结果

程序实例ch23_40.py:是特别将日期字符串调整为旋转60度的结果。
# ch23_40.py
import csv
import matplotlib.pyplot as plt
from datetime import datetime
fn = 'TaipeiWeatherJan.csv'
with open(fn) as csvFile:
csvReader = csv.reader(csvFile)
headerRow = next(csvReader) # 读取文件下一行
dates, highTemps = [], [] # 设定空列表
for row in csvReader:
highTemps.append(row[1]) # 储存最高温
currentDate = datetime.strptime(row[0], "%Y/%m/%d")
dates.append(currentDate)
fig = plt.figure(dpi=80, figsize=(12, 8)) # 设定绘图区大小
plt.plot(dates, highTemps) # 图标增加日期刻度
fig.autofmt_xdate(rotation=60) # 日期旋转
plt.title("Weather Report, Jan. 2017", fontsize=24)
plt.xlabel("", fontsize=14)
plt.ylabel("Temperature (C)", fontsize=14)
plt.tick_params(axis='both', labelsize=12, color='red')
plt.show()
执行结果

23-7-9 绘制最高温与最低温
在TaipeiWeatherJan.csv文件内有最高温与最低温的字段,下列将同时绘制最高温与最低温。
程序实例ch23_41.py:绘制最高温与最低温,这个程序第一个重点是程序第11至21行使用异常处理方式,因为读者在读取真实的网络数据时,常常会有不可预期的数据发生,例如,数据少了或是数据格式错误,往往造成程序中断,为了避免程序因数据不良,所以使用异常处理方式。第二个重点为程序第24和25行是分别绘制最高温与最低温。
# ch23_41.py
import csv
import matplotlib.pyplot as plt
from datetime import datetime
fn = 'TaipeiWeatherJan.csv'
with open(fn) as csvFile:
csvReader = csv.reader(csvFile)
headerRow = next(csvReader) # 读取文件下一行
dates, highTemps, lowTemps = [], [], [] # 设定空列表
for row in csvReader:
try:
currentDate = datetime.strptime(row[0], "%Y/%m/%d")
highTemp = int(row[1]) # 设定最高温
lowTemp = int(row[3]) # 设定最低温
except Exception:
print('有缺值')
else:
highTemps.append(highTemp) # 储存最高温
lowTemps.append(lowTemp) # 储存最低温
dates.append(currentDate) # 储存日期
fig = plt.figure(dpi=80, figsize=(12, 8)) # 设定绘图区大小
plt.plot(dates, highTemps) # 绘制最高温
plt.plot(dates, lowTemps) # 绘制最低温
fig.autofmt_xdate() # 日期旋转
plt.title("Weather Report, Jan. 2017", fontsize=24)
plt.xlabel("", fontsize=14)
plt.ylabel("Temperature (C)", fontsize=14)
plt.tick_params(axis='both', labelsize=12, color='red')
plt.show()
执行结果

23-7-10 填满最高温与最低温之间的区域
可以使用fill_between( )方法执行填满最高温与最低温之间的区域。

程序实例ch23_42.py:使用透明度是0.2的黄色填满区间,这个程序只是增加下列行。
# ch23_42.py
import csv
import matplotlib.pyplot as plt
from datetime import datetime
fn = 'TaipeiWeatherJan.csv'
with open(fn) as csvFile:
csvReader = csv.reader(csvFile)
headerRow = next(csvReader) # 读取文件下一行
dates, highTemps, lowTemps = [], [], [] # 设定空列表
for row in csvReader:
try:
currentDate = datetime.strptime(row[0], "%Y/%m/%d")
highTemp = int(row[1]) # 设定最高温
lowTemp = int(row[3]) # 设定最低温
except Exception:
print('有缺值')
else:
highTemps.append(highTemp) # 储存最高温
lowTemps.append(lowTemp) # 储存最低温
dates.append(currentDate) # 储存日期
fig = plt.figure(dpi=80, figsize=(12, 8)) # 设定绘图区大小
plt.plot(dates, highTemps) # 绘制最高温
plt.plot(dates, lowTemps) # 绘制最低温
plt.fill_between(dates, highTemps, lowTemps, color='y', alpha=0.2) # 填满区间
fig.autofmt_xdate() # 日期旋转
plt.title("Weather Report, Jan. 2017", fontsize=24)
plt.xlabel("", fontsize=14)
plt.ylabel("Temperature (C)", fontsize=14)
plt.tick_params(axis='both', labelsize=12, color='red')
plt.show()
执行结果

23-7-11 再谈轴刻度
在23-1-2小节笔者介绍过可以使用axis( )设定x,y轴的最小和最大刻度,可以参考程序实例ch23_1_1.py,对于上述天气图表而言,x轴是以日期为刻度,如果我们想要设定x轴刻度,可以设置x轴最小刻度是2017-01-01,最大刻度是2017-01-31。
程序实例ch23_43.py:重新设计程序实例ch23_42.py,让x轴最小刻度是2017-01-01,最大刻度是2017-01-31。y轴(气温)则是在0℃~40℃。下列dates[0]就是指2017-01-01,date[30]就是指2017-01-31。

# ch23_43.py
import csv
import matplotlib.pyplot as plt
from datetime import datetime
fn = 'TaipeiWeatherJan.csv'
with open(fn) as csvFile:
csvReader = csv.reader(csvFile)
headerRow = next(csvReader) # 读取文件下一行
dates, highTemps, lowTemps = [], [], [] # 设定空列表
for row in csvReader:
try:
currentDate = datetime.strptime(row[0], "%Y/%m/%d")
highTemp = int(row[1]) # 设定最高温
lowTemp = int(row[3]) # 设定最低温
except Exception:
print('有缺值')
else:
highTemps.append(highTemp) # 储存最高温
lowTemps.append(lowTemp) # 储存最低温
dates.append(currentDate) # 储存日期
fig = plt.figure(dpi=80, figsize=(12, 8)) # 设定绘图区大小
plt.axis([dates[0], dates[30], 0, 40]) # 设定x,y轴刻度
plt.plot(dates, highTemps) # 绘制最高温
plt.plot(dates, lowTemps) # 绘制最低温
plt.fill_between(dates, highTemps, lowTemps, color='y', alpha=0.2) # 填满区间
fig.autofmt_xdate() # 日期旋转
plt.title("Weather Report, Jan. 2017", fontsize=24)
plt.xlabel("", fontsize=14)
plt.ylabel("Temperature (C)", fontsize=14)
plt.tick_params(axis='both', labelsize=12, color='red')
plt.show()
执行结果

结果发现x轴宽度变了,日期标签也变多了。

5665

被折叠的 条评论
为什么被折叠?



