Python数据处理学习笔记 - matplotlib API篇

这是我阅读《用Python进行数据分析》一书的笔记、实验和总结。本篇文章主要讲解pandas包中两大数据结构的图像绘制方法,以及matplotlib API的大致继承关系、使用流程。关于绘图,本文远远未完全覆盖,具体使用请参考matplotlib API手册。

约定俗称的,引入以下包。

import numpy as np
import pandas as pd
np.random.seed(12345)
import matplotlib.pyplot as plt
import matplotlib
%matplotlib inline
plt.rc('figure', figsize=(5, 4)) #rc属性指定一个变量按照某些参数全局使用。
np.set_printoptions(precision=4, suppress=True)

1. matplotlib简要介绍

Matplotlib的层次如下:

png

其由三个层次构成,最底层为我么能提供了画布、画布的作用范围/大小、鼠标和键盘事件的交互,看起来这和Qt的QGView对象很像,一般来说,我们不会操纵这一层的对象。往上一层是表现层,其包含了创建一个2D对象常用的子对象——标签、刻度以及较高层次的Axis轴,在轴这个层次上还有线、文本说明,再往上就是Axes对象,这个对象包含一副完整的图。Figure对象用来表示图像的分辨率、格式、大小、位置,这个层次只能有一个Figure对象,但是可以有多个Axes子图像对象,因为一幅图可以包含多个子图像。每个子图像都含有两个Axis轴、文本、线或者圆等等。最上面就是我们画图调用的pyPlot接口,还有一个API接口称作pyLab,这个接口包含了Numpy,因此你不用单独引入numpy包。而pyPlot提供了一个更加纯粹的Python环境。

1.1 subplot绘制子图形,plot绘图

1.1.1 直接使用plot绘图

第一种方法是较为简单的,直接调用plt.plot传入数据和格式要求,直接创建对象,返回结果。本质上这里所有的参数都会相应传给axes的对应方法,然后自动调用figure进行绘图。

plt.plot(np.random.randn(50).cumsum(), 'r--.')
[<matplotlib.lines.Line2D at 0xf69ade0e48>]

pyplot.plot 可以接受绘制对象和一些类似于命令行的参数,可选颜色、点线状态(水平线、垂直线、星标)等各种形状。plot的参数参见Line 2D,可以这样指定:plot(x, y, color='green', linestyle='dashed', marker='o',markerfacecolor='blue', markersize=12).

png

pyplot可以直接按照面向过程方式处理,调用title、axis即处理当前的图像的对应内容。

from matplotlib.pyplot as plt
plt.plot([1,2,3,4],[2,21,23,11],"r^",[12,21,43,21],"b",[0,2,12,40,18],"y--",)
#x轴、y轴和其颜色、类型、第二个y轴和其颜色、类型、第三个y轴和其颜色、类型
plt.title("My First plt Figure")
plt.axis([-1,5,-10,50]) 
#sets the min and max of the x and y axes, with v = [xmin, xmax, ymin, ymax]
plt.show()

png

1.1.2 子图形、标签、刻度、文本

对于直接使用plot创建的对象,我们可以使用面向过程的方式进行管理:

from matplotlib.ticker import NullFormatter

#设置x轴和y轴的数据
t = np.arange(0,5,0.1)
y1 = np.sin(2*np.pi*t)
y2 = np.sin(2*np.pi*t)

#设置整体图形外观
plt.subplots_adjust(top=2, bottom=0.08, left=0.10, right=0.95, hspace=0.25,
                    wspace=0.35)

#plt.title("My First plot") not work here
#设置第一个子图形的标题、绘图、标签、网格
plt.subplot(411) #用来水平切分4个,垂直切分1个,调用第1个图形
plt.title("My First plot",fontsize=14) #only work here
plt.plot(t,y1,"b-.") #在这里还可以设置绘制的点的样式、颜色等
plt.ylabel("Value-M1")
plt.grid(True)

#同上,调用第二个图形,设置文本和图例
plt.subplot(412)
plt.plot(t,y2,"r--")
plt.ylabel("Value-M2")
plt.text(1,0.5,"text1")
plt.text(3,0.3,"text2")
plt.legend(["Legend"],loc=1) #图例位置和文本内容

#同上,调用第三个图形,gca返回当前instance,设置刻度
plt.subplot(413)
plt.plot(t,y1,"p-.")
plt.gca().xaxis.set_minor_formatter(NullFormatter())#gca获取当前axes,set..设置刻度

plt.show()

png

1.2 figure创建,fig.add_subplot绘制子图形

对于第一种方法而言,其弊端不言而喻。从这里开始介绍的两种方法:调用fig并通过fig.add_subplot以及直接调用subplots生成fig和axeses都是面向对象的,我们可以对每个对象进行分别设置,结构清晰明了。

对于matplotlib.pyplot,另一个显著的入口就是调用plt.figure()以生成figure Object创建一个画布。所有关于图像显示、保存、大小样式之类的操作都在此进行设置。而数据则在另一个称之为Axes Object中,对Axes对象进行设置可以改变数据,比如说数据源、图标类型、标题、刻度、题头、图例等。一个图形必须具备这两个对象,一个负责显示,充当view层,一个负责渲染,充当model/data层。

第一种方法是调用plt.figure()创建画布后,使用fig Object调用fig.add_subplot来创建子图形的axes。调用fig即可返回图像。

fig = plt.figure() #调用一个新的画布
#对于3D对象,这样调用
ax_3D = Axes3D(fig)
ax1 = fig.add_subplot(2, 2, 1) #添加子画布,2×2大小,第一个
ax2 = fig.add_subplot(2, 2, 2) #添加子画布,2×2大小,第二个
ax3 = fig.add_subplot(2, 2, 3) #添加子画布,2×2大小,第三个
# 需要注意,add_subplot返回的不是sub figure而是sub axes对象

png

matplotlib.pyplot.figure(num=None, figsize=None, dpi=None, facecolor=None, edgecolor=None, frameon=True, FigureClass=<class 'matplotlib.figure.Figure'>, clear=False, **kwargs)

1.3 调用subplots(c,r)创建fig和axeses

1.3.1 面向对象的绘制

第三种方法就是直接调用plt.subplots()方法,传入的值对figure起作用,比如定义子画布大小、多少、是否共享x、y轴等等。这个方法返回两个参数,其一为图像,其二为axes ndarray数组。之后详细的设置可以分别对figure和axes[x,y]进行。

需要注意,调用fig, ax = subplots区别于调用fig=plt.figure()然后ax = fig.add_subplot()。如果定义多个子图形,那么在调用subplots的时候返回的ax为一个列表,其中包含各个子图形的axes。但是这里的fig值得均为同一个fig,因为一个图形只能包含一个fig,但是可以包含多个axes。

%matplotlib inline
x = np.arange(0.1, 4, 0.5)
y = np.exp(-x)
fig,ax = plt.subplots(ncols=3,nrows=2,sharey=True,sharex=True) 
#其几个重要参数是行列数、相同的x、y刻度。
#Returns 2 object: fig : matplotlib.figure.Figure 
#object ax : Axes object or array of Axes objects.
plt.show() #等同于fig,是一个全局显示方法。

png

print(ax); #其中ax指的是子plot的axes属性
print(fig) #fig就是图像
[[<matplotlib.axes._subplots.AxesSubplot object at 0x000000F69B66E6D8>
  <matplotlib.axes._subplots.AxesSubplot object at 0x000000F69B8A4278>
  <matplotlib.axes._subplots.AxesSubplot object at 0x000000F69B2D2FD0>]
 [<matplotlib.axes._subplots.AxesSubplot object at 0x000000F69BBCC7F0>
  <matplotlib.axes._subplots.AxesSubplot object at 0x000000F69BBD4B38>
  <matplotlib.axes._subplots.AxesSubplot object at 0x000000F699C74F28>]]

Figure(432x288)

1.3.2 绘图常用方法

Firure常用方法

  • Figure.subplots_adjust等同于调用subplots_adjust(left=None, bottom=None, right=None, top=None,wspace=None, hspace=None)顶级函数,用于调整子plot周围宽度、高度等表现形式。
  • Figure.savefig(fname,dpi,bbox_inches,format)保存图像

Axes常用方法

  • hist 柱状图/pie 饼状图/box 箱型图等等
  • plot 如果不指定图的类型,比如饼状图或者柱状图,直接传入plot,参数填为data即可。
  • add_patch 添加图形
  • annotate 添加箭头
  • text 添加文本说明
  • get(set)_x(y)lim 获得/设置不同轴的范围
  • g/s_(x/y)ticks 设置刻度位置 #plt.xticks((0,1,2),(“a”,”b”,”c”)) 可以快速指定位置和标签值。
  • g/s_(x/y)ticklabels 设置刻度标签
  • g/s_(x/y)label 设置轴名称
  • title 题头设置
  • legend 图例设置

对于Axes调用get/set等很多方法,其实也可以直接调用plt全局对象,不过调用axes单个元素可以获得更精细的控制,并且更面向对象一些。

fig.subplots_adjust(left=None, bottom=None, right=None, top=None,wspace=0, hspace=0) 
#也可以使用plt顶级函数
#histogram 柱状图
ax[0,0].hist(np.random.randint(1,500,size=100),bins=50,color="k",alpha=0.5)
ax[0,1].hist(np.random.randint(1,500,size=100),bins=50,color="k",alpha=0.5)
ax[0,2].hist(np.random.randint(1,500,size=100),bins=50,color="k",alpha=0.5)
ax[1,0].hist(np.random.randint(1,500,size=100),bins=50,color="k",alpha=0.5)
ax[1,1].hist(np.random.randint(1,500,size=100),bins=50,color="k",alpha=0.5)
ax[1,2].hist(np.random.randint(1,500,size=100),bins=50,color="k",alpha=0.5)
fig

png

#fig.savefig("hello.png",format="png",dpi=100,bbox_inches="tight")
#两个较为重要的参数是bbox_inches控制边距,第二个是dpi控制精度
ax[0,0].get_xlim() #ax有很多类型的图表,还有很多关于图形的方法。
plt.close("all")
fig,ax = plt.subplots()
ax.plot(np.random.randn(500).cumsum(),label="a") 
#此处设置plot的label值,即可以调用legend时自动生成图例。
ax.plot(np.random.randn(500).cumsum(),label="b")
ax.set_xticks([0,150,300,450,600])
ax.set_xticklabels(["one","two","three","four","five","six"]) #自动跳过了six
#也可以直接写ax.xticks([真实数值],[对应标签名称])
ax.set_title("table",loc="center")
ax.set_xlabel("numbers")
ax.set_ylabel("values")
ax.legend(loc="best") #legend图例,在plot时传入label进行创建
ax.text(0,10,"Hello World")
# matplotlib.pyplot.text(x, y, s, fontdict=None, withdash=False, **kwargs)
ax.annotate("",
            xy=(0, 0), xycoords='data',
            xytext=(400,10), textcoords='data',
            arrowprops=dict(arrowstyle="->",
                            connectionstyle="arc3"),
            )
Text(400,10,'')

png

plt.close("all")

1.4 特殊绘制技巧

1.4.1 SPINES坐标系绘制

一个笛卡尔坐标系的设置:

%matplotlib inline
fig ,ax = plt.subplots()
x = np.arange(-2*np.pi,2*np.pi,0.01)
y = np.sin(3*x)/x
y2 = np.sin(2*x)/x
y3 = np.sin(x)/x

ax.plot(x,y,"r")
ax.plot(x,y2,"b")
ax.plot(x,y3,"g")

print(plt.gca(),ax)
# AxesSubplot(0.125,0.125;0.775x0.755) AxesSubplot(0.125,0.125;0.775x0.755)

ax.spines["right"].set_visible(False)
#class matplotlib.spines.Spine(axes, spine_type, path, **kwargs)
ax.spines["top"].set_visible(False)
ax.spines["bottom"].set_position(("data",0))
ax.spines["left"].set_position(("data",0))

png

1.4.2 AXES图形的绘制

图形绘制需要调用axes.add_patch()方法,传入参数为图形,也就是line/path/patches对象。和Qt图形绘制很类似,底层API。

import matplotlib.path as mpath
import matplotlib.patches as mpatches
fig = plt.figure(figsize=(12,6))
axes = fig.add_subplot(1,2,1)
grid = np.mgrid[0.2:0.8:3j, 0.2:0.8:3j].reshape(2, -1).T
patches = []
# add a circle
circle = mpatches.Circle(grid[0], 0.1, ec="none")
patches.append(circle)
# add a rectangle
rect = mpatches.Rectangle(grid[1] - [0.025, 0.05], 0.05, 0.1, ec="none")
patches.append(rect)
axes.add_patch(patches[0]);axes.add_patch(patches[1])
<matplotlib.patches.Rectangle at 0xaa66e76a58>

png

1.4.3 AXES3D图表绘制

from mpl_toolkits.mplot3d import Axes3D

x = np.arange(8)#生成数据
y = np.random.randint(0,10,8)
y2 = y + np.random.randint(0,3,8)
y3 = y2 + np.random.randint(0,3,8)
y4 = y3 + np.random.randint(0,3,8)
y5 = y4 + np.random.randint(0,3,8)

fig = plt.figure() ; ax = Axes3D(fig) #普通方法调用fig和ax
#或者采用ax = fig.add_subplot(111, projection='3d')绘制子图形。
# For those using older versions of matplotlib, 
#change ax = fig.add_subplot(111, projection='3d') to ax = Axes3D(fig).

ax.bar(x,y,0,zdir="y")#数据和作为第三轴的轴:y
ax.bar(x,y2,10,zdir="y")
ax.bar(x,y3,20,zdir="y")
ax.bar(x,y4,30,zdir="y")
ax.bar(x,y5,40,zdir="y")
ax.set_xlabel("X label")#标签
ax.set_ylabel("Y label")
ax.set_zlabel("Z label")
ax.view_init(elev=50)#视角
ax.grid(False)
#3D设置在class mpl_toolkits.mplot3d.axes3d.Axes3D(fig, rect=None, *args, **kwargs)

png

1.4.4 子图网格和多面板绘图

使用第一种方法,先创建一个figure()对象,然后调用其添加一个Axes(add_axes()),然后再用同样的方法创建子Axes。

fig = plt.figure()
ax_1 = fig.add_axes([0.1,0.1,1,1]) #此处定义的是图表的 rect [left, bottom, width, height]
ax_2= fig.add_axes([0.7,0.7,0.3,0.3])
ax_1.plot(np.random.randint(0,10,50),"ro")
ax_2.plot(np.random.randint(0,10,3),"g--")

png

在前面add_subplot()的介绍中,可以在此定义“12X”表示水平划分1个,垂直划分2个,选取第X个。但是,为了精确控制位置,可以使用GridSpec对象,传入一个方格大小,然后对其进行切片调用,作为参数传递给add_subplot(),这样就可以进行精细的子图控制。

fig = plt.figure()
gs = plt.GridSpec(3,3) #新建一个网格
fig.add_subplot(gs[1,:2]).plot(np.random.randint(0,10,10),"o") #不再传入1,2,1,而是传入
s2 = fig.add_subplot(gs[0,:2])
s2.plot(np.random.randint(0,10,10),"o")
fig.add_subplot(gs[2,0]).plot(np.random.randint(0,10,10),"o")
fig.add_subplot(gs[:2,2]).plot(np.random.randint(0,10,10),"o")
fig.add_subplot(gs[2,1:]).plot(np.random.randint(0,10,10),"o")

png

1.5 RC全局样式

matplotlib可以对plt.rc进行设置,所有通过plt接口调用的API都会按照此设置进行处理。可以使用plt.rcdefaults()来回复默认设置。plt.rc一般传入一个字典作为参数,如下所示:

font_set = {
    "family":"Vera",
    "size":10
}
#plt.rc("font",**font_set)
plt.rcdefaults()
matplotlib.matplotlib_fname()
'c:\\python\\python36\\lib\\site-packages\\matplotlib\\mpl-data\\matplotlibrc'

2. pandas内置绘图函数

2.1 线型图line

df1 = DataFrame(np.arange(1000).cumsum().reshape(4,250))
df1 = df1.T
df1.plot(kind="line",title="Hello",figsize=(6,4))
#一般来说,plot内含有一个kind定义图标类型,一个figsize定义大小,
#一个title定义标题,一个legend定义图例,xticks和yticks等,返回一个axes对象
#ax参数可以传递一个定义好的axes,xlim/ylim定义界限、xticks定义刻度值、

png

2.2 柱状图bar/barh

fig, axes = plt.subplots(2, 1)
data = pd.Series(np.random.rand(16), index=list('abcdefghijklmnop'))
data.plot.bar(ax=axes[0], color='k', alpha=0.7)
data.plot.barh(ax=axes[1], color='k', alpha=0.7)

png

需要注意,柱状图要绘制多个图,并且共享一个x轴,一般来讲,最好将这些需要重复绘制的项放在column index下(如下),否则就要手动控制plt,多次调用plot,然后每次绘制x轴偏移一定的距离(很麻烦)。

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
df.plot.bar()

png

df.plot.barh(stacked=True, alpha=0.5)
plt.close('all')

png

2.3 散点图scatter

利用颜色

对于多个变量,散点图可以清楚地看出趋势。散点图不仅仅适用于两个变量(x和y轴),还适用于使用颜色、原点大小进行点的区分,这给予了它很大的表现能力。

s = pd.DataFrame({"a":[1,2,213,21,23],"b":[2,12,11,23,1],"kind":["china","china","us","uk","us"]})
x = s.a
y = s.b


def gix(i):
    key = enumerate(s.kind.unique())
    for x in key:
        if x[1] == i:
            return x[0]
 
plt.scatter(x,y,c=s.kind.apply(gix).gix)
#scatter接受x、y轴数据点的数组,c表示颜色区分,需要和x、y的size相同,
#最好使用较小数字表示,采用文字(在本例中为kind列)不起作用。

png

另外几个例子的图如下,这些例子来自sklearn的经典数据集,可以看到颜色是如何区别了不同的点。

png

png

png

点的大小

对于以下数据集,可以作图,注意这里做了两次的图,因为label标签只能表示一个数据,因此我们需要表示两个数据,就要画两次。这两次的点可能重叠,最好使用半透明alpha。最后,记得要打开legend,否则图例不会显示。

    first_name	last_name	age	female	preTestScore	postTestScore
0	Jason	Miller	42	0	4	25
1	Molly	Jacobson	52	1	24	94
2	Tina	Ali	36	1	31	57
3	Jack	Milner	24	0	2	62
4	Amy	Cooze	73	1	3	70

plt.scatter(data[data.female == 1].preTestScore,
data[data.female == 1].postTestScore,s=data.postTestScore*4.5,label="Female",alpha=0.9)

plt.scatter(data[data.female != 1].preTestScore,
data[data.female != 1].postTestScore,s=data.postTestScore*4.5,label="Male",alpha=0.9)
plt.legend(loc=2)
plt.xlabel("preTestScore")
plt.ylabel("preTestScore")

png

另外一个例子的图如下,注意其使用了50%的半透明,因此数据可以很好的区分。

png

2.4 其他图类

比如像 plot.pie() 为饼图,接受一个size list,还有对应的labels list, plot.hist() 为概率分布图(不要和bar搞混)。plot.boxplot() 为箱图。boxplot接受一个[alist,blist],分别绘制在x轴的两个类别中,每个list表示一箱。

pandas内置的绘图基本上都是调用matplotlib,不过做了一定的整合,绘制起来较为方便,但是不够灵活。通过DF/S.plot即可绘制。对于plot.bar()这样可以类似于axes.bar()这样指定类型。pandans对于图像可视化章节有详细的示例,因此在此不再展开。对于matplotlib,官方提供了很多绘图自定类,通过组合底层API,可以绘制不同样式的图,详情参考matplotlib的示例库,真的很漂亮。

————————————————————————————————————

更新日志

2018-02-28 完成《利用Python进行数据分析》阅读和笔记

2018-03-25 完成笔记补充和目录整理

2018-03-28 笔记补充和目录整理,添加了hist、pie、scatter表的说明。

搜索

    Post Directory