热力图

热力图根据矩阵元素数据的大小与指定的颜色查找表建立映射关系,并对各单元进行索引着色。常见的热力图包括普通热力图、圆圈热力图、方块热力图和三角形方块热力图等多种类型。[大谦Excel,dqexcel点com]

普通热力图

热力图常用于矩阵数据的可视化,如图4-28所示。热力图用一个方块表示矩阵中的一个元素。将矩阵数据与颜色查找表之间建立映射关系,将数据的最小值对应色条中的最下端的红色,将最大值对应色条中最上端的黄色,中间的值通过线性插值得到颜色并绘制对应的方块,就得到热力图。

Document Image
\[\]

图4-28 热力图

Document Image
\[\]

图4-29 给热力图添加数据标签

将公式修改为:=heatmap(A1:J11,,,,,,,,2,TRUE),回车,给热力图添加数据标签并设置颜色查找表为第2种,效果如图4-30所示。

Document Image
\[\]

图4-30 修改热力图的颜色查找表

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

code.vba
Sub Test()
  '省略部分代码
  Dim data()
  Dim data2(1 To 9, 1 To 9) As Double
  Dim data3(1 To 9, 1 To 9) As Double
  data = ActiveSheet.Range("B2:J10").Value
  Dim minV As Double
  Dim maxV As Double
  Dim difV As Double
  minV = 1000
  maxV = -1000
  For intI = 1 To 9
    For intJ = 1 To 9
      If minV > data(intI, intJ) Then minV = data(intI, intJ)
      If maxV < data(intI, intJ) Then maxV = data(intI, intJ)
    Next
  Next
  difV = maxV - minV
  For intI = 1 To 9
    For intJ = 1 To 9
      data2(intI, intJ) = (data(intI, intJ) - minV) / difV
    Next
  Next
  For intI = 1 To 9
    For intJ = 1 To 9
      data3(intI, intJ) = data2(9 - intI + 1, intJ)
    Next
  Next
  Dim cm()
  cm = ActiveWorkbook.Sheets(2).Range("A1:C256").Value
  Dim sx1 As Double
  Dim sy1 As Double
  Dim sx2 As Double
  Dim sy2 As Double
  Dim shp1 As Shape
  Dim shp2 As Shape
  Dim shp3 As Shape
  For intI = 0 To 9
    For intJ = 0 To 9
      sx1 = ShapeX(cht, intI)
      sy1 = ShapeY(cht, 0)
      sx2 = ShapeX(cht, intI)
      sy2 = ShapeY(cht, intJ)
      Set shp1 = cht.Shapes.AddLine(sx1, sy1, sx2, sy2)
      shp1.Line.ForeColor.RGB = RGB(0, 0, 0)
      shp1.Line.Weight = 1
      sx1 = ShapeX(cht, 0)
      sy1 = ShapeY(cht, intJ)
      sx2 = ShapeX(cht, intI)
      sy2 = ShapeY(cht, intJ)
      Set shp2 = cht.Shapes.AddLine(sx1, sy1, sx2, sy2)
      shp2.Line.ForeColor.RGB = RGB(0, 0, 0)
      shp2.Line.Weight = 1
    Next
  Next
  Dim w As Double
  Dim w2 As Double
  Dim mg As Double
  Dim lf As Double
  Dim tp As Double
  Dim wd As Double
  Dim ht As Double
  Dim count As Integer
  Dim intR As Integer
  Dim intG As Integer
  Dim intB As Integer
  For intI = 1 To 9
    For intJ = 9 To 1 Step -1
      w = data3(intJ, intI)
      If Int(w * 256) = 0 Then
        count = 1
        intR = cm(1, 1)
        intG = cm(1, 2)
        intB = cm(1, 3)
      Else
        count = Int(w * 256)
        intR = cm(count, 1)
        intG = cm(count, 2)
        intB = cm(count, 3)
      End If
      lf = ShapeX(cht, intI - 1)
      tp = ShapeY(cht, intJ)
      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
      Set shp3 = cht.Shapes.AddShape(msoShapeRectangle, lf, tp, wd, ht)
      shp3.Fill.ForeColor.RGB = RGB(intR, intG, intB)
    Next
  Next
  Dim shp4 As Shape
  lf = ShapeX(cht, 9.5)
  tp = ShapeY(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
  Set shp4 = cht.Shapes.AddShape(msoShapeRectangle, lf, tp, wd, ht)
  With shp4.Fill
    .ForeColor.RGB = RGB(255, 255, 26)
    .OneColorGradient msoGradientHorizontal, 1, 1
    .GradientStops.Insert RGB(255, 0, 0), 1
    .GradientStops.Delete(2)
  End With
  Dim shp5 As Shape
  Dim cmLabelPos(1 To 3) As Double
  Dim cmLabels(1 To 3) As Double
  cmLabelPos(1) = 8.2
  cmLabelPos(2) = 6.9
  cmLabelPos(3) = 5.3
  cmLabels(1) = maxV
  cmLabels(2) = (maxV + minV) / 2
  cmLabels(3) = minV
  For intI = 1 To 3
    lf = ShapeX(cht, 10.9)
    tp = ShapeY(cht, cmLabelPos(intI))
    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
    Set shp5 = cht.Shapes.AddLabel(msoTextOrientationHorizontal, lf, tp, wd, ht)
    shp5.TextFrame2.TextRange.Characters.Text = Format(CStr(cmLabels(intI)), "0.00")
    shp5.TextFrame2.TextRange.Characters.Font.Size = 8
    shp5.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
  Next
  Dim shp6 As Shape
  Dim tk1LabelPos(1 To 9) As Double
  Dim tk1Labels(1 To 9) As Double
  For intI = 1 To 9
    tk1LabelPos(intI) = 9 - intI + 1
  Next
  For intI = 1 To 9
    tk1Labels(intI) = intI
  Next
  For intI = 1 To 9
    lf = ShapeX(cht, -0.6)
    tp = ShapeY(cht, tk1LabelPos(intI) - 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
    Set shp6 = cht.Shapes.AddLabel(msoTextOrientationHorizontal, lf, tp, wd, ht)
    shp6.TextFrame2.TextRange.Characters.Text = Format(CStr(tk1Labels(intI)), "0")
    shp6.TextFrame2.TextRange.Characters.Font.Size = 8
    shp6.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
  Next
  Dim shp7 As Shape
  Dim tk2LabelPos(1 To 9) As Double
  Dim tk2Labels(1 To 9) As Double
  For intI = 0 To 8
    tk2LabelPos(intI + 1) = intI
  Next
  For intI = 1 To 9
    tk2Labels(intI) = intI
  Next
  For intI = 1 To 9
    lf = ShapeX(cht, tk2LabelPos(intI) + 0.2)
    tp = ShapeY(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
    Set shp7 = cht.Shapes.AddLabel(msoTextOrientationHorizontal, lf, tp, wd, ht)
    shp7.TextFrame2.TextRange.Characters.Text = Format(CStr(tk2Labels(intI)), "0")
    shp7.TextFrame2.TextRange.Characters.Font.Size = 8
    shp7.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
  Next
  Dim shp8 As Shape
  lf = ShapeX(cht, 3.5)
  tp = ShapeY(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
  Set shp8 = cht.Shapes.AddLabel(msoTextOrientationHorizontal, lf, tp, wd, ht)
  shp8.TextFrame2.TextRange.Characters.Text = "X Axis Label"
  shp8.TextFrame2.TextRange.Characters.Font.Size = 10
  shp8.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
  Dim shp9 As Shape
  lf = ShapeX(cht, -0.9)
  tp = ShapeY(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
  Set shp9 = cht.Shapes.AddLabel(msoTextOrientationVertical, lf, tp, wd, ht)
  shp9.TextFrame2.TextRange.Characters.Text = "Y Axis Label"
  shp9.TextFrame2.TextRange.Characters.Font.Size = 10
  shp9.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
End Sub

运行代码生成类似图4-28的热力图。

圆圈热力图

图4-31所示的热力图称为圆圈热力图,它在图表的每个方框中用颜色和大小不一的圆形面表示矩阵数据的大小。

Document Image
\[\]

图4-31 圆圈热力图

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

code.vba
Sub Test()
  '省略部分代码
  Dim data()
  Dim data2(1 To 9, 1 To 9) As Double
  Dim data3(1 To 9, 1 To 9) As Double
  data = ActiveSheet.Range("B2:J10").Value
  Dim minV As Double
  Dim maxV As Double
  Dim difV As Double
  minV = 1000
  maxV = -1000
  For intI = 1 To 9
    For intJ = 1 To 9
      If minV > data(intI, intJ) Then minV = data(intI, intJ)
      If maxV < data(intI, intJ) Then maxV = data(intI, intJ)
    Next
  Next
  difV = maxV - minV
  For intI = 1 To 9
    For intJ = 1 To 9
      data2(intI, intJ) = (data(intI, intJ) - minV) / difV
    Next
  Next
    For intI = 1 To 9
    For intJ = 1 To 9
      data3(intI, intJ) = data2(9 - intI + 1, intJ)
    Next
  Next
  Dim cm()
  cm = ActiveWorkbook.Sheets(2).Range("A1:C256").Value
  Dim sx1 As Double
  Dim sy1 As Double
  Dim sx2 As Double
  Dim sy2 As Double
  Dim shp1 As Shape
  Dim shp2 As Shape
  Dim shp3 As Shape
  For intI = 0 To 9
    For intJ = 0 To 9
      sx1 = ShapeX(cht, intI)
      sy1 = ShapeY(cht, 0)
      sx2 = ShapeX(cht, intI)
      sy2 = ShapeY(cht, intJ)
      Set shp1 = cht.Shapes.AddLine(sx1, sy1, sx2, sy2)
      shp1.Line.ForeColor.RGB = RGB(0, 0, 0)
      shp1.Line.Weight = 1
      sx1 = ShapeX(cht, 0)
      sy1 = ShapeY(cht, intJ)
      sx2 = ShapeX(cht, intI)
      sy2 = ShapeY(cht, intJ)
      Set shp2 = cht.Shapes.AddLine(sx1, sy1, sx2, sy2)
      shp2.Line.ForeColor.RGB = RGB(0, 0, 0)
      shp2.Line.Weight = 1
    Next
  Next
  Dim w As Double
  Dim w2 As Double
  Dim mg As Double
  Dim lf As Double
  Dim tp As Double
  Dim wd As Double
  Dim ht As Double
  Dim count As Integer
  Dim intR As Integer
  Dim intG As Integer
  Dim intB As Integer
  For intI = 1 To 9
    For intJ = 9 To 1 Step -1
      w = data3(intJ, intI)
      mg = (1 - w) / 2
      If Int(w * 256) = 0 Then
        count = 1
        intR = cm(1, 1)
        intG = cm(1, 2)
        intB = cm(1, 3)
      Else
        count = Int(w * 256)
        intR = cm(count, 1)
        intG = cm(count, 2)
        intB = cm(count, 3)
      End If
      lf = ShapeX(cht, intI - 1 + mg)
      tp = ShapeY(cht, intJ - 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
      Set shp3 = cht.Shapes.AddShape(9, lf, tp, wd, ht)
      shp3.Fill.ForeColor.RGB = RGB(intR, intG, intB)
      shp3.Line.ForeColor.RGB = RGB(255, 255, 255)
    Next
  Next
  Dim shp4 As Shape
  lf = ShapeX(cht, 9.5)
  tp = ShapeY(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
  Set shp4 = cht.Shapes.AddShape(msoShapeRectangle, lf, tp, wd, ht)
  With shp4.Fill
    .ForeColor.RGB = RGB(255, 255, 26)
    .OneColorGradient msoGradientHorizontal, 1, 1
    .GradientStops.Insert RGB(255, 0, 0), 1
    .GradientStops.Delete(2)
  End With
  Dim shp5 As Shape
  Dim cmLabelPos(1 To 3) As Double
  Dim cmLabels(1 To 3) As Double
  cmLabelPos(1) = 8.2
  cmLabelPos(2) = 6.9
  cmLabelPos(3) = 5.3
  cmLabels(1) = maxV
  cmLabels(2) = (maxV + minV) / 2
  cmLabels(3) = minV
  For intI = 1 To 3
    lf = ShapeX(cht, 10.7)
    tp = ShapeY(cht, cmLabelPos(intI))
    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
    Set shp5 = cht.Shapes.AddLabel(msoTextOrientationHorizontal, lf, tp, wd, ht)
    shp5.TextFrame2.TextRange.Characters.Text = Format(CStr(cmLabels(intI)), "0.00")
    shp5.TextFrame2.TextRange.Characters.Font.Size = 8
    shp5.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
  Next
  Dim shp6 As Shape
  Dim tk1LabelPos(1 To 9) As Double
  Dim tk1Labels(1 To 9) As Double
  For intI = 1 To 9
    tk1LabelPos(intI) = 9 - intI + 1
  Next
  For intI = 1 To 9
    tk1Labels(intI) = intI
  Next
  For intI = 1 To 9
    lf = ShapeX(cht, -0.6)
    tp = ShapeY(cht, tk1LabelPos(intI) - 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
    Set shp6 = cht.Shapes.AddLabel(msoTextOrientationHorizontal, lf, tp, wd, ht)
    shp6.TextFrame2.TextRange.Characters.Text = Format(CStr(tk1Labels(intI)), "0")
    shp6.TextFrame2.TextRange.Characters.Font.Size = 8
    shp6.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
  Next
  Dim shp7 As Shape
  Dim tk2LabelPos(1 To 9) As Double
  Dim tk2Labels(1 To 9) As Double
  For intI = 0 To 8
    tk2LabelPos(intI + 1) = intI
  Next
  For intI = 1 To 9
    tk2Labels(intI) = intI
  Next
  For intI = 1 To 9
    lf = ShapeX(cht, tk2LabelPos(intI) + 0.2)
    tp = ShapeY(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
    Set shp7 = cht.Shapes.AddLabel(msoTextOrientationHorizontal, lf, tp, wd, ht)
    shp7.TextFrame2.TextRange.Characters.Text = Format(CStr(tk2Labels(intI)), "0")
    shp7.TextFrame2.TextRange.Characters.Font.Size = 8
    shp7.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
  Next
  Dim shp8 As Shape
  lf = ShapeX(cht, 3.5)
  tp = ShapeY(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
  Set shp8 = cht.Shapes.AddLabel(msoTextOrientationHorizontal, lf, tp, wd, ht)
  shp8.TextFrame2.TextRange.Characters.Text = "X Axis Label"
  shp8.TextFrame2.TextRange.Characters.Font.Size = 10
  shp8.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
  Dim shp9 As Shape
  lf = ShapeX(cht, -0.9)
  tp = ShapeY(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
  Set shp9 = cht.Shapes.AddLabel(msoTextOrientationVertical, lf, tp, wd, ht)
  shp9.TextFrame2.TextRange.Characters.Text = "Y Axis Label"
  shp9.TextFrame2.TextRange.Characters.Font.Size = 10
  shp9.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
End Sub

运行代码生成类似图4-31的圆圈热力图。

方块热力图

图4-32所示的热力图称为方块热力图,它在图表的每个方框中用颜色和大小不一的正方形面表示矩阵数据的大小。

Document Image
\[\]

图4-32 方块热力图

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

code.vba
Sub Test()
  '省略部分代码
  Dim data()
  Dim data2(1 To 9, 1 To 9) As Double
  Dim data3(1 To 9, 1 To 9) As Double
  data = ActiveSheet.Range("B2:J10").Value
  Dim minV As Double
  Dim maxV As Double
  Dim difV As Double
  minV = 1000
  maxV = -1000
  For intI = 1 To 9
    For intJ = 1 To 9
      If minV > data(intI, intJ) Then minV = data(intI, intJ)
      If maxV < data(intI, intJ) Then maxV = data(intI, intJ)
    Next
  Next
  difV = maxV - minV
  For intI = 1 To 9
    For intJ = 1 To 9
      data2(intI, intJ) = (data(intI, intJ) - minV) / difV
    Next
  Next
    For intI = 1 To 9
    For intJ = 1 To 9
      data3(intI, intJ) = data2(9 - intI + 1, intJ)
    Next
  Next
  Dim cm()
  cm = ActiveWorkbook.Sheets(2).Range("A1:C256").Value
  Dim sx1 As Double
  Dim sy1 As Double
  Dim sx2 As Double
  Dim sy2 As Double
  Dim shp1 As Shape
  Dim shp2 As Shape
  Dim shp3 As Shape
  For intI = 0 To 9
    For intJ = 0 To 9
      sx1 = ShapeX(cht, intI)
      sy1 = ShapeY(cht, 0)
      sx2 = ShapeX(cht, intI)
      sy2 = ShapeY(cht, intJ)
      Set shp1 = cht.Shapes.AddLine(sx1, sy1, sx2, sy2)
      shp1.Line.ForeColor.RGB = RGB(0, 0, 0)
      shp1.Line.Weight = 1
      sx1 = ShapeX(cht, 0)
      sy1 = ShapeY(cht, intJ)
      sx2 = ShapeX(cht, intI)
      sy2 = ShapeY(cht, intJ)
      Set shp2 = cht.Shapes.AddLine(sx1, sy1, sx2, sy2)
      shp2.Line.ForeColor.RGB = RGB(0, 0, 0)
      shp2.Line.Weight = 1
    Next
  Next
  Dim w As Double
  Dim w2 As Double
  Dim mg As Double
  Dim lf As Double
  Dim tp As Double
  Dim wd As Double
  Dim ht As Double
  Dim count As Integer
  Dim intR As Integer
  Dim intG As Integer
  Dim intB As Integer
  For intI = 1 To 9
    For intJ = 9 To 1 Step -1
      w = data3(intJ, intI)
      mg = (1 - w) / 2
      If Int(w * 256) = 0 Then
        count = 1
        intR = cm(1, 1)
        intG = cm(1, 2)
        intB = cm(1, 3)
      Else
        count = Int(w * 256)
        intR = cm(count, 1)
        intG = cm(count, 2)
        intB = cm(count, 3)
      End If
      lf = ShapeX(cht, intI - 1 + mg)
      tp = ShapeY(cht, intJ - 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
      Set shp3 = cht.Shapes.AddShape(msoShapeRectangle, lf, tp, wd, ht)
      shp3.Fill.ForeColor.RGB = RGB(intR, intG, intB)
      'shp3.Line.ForeColor.RGB = RGB(255, 255, 255)
      shp3.Line.Weight = 1
    Next
  Next
  Dim shp4 As Shape
  lf = ShapeX(cht, 9.5)
  tp = ShapeY(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
  Set shp4 = cht.Shapes.AddShape(msoShapeRectangle, lf, tp, wd, ht)
  With shp4.Fill
    .ForeColor.RGB = RGB(255, 255, 26)
    .OneColorGradient msoGradientHorizontal, 1, 1
    .GradientStops.Insert RGB(255, 0, 0), 1
    .GradientStops.Delete(2)
  End With
  Dim shp5 As Shape
  Dim cmLabelPos(1 To 3) As Double
  Dim cmLabels(1 To 3) As Double
  cmLabelPos(1) = 8.2
  cmLabelPos(2) = 6.9
  cmLabelPos(3) = 5.3
  cmLabels(1) = maxV
  cmLabels(2) = (maxV + minV) / 2
  cmLabels(3) = minV
  For intI = 1 To 3
    lf = ShapeX(cht, 10.7)
    tp = ShapeY(cht, cmLabelPos(intI))
    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
    Set shp5 = cht.Shapes.AddLabel(msoTextOrientationHorizontal, lf, tp, wd, ht)
    shp5.TextFrame2.TextRange.Characters.Text = Format(CStr(cmLabels(intI)), "0.00")
    shp5.TextFrame2.TextRange.Characters.Font.Size = 8
    shp5.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
  Next
  Dim shp6 As Shape
  Dim tk1LabelPos(1 To 9) As Double
  Dim tk1Labels(1 To 9) As Double
  For intI = 1 To 9
    tk1LabelPos(intI) = 9 - intI + 1
  Next
  For intI = 1 To 9
    tk1Labels(intI) = intI
  Next
  For intI = 1 To 9
    lf = ShapeX(cht, -0.6)
    tp = ShapeY(cht, tk1LabelPos(intI) - 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
    Set shp6 = cht.Shapes.AddLabel(msoTextOrientationHorizontal, lf, tp, wd, ht)
    shp6.TextFrame2.TextRange.Characters.Text = Format(CStr(tk1Labels(intI)), "0")
    shp6.TextFrame2.TextRange.Characters.Font.Size = 8
    shp6.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
  Next
  Dim shp7 As Shape
  Dim tk2LabelPos(1 To 9) As Double
  Dim tk2Labels(1 To 9) As Double
  For intI = 0 To 8
    tk2LabelPos(intI + 1) = intI
  Next
  For intI = 1 To 9
    tk2Labels(intI) = intI
  Next
  For intI = 1 To 9
    lf = ShapeX(cht, tk2LabelPos(intI) + 0.2)
    tp = ShapeY(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
    Set shp7 = cht.Shapes.AddLabel(msoTextOrientationHorizontal, lf, tp, wd, ht)
    shp7.TextFrame2.TextRange.Characters.Text = Format(CStr(tk2Labels(intI)), "0")
    shp7.TextFrame2.TextRange.Characters.Font.Size = 8
    shp7.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
  Next
  Dim shp8 As Shape
  lf = ShapeX(cht, 3.5)
  tp = ShapeY(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
  Set shp8 = cht.Shapes.AddLabel(msoTextOrientationHorizontal, lf, tp, wd, ht)
  shp8.TextFrame2.TextRange.Characters.Text = "X Axis Label"
  shp8.TextFrame2.TextRange.Characters.Font.Size = 10
  shp8.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
  Dim shp9 As Shape
  lf = ShapeX(cht, -0.9)
  tp = ShapeY(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
  Set shp9 = cht.Shapes.AddLabel(msoTextOrientationVertical, lf, tp, wd, ht)
  shp9.TextFrame2.TextRange.Characters.Text = "Y Axis Label"
  shp9.TextFrame2.TextRange.Characters.Font.Size = 10
  shp9.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
End Sub

运行代码生成类似图4-32的方块热力图。

三角形方块热力图

对称矩阵关于对角线对称,上三角的数据和下三角的数据完全相同,所以绘图时常常只绘一半,要么是上三角,要么是下三角。图4-33中绘制下三角矩阵对应的三角形方块热力图。

Document Image
\[\]

图4-33 三角形方块热力图

用Python xlwings自己编程绘制三角形方块热力图,绘制网格,根据数据大小在网格中绘制相应颜色和大小的矩形区域。本例使用了颜色查找表进行着色,相关内容请参见第4章。完整代码见:Samples->ch07 数值型图表->24 三角形方块热力图->py.py。

code.vba
Sub Test()
  '省略部分代码
  Dim data()
  Dim data2(1 To 9, 1 To 9) As Double
  Dim data3(1 To 9, 1 To 9) As Double
  data = ActiveSheet.Range("B2:J10").Value
  Dim minV As Double
  Dim maxV As Double
  Dim difV As Double
  minV = 1000
  maxV = -1000
  For intI = 1 To 9
    For intJ = 1 To 9
      If minV > data(intI, intJ) Then minV = data(intI, intJ)
      If maxV < data(intI, intJ) Then maxV = data(intI, intJ)
    Next
  Next
  difV = maxV - minV
  For intI = 1 To 9
    For intJ = 1 To 9
      data2(intI, intJ) = (data(intI, intJ) - minV) / difV
    Next
  Next
    For intI = 1 To 9
    For intJ = 1 To 9
      data3(intI, intJ) = data2(9 - intI + 1, intJ)
    Next
  Next
  Dim cm()
  cm = ActiveWorkbook.Sheets(2).Range("A1:C256").Value
  Dim sx1 As Double
  Dim sy1 As Double
  Dim sx2 As Double
  Dim sy2 As Double
  Dim shp1 As Shape
  Dim shp2 As Shape
  Dim shp3 As Shape
  sx1 = ShapeX(cht, 0)
  sy1 = ShapeY(cht, 0)
  sx2 = ShapeX(cht, 0)
  sy2 = ShapeY(cht, 9)
  Set shp1 = cht.Shapes.AddLine(sx1, sy1, sx2, sy2)
  shp1.Line.ForeColor.RGB = RGB(0, 0, 0)
  shp1.Line.Weight = 1
  For intI = 1 To 9
    For intJ = 9 - intI + 1 To 0 Step -1
      sx1 = ShapeX(cht, intI)
      sy1 = ShapeY(cht, 0)
      sx2 = ShapeX(cht, intI)
      sy2 = ShapeY(cht, intJ)
      Set shp1 = cht.Shapes.AddLine(sx1, sy1, sx2, sy2)
      shp1.Line.ForeColor.RGB = RGB(0, 0, 0)
      shp1.Line.Weight = 1
      sx1 = ShapeX(cht, 0)
      sy1 = ShapeY(cht, intJ)
      sx2 = ShapeX(cht, intI)
      sy2 = ShapeY(cht, intJ)
      Set shp2 = cht.Shapes.AddLine(sx1, sy1, sx2, sy2)
      shp2.Line.ForeColor.RGB = RGB(0, 0, 0)
      shp2.Line.Weight = 1
    Next
  Next
  Dim w As Double
  Dim w2 As Double
  Dim mg As Double
  Dim lf As Double
  Dim tp As Double
  Dim wd As Double
  Dim ht As Double
  Dim count As Integer
  Dim intR As Integer
  Dim intG As Integer
  Dim intB As Integer
  For intI = 1 To 9
    For intJ = 9 - intI + 1 To 1 Step -1
      w = data3(intJ, intI)
      mg = (1 - w) / 2
      If Int(w * 256) = 0 Then
        count = 1
        intR = cm(1, 1)
        intG = cm(1, 2)
        intB = cm(1, 3)
      Else
        count = Int(w * 256)
        intR = cm(count, 1)
        intG = cm(count, 2)
        intB = cm(count, 3)
      End If
      lf = ShapeX(cht, intI - 1 + mg)
      tp = ShapeY(cht, intJ - 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
      Set shp3 = cht.Shapes.AddShape(msoShapeRectangle, lf, tp, wd, ht)
      shp3.Fill.ForeColor.RGB = RGB(intR, intG, intB)
      'shp3.Line.ForeColor.RGB = RGB(255, 255, 255)
      shp3.Line.Weight = 1
    Next
  Next
  Dim shp4 As Shape
  lf = ShapeX(cht, 9.3)
  tp = ShapeY(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
  Set shp4 = cht.Shapes.AddShape(msoShapeRectangle, lf, tp, wd, ht)
  With shp4.Fill
    .ForeColor.RGB = RGB(255, 255, 26)
    .OneColorGradient msoGradientHorizontal, 1, 1
    .GradientStops.Insert RGB(255, 0, 0), 1
    .GradientStops.Delete(2)
  End With
  Dim shp5 As Shape
  Dim cmLabelPos(1 To 3) As Double
  Dim cmLabels(1 To 3) As Double
  cmLabelPos(1) = 8.2
  cmLabelPos(2) = 6.9
  cmLabelPos(3) = 5.3
  cmLabels(1) = maxV
  cmLabels(2) = (maxV + minV) / 2
  cmLabels(3) = minV
  For intI = 1 To 3
    lf = ShapeX(cht, 10.7)
    tp = ShapeY(cht, cmLabelPos(intI))
    wd = cht.PlotArea.InsideWidth / (cht.Axes(1).MaximumScale - cht.Axes(1).MinimumScale) * 1.4
    ht = cht.PlotArea.InsideHeight / (cht.Axes(2).MaximumScale - cht.Axes(2).MinimumScale) * 0.6
    Set shp5 = cht.Shapes.AddLabel(msoTextOrientationHorizontal, lf, tp, wd, ht)
    shp5.TextFrame2.TextRange.Characters.Text = Format(CStr(cmLabels(intI)), "0.00")
    shp5.TextFrame2.TextRange.Characters.Font.Size = 8
    shp5.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
  Next
  Dim shp6 As Shape
  Dim tk1LabelPos(1 To 9) As Double
  Dim tk1Labels(1 To 9) As Double
  For intI = 1 To 9
    tk1LabelPos(intI) = 9 - intI + 1
  Next
  For intI = 1 To 9
    tk1Labels(intI) = intI
  Next
  For intI = 1 To 9
    lf = ShapeX(cht, -0.6)
    tp = ShapeY(cht, tk1LabelPos(intI) - 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
    Set shp6 = cht.Shapes.AddLabel(msoTextOrientationHorizontal, lf, tp, wd, ht)
    shp6.TextFrame2.TextRange.Characters.Text = Format(CStr(tk1Labels(intI)), "0")
    shp6.TextFrame2.TextRange.Characters.Font.Size = 8
    shp6.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
  Next
  Dim shp7 As Shape
  Dim tk2LabelPos(1 To 9) As Double
  Dim tk2Labels(1 To 9) As Double
  For intI = 0 To 8
    tk2LabelPos(intI + 1) = intI
  Next
  For intI = 1 To 9
    tk2Labels(intI) = intI
  Next
  For intI = 1 To 9
    lf = ShapeX(cht, tk2LabelPos(intI) + 0.2)
    tp = ShapeY(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
    Set shp7 = cht.Shapes.AddLabel(msoTextOrientationHorizontal, lf, tp, wd, ht)
    shp7.TextFrame2.TextRange.Characters.Text = Format(CStr(tk2Labels(intI)), "0")
    shp7.TextFrame2.TextRange.Characters.Font.Size = 8
    shp7.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
  Next
  Dim shp8 As Shape
  lf = ShapeX(cht, 3.5)
  tp = ShapeY(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
  Set shp8 = cht.Shapes.AddLabel(msoTextOrientationHorizontal, lf, tp, wd, ht)
  shp8.TextFrame2.TextRange.Characters.Text = "X Axis Label"
  shp8.TextFrame2.TextRange.Characters.Font.Size = 10
  shp8.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
  Dim shp9 As Shape
  lf = ShapeX(cht, -0.9)
  tp = ShapeY(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
  Set shp9 = cht.Shapes.AddLabel(msoTextOrientationVertical, lf, tp, wd, ht)
  shp9.TextFrame2.TextRange.Characters.Text = "Y Axis Label"
  shp9.TextFrame2.TextRange.Characters.Font.Size = 10
  shp9.TextFrame2.AutoSize = msoAutoSizeTextToFitShape
End Sub

运行代码生成类似图4-33的三角形方块热力图。[大谦Excel,dqexcel点com]