散点图

散点图是常见的统计图表,图中散点的坐标用两个或三个数值型变量定义。如果有多个分组,可以用不同颜色、类型、大小的标记进行区分,或者分面表示。散点图可以用于探查变量之间的相关关系。[大谦Excel,dqexcel点com]

二维散点图

二维散点图常用于原始数据的探查和描述,或者查看两个数值型变量的相关关系。残差散点图常用于判断回归分析等统计分析的效果好坏。本小节介绍二维简单散点图和复合散点图的绘制。

简单散点图如图4-19所示。绘制简单散点图需要指定两组数据,一组数据表示数据点的x坐标,另外一组数据表示数据点的y坐标。

Document Image

图4-19 散点图

下面的代码用给定数据绘制二维散点图。完整代码见:Samples->ch07 数值型图表->13 二维散点图->py.py。

code.python
root=os.getcwd()    #获取当前工作路径
app=xw.App(visible=True,add_book=False)    #创建Excel应用
wb=app.books.open(root+r'/data.xlsx',read_only=False)    #打开数据文件返回工作簿对象
sht=wb.sheets('Sheet1')    #获取指定工作表对象
sht.api.Range('A1:B100').Select()    #数据
shp=sht.api.Shapes.AddChart2(-1,xw.constants.ChartType.xlXYScatter,20,20,300,200,True)
cht=shp.Chart    #获取图表
set_style(cht)    #设置样式

运行代码生成图4-19。

复合散点图如图4-20所示,图中用多组简单散点图表示多组数据。绘制复合散点图需要为每组简单散点图指定两组数据,一组数据表示数据点的x坐标,另外一组数据表示数据点的y坐标。

Document Image

图4-20 复合散点图

下面的代码用给定数据绘制复合散点图,完整代码见:Samples->ch07 数值型图表->14 二维复合散点图->py.py。

code.python
root=os.getcwd()    #获取当前工作路径
app=xw.App(visible=True,add_book=False)    #创建Excel应用
wb=app.books.open(root+r'/data.xlsx',read_only=False)    #打开数据文件返回工作簿对象
sht=wb.sheets('Sheet1')    #获取指定工作表对象
sht.api.Range('A1:B100').Select()  #数据
shp=sht.api.Shapes.AddChart2(-1,xw.constants.ChartType.xlXYScatter,20,20,300,200,True)
cht=shp.Chart    #获取图表
ser=cht.SeriesCollection().NewSeries()    #新建序列
ser.ChartType=xw.constants.ChartType.xlXYScatter    #图表类型为散点图
ser.XValues=sht.api.Range('C1:C100')    #绑定绘图数据
ser.Values=sht.api.Range('D1:D100')
set_style(cht)    #设置样式

运行代码生成图4-20。

抖动散点图

用具有共同分类的一组数据绘制散点图时,在不抖动的情况下,分组数据点沿一条竖线展布,它们具有相同的x坐标,如图4-21所示。其中有些位置上有重复的点。

Document Image

图4-21 没有抖动的散点图

编写Python xlwings代码,绘制不抖动的散点图。完整代码见:Samples->ch07 数值型图表->15 抖动散点图-没抖动->py.py。

code.python
root=os.getcwd()    #获取当前工作路径
app=xw.App(visible=True,add_book=False)    #创建Excel应用
wb=app.books.open(root+r'/data.xlsx',read_only=False)    #打开数据文件返回工作簿对象
sht=wb.sheets('Sheet1')    #获取指定工作表对象
shp=sht.api.Shapes.AddChart2()    #创建空白图表
shp.Left=20
cht=shp.Chart    #获取图表
cht.ChartType=xw.constants.ChartType.xlXYScatter    #图表类型为散点图
ax1=cht.Axes(1)    #获取横轴
ax2=cht.Axes(2)    #获取纵轴
ax1.MinimumScale=0    #横轴最小值.5
ax1.MaximumScale=4.5
ax2.MinimumScale=0    #纵轴最小值
ax2.MaximumScale=0.35
set_style(cht)    #设置样式
data=sht.range('B2:E21').value    获取数据
rd=[0 for _ in range(20)]
y=[0 for _ in range(20)]
#逐个绘制每组数据的散点图
for i in range(4):
    cht.SeriesCollection().NewSeries()    #新建序列
    for j in range(20):
        rd[j]=i+1
        y[j]=data[j][i]
    cht.SeriesCollection(i+1).XValues=rd    #绑定横轴数据
    cht.SeriesCollection(i+1).Values=y    #纵轴数据

运行代码生成图4-21。

同一分类的数据点绘制成散点时呈直线展布,数据点大量重叠。为了表示这些重复的点,将它们在横向上在指定范围内进行抖动。如果是随机抖动,得到的散点图称为抖动散点图,如图4-22所示。

Document Image

图4-22 抖动散点图

编写Python xlwings代码,绘制抖动的散点图。各组数据点在其x坐标轴线两侧w/2范围内随机抖动,y坐标不变。其中,w表示抖动宽度。完整代码见:Samples->ch07 数值型图表->16 抖动散点图-已抖动->py.py。

code.python
#... 省略部分代码
data=sht.range('B2:E21').value
rd=[0 for _ in range(20)]
y=[0 for _ in range(20)]
#逐个绘制每组数据的抖动散点图
for i in range(4):
    cht.SeriesCollection().NewSeries()    #新建序列
    for j in range(20):
        rd[j]=i+0.75+0.5*np.random.rand(1)[0]    #x坐标在水平方向上抖动
        y[j]=data[j][i]
    cht.SeriesCollection(i+1).XValues=rd    #横轴数据
    cht.SeriesCollection(i+1).Values=y    #纵轴数据

运行代码生成类似图4-22的抖动散点图。

图4-23绘制水平方向的抖动散点图。

Document Image

图4-23 水平抖动散点图

编写Python xlwings代码,绘制抖动的散点图。各组数据点在其y坐标轴线两侧w/2范围内随机抖动,x坐标不变。其中,w表示抖动宽度。完整代码见:Samples->ch07 数值型图表->17 抖动散点图-已抖动-横向->py.py。

code.python
data=sht.range('B2:E21').value
rd=[0 for _ in range(20)]
y=[0 for _ in range(20)]
for i in range(4):
    cht.SeriesCollection().NewSeries()    #新建序列
    for j in range(20):
        rd[j]=i+0.75+0.5*np.random.rand(1)[0]    #在纵向上抖动
        y[j]=data[j][i]
    cht.SeriesCollection(i+1).XValues=y    #横轴数据
    cht.SeriesCollection(i+1).Values=rd    #纵轴数据

运行代码生成类似图4-23的抖动散点图。

规则散点图

图4-24称为规则散点图,它与普通二维散点图的根本区别在于,它是根据分类变量的数据绘制散点图,而普通散点图的两个坐标轴都是数值轴。如图4-24所示,该图在规则网格的节点上绘制散点,可以指定变量定义散点的颜色和大小。颜色通过变量的值映射颜色查找表进行获取。

Document Image

图4-24 规则散点图

用Python xlwings自己编程绘制规则散点图,绘制网格,根据数据大小在网格节点处绘制相应大小和颜色的圆形区域。本例使用了颜色查找表进行着色,相关内容请参见第4章。完整代码见:Samples->ch07 数值型图表->18 规则散点图->py.py。

code.python
import xlwings as xw    #导入xlwings包
import os    #导入OS包
def draw_reg_scatter(wb):
    #绘制分箱散点图
    sht=wb.sheets('Sheet1')    #获取指定工作表对象
    shp=sht.api.Shapes.AddChart2()    #创建空白图表
    shp.Left=20    #图表位置和大小
    shp.Top=20
    shp.Width=330
    shp.Height=400
    cht=shp.Chart    #获取图表
    cht.ChartType=xw.constants.ChartType.xlXYScatter    #图表类型为散点图
    #清空序列
    for i in range(cht.SeriesCollection().Count,0,-1):
        cht.SeriesCollection(i).Delete()

    ax1=cht.Axes(1)    #获取横轴
    ax2=cht.Axes(2)    #获取纵轴
    ax1.MinimumScale=-1    #横轴和纵轴的取值范围
    ax1.MaximumScale=8
    ax2.MinimumScale=-1
    ax2.MaximumScale=9
    ax2.ReversePlotOrder=True    #反转纵轴
    #清空图形对象
    if cht.Shapes.Count>0:
        for i in range(cht.Shapes.Count,0,-1):
            cht.Shapes(i).Delete()

    #数据归一化
    data2=[[0 for _ in range(5)] for _ in range(8)]
    data3=[[0 for _ in range(5)] for _ in range(8)]
    data=sht.range('B2:F9').value
    minv=1000
    maxv=-1000
    for i in range(8):
        for j in range(5):
            if minv>data[i][j]: minv=data[i][j]
            if maxv<data[i][j]: maxv=data[i][j]
    difv=maxv-minv
    for i in range(8):
        for j in range(5):
            data2[i][j]=(data[i][j]-minv)/difv
    for i in range(8):
        for j in range(5):
            data3[i][j]=data2[4-i][j]

    #导入颜色查找表
    cm=wb.sheets('Sheet2').range('A1:C256').value

    #画网格
    for i in range(7):
        for j in range(10):
            sx1=shape_x(cht,i)
            sy1=shape_y(cht,0)
            sx2=shape_x(cht,i)
            sy2=shape_y(cht,j)
            shp1=cht.Shapes.AddLine(sx1,sy1,sx2,sy2)
            shp1.Line.ForeColor.RGB=xw.utils.rgb_to_int((200,200,200))
            shp1.Line.Weight=1
            sx1=shape_x(cht,0)
            sy1=shape_y(cht,j)
            sx2=shape_x(cht,i)
            sy2=shape_y(cht,j)
            shp2=cht.Shapes.AddLine(sx1,sy1,sx2,sy2)
            shp2.Line.ForeColor.RGB=xw.utils.rgb_to_int((200,200,200))
            shp2.Line.Weight=1

    #画圆圈
    for i in range(5):
        for j in range(7,-1,-1):
            w=data3[j][i]
            count=int(w*255)
            if count>255:
                r=int(cm[255][0])
                g=int(cm[255][1])
                b=int(cm[255][2])
            else:
                r=int(cm[count][0])
                g=int(cm[count][1])
                b=int(cm[count][2])

            lf=shape_x(cht,i+1-w/2)
            tp=shape_y(cht,j+1+w/2)
            wd=cht.PlotArea.InsideWidth/(cht.Axes(1).MaximumScale-\
cht.Axes(1).MinimumScale)*w
            ht=cht.PlotArea.InsideHeight/(cht.Axes(2).MaximumScale-\
cht.Axes(2).MinimumScale)*w
            shp3=cht.Shapes.AddShape(9,lf,tp,wd,ht)    #绘圆形区域
            shp3.Fill.ForeColor.RGB=xw.utils.rgb_to_int((r,g,b))
            shp3.Line.Visible=False

    #画色条
    lf=shape_x(cht,6.5)
    tp=shape_y(cht,8)
    wd=cht.PlotArea.InsideWidth/(cht.Axes(1).MaximumScale-cht.Axes(1).MinimumScale) * 0.4
    ht=cht.PlotArea.InsideHeight/(cht.Axes(2).MaximumScale-cht.Axes(2).MinimumScale) * 3
    shp4=cht.Shapes.AddShape(1,lf,tp,wd,ht)    #矩形
    shp4.Fill.ForeColor.RGB=xw.utils.rgb_to_int((128,0,0))    #多色渐变色填充
    shp4.Fill.OneColorGradient(1,1,1)
    shp4.Fill.GradientStops.Insert(xw.utils.rgb_to_int((255,0,0)),0.1)
    shp4.Fill.GradientStops.Delete(2)
    shp4.Fill.GradientStops.Insert(xw.utils.rgb_to_int((255,255,0)),0.38)
    shp4.Fill.GradientStops.Insert(xw.utils.rgb_to_int((0,255,255)),0.63)
    shp4.Fill.GradientStops.Insert(xw.utils.rgb_to_int((0,0,255)),0.88)
    shp4.Fill.GradientStops.Insert(xw.utils.rgb_to_int((0,51,255)),1)

    #绘色条标签
    label_pos=[0 for _ in range(3)]
    labels=[0 for _ in range(3)]
    label_pos[0]=8.2
    label_pos[1]=6.9
    label_pos[2]=5.3
    labels[0]=maxv
    labels[1]=(maxv+minv)/2
    labels[2]=minv
    for i in range(3):
        lf=shape_x(cht,8)
        tp=shape_y(cht,label_pos[i])
        wd=cht.PlotArea.InsideWidth/(cht.Axes(1).MaximumScale-cht.Axes(1).MinimumScale)*1.6
        ht=cht.PlotArea.InsideHeight/(cht.Axes(2).MaximumScale-cht.Axes(2).MinimumScale)*0.6
        shp5=cht.Shapes.AddLabel(1,lf,tp,wd,ht)
        shp5.TextFrame2.TextRange.Characters.Text=str(labels[i])
        shp5.TextFrame2.TextRange.Characters.Font.Size=8
        #shp5.TextFrame2.AutoSize= msoAutoSizeTextToFitShape

    #绘制刻度标签-纵坐标
    ylabel_pos=[0 for _ in range(8)]
    ylabels=[0 for _ in range(8)]
    for i in range(8):
        ylabel_pos[i]=8-i
    for i in range(8):
        ylabels[i]=str(i+1)
    for i in range(8):
        lf=shape_x(cht,-0.6)
        tp=shape_y(cht,ylabel_pos[i]+0.2)
        wd=cht.PlotArea.InsideWidth/(cht.Axes(1).MaximumScale-cht.Axes(1).MinimumScale)*1.5
        ht=cht.PlotArea.InsideHeight/(cht.Axes(2).MaximumScale-cht.Axes(2).MinimumScale)*0.4
        shp6=cht.Shapes.AddLabel(1,lf,tp,wd,ht)
        shp6.TextFrame2.TextRange.Characters.Text=ylabels[i]
        shp6.TextFrame2.TextRange.Characters.Font.Size=8
        #shp6.TextFrame2.AutoSize=msoAutoSizeTextToFitShape

    #绘制刻度标签-横坐标
    xlabel_pos=[0 for _ in range(5)]
    xlabels=[0 for _ in range(5)]
    for i in range(5):
        xlabel_pos[i]=i
    for i in range(5):
        xlabels[i]=str(i+1)
    for i in range(5):
        lf=shape_x(cht,xlabel_pos[i]+0.7)
        tp=shape_y(cht,-0.07)
        wd=cht.PlotArea.InsideWidth/(cht.Axes(1).MaximumScale-cht.Axes(1).MinimumScale)*1.5
        ht=cht.PlotArea.InsideHeight/(cht.Axes(2).MaximumScale-cht.Axes(2).MinimumScale)*0.4
        shp7=cht.Shapes.AddLabel(1,lf,tp,wd,ht)
        shp7.TextFrame2.TextRange.Characters.Text=xlabels[i]
        shp7.TextFrame2.TextRange.Characters.Font.Size=8
        #shp7.TextFrame2.AutoSize=msoAutoSizeTextToFitShape

    #绘制横坐标标题
    lf=shape_x(cht,1.8)
    tp=shape_y(cht,-0.5)
    wd=cht.PlotArea.InsideWidth/(cht.Axes(1).MaximumScale-cht.Axes(1).MinimumScale)*2.5
    ht=cht.PlotArea.InsideHeight/(cht.Axes(2).MaximumScale-cht.Axes(2).MinimumScale)*0.6
    shp8=cht.Shapes.AddLabel(1,lf,tp,wd,ht)
    shp8.TextFrame2.TextRange.Characters.Text='X Axis Label'
    shp8.TextFrame2.TextRange.Characters.Font.Size=10
    #shp8.TextFrame2.AutoSize=msoAutoSizeTextToFitShape

    #绘制纵坐标标题
    lf=shape_x(cht,-1.2)
    tp=shape_y(cht,5.5)
    wd=cht.PlotArea.InsideWidth/(cht.Axes(1).MaximumScale-cht.Axes(1).MinimumScale)*0.6
    ht=cht.PlotArea.InsideHeight/(cht.Axes(2).MaximumScale-cht.Axes(2).MinimumScale)*2.5
    shp9=cht.Shapes.AddLabel(2,lf,tp,wd,ht)
    shp9.TextFrame2.TextRange.Characters.Text='Y Axis Label'
    shp9.TextFrame2.TextRange.Characters.Font.Size=10

    return cht
root=os.getcwd()    #获取当前工作路径
app=xw.App(visible=True,add_book=False)    #创建Excel应用
wb=app.books.open(root+r'/data.xlsx',read_only=False)    #打开数据文件返回工作簿对象
sht=wb.sheets('Sheet1&#x27;)    #获取指定工作表对象
#绘制规则散点图
cht=draw_reg_scatter(wb)

运行代码生成类似图4-24的规则散点图。