python可视化(5-1)高级接口(pandas相关接口)

聊一下数据

马云说:“我们是通过卖东西收集数据,数据是阿里最值钱的财富。”他重新定义阿里并说阿里集团本质上是一家扩大数据价值的公司,同时他提到计算是生产力,互联网是生产关系,如果未来不运用数据,可能要比不用电更加可怕,未来计算的能力和数据就像今天的石油和电一样。可视化作为数据挖掘/展示的一个方向,已经有了很长时间的发展,在论述问题方面,“字不如表,表不如图”的理念已在各行各业获得认可,但归根至底,他的土壤还是数据。所以有必要在正文开始前,花一定的时间讨论下。

数据库

从数据库的角度看,目前存储数据主要包括两种类型的数据库,一种是SQL型数据库,又被称为特征型数据库,代表的有Oracle,Mysql等,他的存储特征为行列,行代表样本,列代表特征,数据呈栅格状;另一种是NoSQL数据库,代表的有Redis,MongoDB等,存储特征为键值(key-value),即python的字典数据结构,数据长短不一,以一条一条的形式存储在数据库中,当每条数据的键(key)均相同时,NoSQL数据库又可以快速且容易地转为SQL数据库,这也是为什么他的全称是“not only sql”的原因了,整体来看,NoSQL数据库自由度更大,在大数据(多维度,非结构)时代似乎更容易成为主角。但是,大多数非互联网从业人员接触到的几乎都是结构化的SQL型数据库,就像Excel,他的表对象(sheet)就是结构型数据库。

数据结构

大多数的数据存储形式,可以分为长数据和宽数据(《R in action》),举个例子:

表1 长数据

姓名课程分数
张三语文85
张三数学86
张三英语84
李四语文87
李四数学86
李四英语85

表2 宽数据

姓名语文数学英语
张三858684
李四878685

表2与表1都表达了相同的信息,但是表的结构却有很大的差异,相比于表1,表2把分数这个特征进行了交叉(或者Excel说的数据透视),因为行列交叉的数量为1,所以聚合函数可以是均值,最大值,最小值,求和等。作为展示而言,表2更合适,一目了然;但是,对于分析而言,几乎所有分析软件(SPSS,R,SAS等)都会用表1的数据格式,SQL型数据库也采用的是表1的形式进行数据存储的。为什么?原因或许是几乎所有的数学分析手段都是针对特征展开的

变量类型

变量类型分为连续变量分类变量(又叫离散变量),《潜变量分析》对连续变量的定义是在变量的取值范围内存在任意可能值的变量,而离散变量虽然也常用数值表示,但是数值仅限一个符号,而没有绝对量化的关系,比如我们用1、2分别表示性别的男、女,决不意味着男性就是女性的2倍,或者比女性绝对值大1,换句话说,他们之前没有数据在量上的关系。后续我们就会看到pandas针对这两种变量设计的接口。

为什么要铺垫那么多,因为他们都和今天的主题pandas库密切相关,pandas是基于numpy封装用于数据分析的强大库,他的主要对象DataFrame就是典型的长数据,在变量类型上主要分为时间序列变量、分类变量(有序或无序),连续变量。在二维数据(栅格数据)分析方面,pandas毫无疑问是处于python生态的最中心,因本系列是针对可视化的,因此大多数基于DataFrame的数据分析接口只涉及到分组聚合(groupby)和筛选(loc,iloc),更多的接口没有展示,但这丝毫不影响他的江湖地位。另一方面,pandas基于matplotlib封装的高级接口,极大程度地提高了绘图效率,并显著地降低了代码量。那为什么花那么多时间讲matplotlib,而不是一开始就直接用pandas呢,原因是用pandas画出一张图可能是很简单的,但要详细地修饰成想要的图,又必须回到最底层的matplotlib接口去。金庸先生不是说了嘛,练上层武功,往往需要强大的内力,否则容易走火入魔。


本文的运行环境为 jupyter notebook

python版本为3.7

本文所用到的库包括

%matplotlib inline
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import seaborn as sns
from sklearn import datasets

plt.rcParams['xtick.labelsize'] = 15
plt.rcParams['ytick.labelsize'] = 15
plt.rcParams['legend.fontsize'] = 15
plt.rcParams['axes.labelsize'] = 15
plt.rcParams['axes.titlesize'] = 15

什么是高级接口

以下案例展示了高级接口相对普通接口的区别。

构造数据集

以下通过sklearn下的 datasets.make_blobs 接口构造了一个二分类的数据集,该数据集包含5个特征列,一个分类标签列,如下:

X,y=datasets.make_blobs(n_samples=100, n_features=5, centers=2, random_state=121)
columns=['fea%d'%i for i in range(X.shape[1])]
df=pd.DataFrame(data=X,columns=columns)
df['classes']=y
df.head()

输出

fea0fea1fea2fea3fea4classes
0-0.6607170.9891614.898791-4.3539109.2796721
1-2.0929280.1956056.234234-4.5341958.4073221
2-1.5569740.2327114.807226-5.2879269.8039041
3-6.948959-8.871373-4.176408-5.8784577.0893590
4-7.755544-5.646643-5.299828-7.6759615.6339140

安德鲁曲线的朴素实现

f ( t ) = 1 2 x 1 + x 2 s i n ( t ) + x 3 c o s ( t ) + x 4 s i n ( 2 t ) + x 5 c o s ( 2 t ) + … f(t) = \frac{1}{\sqrt 2} x_1 + x_2 sin(t) + x_3 cos(t) +x_4 sin(2t) + x_5 cos(2t) + … f(t)=2 1x1+x2sin(t)+x3cos(t)+x4sin(2t)+x5cos(2t)+

安德鲁曲线通过以上公式实现了高维数据的平面可视化,以下为其朴素实现

# 定义函数计算t时刻的曲线值
def andrews(array, t):
    # array :1,n
    # w: n,1
    # return shape=1,1
    w = [1/np.sqrt(2)]
    for i, a in enumerate(array):
        if i == 0:
            continue
        elif i % 2 == 0:
            w.append(np.cos(i/2*t))
        else:
            w.append(np.sin((i+1)/2*t))
    w = np.array(w)
    return np.matmul(array, w)


fig, ax = plt.subplots(1, 1,figsize=(8,6))
colors = ['green', 'red']

for row in range(len(df)):
    array = df.iloc[row, :-1]
    label = df.iloc[row, -1]
    ts = np.linspace(-1*np.pi, np.pi, 100)
    lines = [andrews(array, t) for t in ts]

    ax.plot(ts, lines, color=colors[label], lw=2, label=label)


handles, labels = ax.get_legend_handles_labels()
ax.legend([handles[0], handles[-1]], [labels[0], labels[-1]], loc=1)
ax.grid()

接口实现

而同样的工作,只需要调用pd.plotting.andrews_curves 接口一步实现,这就是对matplotlib基础库封装带来的直观效果,极度精简。

fig,ax=plt.subplots(1,1,figsize=(8,6))
pd.plotting.andrews_curves(frame=df,class_column='classes',color=['r','g'],ax=ax)

高级接口带给了我们什么?

为什么人工智能在这几年火了,而且对各个行业的改变如此之大,从医疗到生物科技,从制造业到工业互联网?这个问题可能很难回答,普遍的共识是:一方面,计算机的算力得到了很大的程度地提升;另一方面,互联网时代强大的数据收集能力提供了足够多的底层数据支撑了这个产业的发展。除此之外,还有一个因素不可或缺,那就是python编程语言速度获得各个社区的支持,并以他为生态开发了大量的功能库,并在各行业得到了一定程度地普及。python有一个非常大的特点——简洁,该特性让使用者有充足的时间关注业务逻辑本身,而不必费神于编程,这个效果促成了大多数非IT行业人员将最原始、最符合业务的逻辑转化成代码,从而催生了产业快速迭代、不断发展。很难想象,安德鲁曲线如果用几千行代码才能实现,那么又有多少人愿意去使用它呢?

高维数据可视化

在案例 多元线性回归 中,我们讨论了数据可视化维度的问题,并用一个例子说明了在提升可视化维度上,散点图是非常有实用价值的。前文提到的安德鲁曲线也是将多维度数据投影到二维平面一种方式,与此同时,再介绍两种将高维数据投影到二维平面的可视化模型,他们分别是平行坐标雷达图,pandas均有现成的接口,分别是pd.plotting.parallel_coordinatespd.plotting.radviz,平行坐标原理相对简单,雷达图理解可参考:一种改进的Radviz数据可视化方法

平行坐标

X, y = datasets.make_blobs(n_samples=100, n_features=8,
                           centers=2, random_state=121)
columns = ['fea%d' % i for i in range(X.shape[1])]
df = pd.DataFrame(data=X, columns=columns)
df['classes'] = y

fig,ax=plt.subplots(1,1,figsize=(8,6))
pd.plotting.parallel_coordinates(frame=df, 
                                 class_column='classes',
                                 color=['green','red'],
                                 ax=ax)

雷达图

X, y = datasets.make_blobs(n_samples=100, n_features=8,
                           centers=2, random_state=121)
columns = ['fea%d' % i for i in range(X.shape[1])]
df = pd.DataFrame(data=X, columns=columns)
df['classes'] = y


fig,ax=plt.subplots(1,1,figsize=(8,6))
res=pd.plotting.radviz(frame=df, class_column='classes',ax=ax)

from matplotlib.text import Text
for child in res.get_children():
    if isinstance(child,Text):
        child.set_fontsize(20)

pandas主要通过两种方式调用绘图接口:

方式一:静态函数接口。即pd.plotting相关的接口;主要包括三大类:箱线图、直方图、散点图。他们都需要传入数据框(DataFrame)对象,随后可视化数据框中的特征。

方式二:数据框对象方法。即df相关的接口,通过参数kind调节可视化对象,通过layout调节画布网格,通过subplots调节特征分面。

pd.plotting相关接口绘图

箱线图

X, y = datasets.make_blobs(n_samples=100, n_features=6,
                           centers=2, random_state=121)
columns = ['fea%d' % i for i in range(X.shape[1])]
df = pd.DataFrame(data=X, columns=columns)
df['classes'] = y

plt.rcParams['figure.titlesize'] = 15
fig, axs = plt.subplots(2, 3, figsize=(9, 6),)

axs = pd.plotting.boxplot(data=df, by='classes', layout=(2, 3), return_type='axes', ax=axs)
# 该接口等价于df.boxplot(by='classes',layout=(2,3),return_type='axes',ax=axs)

fig.suptitle(' ')

# 通过返回的子图列表,进行子图的修饰
axs[0].set_xlabel('xlabel')

plt.tight_layout()

直方图

X, y = datasets.make_blobs(n_samples=100, n_features=6,
                           centers=2, random_state=121)
columns = ['fea%d' % i for i in range(X.shape[1])]
df = pd.DataFrame(data=X, columns=columns)
df['classes'] = y

axs=pd.plotting.hist_frame(data=df[columns],layout=(2,3),figsize=(9,6),grid=False)
# 返回子图列表

# 通过调用子图列表对各子图修饰
axs[0,0].set_xlabel('xlabel')

plt.tight_layout()

散点-直方图

X, y = datasets.make_blobs(n_samples=100, n_features=3,
                           centers=2, random_state=121)
columns = ['fea%d' % i for i in range(X.shape[1])]
df = pd.DataFrame(data=X, columns=columns)
df['classes'] = y

axes = pd.plotting.scatter_matrix(frame=df[columns],
                                  c=df['classes'],
                                  marker='.',
                                  s=300,
                                  alpha=0.6,
                                  edgecolor='blue',
                                  cmap=plt.cm.RdBu,
                                  hist_kwds={'color':'tab:red'},  # 直方图的参数
                                  figsize=(9, 9))

# 通过调用axes列表对各子图修饰
axes = axes.ravel()
for ax in axes:
    for label in ax.get_xticklabels()+ax.get_yticklabels():
        label.set_fontsize(15)
        label.set_rotation(0)

数据框对象相关接口绘图

数据框对象绘图最重要的参数为kind,通过改变kind,可以得到不同的绘图对象。

如以下两个案例分别得到了散点图和条形图。

散点图

X, y = datasets.make_blobs(n_samples=100, n_features=3, center_box=(10.0, 20.0),
                           centers=2, random_state=121)
columns = ['fea%d' % i for i in range(X.shape[1])]
df = pd.DataFrame(data=X, columns=columns)
df['classes'] = y

fig, ax = plt.subplots(1, 1, figsize=(6, 4))

df.plot(kind='scatter', ax=ax, colormap=plt.cm.RdBu, x='fea0',
        y='fea1', c='classes', s=150)

plt.tight_layout()

条形图

X, y = datasets.make_blobs(n_samples=100, n_features=3, center_box=(10.0, 20.0),
                           centers=3, random_state=121)
columns = ['fea%d' % i for i in range(X.shape[1])]
df = pd.DataFrame(data=X, columns=columns)
df['classes'] = y

groups = df.groupby(['classes'])[columns].mean()

ax= groups.plot(kind='bar',
                  xlabel='the classes of production',
                  ylabel='the values of features',
                  figsize=(6, 4))

ax.set_xticklabels(ax.get_xticklabels(),rotation=0)

plt.tight_layout()

分类变量分面图-subplots

分类变量分面可视化可通过调节参数 subplots 实现

X, y = datasets.make_blobs(n_samples=100, n_features=3, center_box=(10.0, 20.0),
                           centers=3, random_state=121)
columns = ['fea%d' % i for i in range(X.shape[1])]
df = pd.DataFrame(data=X, columns=columns)
df['classes'] = y

groups = df.groupby(['classes'])[columns].mean()

axs = groups.plot(kind='bar',
                  subplots=True,
                  layout=(1, 3),
                  xlabel='the classes of production',
                  ylabel='the values of features',
                  figsize=(9, 4))

for ax in axs.ravel():
    for xlabel in ax.get_xticklabels():
        xlabel.set_rotation(0)

plt.tight_layout()

绘图对象-kind-用以绘制分类变量的聚合结果

‘line’,‘bar’ ,‘barh’,‘area’ 四种参数适用于表现分类变量的聚合结果

X, y = datasets.make_blobs(n_samples=100, n_features=3,center_box=(10.0, 20.0),
                           centers=3, random_state=121)
columns = ['fea%d' % i for i in range(X.shape[1])]
df = pd.DataFrame(data=X, columns=columns)
df['classes'] = y

groups=df.groupby(['classes'])[columns].mean()

fig,axs=plt.subplots(nrows=2,ncols=2,figsize=(8,6))
axs=axs.ravel()
kinds=['line','bar' ,'barh','area']
for i,kind in enumerate(kinds):
    groups.plot(kind=kind,ax=axs[i])
    axs[i].set_title(kind)

# 对子图的修饰
axs[0].set_xticks([0,1,2])
axs[1].set_xticklabels(axs[1].get_xticklabels(),rotation=0)
axs[3].set_xticks([0,1,2])

plt.tight_layout() 

绘图对象-kind-用以绘制连续变量数据分布

‘line’,‘hist’,‘box’,'kde’四种参数常用于表现连续变量的分布情况

X, y = datasets.make_blobs(n_samples=100, n_features=3,center_box=(10.0, 20.0),
                           centers=3, random_state=121)
columns = ['fea%d' % i for i in range(X.shape[1])]
df = pd.DataFrame(data=X, columns=columns)
df['classes'] = y

kinds=['line','hist','box','kde'] ## pandas会自动过滤非连续变量
fig,axs=plt.subplots(2,2,figsize=(8,6))
axs=axs.ravel()
for i,kind in enumerate(kinds):
    df[columns].plot(kind=kind,ax=axs[i])
    axs[i].set_title(kind)

plt.tight_layout()

总结

pandas高级接口的一些共性:

  • 可视化的数据对象为数据框(DataFrame);
  • 画布大小通过figsize参数确定,子图布局通过layout参数确定,绘图对象通过kind参数确定;
  • 子图修饰通过绘图接口的返回对象(axs)进行,至于如何修饰,传送门如下:
    面向对象绘图(子图和他的小伙伴们)

最后,适当的时候,忘了matplotlib吧,解决问题永远比选择工具更重要。

希望对你有所帮助和启发!

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值