Python 数据分析——matplotlib 快速绘图

matplotlib采用面向对象的技术来实现,因此组成图表的各个元素都是对象,在编写较大的应用程序时通过面向对象的方式使用matplotlib将更加有效。但是使用这种面向对象的调用接口进行绘图比较烦琐,因此matplotlib还提供了快速绘图的pyplot模块。本节首先介绍该模块的使用方法。

为了将matplotlib绘制的图表嵌入Notebook中,需要执行下面的命令:

%matplotlib inline

使用inline模式在Notebook中绘制的图表会自动关闭,为了在Notebook的多个单元格内操作同一幅图表,需要运行下面的魔法命令:

%config InlineBackend.close_figures = False

一、使用pyplot模块绘图

matplotlib的pyplot模块提供了与MATLAB类似的绘图函数调用接口,方便用户快速绘制二维图表。我们先看一个简单的例子:

pylab模块

matplotlib还提供了一个名为pylab的模块,其中包括了许多NumPy和pyplot模块中常用的函数,方便用户快速进行计算和绘图,十分适合在IPython交互式环境中使用。

import matplotlib.pyplot as plt ❶  
  
x = np.linspace(0, 10, 1000)  
y = np.sin(x)  
z = np.cos(x**2)  
  
plt.figure(figsize=(8,4)) ❷  
  
plt.plot(x,y,label="$sin(x)$",color="red",linewidth=2) ❸  
plt.plot(x,z,"b--",label="$cos(x^2)$") ❹  
  
plt.xlabel("Time(s)") ❺  
plt.ylabel("Volt")  
plt.title("PyPlot First Example")  
plt.ylim(-1.2,1.2)  
plt.legend()  
  
plt.show() ❻

程序的输出如图1所示。

图1 使用pyplot模块快速将数据绘制成曲线

❶首先载入matplotlib的绘图模块pyplot,并且重命名为plt。❷调用figure()创建一个Figure(图表)对象,并且它将成为当前Figure对象。也可以不创建Figure对象而直接调用接下来的plot()进行绘图,这时matplotlib会自动创建一个Figure对象。figsize参数指定Figure对象的宽度和高度,单位为英寸。此外还可以用dpi参数指定Figure对象的分辨率,即每英寸所表示的像素数,这里使用默认值80。因此本例中所创建的Figure对象的宽度为8*80 = 640个像素。

❸创建Figure对象之后,接下来调用plot()在当前的Figure对象中绘图。实际上plot()是在Axes(子图)对象上绘图,如果当前的Figure对象中没有Axes对象,将会为之创建一个几乎充满整个图表的Axes对象,并且使此Axes对象成为当前的Axes对象。plot()的前两个参数是分别表示X、Y轴数据的对象,这里使用的是NumPy数组。使用关键字参数可以指定所绘制曲线的各种属性:

·label:给曲线指定一个标签,此标签将在图示中显示。如果标签字符串的前后有字符'$',matplotlib会使用内嵌的LaTeX引擎将其显示为数学公式。

·color:指定曲线的颜色,颜色可以用英文单词或以'#'字符开头的6位十六进制数表示,例如'#ff0000'表示红色。或者使用值在0到1范围之内的三个元素的元组来表示,例如(1.0, 0.0, 0.0)也表示红色。

·linewidth:指定曲线的宽度,可以不是整数,也可以使用缩写形式的参数名lw。

使用LaTeX语法绘制数学公式会极大地降低图表的描绘速度。

❹直接通过第三个参数'b--'指定曲线的颜色和线型,它通过一些易记的符号指定曲线的样式。其中'b'表示蓝色,'--'表示线型为虚线。在IPython中输入plt.plot?可以查看格式化字符串以及各个参数的详细说明。

❺接下来通过一系列函数设置当前Axes对象的各个属性:

·xlabel、ylabel:分别设置X、Y轴的标题文字。

·title:设置子图的标题。

·xlim、ylim:分别设置X、Y轴的显示范围。

·legend:显示图示,即图中表示每条曲线的标签(label)和样式的矩形区域。

❻最后调用plt.show()显示绘图窗口,在Notebook中可以省略此步骤。在通常的运行情况下,show()将会阻塞程序的运行,直到用户关闭绘图窗口。

还可以调用plt.savefig()将当前的Figure对象保存成图像文件,图像格式由图像文件的扩展名决定。下面的程序将当前的图表保存为test.png,并且通过dpi参数指定图像的分辨率为120,因此输出图像的宽度为8*120 = 960个像素。

plt.savefig("test.png", dpi=120)

如果关闭了图表窗口,则无法使用savefig()保存图像。实际上不需要调用show()显示图表,可以直接用savefig()将图表保存成图像文件。使用这种方法可以很容易编写批量输出图表的程序。

savefig()的第一个参数可以是文件名,也可以是和Python的文件对象有相同调用接口的对象。例如可以将图像保存到io.BytesIO对象中,这样就得到了一个表示图像内容的字符串。这里需要使用fmt参数指定保存的图像格式。

import io  
buf = io.BytesIO() # 创建一个用来保存图像内容的BytesIO对象  
plt.savefig(buf, fmt="png") # 将图像以png格式保存到buf中  
buf.getvalue()[:20] # 显示图像内容的前20个字节  
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x03 '

二、面向对象方式绘图

matplotlib实际上是一套面向对象的绘图库,它所绘制的图表中的每个绘图元素,例如线条、文字、刻度等在内存中都有一个对象与之对应。为了方便快速绘图,matplotlib通过pyplot模块提供了一套和MATLAB类似的绘图API,将众多绘图对象所构成的复杂结构隐藏在这套API内部。我们只需要调用pyplot模块所提供的函数就可以实现快速绘图以及设置图表的各种细节。

为了将面向对象的绘图库包装成只使用函数的API,pyplot模块的内部保存了当前图表以及当前子图等信息。可以使用gcf()和gca()获得这两个对象,它们分别是"Get Current Figure"和"Get Current Axes"开头字母的缩写。gcf()获得的是表示图表的Figure对象,而gca()获得的则是表示子图的Axes对象。

fig = plt.gcf()  
axes = plt.gca()  
print fig, axes  
Figure(640x320) Axes(0.125,0.1;0.775x0.8)

在pyplot模块中,许多函数都是对当前的Figure或Axes对象进行处理,例如前面介绍的plot()、xlabel()、savefig()等。我们可以在IPython中输入函数名并加"??",查看这些函数的源代码以了解它们是如何调用各种对象的方法进行绘图处理的。例如下面的例子查看plot()函数的源程序,可以看到plot()函数实际上会通过gca()获得当前的Axes对象ax,然后再调用它的plot()方法来实现真正的绘图。请读者使用类似的方法查看pyplot模块的其他函数是如何对各种绘图对象进行包装的。

def plot(*args, **kwargs):  
ax = gca()  
...  
try:  
ret = ax.plot(*args, **kwargs)  
...  
finally:  
ax.hold(washold)

三、配置属性

matplotlib所绘制图表的每个组成部分都和一个对象对应,可以通过调用这些对象的属性设置方法set_*()或者pyplot模块的属性设置函数setp()来设置它们的属性值。例如plot()返回一个元素类型为Line2D的列表,下面的例子设置Line2D对象的属性:

plt.figure(figsize=(4, 3))  
x = np.arange(0, 5, 0.1)  
line = plt.plot(x, 0.05*x*x)[0] # plot返回一个列表  
line.set_alpha(0.5) # 调用Line2D对象的set_*()方法来设置属性值

上面的例子中,通过调用Line2D对象的set_alpha(),修改它在图表中对应曲线的透明度。下面的语句同时绘制正弦和余弦两条曲线,lines是一个有两个Line2D对象的列表:

lines = plt.plot(x, np.sin(x), x, np.cos(x))

调用setp()可以同时配置多个对象的属性,这里同时设置两条曲线的颜色和线宽:

plt.setp(lines, color="r", linewidth=4.0)

图2 配置绘图对象的属性

同样,可以通过调用Line2D对象的get_*()或者通过plt.getp()来获取对象的属性值:

print line.get_linewidth()  
print plt.getp(lines[0], "color") # 返回color属性  
2.0  
r

注意getp()和setp()不同,它只能对一个对象进行操作,它有两种用法:

·指定属性名:返回对象的某个属性的值。

·不指定属性名:输出对象的所有属性和值。

下面通过getp()查看Figure对象的属性:

f = plt.gcf()  
plt.getp(f)  
agg_filter = None  
alpha = None  
animated = False  
axes = [<matplotlib.axes._subplots.AxesSubplot object at ...  
...

Figure对象的axes属性是一个列表,它保存图表中的所有子图对象。下面的程序查看当前图表的axes属性,它就是gca()所获得的当前子图对象:

print plt.getp(f, "axes"), plt.getp(f, "axes")[0] is plt.gca()  
[<matplotlib.axes._subplots.AxesSubplot object at 0x05DE5790>] True

用plt.getp()可以继续获取AxesSubplot对象的属性,例如它的lines属性为子图中的Line2D对象列表:

alllines = plt.getp(plt.gca(), "lines")  
print alllines, alllines[0] is line # 其中的第一条曲线就是最开始绘制的那条曲线  
<a list of 3 Line2D objects> True

通过这种方法可以很容易查看对象的属性值以及各个对象之间的关系,找到需要配置的属性。因为matplotlib实际上是一套面向对象的绘图库,因此也可以直接获取对象的属性,例如:

print f.axes, len(f.axes[0].lines)  
[<matplotlib.axes._subplots.AxesSubplot object at 0x05DE5790>] 3

四、绘制多子图

一个Figure对象可以包含多个子图(Axes),在matplotlib中用Axes对象表示一个绘图区域,在本书中称之为子图。在前面的例子中,Figure对象只包括一个子图。可以使用subplot()快速绘制包含多个子图的图表,它的调用形式如下:

subplot(numRows, numCols, plotNum)

图表的整个绘图区域被等分为numRows行和numCols列,然后按照从左到右、从上到下的顺序对每个区域进行编号,左上区域的编号为1。plotNum参数指定所创建Axes对象的区域。如果numRows、numCols和plotNum三个参数都小于10,则可以把它们缩写成一个整数,例如subplot(323)和subplot(3,2,3)的含义相同。如果新创建的子图和之前创建的子图区域有重叠的部分,之前的子图将被删除。

下面的程序创建如图3(左)所示的3行2列共6个子图,并通过axisbg参数给每个子图设置不同的背景颜色。

图3 在Figure对象中创建多个子图

for idx, color in enumerate("rgbyck"):

plt.subplot(321+idx, axisbg=color)

如果希望某个子图占据整行或整列,可以如下调用subplot(),程序的输出如图3(右)所示。

plt.subplot(221) # 第一行的左图  
plt.subplot(222) # 第一行的右图  
plt.subplot(212) # 第二整行

在绘图窗口的工具栏中,有一个名为"Configure Subplots"的按钮,单击它会弹出调节子图间距和子图与图表边框距离的对话框。也可以在程序中调用subplots_adjust()调节这些参数,它有left、right、bottom、top、wspace和hspace共6个参数,这些参数与对话框中的各个控件对应。参数的取值范围为0到1,它们是以图表绘图区域的宽和高进行正规化之后的坐标或长度。

subplot()返回它所创建的Axes对象,我们可以将这些对象用变量保存起来,然后用sca()交替让它们成为当前Axes对象,并调用plot()在其中绘图。如果需要同时绘制多幅图表,可以给figure()传递一个整数参数来指定Figure对象的序号。如果序号所指定的Figure对象已经存在,将不创建新的对象,而只是让它成为当前的Figure对象。下面的程序演示了依次在不同图表的不同子图中绘制曲线:

plt.figure(1) # 创建图表1  
plt.figure(2) # 创建图表2  
ax1 = plt.subplot(121) # 在图表2中创建子图1  
ax2 = plt.subplot(122) # 在图表2中创建子图2  
  
x = np.linspace(0, 3, 100)  
for i in xrange(5):  
plt.figure(1)  ❶选择图表1  
plt.plot(x, np.exp(i*x/3))  
plt.sca(ax1)   ❷选择图表2的子图1  
plt.plot(x, np.sin(i*x))  
plt.sca(ax2)  # 选择图表2的子图2  
plt.plot(x, np.cos(i*x))

也可以不调用sca()指定当前子图,而直接调用ax1和ax2的plot()方法来绘图。

首先通过figure()创建了两个图表,它们的序号分别为1和2。然后在图表2中创建了左右并排的两个子图,并用变量ax1和ax2保存。

在循环中,❶先调用figure(1)让图表1成为当前图表,并在其中绘图。❷然后调用sca(ax1)和sca(ax2)分别让子图ax1和ax2成为当前子图,并在其中绘图。当它们成为当前子图时,包含它们的图表2也自动成为当前图表,因此不需要调用figure(2)。这样依次在图表1和图表2的两个子图之间切换,逐步在其中添加新的曲线,效果如图4所示。

图4 同时在多幅图表、多个子图中进行绘图

此外subplots()可以一次生成多个子图,并返回图表对象和保存子图对象的数组。在下面的例子中,axes是一个形状为(2, 3)的数组,每个元素都是一个子图对象,可以利用Python的赋值功能将这个数组中的每个元素用一个变量表示:

fig, axes = plt.subplots(2, 3)  
[a, b, c], [d, e, f] = axes  
print axes.shape  
print b  
(2, 3)  
Axes(0.398529,0.536364;0.227941x0.363636)

还可以调用subplot2grid()进行更复杂的表格布局。表格布局和在Excel或Word中绘制表格十分类似,其调用参数如下:

subplot2grid(shape, loc, rowspan=1, colspan=1, **kwargs)

其中,shape为表示表格形状的元组:(行数, 列数)。loc为子图左上角所在的坐标:(行, 列)。rowspan和colspan分别为子图所占据的行数和列数。在下面的例子中,在3×3的网格上创建5个子图,在每个子图中间显示该子图对应的变量名,如图5所示:

图5 使用subplot2grid()创建表格布局

fig = plt.figure(figsize=(6, 6))  
ax1 = plt.subplot2grid((3, 3), (0, 0), colspan=2)  
ax2 = plt.subplot2grid((3, 3), (0, 2), rowspan=2)  
ax3 = plt.subplot2grid((3, 3), (1, 0), rowspan=2)  
ax4 = plt.subplot2grid((3, 3), (2, 1), colspan=2)  
ax5 = plt.subplot2grid((3, 3), (1, 1))

五、配置文件

绘制一幅图需要对许多对象的属性进行配置,例如颜色、字体、线型等。在前面的绘图程序中,并没有逐一对这些属性进行配置,而是直接采用matplotlib的默认配置。matplotlib将这些默认配置保存在一个名为matplotlibrc的配置文件中,通过修改配置文件,可以修改图表的默认样式。

在matplotlib中可以使用多个matplotlibrc配置文件,它们的搜索顺序如下:顺序靠前的配置文件将会被优先采用。

·当前路径:程序的当前路径。

·用户配置路径:通常在用户文件夹的.matplotlib目录下,可以通过环境变量MATPLOTLIBRC修改它的位置。

·系统配置路径:保存在matplotlib的安装目录下的mpl-data中。

通过下面的语句可以获取用户配置路径:

from os import path  
path.abspath(matplotlib.get_configdir())  
u'C:\\Users\\RY\\Dropbox\\scipybook2\\settings\\.matplotlib'

通过下面的语句可以获得目前使用的配置文件的路径:

path.abspath(matplotlib.matplotlib_fname())  
u'C:\\Users\\RY\\Dropbox\\scipybook2\\settings\\.matplotlib\\matplotlibrc'

如果使用文本编辑器打开此配置文件,就会发现它实际上是一个字典。为了对众多的配置进行区分,字典的键根据配置的种类,用"."分为多段。配置文件的读入可以使用rc_params(),它返回一个配置字典:

print(matplotlib.rc_params())  
agg.path.chunksize: 0  
animation.avconv_args: []  
animation.avconv_path: avconv  
animation.bitrate: -1  
...

在matplotlib模块载入时会调用rc_params(),并把得到的配置字典保存到rcParams变量中:

print(matplotlib.rcParams)  
agg.path.chunksize: 0  
animation.avconv_args: []  
animation.avconv_path: avconv  
animation.bitrate: -1  
...

matplotlib将使用rcParams字典中的配置进行绘图。用户可以直接修改此字典中的配置,所做的改变会反映到此后创建的绘图元素。例如下面的脚本所绘制的折线将带有圆形的点标识符:

matplotlib.rcParams["lines.marker"] = "o"  
plt.plot([1,2,3,2])

为了方便对配置字典进行设置,可以使用rc()。下面的例子同时配置点标识符、线宽和颜色:

matplotlib.rc("lines", marker="x", linewidth=2, color="red")

如果希望恢复到matplotlib载入时从配置文件读入的默认配置,可以调用rcdefaults():

matplotlib.rcdefaults()

如果手工修改了配置文件,希望重新从配置文件载入最新的配置,可以调用:

matplotlib.rcParams.update( matplotlib.rc_params() )

通过pyplot模块也可以使用rcParams、rc和rcdefaults。

matplotlib.style模块提供绘图样式切换功能,所有可选样式可以通过available获得:

from matplotlib import style  
print style.available  
[u'dark_background', u'bmh', u'grayscale', u'ggplot', u'fivethirtyeight']

调用use()函数即可切换样式,例如下面使用ggplot样式绘图,效果如图6所示。

图6 使用ggplot样式绘图

style.use("ggplot")

六、在图表中显示中文

matplotlib的默认配置文件中所使用的字体无法正确显示中文,可以通过下面几种方法设置中文字体:

·在程序中直接指定字体。

·在程序开头修改配置字典rcParams。

·修改配置文件。

在matplotlib中可以通过字体名指定字体,而每个字体名都与一个字体文件相对应。通过下面的程序可以获得所有可用字体的列表:

from matplotlib.font_manager import fontManager  
fontManager.ttflist[:6]  
[<Font 'cmss10' (cmss10.ttf) normal normal 400 normal>,  
<Font 'cmb10' (cmb10.ttf) normal normal 400 normal>,  
<Font 'cmex10' (cmex10.ttf) normal normal 400 normal>,  
<Font 'STIXSizeFourSym' (STIXSizFourSymBol.ttf) normal normal 700 normal>,  
<Font 'Bitstream Vera Serif' (VeraSeBd.ttf) normal normal 700 normal>,  
<Font 'Bitstream Vera Sans' (VeraIt.ttf) oblique normal 400 normal>]

ttflist是matplotlib的系统字体列表。其中每个元素都是表示字体的Font对象,下面的程序显示了第一个字体文件的全路径和字体名,由路径可知它是matplotlib自带的字体:

print fontManager.ttflist[0].name  
print fontManager.ttflist[0].fname  
cmss10  
C:\WinPython-32bit-2.7.9.2\python-2.7.9\lib\site-packages\matplotlib\mpl-data\fonts\ttf\cmss10.ttf

下面的程序使用字体列表中的字体显示中文文字,效果图7所示。

图7 显示系统中所有的中文字体名

scpy2/matplotlib/chinese_fonts.py:显示系统中所有文件大于1MB的TTF字体,请读者使用该程序查询计算机中可使用的中文字体名。

import os  
from os import path  
  
fig = plt.figure(figsize=(8, 7))  
ax = fig.add_subplot(111)  
plt.subplots_adjust(0, 0, 1, 1, 0, 0)  
plt.xticks([])  
plt.yticks([])  
x, y = 0.05, 0.05  
fonts = [font.name for font in fontManager.ttflist if  
path.exists(font.fname) and os.stat(font.fname).st_size>1e6] ❶  
font = set(fonts)  
dy = (1.0 - y) / (len(fonts) // 4 + (len(fonts)%4 != 0))  
  
for font in fonts:  
t = ax.text(x, y + dy / 2, u"中文字体",  
{'fontname':font, 'fontsize':14}, transform=ax.transAxes) ❷  
ax.text(x, y, font, {'fontsize':12}, transform=ax.transAxes)  
x += 0.25  
if x >= 1.0:  
y += dy  
x = 0.05  
plt.show()

❶利用os模块中的stat()获取字体文件的大小,并保留字体列表中所有大于1MB的字体文件。由于中文字体文件通常都很大,因此使用这种方法可以粗略地找出所有的中文字体文件。

❷调用子图对象的text()在其中添加文字,注意文字必须是Unicode字符串。通过一个描述字体的字典指定文字的字体:'fontname'键对应的值就是字体名。

由于matplotlib只搜索TTF字体文件,因此无法通过上述方法使用系统中安装的许多复合TTC字体文件。可以直接创建使用字体文件的FontProperties对象,并使用此对象指定图表中的各种文字的字体。下面是一个例子:

from matplotlib.font_manager import FontProperties  
font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14) ❶  
t = np.linspace(0, 10, 1000)  
y = np.sin(t)  
plt.close("all")  
plt.plot(t, y)  
plt.xlabel(u"时间", fontproperties=font) ❷  
plt.ylabel(u"振幅", fontproperties=font)  
plt.title(u"正弦波", fontproperties=font)  
plt.show()

❶创建一个描述字体属性的FontProperties对象,并设置其fname属性为字体文件的绝对路径。❷通过fontproperties参数将FontProperties对象传递给显示文字的函数。

还可以通过字体工具将TTC字体文件分解为多个TTF字体文件,并将其复制到系统的字体文件夹中。为了缩短启动时间,matplotlib不会每次启动时都重新扫描所有的字体文件并创建字体列表,因此在复制完字体文件之后,需要运行下面的语句重新创建字体列表:

from matplotlib.font_manager import _rebuild  
_rebuild()

还可以直接修改配置字典,设置默认字体,这样就不需要在每次绘制文字时设置字体了。例如:

plt.rcParams["font.family"] = "SimHei"  
plt.plot([1,2,3])  
plt.xlabel(0.5 ,0.5, u"中文字体")

或者修改上节介绍的配置文件,修改其中的font.family配置为SimHei,注意SimHei是字体名,请读者运行前面的代码来查看系统中所有可用的中文字体名。
---------------------------END---------------------------

题外话

感兴趣的小伙伴,赠送全套Python学习资料,包含面试题、简历资料等具体看下方。

👉CSDN大礼包🎁:全网最全《Python学习资料》免费赠送🆓!(安全链接,放心点击)

一、Python所有方向的学习路线

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。


二、Python必备开发工具

工具都帮大家整理好了,安装就可直接上手!

三、最新Python学习笔记

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。

四、Python视频合集

观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

五、实战案例

纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

六、面试宝典

简历模板

👉CSDN大礼包🎁:全网最全《Python学习资料》免费赠送🆓!(安全链接,放心点击)

如有侵权,请联系删除。

相关推荐
赛丽曼18 分钟前
Python中的HTML
python·html
数模竞赛Paid answer20 分钟前
2023年MathorCup数学建模B题城市轨道交通列车时刻表优化问题解题全过程文档加程序
数学建模·数据分析·mathorcup
luky!26 分钟前
算法--解决熄灯问题
python·算法
深度学习lover40 分钟前
<项目代码>YOLOv8 番茄识别<目标检测>
人工智能·python·yolo·目标检测·计算机视觉·番茄识别
IT古董41 分钟前
【机器学习】机器学习中用到的高等数学知识-1.线性代数 (Linear Algebra)
人工智能·python·线性代数·机器学习
生信与遗传解读1 小时前
基于python的线性代数相关计算
python·线性代数·机器学习
Py小趴1 小时前
Python自学之Colormaps指南
开发语言·python·数据可视化
晒足以百八十1 小时前
基于Python 和 pyecharts 制作招聘数据可视化分析大屏
开发语言·python·信息可视化
敲代码不忘补水1 小时前
生成式GPT商品推荐:精准满足用户需求
开发语言·python·gpt·产品运营·产品经理
孤客网络科技工作室1 小时前
Python Plotly 库使用教程
python·信息可视化·plotly