散点图是常见的统计图表,图中散点的坐标用两个或三个数值型变量定义。如果有多个分组,可以用不同颜色、类型、大小的标记进行区分,或者分面表示。散点图可以用于探查变量之间的相关关系。[大谦Excel,dqexcel点com]
二维散点图
二维散点图常用于原始数据的探查和描述,或者查看两个数值型变量的相关关系。残差散点图常用于判断回归分析等统计分析的效果好坏。本小节介绍二维简单散点图和复合散点图的绘制。
简单散点图如图4-19所示。绘制简单散点图需要指定两组数据,一组数据表示数据点的x坐标,另外一组数据表示数据点的y坐标。
图4-19 散点图
下面的代码用给定数据绘制二维散点图。完整代码见:Samples->ch07 数值型图表->13 二维散点图->py.py。
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坐标。
图4-20 复合散点图
下面的代码用给定数据绘制复合散点图,完整代码见:Samples->ch07 数值型图表->14 二维复合散点图->py.py。
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所示。其中有些位置上有重复的点。
图4-21 没有抖动的散点图
编写Python xlwings代码,绘制不抖动的散点图。完整代码见:Samples->ch07 数值型图表->15 抖动散点图-没抖动->py.py。
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所示。
图4-22 抖动散点图
编写Python xlwings代码,绘制抖动的散点图。各组数据点在其x坐标轴线两侧w/2范围内随机抖动,y坐标不变。其中,w表示抖动宽度。完整代码见:Samples->ch07 数值型图表->16 抖动散点图-已抖动->py.py。
#... 省略部分代码
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绘制水平方向的抖动散点图。
图4-23 水平抖动散点图
编写Python xlwings代码,绘制抖动的散点图。各组数据点在其y坐标轴线两侧w/2范围内随机抖动,x坐标不变。其中,w表示抖动宽度。完整代码见:Samples->ch07 数值型图表->17 抖动散点图-已抖动-横向->py.py。
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所示,该图在规则网格的节点上绘制散点,可以指定变量定义散点的颜色和大小。颜色通过变量的值映射颜色查找表进行获取。
图4-24 规则散点图
用Python xlwings自己编程绘制规则散点图,绘制网格,根据数据大小在网格节点处绘制相应大小和颜色的圆形区域。本例使用了颜色查找表进行着色,相关内容请参见第4章。完整代码见:Samples->ch07 数值型图表->18 规则散点图->py.py。
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') #获取指定工作表对象
#绘制规则散点图
cht=draw_reg_scatter(wb)
运行代码生成类似图4-24的规则散点图。