2.2节介绍了点、线、面和文本等基本图形元素的创建,有了基本图形元素后,就可以用它们搭建新的图表了。本节结合若干实例进行介绍。[大谦Excel,dqexcel点com]
自定义堆叠柱状图
堆叠柱状图是复合柱状图的另外一种表现形式,它将每个分组中各序列的柱面从下往上依次堆叠,如图2-32所示。用Python xlwings可以用Shapes对象的AddChart2方法绘制堆叠柱状图,这里我们自己来创建该图。自定义绘制时,可以用矩形面绘制柱面,也可以用4条边的多边形绘制柱面。本例使用后一种方法,注意使用comtypes包实现多边形的绘制,参见2.2.4小节。
图2-32 自定义堆叠柱状图
下面定义draw_poly_4函数根据给定的顶点坐标和颜色绘制四边形小柱面。
def draw_poly_4(cht,pts,r,g,b):
#画四边形
#pts为顶点坐标;r,g,b为颜色,[255 0 0]
pt=[[0 for _ in range(2)] for _ in range(5)]
pt[0][0]=shape_x(cht, pts[0][0]) #4个顶点的横坐标和纵坐标
pt[0][1]=shape_y(cht, pts[0][1])
pt[1][0]=shape_x(cht, pts[1][0])
pt[1][1]=shape_y(cht, pts[1][1])
pt[2][0]=shape_x(cht, pts[2][0])
pt[2][1]=shape_y(cht, pts[2][1])
pt[3][0]=shape_x(cht, pts[3][0])
pt[3][1]=shape_y(cht, pts[3][1])
pt[4][0]=pt[0][0] #第5个顶点与第1个顶点重合
pt[4][1]=pt[0][1]
shp=cht.Shapes.AddPolyline(pt) #在图表中添加多边形
shp.Fill.ForeColor.RGB=xw.utils.rgb_to_int((r,g,b)) #单色填充
#shp.Fill.Transparency=0.5 #半透明
#shp.Line.ForeColor.RGB=xw.utils.rgb_to_int((r,g,b))
#shp.Line.Weight=1.5 #边线的宽度
shp.Line.Visible=False #隐藏边线
自定义堆叠柱状图,重点在于计算每个小柱面4个顶点的坐标。各顶点的x坐标很好计算,y坐标则需要根据各分组中每个序列的长度累加计算。下面的代码实现自定义堆叠柱状图。完整代码见:Samples->ch05 创建新图表->30 自定义堆叠柱状图->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') #获取指定工作表对象
data=sht.range('B1:D6').value #用xlwings获取数据
app.kill() #关闭当前应用
#从comtypes包中导入CreateObject函数
from comtypes.client import CreateObject
app2=CreateObject("Excel.Application") #创建Excel应用
app2.Visible=True #应用窗口可见
wb2=app2.Workbooks.Open(root+r'/data.xlsx') #添加工作簿
sht2=wb2.Sheets('Sheet1') #获取第1个工作表
shp=sht2.Shapes.AddChart2() #创建空白图表
shp.Left=20
cht=shp.Chart #获取图表
cht.ChartType=-4169 #散点图
ax1=cht.Axes(1) #获取横轴
ax2=cht.Axes(2) #获取纵轴
ax1.MinimumScale=0 #横轴最小值
ax1.MaximumScale=7
ax2.MinimumScale=0 #纵轴最小值
ax2.MaximumScale=0.9
set_style(cht) #设置样式
cht.SeriesCollection().NewSeries() #新建序列
dt=np.zeros([6,4]) #初始化数组
for i in range(6):
my_sum=0
for j in range(3):
my_sum+=data[i][j] #累加计算分组中各序列柱面的高度
dt[i][j+1]=my_sum
colors=[[0,176,240],[146,208,80],[255,192,0]] #定义各序列的颜色
pts=np.zeros([4,2])
for i in range(6):
for j in range(3):
pts[0][0]=i+1-0.25 #各柱面4个顶点的坐标
pts[0][1]=dt[i][j]
pts[1][0]=i+1+0.25
pts[1][1]=dt[i][j]
pts[2][0]=i+1+0.25
pts[2][1]=dt[i][j+1]
pts[3][0]=i+1-0.25
pts[3][1]=dt[i][j+1]
draw_poly_4(cht,pts,colors[j][0],colors[j][1],colors[j][2]) #绘制多边形
运行代码生成图2-32。
自定义冲击图
冲击图如图2-33所示。在堆叠柱状图的基础上,冲击图将相邻分组之间间隔的区域进行了颜色填充。填充方式仍然是用相同序列的顶点围成的多边形使用对应序列的颜色进行填充,增加透明性。
图2-33 自定义冲击图
绘制冲击图的方法与2.3.1小节绘制自定义堆叠柱状图的方法一样,重点在于分组柱面之间各小四边形面顶点坐标的计算和四边形面的绘制。
下面的代码计算柱间多边形的顶点坐标,并调用draw_poly_4函数进行绘制。完整代码见:Samples->ch05 创建新图表->31 自定义冲击图->py.py。
#... 省略部分代码
#绘柱间多边形
for i in range(5):
for j in range(3):
pts[0][0]=i+1+0.25
pts[0][1]=dt[i][j]
pts[1][0]=i+2-0.25
pts[1][1]=dt[i+1][j]
pts[2][0]=i+2-0.25
pts[2][1]=dt[i+1][j+1]
pts[3][0]=i+1+0.25
pts[3][1]=dt[i][j+1]
draw_poly_4(cht,pts,colors[j][0],colors[j][1],colors[j][2],1)
运行代码生成图2-33。
自定义散点柱状图
散点柱状图是常见的统计图表,它是简单柱状图和抖动散点图的组合图。该图用分组数据的均值向量绘制简单柱状图,用各分组原始数据绘制抖动散点图。所以,该图既能反映各分组数据的综述统计信息,也能反映原始数据的分布。因为Excel不能直接绘制抖动散点图,所以需要自己进行创建。
图2-34 自定义散点柱状图
本书中介绍了多种方法绘制散点柱状图,这里用多边形绘制柱状图,用2.2.1小节介绍的绘点的方法绘制抖动散点。第7章绘详细介绍抖动散点图的绘制。
下面用draw_bar函数绘制单个柱面。
def draw_bar(cht,y,n,x,r,g,b,w,grad):
'''
柱面填充
y为高度,r,g,b为颜色,[255 0 0]
x为中心横坐标,w为宽度,grad为是否渐变色填充
'''
aveg=np.mean(y) #求给定y数组的均值
#画柱面
pt=[[0 for _ in range(2)] for _ in range(5)] #初始化
pt[0][0]=shape_x(cht,x-w/2)
pt[0][1]=shape_y(cht,cht.Axes(2).MinimumScale)
pt[1][0]=shape_x(cht,x+w/2)
pt[1][1]=shape_y(cht,cht.Axes(2).MinimumScale)
pt[2][0]=shape_x(cht,x+w/2)
pt[2][1]=shape_y(cht,aveg)
pt[3][0]=shape_x(cht,x-w/2)
pt[3][1]=shape_y(cht,aveg)
pt[4][0]=pt[0][0]
pt[4][1]=pt[0][1]
shp=cht.Shapes.AddPolyline(pt) #在图表上绘制多边形
if grad: #如果渐变色填充
shp.Fill.ForeColor.RGB=xw.utils.rgb_to_int((r,g,b))
shp.Fill.OneColorGradient(1,1,1) #单色渐变色填充
shp.Line.ForeColor.RGB=xw.utils.rgb_to_int((r,g,b)) #边线的颜色
shp.Line.Weight=1.5 #边线的宽度
else: #否则
shp.Fill.ForeColor.RGB=xw.utils.rgb_to_int((r,g,b)) #单色填充
shp.Line.ForeColor.RGB=xw.utils.rgb_to_int((r,g,b))
shp.Line.Weight=1.5
下面用draw_rnd_scatter函数绘制单个分组数据的抖动散点图。
def draw_rnd_scatter(cht,x,y,n,w,r,g,b):
'''
绘制抖动散点图
x: x坐标 y(0 to n): y坐标 w: 宽度
'''
rd=[]
#对于每个散点,抖动x坐标
for i in range(n):
rd.append(x-w/2+w*np.random.rand(1)[0])
#绘制每个散点
for i in range(n):
bx=shape_x(cht,rd[i])
by=shape_y(cht,y[i])
ex=cht.PlotArea.InsideWidth/(cht.Axes(1).MaximumScale- \
cht.Axes(1).MinimumScale)*0.09
ey=ex
shp=cht.Shapes.AddShape(9,bx,by,ex,ey) #绘制散点
shp.Fill.ForeColor.RGB=xw.utils.rgb_to_int((r,g,b))
shp.Line.Weight=1
shp.Line.ForeColor.RGB=xw.utils.rgb_to_int((r,g,b))
下面的代码根据分组变量对指定数据进行分组,然后利用分组数据绘制柱状图和抖动散点图,并一起组合成散点柱状图。完整代码见:Samples->ch05 创建新图表->32 自定义散点柱状图->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') #获取指定工作表对象
data=sht.range('B2:C101').value #获取数据
app.kill() #退出应用
#从comtypes包中导入CreateObject函数
from comtypes.client import CreateObject
app2=CreateObject("Excel.Application") #创建Excel应用
app2.Visible=True #应用窗口可见
wb2=app2.Workbooks.Open(root+r'/data.xlsx') #添加工作簿
sht2=wb2.Sheets('Sheet1') #获取第1个工作表
shp=sht2.Shapes.AddChart2() #创建空白图表
shp.Left=20
cht=shp.Chart #获取图表
cht.ChartType=-4169 #散点图
ax1=cht.Axes(1) #获取横轴
ax2=cht.Axes(2) #获取纵轴
ax1.MinimumScale=0 #横轴最小值
ax1.MaximumScale=7
ax2.MinimumScale=0 #纵轴最小值
ax2.MaximumScale=0.35
set_style(cht) #设置样式
cht.SeriesCollection().NewSeries() #新建序列
count1=count2=count3=count4=count5=count6=0
d1=[];d2=[];d3=[];d4=[];d5=[];d6=[]
#遍历原始数据,根据第2列的唯一值对第1列数据进行筛选
#将当前值写入对应的列表,该列表中元素个数加1
for i in range(100):
if data[i][1]==1:
count1+=1
d1.append(data[i][0])
elif data[i][1]==2:
count2+=1
d2.append(data[i][0])
elif data[i][1]==3:
count3+=1
d3.append(data[i][0])
elif data[i][1]==4:
count4+=1
d4.append(data[i][0])
elif data[i][1]==5:
count5+=1
d5.append(data[i][0])
elif data[i][1]==6:
count6+=1
d6.append(data[i][0])
#绘制柱状图
draw_bar(cht,d1,count1,1,76,200,132,0.5,False)
draw_bar(cht,d2,count2,2,76,200,132,0.5,False)
draw_bar(cht,d3,count3,3,76,200,132,0.5,False)
draw_bar(cht,d4,count4,4,76,200,132,0.5,False)
draw_bar(cht,d5,count5,5,76,200,132,0.5,False)
draw_bar(cht,d6,count6,6,76,200,132,0.5,False)
#绘制抖动散点图
draw_rnd_scatter(cht,1,d1,count1,0.5,192,0,0)
draw_rnd_scatter(cht,2,d2,count2,0.5,255,192,0)
draw_rnd_scatter(cht,3,d3,count3,0.5,146,208,80)
draw_rnd_scatter(cht,4,d4,count4,0.5,0,176,80)
draw_rnd_scatter(cht,5,d5,count5,0.5,0,176,240)
draw_rnd_scatter(cht,6,d6,count6,0.5,0,112,192)
运行代码生成图2-34。
自定义三角形柱状图
2.3.1小节和2.3.2小节用多边形绘制了四边形,这里用多边形绘制三角形,用三角形代替柱状图中的矩形柱面,并用渐变色填充三角形柱面,如图2-35所示。
图2-35 三角形柱状图
下面的代码实现三角形柱状图的绘制,并用渐变色对三角形进行填充。完整代码见:Samples->ch05 创建新图表->33 三角形柱状图->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') #获取指定工作表对象
data=sht.range('A2:B7').value #获取数据
app.kill() #退出应用
#从comtypes包中导入CreateObject函数
from comtypes.client import CreateObject
app2=CreateObject("Excel.Application") #创建Excel应用
app2.Visible=True #应用窗口可见
wb2=app2.Workbooks.Open(root+r'/data.xlsx') #添加工作簿
sht2=wb2.Sheets('Sheet1') #获取第1个工作表
shp=sht2.Shapes.AddChart2() #创建空白图表
shp.Left=20
cht=shp.Chart #获取图表
cht.ChartType=-4169 #散点图 #散点图
ax1=cht.Axes(1) #获取横轴
ax2=cht.Axes(2) #获取纵轴
ax1.MinimumScale=0 #横轴最小值
ax1.MaximumScale=7
ax2.MinimumScale=0 #纵轴最小值
ax2.MaximumScale=0.22
set_style(cht) #设置样式
cht.SeriesCollection().NewSeries() #新建序列
#画三角形柱面
pt=[[0 for _ in range(2)] for _ in range(4)]
for i in range(6):
pt[0][0]=shape_x(cht,i+1-0.25)
pt[0][1]=shape_y(cht,0)
pt[1][0]=shape_x(cht,i+1+0.25)
pt[1][1]=shape_y(cht,0)
pt[2][0]=shape_x(cht,i+1)
pt[2][1]=shape_y(cht,data[i][1])
pt[3][0]=pt[0][0]
pt[3][1]=pt[0][1]
shp=cht.Shapes.AddPolyline(pt) #在图表中添加多边形表示的三角形柱面
shp.Fill.ForeColor.RGB=xw.utils.rgb_to_int((255,192,0)) #渐变色填充
shp.Fill.TwoColorGradient(2,1)
shp.Fill.BackColor.RGB=xw.utils.rgb_to_int((240,240,240))
shp.Line.Visible=False #隐藏边线
运行代码生成图2-35。
自定义倒三角形柱状图
学术期刊中还可以看到图2-36所示的倒三角形柱状图。该图的绘制方法与三角形柱状图的类似,不同的是用多边形绘制倒三角形。
图2-36 倒三角形柱状图
下面的代码实现倒三角形柱状图的绘制,并用渐变色对三角形进行填充,添加数据标签。完整代码见:Samples->ch05 创建新图表->34 倒三角形柱状图->py.py。
#... 省略部分代码
pt=[[0 for _ in range(2)] for _ in range(4)]
#逐个绘制倒三角形柱面
for i in range(6):
pt[0][0]=shape_x(cht,i+1-0.25)
pt[0][1]=shape_y(cht,data[i][1])
pt[1][0]=shape_x(cht,i+1)
pt[1][1]=shape_y(cht,0)
pt[2][0]=shape_x(cht,i+1+0.25)
pt[2][1]=shape_y(cht,data[i][1])
pt[3][0]=pt[0][0]
pt[3][1]=pt[0][1]
shp=cht.Shapes.AddPolyline(pt) #用多边形绘制倒三角形柱面
shp.Fill.ForeColor.RGB=xw.utils.rgb_to_int((255,192,0)) #渐变色填充
shp.Fill.TwoColorGradient(2,1)
shp.Fill.BackColor.RGB=xw.utils.rgb_to_int((240,240,240))
shp.Line.Visible=False #隐藏边线
#添加数据标签
x=shape_x(cht,i+1-0.35)
y=shape_y(cht,data[i][1]+0.025)
w=cht.PlotArea.InsideWidth/(ax1.MaximumScale-ax1.MinimumScale)*1
h=cht.PlotArea.InsideHeight/(ax2.MaximumScale-ax2.MinimumScale)*0.04
shp2=cht.Shapes.AddLabel(1,x,y,w,h) #添加标签
shp2.TextFrame.Characters().Text=str(data[i][1]) #标签文本
shp2.TextFrame.Characters().Font.Color=xw.utils.rgb_to_int((0,0,0))
shp2.TextFrame.Characters().Font.Size=8
运行代码生成图2-36。