Matplotlib绘图和可视化
绘图是数据分析工作中最重要的任务之一,是探索过程的一部分,例如,帮助我们找出异常值、必要的数据转换、得出有关模型的idea等。此外,还可以利用诸如d3.js之类的工具为Web应用构建交互式图像。但是在这里主要讲解的是matplotlib,matplotlib是一个用于创建出版质量图表的桌面绘画包,其目的是为python构建一个MATLAB式的绘图接口。
Matplotlib api入门:
matplotlib API函数(如plot和close)都位于matplotlib.pyplot模块中,其通常的引入约定是:
import matplotlib.pyplot as plt
Figure和Subplot:
matplotlib的图像位于Figure对象中。你可以用plt.figure创建一个新的Figure,创建完之后会弹出一个空窗口:
fig = plt.figure()
plt.figure有一些选项,特别是figsize,它用于确保当图片保存到磁盘时具有一定大小和横纵比,通过plt.gcf()函数即可得到当前Figure的引用。
Figure只是弹出一个空白的窗口,不能通过Figure绘图,必须使用add_subplot()函数创建一个或多个subplot才行:
ax1 = fig.add_subplot(2,2,1)
这条代码的意思是:创建的2×2的subplot,且当前选中的是4个subplot中的第一个(编号从1开始),我们也可以依次创建出第二个、第三个:
ax2 = fig.add_subplot(2,2,2)
ax3 = fig.add_subplot(2,2,3)
如果这个时候发出一条绘图命令(如plt.plot([1.5,3.5,-2,1.6])),matplotlib就会在最后一个用过的subplot(如果没有就创建一个)上绘制,例如:
from numpy.random import randn
plt.plot(randn(50).cumsum(), 'k--')
其中"k--"是一个线型选项,用于告诉matplotlib绘制黑色虚线图。如果想对其他的subplot进行绘图的话,可以通过上面fig.add_subplot()函数返回的AxesSubplot对象进行绘制,即:
ax1.hist(randn(100), bins=20, color='k', alpha=0.3)
ax2.scatter(np.arange(30), np.arange(30)+3*randn(30))
还有一种比较轻松创建特定布局的Figure和subplot的方式,就是使用plt.subplots()函数,它可以创建一个新的Figure,并返回一个包含已创建的subplot对象的Numpy数组,可以轻松地对axes数组进行索引,就好像一个二维数组一样(例如axes[0,1]):
fig, axes = plt.subplots(2, 3)
pyplot.subplots()函数的具体参数如下所示:
默认情况下,matplotlib会在subplot外围留下一定的边距,并在subplot之间留下一定的间距,间距跟图像的高度和宽度有关,如果你调整了图像的大小其间距也会调整。我们可以利用Figure的subplot_adjust()函数可以轻而易举地修改间距,具体的函数如下所示:
subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)
wspace和hspace用于控制宽度和高度的百分比,可以用作subplot之间的间距。下面是一个简单的例子,我们把间距收缩到了0:
fig, axes = plt.subplots(2,2,sharex=True,sharey=True)
for i in range(2):
for j in range(2):
axes[i,j].hist(randn(50),bins=50,color='k',alpha=0.5)
plt.subplots_adjust(wspace=0,hspace=0)
颜色、标记和线型:
matplotlib的plot()函数接受一组X和Y坐标,还可以接受一个表示颜色和线型字符串缩写。例如,要根据x和y绘制绿色虚线:
ax.plot(x,y,'g--')
ax.plot(x,y,linestyle='--',color='g')
以上两条代码完成的功能是一样的,但是前一个是在一个字符串中指定颜色和线型的。常用的颜色都有一个缩写词,要使用其他任意颜色则可以通过指定其RGB值的形式使用(例如:#CECECE)。
线型图上还可以加上一些标记,以强调实际的数据点,标记也可以放到格式字符串中,但标记类型和线型必须放在颜色后面:
plt.plot(randn(30).cumsum(), 'ko--')
还可以写成更为明确的形式:
plt.plot(randn(30).cumsum(), color='k', linestyle='dashed', marker='o')
刻度、标签和图例:
对于大多数的图表装饰项,其主要实现方式有二:使用过程型的pyplot接口以及更为面向对象的原生matplotlib API。
pyplot接口的设计目的就是交互式使用,含有诸如xlim、xticks、xticklabels之类的方法,它们分别控制图的范围、刻度位置、刻度标签。其使用方式又两种:
(1)、调用时不带参数,则返回当前的参数值。例如,plt.xlim()返回当前的X轴绘图范围;
(2)、调用时带参数,则设置参数值。因此,plt.xlim([0,10])会将X轴的范围设置为0到10;
所有这些函数都是对当前或最近创建的AxesSubplot起作用的,它们各自对应subplot对象上的两个方法,以xlim为例,就是ax.get_xlim()和ax.set_xlim()。
为了说明如何进行刻度、标签和图例的创建,我们先创建一个简单的图像并绘制一段随机漫步:
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(randn(1000).cumsum())
如果想要修改X轴的刻度,最简单的方法就是使用set_xticks()函数和set_xticklabels()函数,set_xticks()函数是告诉matplotlib要将刻度放在数据范围中的哪些位置,默认情况下这些位置就是刻度标签,但是我们可以通过set_xticklabels()函数将任何其他值作用于标签:
ticks = ax.set_xticks([0,250,500,750,1000])
labels = ax.set_xticklabels(['one','two','three','four','five'], rotation=30, fontsize='small')
再用set_xlabel()函数为X轴设置一个名称,并用set_title()函数设置一个标题:
ax.set_title('My first matplotlib plot')
ax.set_xlabel('Stages')
图例(legend)是另一种用于标识图表元素的重要工具,添加图例最简单的方式就是在添加subplot的时候传入label参数:
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(randn(1000).cumsum(), 'k', label='one')
ax.plot(randn(1000).cumsum(), 'k--', label='two')
ax.plot(randn(1000).cumsum(), 'k.', label='three')
ax.legend(loc='best')
ax.legend(loc='best')表示自动创建图例,参数loc表示matplotlib要将图例放在哪
注解以及在Subplot上绘图:
除了标准的图表对象之外,我们还希望能自定义一些注解(比如文本、箭头或其他图形)。注解可以通过text()、arrow()和annotate()等函数进行添加。
text()函数可以将文本绘制在图表的指定坐标(x,y),还可以加上一些自定义格式:
ax.text(x, y, 'Hello world', family='monospace', fontsize=10)
注解中可以既含有文本也含有箭头,图形绘制要麻烦一些,matplotlib中有些表示常见图形的对象,这些对象被称为块(patch)。其中有些可以在matplotlib.pyplot中找到(如Rectangle和Circle),但完整的集合位于matplotlib.patches中。下面我们用一个例子来进行说明:
from datetime import datetime
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
data = pd.read_csv('spx.csv', index_col=0, parse_dates=True)
spx = data['SPX']
spx.plot(ax=ax,style='k--')
crisis_data=[(datetime(2007,10,11),'Peak of bull market'),
(datetime(2008,3,12),'Bear Stearns Fails'),
(datetime(2008,9,15),'Lehman Bankruptcy')]
for date,label in crisis_data:
ax.annotate(label, xy=(data, spx.asof(date)+50),xytext=(date, spx.asof(date)+200),arrowprops=dict(facecolor='black'),horizontalalignment='left',verticalalignment='top')
ax.set_xlim(['1/1/2007','1/1/2011'])
ax.set_ylim([600,1800])
ax.set_title('Important dates in 2008-2009 financial crisis')
如果想要在图表中添加一个图形,你需要创建一个块对象shp,然后通过ax.add_patch(shp)将其添加到subplot中。下面就是一个例子:
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
rect = plt.Rectangle((0.2,0.75),0.4,0.15,color='k',alpha=0.3)
circ = plt.Circle((0.7,0.2),0.15,color='b',alpha=0.3)
pgon = plt.Polygon([[0.15,0.15],[0.35,0.4],[0.2,0.6]], color='g', alpha=0.5)
ax.add_patch(rect)
ax.add_patch(circ)
ax.add_patch(pgon)
将图表保存到文件:
利用plt.savefig()函数可以将当前图表保存到文件,该方法相当于Figure对象的实例方法savefig()。savefig()函数的具体参数如下所示:
例如,要将图表保存为SVG文件:
plt.savefig('figpath.svg')
文件类型是通过文件扩展名推断出来的,因此,如果我们使用的是.pdf,就会得到一个PDF文件。
如果你想要得到一张带有最小白边且分辨率为400DPI的PNG图片:
plt.savefig('figpath.png', dpi=400, bbox_inches='tight')
savefig并非一定要写入磁盘,也可以写入任何文件型的对象,比如StringIO:
from io import StringIO
buffer = StringIO()
plt.savefig(buffer)
plot_data = buffer.getvalue()
这对在Web上提供动态生成的图片是很实用的。
pandas中的绘图函数:
实际上,matplotlib是一种比较低级的工具,要组装一张图表,你得用它的各种基础组件才行:数据展示(即图表类型:线型图、柱状图、盒形图、散布图、等值线图等)、图例、标题、刻度标签以及其他注解型信息。
在pandas中,我们有行标签、列标签以及分组信息,这就是说原本需要一大堆matplotlib代码绘制一张完整的图表,现在只要一两条简洁的语句就行了。pandas中有许多能够利用DataFrame对象数据组织特点来创建标准图表的高级绘图方法。
线型图:
Series和DataFrame都有一个用于生成各类图表的plot()函数,默认情况下,它们所生成的线型图,plot()函数具体的参数如下所示:
例1:创建一个Series并为其绘制出图表:
s = pd.Series(np.random.randn(10).cumsum(), index=np.arange(0,100,10))
s.plot()
该Series对象的索引会被传给matplotlib,并且用于绘制X轴。可以通过use_index=False禁用该功能。X轴的刻度和界限可以通过参数xticks和参数xlim进行调节,Y轴就使用参数yticks和参数ylim。
例2:创建一个DataFrame并且为其绘制图表:
df = pd.DataFrame(np.random.randn(10,4).cumsum(0), colunms=['A','B','C','D'], index=np.arange(0,100,10))
df.plot()
柱状图:
在生成线型图的代码中加上kind='bar'(垂直柱状图)或kind='barh'(水平柱状图)即可生成柱状图,Series或DataFrame的索引将会被用作X(bar)或Y(barh)刻度。
例1:通过一个Series来绘制柱状图:
fig, axes = plt.subplots(2,1)
data = pd.Series(np.random.rand(16), index=list('abcdefghijklmnop'))
data.plot(kind='bar', ax=axes[0], color='k', alpha=0.7)
data.plot(kind='barh', ax=axes[1], color='k', alpha=0.7)
例2:通过DataFrame绘制图表,对于DataFrame,柱状图会将每一行的值分为一组:
df = pd.DataFrame(np.random.rand(6,4), index=['one','two','three','four','five,'six'], columns=pd.index(['A','B','C''D'], name='Genus'))
df.plot(kind='bar')
设置stacked=True即可为DataFrame生成堆积柱状图,这样每行的值就会被堆积在一起:
df.plot(kind='barh', stacked=True, alpha=0.5)
直方图和密度图:
直方图(histogram)是一种可以对值的频率进行离散化显示的柱状图。数据点被拆分到离散的、间隔均匀的面元中,绘制的是各面元中数据点的数量。
tips['tip_pct'] = tips['tip']/tips['total_bill']
tips['tip_pct'].hist(bins=50)
还有一种与此相关的图表是密度图,它是通过计算“可能会产生观测数据的连续概率分布的估计”(也就是对数据的生成分布进行估计)。一般的过程是将该分布近似为一组核(即诸如正态分布之类的较为简单的分布),因此,密度图也被称为KDE(Kernel Density Estimate,核密度估计)。调用plot()时附加上kind='kde'即可生成一张密度图。
tip['tip_pct'].plot(kind='kde')
这两种图表常常会被画在一起,直方图以规格化形式给出(以便给出面元化密度),然后再在其上绘制核密度估计。
comp1 = np.random.normal(0,1,size=200) # N(0,1)
comp2 = np.random.normal(10,2,size=200) # N(10,4)
values = Pd.Series(np.concatenate([comp1,comp2]))
values.hist(bins=100, alpha=0.3, color='k', normed=True)
values.plot(kind='kde', style='k--')
散布图:
散布图(scatter plot)是观察两个一维数据序列之间的关系的有效手段,matplotlib中的scatter()函数是绘制散布图的主要方法。
macro = pd.read_csv('macrodata.csv')
data = macro[['cpi','m1','tbilrate','unemp']]
trans_data = np.log(data).diff().dropna()
plt.scatter(trans_data['m1'], trans_data['unemp'])
plt.title('Changes in log %s vs. log %s' %('m1','unemp'))
有些时候同时观察一组变量的散布图在探索式数据分析工作中是很有用的,这也被称为散步图矩阵(scatter plot matrix)。pandas提供了一个能从DataFrame创建散步图矩阵的scatter_matrix()函数,它还支持在对角线上放置各变量的直方图或密度图。
pd.scatter_matrix(trans_data, diagonal='kde', color='k', alpha=0.3)
例:绘制地图:图形化显示海地地震危机数据:
python图形化工具生态系统:
Chaco、mayavi等等
下一篇: 基于QT5的计算器简单实现