热力图根据矩阵元素数据的大小与指定的颜色查找表建立映射关系,并对各单元进行索引着色。常见的热力图包括普通热力图、圆圈热力图、方块热力图和三角形方块热力图等多种类型。[大谦Excel,dqexcel点com]
普通热力图
热力图常用于矩阵数据的可视化,如图4-28所示。热力图用一个方块表示矩阵中的一个元素。将矩阵数据与颜色查找表之间建立映射关系,将数据的最小值对应色条中的最下端的红色,将最大值对应色条中最上端的黄色,中间的值通过线性插值得到颜色并绘制对应的方块,就得到热力图。
图4-28 热力图
图4-29 给热力图添加数据标签
将公式修改为:=heatmap(A1:J11,,,,,,,,2,TRUE),回车,给热力图添加数据标签并设置颜色查找表为第2种,效果如图4-30所示。
图4-30 修改热力图的颜色查找表
用Python xlwings自己编程绘制热力图,绘制网格,根据数据大小在网格中绘制相应颜色的矩形区域。本例使用了颜色查找表进行着色,相关内容请参见第4章。完整代码见:Samples->ch07 数值型图表->21 热力图->py.py。
import xlwings as xw #导入xlwings包
import os #导入OS包
def draw_hot(wb):
#绘制分箱散点图
sht=wb.sheets('Sheet1') #获取指定工作表对象
shp=sht.api.Shapes.AddChart2() #创建空白图表
shp.Left=20 #图表位置和大小
shp.Top=20
shp.Width=380
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=10.3
ax2.MinimumScale=-1
ax2.MaximumScale=10
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(9)] for _ in range(9)]
data3=[[0 for _ in range(9)] for _ in range(9)]
data=sht.range('B2:J10').value
minv=1000
maxv=-1000
for i in range(9):
for j in range(9):
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(9):
for j in range(9):
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(9):
for j in range(8,-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)
tp=shape_y(cht,j+1)
wd=cht.PlotArea.InsideWidth/(cht.Axes(1).MaximumScale-\
cht.Axes(1).MinimumScale)*1
ht=cht.PlotArea.InsideHeight/(cht.Axes(2).MaximumScale-\
cht.Axes(2).MinimumScale)*1
shp3=cht.Shapes.AddShape(1,lf,tp,wd,ht) #绘矩形
shp3.Fill.ForeColor.RGB=xw.utils.rgb_to_int((r,g,b)) #颜色
shp3.Line.Visible=False #隐藏边线
#画网格
for i in range(10):
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
#画色条
lf=shape_x(cht,9.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((255,255,26)) #多色渐变色填充
shp4.Fill.OneColorGradient(1,1,1)
shp4.Fill.GradientStops.Insert(xw.utils.rgb_to_int((255,0,0)),1)
shp4.Fill.GradientStops.Delete(2)
#色条标签
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,10.9)
tp=shape_y(cht,label_pos[i])
wd=cht.PlotArea.InsideWidth/(cht.Axes(1).MaximumScale-cht.Axes(1).MinimumScale)*1.2
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(9)]
ylabels=[0 for _ in range(9)]
for i in range(9):
ylabel_pos[i]=9-i
for i in range(9):
ylabels[i]=str(i+1)
for i in range(9):
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(9)]
xlabels=[0 for _ in range(9)]
for i in range(9):
xlabel_pos[i]=i
for i in range(9):
xlabels[i]=str(i+1)
for i in range(9):
lf=shape_x(cht,xlabel_pos[i]+0.2)
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,3.5)
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
#shp9.TextFrame2.AutoSize=msoAutoSizeTextToFitShape
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_hot(wb)
运行代码生成类似图4-28的热力图。
圆圈热力图
图4-31所示的热力图称为圆圈热力图,它在图表的每个方框中用颜色和大小不一的圆形面表示矩阵数据的大小。
图4-31 圆圈热力图
用Python xlwings自己编程绘制圆圈热力图,绘制网格,根据数据大小在网格中绘制相应颜色和大小的圆形区域。本例使用了颜色查找表进行着色,相关内容请参见第4章。完整代码见:Samples->ch07 数值型图表->22 圆圈热力图->py.py。
import xlwings as xw #导入xlwings包
import os #导入OS包
def draw_hot(wb):
#绘制圆圈热力图
sht=wb.sheets('Sheet1') #获取指定工作表对象
shp=sht.api.Shapes.AddChart2() #创建空白图表
shp.Left=20 #图表位置和大小
shp.Top=20
shp.Width=380
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=10.3
ax2.MinimumScale=-1
ax2.MaximumScale=10
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(9)] for _ in range(9)]
data3=[[0 for _ in range(9)] for _ in range(9)]
data=sht.range('B2:J10').value
minv=1000
maxv=-1000
for i in range(9):
for j in range(9):
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(9):
for j in range(9):
data2[i][j]=(data[i][j]-minv)/difv
for i in range(9):
for j in range(9):
data3[i][j]=data2[8-i][j]
#导入颜色查找表
cm=wb.sheets('Sheet2').range('A1:C256').value
#画圆圈
for i in range(9):
for j in range(8,-1,-1):
w=data3[j][i]
mg=(1-w)/2
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+mg)
tp=shape_y(cht,j+1-mg)
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
#画网格
for i in range(10):
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((100,100,100))
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((100,100,100))
shp2.Line.Weight=1
#画色条
lf=shape_x(cht,9.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((255,255,26))
shp4.Fill.OneColorGradient(1,1,1)
shp4.Fill.GradientStops.Insert(xw.utils.rgb_to_int((255,0,0)),1)
shp4.Fill.GradientStops.Delete(2)
#色条标签
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,10.9)
tp=shape_y(cht,label_pos[i])
wd=cht.PlotArea.InsideWidth/(cht.Axes(1).MaximumScale-cht.Axes(1).MinimumScale)*1.2
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(9)]
ylabels=[0 for _ in range(9)]
for i in range(9):
ylabel_pos[i]=9-i
for i in range(9):
ylabels[i]=str(i+1)
for i in range(9):
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(9)]
xlabels=[0 for _ in range(9)]
for i in range(9):
xlabel_pos[i]=i
for i in range(9):
xlabels[i]=str(i+1)
for i in range(9):
lf=shape_x(cht,xlabel_pos[i]+0.2)
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,3.5)
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
#shp9.TextFrame2.AutoSize=msoAutoSizeTextToFitShape
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_hot(wb)
运行代码生成类似图4-31的圆圈热力图。
方块热力图
图4-32所示的热力图称为方块热力图,它在图表的每个方框中用颜色和大小不一的正方形面表示矩阵数据的大小。
图4-32 方块热力图
用Python xlwings自己编程绘制方块热力图,绘制网格,根据数据大小在网格中绘制相应颜色和大小的矩形区域。本例使用了颜色查找表进行着色,相关内容请参见第4章。完整代码见:Samples->ch07 数值型图表->23 方块热力图->py.py。
#... 省略部分代码
#画方块
for i in range(9):
for j in range(8,-1,-1):
w=data3[j][i]
mg=(1-w)/2
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+mg)
tp=shape_y(cht,j+1-mg)
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(1,lf,tp,wd,ht) #绘矩形
shp3.Fill.ForeColor.RGB=xw.utils.rgb_to_int((r,g,b)) #颜色
shp3.Line.Visible=False #隐藏边线
运行代码生成类似图4-32的方块热力图。
三角形方块热力图
对称矩阵关于对角线对称,上三角的数据和下三角的数据完全相同,所以绘图时常常只绘一半,要么是上三角,要么是下三角。图4-33中绘制下三角矩阵对应的三角形方块热力图。
图4-33 三角形方块热力图
用Python xlwings自己编程绘制三角形方块热力图,绘制网格,根据数据大小在网格中绘制相应颜色和大小的矩形区域。本例使用了颜色查找表进行着色,相关内容请参见第4章。完整代码见:Samples->ch07 数值型图表->24 三角形方块热力图->py.py。
#... 省略部分代码
#画方块
for i in range(9):
for j in range(8-i,-1,-1):
w=data3[j][i]
mg=(1-w)/2
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+mg)
tp=shape_y(cht,j+1-mg)
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(1,lf,tp,wd,ht)
shp3.Fill.ForeColor.RGB=xw.utils.rgb_to_int((r,g,b))
shp3.Line.Visible=False
#画网格
sx1=shape_x(cht,0)
sy1=shape_y(cht,0)
sx2=shape_x(cht,0)
sy2=shape_y(cht,9)
shp1=cht.Shapes.AddLine(sx1,sy1,sx2,sy2)
shp1.Line.ForeColor.RGB=xw.utils.rgb_to_int((100,100,100))
shp1.Line.Weight=1
for i in range(1,10):
for j in range(10-i,-1,-1):
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((100,100,100))
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((100,100,100))
shp2.Line.Weight=1
#... 省略部分代码
运行代码生成类似图4-33的三角形方块热力图。