使用matplotlib制作分类散点图与气泡图

在机器学习中,一类常见的问题便是监督学习问题,有时也称为分类问题,即通过对数据现有的特征与分类标签进行分析,建立特定的模式与函数,从而可以为的新的数据建立分类标签。在数据特征为二维时,通常可以通过绘制分类散点图观察数据的特征,从而使得分类问题变得更加直观,也可以使用这种方法来选择分类性能最好的特征。分类散点图与传统散点图的最大区别是其数据点带有分类标签,通常使用颜色与符号来区分不同的类别。一个典型的分类散点如下:

IMG

图中使用了蓝色圆形标签表示本国汽车,使用红色星形标签表示外国汽车,横轴为汽车的重量,竖轴为汽车的长度,可以看出这个变量无法很好的区分本国与外国汽车。

Python的matplotlib库提供了散点图的绘制模块,但为了绘制分类散点图还需要进行多重设置,如果每次都进行这些设置则略显繁琐。为此,笔者编写了一个专门绘制分类散点图的函数,可以简化相关的设置工作。首先,我们需要导入相关库:

# coding = utf-8
import numpy as np
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings("ignore")
import seaborn as sns
import pandas as pd
from algo import latex
sns.set(style="white",context='poster',color_codes=True)

其中algo是笔者自身编写的库,其中latex函数可以将普通文本转化为latex格式,使得相关图片标签更加美观,最好一行使用seaborn库描述了所绘制图形的风格与格式。接下来便是函数本身:

def kindScatter(x,y,group,size=100,legendloc=2):
    kind = list(set(group))
    if len(kind) > 7:
        print "there is too much groups!"
    else:
        markers = ['o', '*', '+', 'x','s', 'p','h']
        col = ['b','r','g','c','y','m','k']
        for i in range(len(kind)):
            xx = x[group==kind[i]]
            yy = y[group==kind[i]]      
            plt.scatter(xx,yy,marker=markers[i],s=size,c=col[i],alpha=0.8,label=latex(kind[i]))
            plt.legend(loc=legendloc,frameon=True)

函数的原理是将分类标签不同的数据分组,并分别使用不同的颜色与标记绘制散点图,最后得到的散点图可以通过颜色与标记明确区分不同的类别。函数包括五个参数,前两个参数分别为X轴变量与Y轴变量,第三个参数为分类标签变量,以上三个变量都为pandas中的series格式,通常可以用DataFrame.x的形式来表示。第四个参数为数据点的大小,默认为100,可以根据数据点的稀疏来放大或缩小数据点;第五个参数为legend的位置,可以用1、2、3、4表示legend位于右上角、左上角、左下角与右下角。需要注意的是,由于可供选择的颜色与数据标签有限,函数最多支持七个类别,如果超过七个类别,则函数会报错。不过通常来说,七个类别已经足够,再多的类别也会使得图形非常混乱,变量更加不直观。

鸢尾花数据示例

为检验函数的功能,使用该函数对著名的iris数据集进行了绘制。iris数据集,也称鸢尾花卉数据集,是常用的分类实验数据集,由Fisher(1936)收集整理。数据集包含150个数据,分为3类,每类50个数据,每个数据包含4个属性。可通过花萼长度,花萼宽度,花瓣长度,花瓣宽度4个属性预测鸢尾花卉属于(Setosa,Versicolour,Virginica)三个种类中的哪一类。其中的Setosa与另外两个种类是线性可分离的,而后两个种类是非线性可分离的。

可以看出,使用sepal length与speal widthd两个特征可以很轻易地对setosa与其它两种花进行区别,但无法有效的区分viginica与versicolor两种花。以上现象说明了,选择不同的数据特征,其分类性能是不同的。

IMG

决策边界

对于某些分类散点图,画出决策边界(decision boundary)通常有利于对分类有更进一步的认识。如在Andrew Ng的机器学习课程中,给出了个学生考试成绩与通过情况的数据集。数据共包括100个案例,3个变量,前两个变量分别为两门考试的分类,最后一项表示学生是否通过考试,通过了则为1,未通过则为0。与以上类似,我们可以画出该数据集的分类散点图。另外,我们想画出决策边界,以使我们直观的认识分类算法的性能。

我们使用logistic回归来决定决策边界,即两科考试分数为自变量,考试通过情况为因变量进行回归,得到以下模型:

$$ logit(result) = {\beta _0} + {\beta _1}examscor{e_1} + {\beta _2}examscor{e_2} $$

回归结果如下:

$$ logit(result) = - 25.161 + 0.206examscor{e_1} + 0.201examscor{e_2} $$

为得到决策边界,一般令\(logit(result) = 0​\),如此可以解出:

$$ examscor{e_2} = 124.87 - 1.02examscor{e_1} $$

将这一直线绘制在分类散点图上,便可以得到决策边界,如下图所示:

IMG

气泡图

气泡图与分类散点图类似,都是在对基础散点图的基础上修改而成的。区别在于,分类散点图适用于如分类数据这样的离散变量,而气泡图则适用于连续变量。前者通常将离散变量映射到数据点的颜色与标记上,而后者将连续变量映射到数据点的大小与颜色上。这一思想来自于R语言的经典绘图包ggplot2,正是在这一绘图包里,Hadley Wickham创建了一种新型绘图语法,引入了映射、标度与变换等概念,使数据分析师能够更加简洁地做出更加强大的统计图形。借用ggplot2的思想,我们用python语言编写了一个绘制气泡图的函数,函数如下:

def bubble(xx,yy,size,col,data,base=100):
    x = data[xx]
    y = data[yy]
    area = data[size]
    colors = data[col]
    mincol = min(colors)
    maxcol = max(colors)
    std = (area-float(min(area)))/(max(area)-min(area))
    stdArea = base + 500*std
    stdColor = (colors-float(min(colors)))/(max(colors)-min(colors))
    fig, ax = plt.subplots()
    cax = ax.scatter(x,y,s=stdArea,c=stdColor,cmap='jet',alpha=0.8)
    cbar = fig.colorbar(cax, ticks=[0,0.5,1])
    cbar.ax.set_yticklabels([str(mincol),latex(col),str(maxcol)])
    plt.xlabel(latex(xx))
    plt.ylabel(latex(yy))

函数包含六个参数:前四个参数的数据分别映射到气泡图的X轴、Y轴、数据点的大小以及颜色,最后两个参数对应的数据应是一致的。Data参数为一个pandas的dataframe,base参数描述数据点的最小尺寸。需要注意的是,由于函数之后需要绘制相关数据标签,则前四个参数应是文本格式,如:

    bubble('weight','length','price','price',data=auto,base=200)

函数为将相应数据映射到数据点的颜色与大小上,首先需要将相应数据标准化,标准化的方式是将每个数据减去其最小值再除以最大值与最小值之差,如此数据集将被映射到0-1闭区间,在最小值时取0,最大值时取1。此外,为气泡图加上了colorbar,以明确不同颜色对应的取值范围。为测试函数功能,使用stata自带的auto数据集。

将汽车的weight与length映射到X轴与Y轴,将汽车的价格映射到数据点的颜色与大小,则可以得到:

IMG