信息可视化(也叫绘图)是数据分析中最重要的工作之一。它可能是探索过程的一部分,例如,帮助我们找出异常值、必要的数据转换、得出有关模型的想法等。另外,做一个可交互的数据可视化也许是工作的最终目标。Python 有许多库进行静态或动态的数据可视化,但我这里重要关注于 matplotlib 和基于它的库。
matplotlib 是一个用于创建出版质量图表的桌面绘图包(主要是 2D 方面)。该项目是由 John Hunter 于 2002 年启动的,其目的是为 Python 构建一个 MATLAB 式的绘图接口。matplotlib 和 IPython 社区进行合作,简化了从 IPython shell(包括现在的 Jupyter 笔记本)进行交互式绘图。matplotlib 支持各种操作系统上许多不同的 GUI 后端,而且还能将图片导出为各种常见的向量(vector)和光栅(raster)图:PDF、SVG、JPG、PNG、BMP、GIF 等。除了几张,本书中的大部分图都是用它生成的。
随着时间的发展,matplotlib 衍生出了多个数据可视化的工具集,它们使用 matplotlib 作为底层。其中之一是 seaborn,本章后面会学习它。
学习本章代码案例的最简单方法是在 Jupyter 笔记本进行交互式绘图。在 Jupyter 笔记本中执行下面的语句:
%matplotlib notebook
9.1 matplotlib API 入门
matplotlib通常的引入方式如下
In [11]: import matplotlib.pyplot as plt
创建一个简单的图形
In [12]: import numpy as np
In [13]: data = np.arange(10)
In [14]: data
Out[14]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [15]: plt.plot(data)
图形和子图
matplotlib图像都位于图形中,可以使用plt.figure创建一个新的图形
In [16]: fig = plt.figure()
plt.figure有一些选项,特别是figsize,它用于确保当图片保存到磁盘时具有一定的大小和纵横比。figsizie用来指定图形的长和宽,传入的是一个二元数组
plt.figure(figsize=(2,4))
使用add_subplot 可以在图形中创建多个子图
In [17]: ax1 = fig.add_subplot(2, 2, 1)
这条代码的意思是图像是2*2的分布的,且当前选中的是4张图的第一个。。如果再把后面两个子图也创建出来,最终得到的图像如图 9-2 所示:
In [18]: ax2 = fig.add_subplot(2, 2, 2)
In [19]: ax3 = fig.add_subplot(2, 2, 3)
如果这时执行一条绘图命令(如plt.plot([1.5, 3.5, -2, 1.6])),matplotlib 就会在最后一个用过的子图(如果没有则创建一个)上进行绘制,隐藏创建图形和子图的过程。因此,如果我们执行下列命令,你就会得到如图 9-3 所示的结果:
In [20]: plt.plot(np.random.randn(50).cumsum(), 'k--')
k–是线性选项,告诉matplotlib绘制的是黑色线性虚线图。 上面那些有fig.add_subplot所返回的对象是AxesSubplot对象,直接调用他们的实例方法就可以在其所代表的空格中画图了
fig = plt.figure()
ax1 = fig.add_subplot(2,2,1)
ax2 = fig.add_subplot(2,2,2)
ax3 = fig.add_subplot(2,2,3)
plt.plot(np.random.randn(40).cumsum(),'k--')
ax1.hist(np.random.randn(100),bins=20,color='k',alpha = 0.3)
ax2.scatter(np.arange(30),np.arange(30)+3+np.random.randn(30))
创建包含子图网格的图形是一个常见的任务,matplotlib有一个更便捷的方法plt.subplots,它可以创建一个新的图形,并返回一个已含有子图对象的numpy数组 plt.subplots生产两个对象一个是图形对象,一个是子图对象的数组
In [24]: fig, axes = plt.subplots(2, 3)
In [25]: axes
Out[25]:
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x7fb626374048>,
<matplotlib.axes._subplots.AxesSubplot object at 0x7fb62625db00>,
<matplotlib.axes._subplots.AxesSubplot object at 0x7fb6262f6c88>],
[<matplotlib.axes._subplots.AxesSubplot object at 0x7fb6261a36a0>,
<matplotlib.axes._subplots.AxesSubplot object at 0x7fb626181860>,
<matplotlib.axes._subplots.AxesSubplot object at 0x7fb6260fd4e0>]], dtype
=object)
这是非常实用的,因为可以轻松地对axes数组进行索引,就好像是一个二维数组一样,例如axes[0,1]。你还可以通过sharex和sharey指定子图应该具有相同的 X 轴或 Y 轴。在比较相同范围的数据时,这也是非常实用的,否则,matplotlib 会自动缩放各图表的界限。有关该方法的更多信息,请参见表 9-1。
调整子图周围的间距
默认情况下,matplotlib会在子图周围留下一定的间距,间距和图像的高度与宽度有关,因此你调整了图像的大小,间距的大小也会改变,利用图形的subplots_adjust方法可以调增子图的间距,同时它也是一个顶级函数
subplots_adjust(left=None, bottom=None, right=None, top=None,
wspace=None, hspace=None)
wspace和hspace是用来控制高度和宽度的间距的百分比(这个百分比是相对于图像的百分比,1就是一个图像的间距),
fig, axes = plt.subplots(2, 2, sharex=True, sharey=True)
for i in range(2):
for j in range(2):
axes[i, j].hist(np.random.randn(500), bins=50, color='k', alpha=0.5)
plt.subplots_adjust(wspace=0, hspace=0)
颜色,标记和线性
matplotlib中的plot函数接受一组X和Y坐标,还可以接受表示颜色和线性的字符串缩写。例如要画出一个绿色,虚线的图形,可以这样话
ax.plot(x, y, 'g--')
这种在一个字符串中指定颜色和线型的方式非常方便。在实际中,如果你是用代码绘图,你可能不想通过处理字符串来获得想要的格式。通过下面这种更为明确的方式也能得到同样的效果: 常见的表示颜色的快捷字符串都是常见颜色的首字母(比如蓝色是b) 常见的表示线性的快捷字符串有"-“,”–“,”- “,”:"
ax.plot(x, y, linestyle='--', color='g')
常用的颜色可以用颜色名缩写,也可以指定颜色码(例如,‘#CECECE’),可以查看plot文档字符串查看所有线性的合集(在 IPython 和 Jupyter 中使用plot?)。
对折线图上的点设置形状以便区分,使用关键字marker可以进行设置,一些常用的形状也可以使用字符串快捷标记(常用的快捷标记有".“,“o”,”^“,”>“,”<","v"等
In [30]: from numpy.random import randn
In [31]: plt.plot(randn(30).cumsum(), 'ko--')
还可以将其写成更为明确的形式:
plot(randn(30).cumsum(), color='k', linestyle='dashed', marker='o')
关键字linestyle可以用ls代替
在线形图中,点与点的连接方式是线型,但是可以通过关键字drawstyle来进行设置 注意drawstyle是指定点与点的连接方式,连接方式有:drawstyle or ds: {‘default’, ‘steps’, ‘steps-pre’, ‘steps-mid’, ‘steps-post’}, default: ‘default’ plt.legend(loc=‘best’)用来展现图例,参数loc='best’是用来设置图例的位置的
In [33]: data = np.random.randn(30).cumsum()
In [34]: plt.plot(data, 'k--', label='Default')
Out[34]: [<matplotlib.lines.Line2D at 0x7fb624d86160>]
In [35]: plt.plot(data, 'k-', drawstyle='steps-post', label='steps-post')
Out[35]: [<matplotlib.lines.Line2D at 0x7fb624d869e8>]
In [36]: plt.legend(loc='best')
刻度、标签和图例
对于大多数的图表装饰项,其主要实现方式有二:使用过程型的pyplot接口(例如,matplotlib.pyplot)以及更为面向对象的原生 matplotlib API。
pyplot接口的设计目的就是交互式使用,含有诸如xlim、xticks和xticklabels之类的方法。它们分别控制图表的范围、刻度位置、刻度标签等。其使用方式有以下两种:
调用时不带参数,则返回当前的参数值(例如,plt.xlim()返回当前的 X 轴绘图范围)。 调用时带参数,则设置参数值(例如,plt.xlim([0,10])会将 X 轴的范围设置为 0 到 10)。 所有这些方法都是对当前或最近创建的AxesSubplot起作用的。它们各自对应子图对象上的两个方法,以xlim为例,就是ax.get_xlim和ax.set_xlim。我更喜欢使用子图的实例方法(因为我喜欢明确的事情,而且在处理多个子图时这样也更清楚一些)。当然你完全可以选择自己觉得方便的那个。
设置标题、轴标签、刻度以及刻度标签
由于画图中出现中文的话,只能显示成方框,所以可以添加以下代码解决问题
from pylab import *
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False
然后画图 具体设置方法见下面的代码解释
fig = plt.figure()
plt.plot(np.random.randn(100).cumsum(),'g--',label = '折线')
plt.xticks([0,25,50,75,100],labels=['one','two','three','four','five'],rotation = 90,fontsize = 10)
plt.title('随机漫步折线图')
plt.xlabel('X轴')
plt.ylabel('y轴')
plt.legend(loc = 'best')
参考资料matplotlib画图之修改坐标轴刻度
添加图例
图例(legend)是一种用于标识图表的重要工具,添加图例的方式有很多种,但是最简单的莫过于在添加子图的时候传入label参数 plt.legend是用来显示图例的,loc = 'best’表示把图例放在最合适的位置
fig = plt.figure(figsize=(8,3))
plt.plot(np.random.randn(100).cumsum(),'g',label = 'one')
plt.plot(np.random.randn(100).cumsum(),'g--',label = 'two')
plt.plot(np.random.randn(100).cumsum(),'g.',label = 'three')
plt.legend(loc = 'best')
注解
注解主要使用text函数,前两个参数用来定位注解的位置,第三个参数用来指定注解。还有些可选的参数主要用来设置注解字体,位置,大小等 例子
fig = plt.figure(figsize=(8,3))
plt.plot(np.random.randn(100).cumsum(),'g--',label = '折线')
plt.xticks([0,25,50,75,100],labels=['one','two','three','four','five'],rotation = 90,fontsize = 10)
plt.title('随机漫步折线图')
plt.xlabel('X轴')
plt.ylabel('y轴')
plt.legend(loc = 'best')
plt.text(20,7,'你好',fontsize = 20)
将图表保存到文件
利用plt.savefig可以将当前图表保存到文件中,该方法相当于图形对象的实例方法savefig。例如要讲图表保存为svg格式,你只需要输入
plt.savefig('figpath.svg')
文件类型可以通过扩展名进行指定,当前支持的扩展名有eps, jpeg, jpg, pdf, pgf, png, ps, raw, rgba, svg, svgz, tif, tiff等。 参数dpi可以设置文件分辨率 参数bbox_inches可以剪去图片中空白的部分
plt.savefig('figpath.png', dpi=400, bbox_inches='tight')
表 9-2 列出了savefig的其它选项。
9.2 使用 pandas 和 seaborn 绘图
matplotlib 实际上是一种比较低级的工具。要绘制一张图表,你组装一些基本组件就行:数据展示(即图表类型:线型图、柱状图、盒形图、散点图、等值线图等)、图例、标题、刻度标签以及其他注解型信息。
在 pandas 中,我们有多列数据,还有行和列标签。pandas 自身就有内置的方法,用于简化从DataFrame和Series绘制图形。另一个库 seaborn,由 Michael Waskom 创建的静态图形库。Seaborn 简化了许多常见可视类型的创建。
提示:引入 seaborn 会修改 matplotlib 默认的颜色方案和绘图类型,以提高可读性和美观度。即使你不使用 seaborn API,你可能也会引入 seaborn,作为提高美观度和绘制常见 matplotlib 图形的简化方法。
线形图
Series和DataFrame,都有一个用于生成各类图表的plot方法(相当于pandas里面内置了matplotlib),默认情况下他们生成的是线形图
In [60]: s = pd.Series(np.random.randn(10).cumsum(), index=np.arange(0, 100, 10))
In [61]: s.plot()
该Series的索引会被传给matplotlib并用于绘制X轴,可以使用参数use_index = False禁用该功能。X 轴的刻度和界限可以通过xticks和xlim选项进行调节,Y 轴就用yticks和ylim。plot参数的完整列表请参见表 9-3。我只会讲解其中几个,剩下的就留给读者自己去研究了。
pandas大部分绘图的方法都有一个ax参数,此参数是用来指定该图形要画在哪个子图对象上,如果不进行指定则画在最近一个使用的图像上
In [62]: df = pd.DataFrame(np.random.randn(10, 4).cumsum(0),
....: columns=['A', 'B', 'C', 'D'],
....: index=np.arange(0, 100, 10))
In [63]: df.plot()
针对DataFrame格式的对象进行画图,其每列都会生成一个折线,列名会变成图例,行标签会变成X轴标签 DataFrame还有一些对列进行灵活处理的选项,例如是把所有的列绘制在一个子图中还是创建各自的子图。详细信息请参照列表
柱状图
plot.bar()和plot.barh()分别用来绘制水平和垂直的柱状图,Series和DataFrame的行标签为X轴(bar)或Y轴(barh)的刻度
In [64]: fig, axes = plt.subplots(2, 1)
In [65]: data = pd.Series(np.random.rand(16), index=list('abcdefghijklmnop'))
In [66]: data.plot.bar(ax=axes[0], color='k', alpha=0.7)
Out[66]: <matplotlib.axes._subplots.AxesSubplot at 0x7fb62493d470>
In [67]: data.plot.barh(ax=axes[1], color='k', alpha=0.7)
color和alpha是用来设置颜色以及透明度的
对于DataFrame,其每行的值为一组,并排显示
In [69]: df = pd.DataFrame(np.random.rand(6, 4),
....: index=['one', 'two', 'three', 'four', 'five', 'six'],
....: columns=pd.Index(['A', 'B', 'C', 'D'], name='Genus'))
In [70]: df
Out[70]:
Genus A B C D
one 0.370670 0.602792 0.229159 0.486744
two 0.420082 0.571653 0.049024 0.880592
three 0.814568 0.277160 0.880316 0.431326
four 0.374020 0.899420 0.460304 0.100843
five 0.433270 0.125107 0.494675 0.961825
six 0.601648 0.478576 0.205690 0.560547
In [71]: df.plot.bar()
注意DataFrame的列名称被用作的图例的名称 设置stacked = True即可为DataFrame生成堆积图,这样每行的值就会堆积在一起
In [73]: df.plot.barh(stacked=True, alpha=0.5)
注意:柱状图有一个不错的功能,可以画出Series对象值出现的频率。s.count_value.plot.bar()
直方图和密度图
|