估计阅读时长: 4 分钟

热图(Heat Map)是在二维空间中以颜色的形式显示一个现象的绝对量一种数据可视化技术。颜色的变化可能是通过色调或强度,给读者提供明显的视觉提示,说明现象是如何在空间上聚集或变化的。热图有两种完全不同的类别:聚集热图和空间热图。

  1. 在聚集热图中,幅度被排列成一个固定单元格大小的矩阵,其行和列是离散的现象和类别,行和列的排序是有意的,而且有些随意,目的是暗示聚集或描绘出通过统计分析发现的聚集。单元格的大小是任意的,但足够大,可以清晰可见。
  2. 相比之下,空间热图中某一量级的位置是由该量级在该空间中的位置所决定的,没有单元的概念,现象被认为是连续变化的。

最常见的表达量矩阵的热图就是一种非常典型的聚集热图。在聚集热图之中,变量之间是相互离散独立的

对于空间热图而言,所可视化的目标变量是一种在空间上连续的数据,例如对象物件图像在空间坐标系内的x坐标轴以及y坐标轴

除了上面的两种典型的热图呈现形式,在生物信息学数据分析之中,热图可视化还可以更加具体的与相应的生物学功能组合在一起。例如如果我们不再将数据通过聚集热图的形式进行展现,而是将具体的离散变量可视化在代谢通路对应的节点位置上:

热图颜色插值

三次样条插值

如果之前了解过质谱成像热图可视化的原理我们就知道,我们只需要将实际的数据做线性变化,映射到一个颜色列表之中即可。实际上按照对颜色的线性变换映射进行热图绘制的过程是非常的简单的,但是在进行线性变换映射之前,我们需要解决一个颜色列表的数据来源问题:我们在进行热图绘制的时候,会有不同的颜色等级映射需求,例如将数据分成30个等级,60个等级或者120个等级等。数据等级一般会存在好几十个等级,但是我们的颜色版中的关键颜色一般只有几种。在这种情况下我们就需要一种计算方法将我们的颜色填充到这些实际需求的数据等级中去,这种颜色填充的过程就是热图颜色插值。

在颜色插值计算过程之中,我们一般是可以通过三次样条(cubic spline)插值的方法来从基础的颜色板上获取一个连续的颜色谱。三次样条插值(Cubic Spline Interpolation)简称Spline插值,是通过一系列形值点的一条光滑曲线,数学上通过求解三弯矩方程组得出曲线函数组的过程。下面我们来讲解一下通过三次样条插值来生成连续的颜色谱的具体原理。在讲解颜色插值计算之前,我们先来看一下一个简单的三次样条插值的示意图:

关于三次样条插值的具体计算公式原理,大家可以阅读《Cubic Spline Interpolation — Python Numerical Methods》

from scipy.interpolate import CubicSpline
import numpy as np

x = [0, 1, 2]
y = [1, 3, 2]

# use bc_type = 'natural' adds the constraints as we described above
f = CubicSpline(x, y, bc_type='natural')
x_new = np.linspace(0, 2, 100)
y_new = f(x_new)

从上面的示意图之中我们可以看得见,假设我们手头上目前有y1,y2,y3,y4这四个离散的二维散点的话,通过cubic spline算法可以为我们在这4个离散点的基础上产生一条连续的曲线。那现在问题回到我们的热图颜色插值的需求上,假设我们现在有如下所示的颜色谱,该如何基于当前的这个cubic spline方法产生一个连续的颜色谱呢?

颜色插值计算过程

关键色列表

序号 颜色代码 R G B
1 #30123B 48 18 59
2 #4454C4 68 84 196
3 #4490FE 68 144 254
4 #1FC8DE 31 200 222
5 #29EFA2 41 239 162
6 #7DFF56 125 255 86
7 #C1F334 193 243 52
8 #F1CA3A 241 202 58
9 #FE922A 254 146 42
10 #EA4F0D 234 79 13
11 #BE2102 190 33 2
12 #7A0403 122 4 3

那我们现在有了进行颜色插值所需要的基础颜色列表之后,我们把颜色值对应的RGB通道值都分别取出来,这样子我们假设就得到了三个y坐标轴的数据。如果我们现在再将颜色对应的序号作为x坐标轴数据的话,那我们是不是就可以得到了类似于上面的用于曲线数据插值计算用的散点数据了呢?没错,在进行颜色插值计算的时候,我们只需要将颜色RGB三个通道的数据分别做插值(做三条曲线的插值计算)就可以得到一个连续的颜色谱了。下面所展示的就是实际的工作代码:

Public Function CubicSpline(colors As IEnumerable(Of Color), Optional n% = 256, Optional alpha% = 255) As Color()
    Dim source As Color() = colors.ToArray

    If source.Length = 1 Then
        Call $"multiple color value is required, but you just provides one color, color seqeucne will just contains one single color: {source(Scan0).ToString}".Warning

        Return source(Scan0) _
            .Alpha(alpha) _
            .Replicate(n) _
            .ToArray
    ElseIf n <= source.Length Then
        Return source.Take(n).ToArray
    End If

    Dim x As New CubicSplineVector(source.Select(Function(c) CSng(c.R)))
    Dim y As New CubicSplineVector(source.Select(Function(c) CSng(c.G)))
    Dim z As New CubicSplineVector(source.Select(Function(c) CSng(c.B)))

    Call x.CalcSpline()
    Call y.CalcSpline()
    Call z.CalcSpline()

    Dim delta! = 1 / n
    Dim out As New List(Of Color)

    For f! = 0 To 1.0! Step delta!
        Dim r% = rangeConstraint(x.GetPoint(f))
        Dim g% = rangeConstraint(y.GetPoint(f))
        Dim b% = rangeConstraint(z.GetPoint(f))

        out += Color.FromArgb(alpha, r, g, b)
    Next

    Return out
End Function

''' <summary>
''' Limit <see cref="CubicSpline"/> result in range [0, 255]
''' </summary>
''' <param name="x!"></param>
''' <returns></returns>
Private Function rangeConstraint(x!) As Integer
    If x < 0! Then
        x = 0!
    ElseIf x > 255.0! Then
        x = 255.0!
    End If

    Return x
End Function

从上面的代码之中我们可以很清楚的看得到,做完RGB三个颜色通道的数据插值之后,再将结果数据分别一一对应的提取出来,重新放回到颜色的RGB通道之中,我们就可以得到一个连续的颜色谱了。在拿到这个颜色数据之后,我们只需要将我们的原始数据做[0,1]正则化,完成线性变换,乘上数组的长度即可得到对应的颜色元素的数组下标值。根据这个计算得到的下标值取出对应的颜色值即可完成整个热图可视化的线性变换映射操作了。

接下来我们将颜色绘制到具体的热图中的单元格,就可以完成热图的绘制了。

Latest posts by 谢桂纲 (see all)

Attachments

No responses yet

Leave a Reply

Your email address will not be published. Required fields are marked *