您的位置:首页 > 编程语言 > Python开发

python数据可视化(matplotlib,pandas绘图,直方图,散点图,柱状图,折线图,箱线图)

2016-10-09 15:58 896 查看




数据可视化


数据可视化对于数据描述以及探索性分析至关重,恰当的统计图表可以更有效的传递数据信息。在 Python 中已经有很多数据可视化方面的第三方程序包,例如:

matplotlib
Chaco
PyX
Bokeh
本节,我们将重点学习 matplotlib 的基础绘图功能以及 pandas 的高级可视化功能,这也是现在最为常用、最为稳健,同时功能也非常丰富的数据可视化的解决方案。


课程目标:

熟悉几种常用的数据可视化展示方式
掌握散点图、直方图、箱线图等图像绘制方法

In [1]:

# 导入一些需要用到的包
import pandas as pd
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
%matplotlib inline

# 配置 pandas
pd.set_option('display.notebook_repr_html', False)
pd.set_option('display.max_columns', 20)
pd.set_option('display.max_rows', 25)



Matplotlib 绘图

In [2]:

plt.plot(np.random.normal(size=100), np.random.normal(size=100), 'ro')


Out[2]:

[<matplotlib.lines.Line2D at 0x7f27e30e08d0>]


上图绘制了两组来自正态分布的随机数的散点图,“ro”是一个缩写参数,表示用“红色圆圈”绘图。这种绘图很简单,我们可以进一步通过拆分绘图流程来得到更加复杂的图像。



In [3]:

with mpl.rc_context(rc={'font.family': 'serif', 'font.weight': 'bold', 'font.size': 8}):
fig = plt.figure(figsize=(6,3))
ax1 = fig.add_subplot(121)
ax1.set_xlabel('some random numbers')
ax1.set_ylabel('more random numbers')
ax1.set_title("Random scatterplot")
plt.plot(np.random.normal(size=100), np.random.normal(size=100), 'r.')
ax2 = fig.add_subplot(122)
plt.hist(np.random.normal(size=100), bins=15)
ax2.set_xlabel('sample')
ax2.set_ylabel('cumulative sum')
ax2.set_title("Normal distrubution")
plt.tight_layout()
plt.savefig("normalvars.png", dpi=150)




fig = plt.figure(figsize=(6,3)),定义一个长为6宽为3英寸的画布
ax1 = fig.add_subplot(121),将画布分为1行2列,从左到右从上到下取第1块

matplotlib 是一个相对初级的绘图包,直接输出的结果布局并不是十分美观,但是使用者可以通过很多种灵活的方式来调试输出的图像。


Pandas 绘图

相对于 matplotlib 来讲,Pandas 支持 DataFrame 和 Series 两种比较高级的数据结构,可以想象得到用其绘制出图像的形式。

In [4]:

normals = pd.Series(np.random.normal(size=10))
normals.plot()


Out[4]:

<matplotlib.axes._subplots.AxesSubplot at 0x7f27e30f23d0>




可以发现,默认绘制的是折线图,并且有灰色的网格线。当然,这些都可以修改。

In [5]:

normals.cumsum().plot(grid=False)


Out[5]:

<matplotlib.axes._subplots.AxesSubplot at 0x7f27df5a6090>



对于 DataFrame 结构的数据,也可以进行类似的操作:

In [6]:

variables = pd.DataFrame({'normal': np.random.normal(size=100),
'gamma': np.random.gamma(1, size=100),
'poisson': np.random.poisson(size=100)})
variables.cumsum(0).plot()


Out[6]:

<matplotlib.axes._subplots.AxesSubplot at 0x7f27df4aa410>



Pandas 还支持这样的操作:利用参数“subplots”绘制 DataFrame 中每个序列对应的子图

In [7]:

variables.cumsum(0).plot(subplots=True)


Out[7]:

array([<matplotlib.axes._subplots.AxesSubplot object at 0x7f27df48c750>,
<matplotlib.axes._subplots.AxesSubplot object at 0x7f27df321990>,
<matplotlib.axes._subplots.AxesSubplot object at 0x7f27df2a66d0>], dtype=object)



或者,我们也可以绘制双坐标轴,将某些序列用次坐标轴展示,这样可以展示更多细节并且图像中没有过多空白。

In [8]:

variables.cumsum(0).plot(secondary_y='normal')


Out[8]:

<matplotlib.axes._subplots.AxesSubplot at 0x7f27df4b4910>



如果你有更多的绘图要求,也可以直接利用 matplotlib 的 “subplots” 方法,手动设置图像位置:

In [9]:

fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(12, 4))
for i,var in enumerate(['normal','gamma','poisson']):
variables[var].cumsum(0).plot(ax=axes[i], title=var)
axes[0].set_ylabel('cumulative sum')


Out[9]:

<matplotlib.text.Text at 0x7f27deffc3d0>




柱状图

我们常用柱状图来展示和对比可测数据,比如计数或流量统计。在 Pandas 中,我们通过设置“plot”方法“kind='bar”参数,即可绘制柱状图。 下面我们以泰坦尼克数据集为例,进行相关绘图说明。

In [10]:

titanic = pd.read_excel("data/titanic.xls", "titanic")
titanic.head()


Out[10]:

pclass  survived                                             name     sex  \
0       1         1                    Allen, Miss. Elisabeth Walton  female
1       1         1                   Allison, Master. Hudson Trevor    male
2       1         0                     Allison, Miss. Helen Loraine  female
3       1         0             Allison, Mr. Hudson Joshua Creighton    male
4       1         0  Allison, Mrs. Hudson J C (Bessie Waldo Daniels)  female

age  sibsp  parch  ticket      fare    cabin embarked boat   body  \
0  29.0000      0      0   24160  211.3375       B5        S    2    NaN
1   0.9167      1      2  113781  151.5500  C22 C26        S   11    NaN
2   2.0000      1      2  113781  151.5500  C22 C26        S  NaN    NaN
3  30.0000      1      2  113781  151.5500  C22 C26        S  NaN  135.0
4  25.0000      1      2  113781  151.5500  C22 C26        S  NaN    NaN

home.dest
0                     St Louis, MO
1  Montreal, PQ / Chesterville, ON
2  Montreal, PQ / Chesterville, ON
3  Montreal, PQ / Chesterville, ON
4  Montreal, PQ / Chesterville, ON


In [11]:

titanic.groupby('pclass').survived.sum().plot(kind='bar')


Out[11]:

<matplotlib.axes._subplots.AxesSubplot at 0x7f27deaecc50>



In [12]:

titanic.groupby(['sex','pclass']).survived.sum().plot(kind='barh')


Out[12]:

<matplotlib.axes._subplots.AxesSubplot at 0x7f27deb28250>



In [13]:

death_counts = pd.crosstab([titanic.pclass, titanic.sex], titanic.survived.astype(bool))
death_counts.plot(kind='bar', stacked=True, color=['black','gold'], grid=False)


Out[13]:

<matplotlib.axes._subplots.AxesSubplot at 0x7f27dea6d290>



还可以通过除以该组的总人数来对比不同组之间的存活率

In [14]:

death_counts.div(death_counts.sum(1).astype(float), axis=0).plot(kind='barh', stacked=True, color=['black','gold'])


Out[14]:

<matplotlib.axes._subplots.AxesSubplot at 0x7f27de9847d0>




直方图

在数据分析之前,频数/率可以帮助我们了解数据的分布情况。直方图是一种展示数据频数/率的特殊的柱状图,也就是说,y 轴是频数/率的度量,既可以是频数(计数)也可以是频率(占比)。比如,我们想知道泰坦尼克船票价格的分布情况:

In [15]:

titanic.fare.hist(grid=False)


Out[15]:

<matplotlib.axes._subplots.AxesSubplot at 0x7f27de7a5190>



“hist”方法默认将连续票价划分为 10 个区间,我们可以通过设置参数“bins”来设定区间个数(或者说设定直方图的宽度):

In [16]:

titanic.fare.hist(bins=30)


Out[16]:

<matplotlib.axes._subplots.AxesSubplot at 0x7f27de7eca50>



有很多种计算最优区间个数的算法,且都会一定程度的随观测值个数变化。

In [17]:

sturges = lambda n: int(np.log2(n) + 1)
square_root = lambda n: int(np.sqrt(n))
from scipy.stats import kurtosis
doanes = lambda data: int(1 + np.log(len(data)) + np.log(1 + kurtosis(data) * (len(data) / 6.) ** 0.5))

n = len(titanic)
sturges(n), square_root(n), doanes(titanic.fare.dropna())


Out[17]:

(11, 36, 14)


In [18]:

titanic.fare.hist(bins=doanes(titanic.fare.dropna()))


Out[18]:

<matplotlib.axes._subplots.AxesSubplot at 0x7f27de69bc10>


和直方图类似,密度图也描述了数据的分布情况,可以看成将直方图区间无限细分后形成的平滑曲线。设置“plot”方法的参数 kind='kde',即可绘制密度图,其中 'kde' 表示“kernel density estimate”。

In [19]:

titanic.fare.dropna().plot(kind='kde', xlim=(0,600))


Out[19]:

<matplotlib.axes._subplots.AxesSubplot at 0x7f27d810a950>



通常,直方图和密度图会绘制在同一张图里:

In [20]:

titanic.fare.hist(bins=doanes(titanic.fare.dropna()), normed=True, color='lightseagreen')
titanic.fare.dropna().plot(kind='kde', xlim=(0,600), style='r--')


Out[20]:

<matplotlib.axes._subplots.AxesSubplot at 0x7f27d8103350>



这里我们需要通过设置参数“normed=True”将直方图标准化处理,因为概率密度分布是标准化的,


箱线图

另外一种展示数据分布的可视化图形是箱线图,主要展示分位数,具体包括上四分位数、下四分位数、中位数以及上下5%的极值。

In [21]:

titanic.boxplot(column='fare', by='pclass', grid=False)


Out[21]:

<matplotlib.axes._subplots.AxesSubplot at 0x7f27d7fc8590>


可以将箱线图看成数据的分布图,图中“+”的点表示异常值。

可以将实际数据同时展示在箱线图中来丰富图像信息,这种做法比较适合观测值较少的数据集。

In [22]:

bp = titanic.boxplot(column='age', by='pclass', grid=False)
for i in [1,2,3]:
y = titanic.age[titanic.pclass==i].dropna()
# 通过设置随机值,使观测值在x轴方向不重叠
x = np.random.normal(i, 0.04, size=len(y))
plt.plot(x, y, 'r.', alpha=0.2)


当数据分布非常稠密时,有以下两种方式来增强图形的可读性: 1、降低透明度,使数据点半透明 2、增加数据点 x 轴方向的随机距离,减少数据点之间的重叠

一个和箱线图相近但远不及箱线图的图像叫做“dynamite”图,本质上就是给出标准偏差的柱状图

In [23]:

titanic.groupby('pclass')['fare'].mean().plot(kind='bar', yerr=titanic.groupby('pclass')['fare'].std())


Out[23]:

<matplotlib.axes._subplots.AxesSubplot at 0x7f27d7d22490>


为什么我们比较少用这种绘图方式呢?
柱状图覆盖的区域并没有实际含义
信息含量低,只反映了6个数值:三个均值和三个标准差
掩藏了原始数据的真实信息
相比之下,箱线图是更好的选择。

In [24]:

data1 = [150, 155, 175, 200, 245, 255, 395, 300, 305, 320, 375, 400, 420, 430, 440]
data2 = [225, 380]

fake_data = pd.DataFrame([data1, data2]).transpose()
p = fake_data.mean().plot(kind='bar', yerr=fake_data.std(), grid=False)


In [25]:

fake_data = pd.DataFrame([data1, data2]).transpose()
p = fake_data.mean().plot(kind='bar', yerr=fake_data.std(), grid=False)
x1, x2 = p.xaxis.get_majorticklocs()
plt.plot(np.random.normal(x1, 0.01, size=len(data1)), data1, 'ro')
plt.plot([x2]*len(data2), data2, 'ro')


Out[25]:

[<matplotlib.lines.Line2D at 0x7f27d7c2c910>]



练习

用泰坦尼克数据集,绘制幸存者和死亡者年龄的密度图


散点图

下面以棒球数据集为例,讲解如何绘制散点图。

In [26]:

baseball = pd.read_csv("data/baseball.csv")
baseball.head()


Out[26]:

id     player  year  stint team  lg   g  ab  r   h  ...   rbi   sb   cs  \
0  88641  womacto01  2006      2  CHN  NL  19  50  6  14  ...   2.0  1.0  1.0
1  88643  schilcu01  2006      1  BOS  AL  31   2  0   1  ...   0.0  0.0  0.0
2  88645  myersmi01  2006      1  NYA  AL  62   0  0   0  ...   0.0  0.0  0.0
3  88649  helliri01  2006      1  MIL  NL  20   3  0   0  ...   0.0  0.0  0.0
4  88650  johnsra05  2006      1  NYA  AL  33   6  0   1  ...   0.0  0.0  0.0

bb   so  ibb  hbp   sh   sf  gidp
0   4  4.0  0.0  0.0  3.0  0.0   0.0
1   0  1.0  0.0  0.0  0.0  0.0   0.0
2   0  0.0  0.0  0.0  0.0  0.0   0.0
3   0  2.0  0.0  0.0  0.0  0.0   0.0
4   0  4.0  0.0  0.0  0.0  0.0   0.0

[5 rows x 23 columns]


在探索变量之间的关系=时,可以绘制散点图。Series 或者 DataFrame 结构的数据并没有相应的绘制散点图的方法,必须使用 matplotlib 的“scatter”函数。

In [27]:

plt.scatter(baseball.ab, baseball.h)
plt.xlim(0, 700); plt.ylim(0, 200)


Out[27]:

(0, 200)


可以通过赋予样本点不同的大小和颜色来展示更多的信息。

In [28]:

plt.scatter(baseball.ab, baseball.h, s=baseball.hr*10, alpha=0.5)
plt.xlim(0, 700); plt.ylim(0, 200)


Out[28]:

(0, 200)


In [29]:

plt.scatter(baseball.ab, baseball.h, c=baseball.hr, s=40, cmap='hot')
plt.xlim(0, 700); plt.ylim(0, 200);


可以使用“scatter_matrix”函数同时展示多个变量之间的散点图,最终会生成一个两两对应的散点图矩阵,可以选择在对角线展示直方图或者密度图。

In [30]:

_ = pd.scatter_matrix(baseball.loc[:,'r':'sb'], figsize=(12,8), diagonal='kde')



栅栏(trellis)图形

最近 Pandas 增加了利用 GofG 方法的绘图函数,使得栅栏图的绘制变得十分简单。栅栏图可以用于展示两个变量在不同条件下的关系,实现了利用平面图形展示高于两个维度的信息。

下面利用泰坦尼克数据集绘制一个栅栏图来同时展示 4 个变量之间的关系,有以下 4 个步骤: 1、创建一个仅和数据集中两个变量有关的“RPlot” 2、利用“passenger class ”和“sex”两个变量的不同取值最为条件区分,添加网格 3、增加用于可视化的实际数据 4、可视化展示

In [31]:

from pandas.tools.rplot import *

titanic = titanic[titanic.age.notnull() & titanic.fare.notnull()]

tp = RPlot(titanic, x='age')
tp.add(TrellisGrid(['pclass', 'sex']))
tp.add(GeomDensity())
_ = tp.render(plt.gcf())


/home/datartisan/.pyenv/versions/2.7.12/envs/datacademy-lesson-27/lib/python2.7/site-packages/ipykernel/__main__.py:1: FutureWarning:
The rplot trellis plotting interface is deprecated and will be removed in a future version. We refer to external packages like seaborn for similar but more refined functionality.

See our docs http://pandas.pydata.org/pandas-docs/stable/visualization.html#rplot for some example how to convert your existing code to these packages.
if __name__ == '__main__':


利用张力障碍数据集,将“age”和“twstrs”两个变量之间的关系作为“week”和“treat”两个变量的函数,通过绘制散点图,我们可以同时探索不同情境下二者之间的关系,并进行拟合:

In [32]:

cdystonia = pd.read_csv("data/cdystonia.csv", index_col=None)
cdystonia.head()


Out[32]:

patient  obs  week  site  id  treat  age sex  twstrs
0        1    1     0     1   1  5000U   65   F      32
1        1    2     2     1   1  5000U   65   F      30
2        1    3     4     1   1  5000U   65   F      24
3        1    4     8     1   1  5000U   65   F      37
4        1    5    12     1   1  5000U   65   F      39


In [33]:

plt.figure(figsize=(12,12))
bbp = RPlot(cdystonia, x='age', y='twstrs')
bbp.add(TrellisGrid(['week', 'treat']))
bbp.add(GeomScatter())
bbp.add(GeomPolyFit(degree=2))
_ = bbp.render(plt.gcf())


RPlot 不仅仅可以被用来绘制栅栏图,也可以通过使用不同的颜色、大小、形状等将多个变量绘制在一起。

In [34]:

cdystonia['site'] = cdystonia.site.astype(float)


In [35]:

plt.figure(figsize=(6,6))
cp = RPlot(cdystonia, x='age', y='twstrs')
cp.add(GeomPoint(colour=ScaleGradient('site', colour1=(1.0, 1.0, 0.5), colour2=(1.0, 0.0, 0.0)),
size=ScaleSize('week', min_size=10.0, max_size=200.0),
shape=ScaleShape('treat')))
_ = cp.render(plt.gcf())


/home/datartisan/.pyenv/versions/2.7.12/envs/datacademy-lesson-27/lib/python2.7/site-packages/pandas/tools/rplot.py:167: FutureWarning: iget(i) is deprecated. Please use .iloc[i] or .iat[i]
x = data[self.column].iget(index)
/home/datartisan/.pyenv/versions/2.7.12/envs/datacademy-lesson-27/lib/python2.7/site-packages/pandas/tools/rplot.py:66: FutureWarning: iget(i) is deprecated. Please use .iloc[i] or .iat[i]
x = data[self.column].iget(index)
/home/datartisan/.pyenv/versions/2.7.12/envs/datacademy-lesson-27/lib/python2.7/site-packages/pandas/tools/rplot.py:208: FutureWarning: iget(i) is deprecated. Please use .iloc[i] or .iat[i]
x = data[self.column].iget(index)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: