《利用python进行数据分析》读书笔记--第八章 绘图和可视化

python有许多可视化工具,本书主要讲解matplotlib。matplotlib是用于创建出版质量图表的桌面绘图包(主要是2D方面)。matplotlib的目的是为了构建一个MATLAB式的绘图接口。本书中的大部分图都是用它生成的。除了图形界面显示,还可以把图片保存为pdf、svg、jpg、png、gif等形式。

1、matplotlib API入门

Ipython可以用close()关闭界面。

Figure和Subplot

matplotlib的图像都位于Figure对象中。用plt.figure创建一个新的Figure。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
'''
#plt.plot(np.arange(10))
fig = plt.figure()
#plt.show()
#figsize 有一些重要的选项,特别是figsize,规定的是图片保存到磁盘时具有一定大小的纵横比。
#plt.gcf()即可得到当前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(50).cumsum(),'k--')


#fig.add_subplot 返回的对象是AxesSubplot对象,下面调用就可以了
_ = 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))
plt.show()
'''
#由于Figure 和 subplot是一件非常常见的任务,于是出现了更为方便的方法(plt.subplots ),它可以创建一个新的Figure,
#并返回一个含有已创建的subplot对象的Numpy数组
fig,axes = plt.subplots(2,3)

#print fig
print axes[0][0]
#axes[0][0].hist(np.random.randn(100),bins = 20,color = 'k',alpha = 0.3)
plt.show()
#这是非常实用的,因为可以轻松地对axes数组进行索引,就好像一个是一个二维数组一样,例如
#axes[0,1].还可以通过sharex和sharey指定subplot具有相同的x轴和y轴。在比较相同范围的数据时,这是
#非常实用的,否则matplotlib会自动缩放各图表的界限。

看一下subplots的作用:

image

pyplot.subplots的选项还有:

image

上面的**fig_k可以有很多的参数,文档中有更多的内容。

调整subplot周围的间距

默认情况下,matplotlib会在subplot外围留下一定的边距,并在subplot之间留下一定的间距。间距和图像的高度和宽度有关,会自动调整。利用Figure的subplots——adjust方法可以修改间距,因此,它是一个顶级函数。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

subplots_adjust(left = None,bottom = None,right = None,top = None,wspace = None,hspace = None)
#wspace和space用于控制宽度和高度的百分比,可以用做subplot之间的间距,下面是个例子:
'''
fig,ax = plt.subplots(2,2,sharex = True,sharey = True)
for i in range(2):
     for j in range(2):
          ax[i,j].hist(np.random.randn(500),bins = 50,color = 'k',alpha = 0.5)
plt.subplots_adjust(wspace = 0.5,hspace = 0.5)
plt.show()
#matplotlib不会检查标签的重叠(确实是这样)。
# -*- encoding: UTF-8 -*- 
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt



fig,ax = plt.subplots(2,2)

#cecece是白色……
ax[0,0].plot(np.arange(10),linestyle = '--',color = '#CECECE')

#线上面还可以添加一些标记(marker),以强调实际的数据点。由于matplotlib创建的是连续的线形图,因此有时可能不太容易看到真实点的位置,标记可以放到格式字符串中,但是标记类型和线性必须在颜色的后面
ax[0,1].plot(np.random.randn(30).cumsum(),'ko--')
ax[1,0].plot(np.random.randn(30).cumsum(),color = 'k',linestyle = '--',marker = 'o')
#在线型图中,非实际数据点默认是按照线性插值的,可以通过drawstyle选项修改这一点。
data = np.random.randn(30).cumsum()
ax[1,1].plot(data,'ko--')
ax[1,1].plot(data,'k--',drawstyle = 'steps-post')
plt.show()

注意上面的drawstyle选项可以规定点与点之间的连接方式,或者说是插值方式,结果为:

image

设置标题、轴标签、刻度以及刻度标签

# -*- encoding: UTF-8 -*- 
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import numpy.random as npr

fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(npr.randn(1000).cumsum())

#要想修改x的刻度,最简单的方法就是使用set_xticks和set_xticklabels.前者告诉matplotlib将
#刻度放在数据范围中的哪些位置,默认情况下,这些位置就是刻度标签。但是可以使用set_xticklabels将#任何其他的值用作标签
ticks = ax.set_xticks([0,250,500,700,900,1000])
#下面的totation是规定旋转角度
labels = ax.set_xticklabels(['a','b','c','d','e','f'],rotation = 30,fontsize = 'small')
#可以为x轴设置名称
ax.set_xlabel('Stages')

plt.show()

image

图例

# -*- encoding: UTF-8 -*- 
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import numpy.random as npr
from datetime import datetime

#添加图例
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(npr.randn(1000).cumsum(),'k',label = 'one')
ax.plot(npr.randn(1000).cumsum(),'k--',label = 'two')
ax.plot(npr.randn(1000).cumsum(),'k.',label = 'three')
ax.legend(loc = 'best')
plt.show()

image

注解与绘图

# -*- encoding: UTF-8 -*- 
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import numpy.random as npr
from datetime import datetime


fig = plt.figure()
ax = fig.add_subplot(1,1,1)

data = pd.read_csv('E:\\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 = (date,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 finacial crisis')
plt.show()
#更多关于注解的示例,请看文档

#图形的绘制要麻烦些,有一些常见的图形的对象,这些对象成为块(patch)
#如Rectangle 和 Circle,完整的块位于matplotlib.patches
#要绘制图形,需要创建一个块对象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.show()

image

image

将图表保存到文件

# -*- encoding: UTF-8 -*- 
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import numpy.random as npr
from datetime import datetime
from io import StringIO

#将图标保存到文件
#savefig函数可以保存图形文件,不同的扩展名保存为不同的格式
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)

#注意下面的dpi(每英寸点数)和bbox_inches(可以剪除当前图标周围的空白部分)(确实有效)
#plt.savefig('pic.jpg',dpi = 100,bbox_inches = 'tight')

#不一定save到文件中,也可以写入任何文件型对象,比如StringIO:

buffer = StringIO()
plt.savefig(buffer)
plot_data = buffer.getvalue()

#这对Web上提供动态生成的图片是很实用的

#plt.show()

savefig的一些选项:

image

matplotlib配置

matplotlib的一些属性是可以设置的,比如图像大小、subplot边距、配色方案、字体大小、网格类型等。有两种方式进行操作。第一种是Python变成方式,即利用rc方法。比如:

plt.rc('figure',figsize = (10,10))

rc的第一个参数是希望自定义的对象,比如‘figure’、‘axes’、‘xtick’、‘ytick’、‘grid’、‘legend’等。其后可以跟上一系列的关键字参数。最简单的就是写成一个字典:

font_options = {'family':'monospace',
                         'weight':'bold',
                         'size':'small'}
plt.rc('font',**font_options)

matplotlibrc是配置文件,定义好以后每次加载就会用设置的参数。

2、pandas中的绘图函数

matplotlib是一种比较低级的工具,需要将各种组件组合好:数据展示(线型图、柱状图等)、图例、标题、刻度标签以及注解。这是因为制作一张图表一般需要用到多个对象。在pandas中,会省事不少。pandas能够利用DataFrame的对象特点创建标准图表的高级绘图方法。作者说pandas在线文档时最好的学习工具,书上的代码可能过时了。

线型图

#-*- encoding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pandas import Series,DataFrame

s = Series(np.random.randn(10).cumsum(),index = np.arange(0,100,10))
#该Series对象的索引会被传给matplotlib,并绘制X轴。
#可以用use_index = False 禁用该功能
s.plot(use_index = False)

#X轴的刻度和界限可以通过xticks和xlim选项进行调节,Y轴通过xticks和ylim调节

plt.show()

#pandas的大部分方法都有一个可选的ax参数,可以是一个subplot对象。这可以
#使在网格中更为灵活地处理subplot的位置。
#DataFrame的plot方法会在一个subplot中为各列绘制线型图,并自动添加图例
df = DataFrame(np.random.randn(10,4).cumsum(0),
    columns = ['A','B','C','D'],
    index = np.arange(0,100,10))
df.plot()
plt.show()

image

image

下面把参数贴一下:

image

image

DataFrame还有一些对列进行处理的参数:

image

自下面开始就有一些专门的图形,绘制的时候可以与R语言进行对比:http://www.cnblogs.com/batteryhp/p/4733474.html

柱状图

#-*- encoding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pandas import Series,DataFrame


#生成的线形图中代码加上kind = ‘bar’(垂直柱图) 或者 (水平)kind = ‘barh’(水平柱图)
#Series和DataFrame的索引被用作X(bar)或者Y(barh)的刻度

fig,axes = plt.subplots(2,1)
data = Series(np.random.randn(16),index = list('abcdefghijklmnop'))

data.plot(kind = 'barh',ax = axes[0],color = 'k',alpha = 0.7)
data.plot(kind = 'bar',ax = axes[1],color = 'k',alpha = 0.7)

#DataFrame会按照行对数据进行分组
df = DataFrame(np.random.randn(6,4),index = ['one','two','three','four','five','six'],
    columns = pd.Index(['A','B','C','D'],name = 'Genus')) 
#注意这里的name会被用作图例的标题,因为,这本来就是列的名字
print df
df.plot(kind = 'bar')
plt.show()
#这里的stacked是标明画累计柱图
df.plot(kind = 'bar',stacked = True,alpha = 0.5)
plt.show()

#Series的value_counts可以用来显示Series中各值的频数(实验证明)
s = Series([1,2,2,3,4,4,4,5,5,5])
s.value_counts().plot(kind = 'bar')
plt.show()

image

image

image

image

下面看一个例子:

#-*- encoding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pandas import Series,DataFrame

#下面是一个例子:做一张堆积柱状图来显示每天各种聚会规模的数据点百分比
tips = pd.read_csv('E:\\tips.csv')
party_counts = pd.crosstab(tips.day,tips.size)
print party_counts
party_counts = party_counts.ix[:,2:5]
#然后进行归一化是各行和为1
party_pcts = party_counts.div(party_counts.sum(1).astype(float),axis = 0)
print party_pcts
party_pcts.plot(kind = 'bar',stacked = True)
plt.show()

image

直方图和密度图

#-*- encoding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pandas import Series,DataFrame
#绘制小费百分比直方图
tips = pd.read_csv('E:\\tips.csv')
tips['tip_pct'] = tips['tip'] / tips['total_bill']
#bins规定一共分多少个组
tips['tip_pct'].hist(bins = 50)
plt.show()

#与此相关的是密度图:他是通过计算“可能会产生观测数据的连续概率分布的估计”
#而产生的。一般的过程将该分布金思维一组核(诸如正态之类的较为简单的分布)。
#此时的密度图称为KDE图。kind = ‘kde’即可。
tips['tip_pct'].plot(kind = 'kde')
plt.show()

#显然,直方图和密度图经常会在一起出现
comp1 = np.random.normal(0,1,size = 200)
comp2 = np.random.normal(10,2,size = 200)
values = Series(np.concatenate([comp1,comp2]))
print values
values.hist(bins = 100,alpha = 0.3,color = 'k',normed = True)
values.plot(kind = 'kde',style = 'k--')
plt.show()

image

image

image

散布图

散布图(scantter plot)是观察两个一维数据序列之间的关系的有效手段。matplotlib中的scantter方法是绘制散布图的主要方法。

#-*- encoding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pandas import Series,DataFrame

#下面加载macrodata中的数据集,选择其中几列并计算对数差
macro = pd.read_csv('E:\\macrodata.csv')
data = macro[['cpi','m1','tbilrate','unemp']]
#这里的diff函数是用来计算相邻两数只差,对每一列,后一个数减前一个数
trans_data = np.log(data).diff().dropna()
#print np.log(data).head()
#print np.log(data).diff().head()
print trans_data.head()

plt.scatter(trans_data['m1'],trans_data['unemp'])
plt.title('Changes in log %s vs. log %s'%('m1','unemp'))
plt.show()
#画散布图矩阵式很有意义的pandas提供了scantter_matrix函数来创建散步矩阵
#关于 diagonal 参数,是为了不让对角线上的图形(自己和自己的散布图)显示为一条直线而设置的关于这种数据的某些图形显示
#比如 diagonal = 'kde'就是画密度图且核为kde,若diagonal='hist',则为直方图
pd.scatter_matrix(trans_data,diagonal = 'kde',color = 'k',alpha = 0.3)
pd.scatter_matrix(trans_data,diagonal = 'hist',color = 'k',alpha = 0.3)
plt.show()

image

image

image

绘制地图:图形化显示海地地震危机数据

这是一个例子。

#-*- encoding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pandas import Series,DataFrame
from mpl_toolkits.basemap import Basemap

#下面的例子应该是比较综合的
data = pd.read_csv('E:\\Haiti.csv')
#print data
#下面处理一下数据,下面的为日期,纬度、经度
#print data[['INCIDENT DATE','LATITUDE','LONGITUDE']][:10]
#print data['CATEGORY'][:6]   #这些代表消息的类型
#数据中很有可能有异常值、缺失值,下面看一下
#print data.describe()
#清除错误信息并移除缺失分类信息是“一件简单的事情”
data = data[(data.LATITUDE > 18) & (data.LATITUDE < 20) & (data.LONGITUDE > -75) &
           (data.LONGITUDE < -70) & data.CATEGORY.notnull()]

#我们想根据分类对数据做一些分析或者图形化工作,但是各个分类字段中可能含有多个分类。此外,各个分类信息
#不仅有一个编码,还有一个英语(法语)名称。因此需要对数据进行规整化处理。下面编写两(三)个
#函数,一个用于获取所有分类的列表,一个用于将各个分类信息拆分为编码和英语明名称

#sptrip 是删除空白字符,'\n'等;注意作者这种隐式循环写法
def to_cat_list(catstr):
    stripped = (x.strip() for x in catstr.split(','))
    return [x for x in stripped if x]
def get_all_categoties(cat_series):
    cat_sets = (set(to_cat_list(x)) for x in cat_series)
    return sorted(set.union(*cat_sets))
def get_english(cat):
    code,names = cat.split('.')
    if '|' in names:
        names = names.split('|')[1]
    return code,names.strip()

#下面进行一下ceshi
#print get_english('2.Urgences logistiques | Vital Lines')
#接下来做了一个将编码跟名称映射起来的字典,这是因为我们等会要用编码进行分析。
#下面将所有组合弄出来
all_cats = get_all_categoties(data.CATEGORY)
#print data.CATEGORY[:10]
#print all_cats
#生成器表达式
#生成字典
english_mapping = dict(get_english(x) for x in all_cats)
#print english_mapping['2a']
#print english_mapping['6c']
#根据分类选取记录的方式有很多,其中之一就是添加指标(或者哑变量)列,每个分类一列。
#为此,首先抽取出唯一的分类编码,并构造一个全零DataFrame(列为分类编码,索引跟data的索引一样)
def get_code(seq):
    return [x.split('.')[0] for x in seq if x]
#下面是将所有的key取出来
all_codes = get_code(all_cats)
#print all_codes
code_index = pd.Index(np.unique(all_codes))
#print code_index
dummy_frame = DataFrame(np.zeros((len(data),len(code_index))),index = data.index,columns = code_index)
#print len(data)
#print dummy_frame.ix[:,:6]
#下面将各行中适当的项设置为1,然后再与data进行连接:

for row,cat in zip(data.index,data.CATEGORY):
    codes = get_code(to_cat_list(cat))
    dummy_frame.ix[row,codes] = 1
#添加前缀,并且合并一下
data = data.join(dummy_frame.add_prefix('category_'))
#print data
#接下来开始画图吧,我们希望把数据绘制在海地的地图上。basemap数据集是matplotloib的一个插件
#使得能够用Python在地图上绘制2D数据。basemap提供了许多不同的地球投影以及一种将地球上的经纬度
#坐标投影转换为二维matplotlib图的方式。
#“经过一遍又一遍的尝试”,作者编写了下面的函数,绘制出一张简单的黑白地图。

def basic_haiti_map(ax = None,lllat = 17.25,urlat = 20.25,lllon = -75,urlon = -71):
    #创建极球面投影的Basemap实例。
    m  = Basemap(ax = ax,projection = 'stere',
        lon_0 = (urlon + lllon) / 2,
        lat_0 = (urlat + lllat) / 2,
        llcrnrlat = lllat,urcrnrlat = urlat,
        llcrnrlon = lllon,urcrnrlon = urlon,
        resolution = 'f' )

由于window下安装geos不成功,这部分等ubuntu装好了再接着写。

4、Python图形化工具生态系统

介绍几个其他的绘图工具。

Chaco

特点:静态图 + 交互图形,非常适合用复杂的图形化方法表示数据的内部关系。对交互支持的好的多,交互式GUI是个不错选择。

mayavi

这是一个基于开源C++图形库VTK的3D图形工具包。可以集成到Ipython交互使用。

其他库

其他库或者应用还有:PyQwt、Veusz、gnuplotpy、biggles等,大部库都在向基于Web的技术发展,并逐渐远离桌面图形技术。

图形化工具的未来

基于Web技术(如Javascript)的图形化是必然的发展趋势,现在已经有不少了,higncharts等。

 

 

posted @ 2015-12-07 12:49  司空格子Ored  阅读(3617)  评论(3编辑  收藏  举报