zoukankan      html  css  js  c++  java
  • 数据可视化之热力图

    转自原文 数据可视化之热力图

          最近看了一下百度的热力图,通过百度地图,确实是一个实时大数据渲染的一个形象表达形式,正好借这个机会学习一下,刚买的机械键盘,发现有两个好处:每天不写点代码(或调试),感觉对不起这价钱啊,估计我之前买的所有键盘+鼠标花费总和都不如这个键盘贵;其次就是控制自己不再吃零食了,怕掉进键盘里心疼啊。

          好了,热力图还是相对比较容易,我们主要讨论如下3+1点吧,主要是前三部分,后面只是简单分析一下百度热力图和个人的简单看法。热点图的实现参考了SuperMap的热点图和百度Echarts的热点图实现。

    • 原理

    • 实现

    • 优化

    • 百度热力图简述

    原理

    1

          如上是全国范围内的截图,一看就能了解当前中国人口密集度。每个区域的形状不规则,而且还五颜六色。直觉上,我们会觉得每个区域都应该有一个位置点,还应该有一个缓冲范围,然后对这个范围内进行一个渐变效果。这个思路应该还是比较理性的,只是还是无法解释区域的不规则,但抽象了位置点(XY)和渐变(五颜六色)的数据概念。那我们再结合数据,看看我们的推理是否准确。

    2

          这是一个示例数据,可见每一个HeatPoint由三部分组成(X,Y,Weight)。这个和我们之前分析的比较类似,每一个热点都有一个位置和权重,权重越大,则该点越显著,也就代表其渐变的一个衰变因素。不规则的区域又是如何形成的?看完代码后发现,是每个热点各管各的,然后相互叠加影响,形成了最终具有真实意义的奇形怪状的热点图。想想也是,不然好看不实用,那热点图的设计也就本末倒置了。另外还有一个半径属性,主要是缓冲区的半径,表示该热点的影响范围,通常我们认为所有热点的影响范围即半径都是一样的,只是权重不同,这也是为了处理的方便。

    3

          打个比方,下雨天的池塘,每一个雨滴都会引起一个涟漪,这就相当于一个热点,位置不同,雨滴的大小速度质量等并不完全一样,因此具有不同的权重,但在水面上,相互影响,形成了很多不同的形状,而水波的密集度可以用渐变来体现。这样,我们则把每一个个离散点,通过一个缓冲区,转变为连续的面,进而对其进行可视化展现。

          当然,在数据上需要多提一点,实际中,因为热点数据量非常大,所以在不同级别下的数据会有优化,比如全国范围内(大)的数据比较粗略(小),而区域范围内(小)数据精细(大)。这里面有一个抽稀和聚类的处理(个人感觉应该是这个思路),这是数据处理层面的问题,我们下面还是专注可视化方面的问题。

    实现

          这些代码都比较简单,如果你对canvas有一定基础,相信也能明白,主要是看思路。首先就是对所有点进行一次筛选和统计,只保留屏幕范围内的热点,并根据权重获取当前最大和最小权重。这样我们获取了一个[minWeight,maxWeight]以及所有需要处理的drawPoints数组,为下面的渲染准备好数据。

    4

          渲染的过程其实就是对每一个热点权重范围内的点进行颜色的更新,颜色包括两个部分:RGB和Alpha。而渲染过程也分为两个过程:权重的计算和权重对应的颜色(RGB + Alpha)。

          首先,权重的计算是一个插值的过程,根据权重值和范围值(距离热点的距离)的算法,你可以根据具体的需要选择合适的算法,而计算主要有两种情况,当前点还没有任何权重值;累加当前点的权重值。点越密,累加后的权重值就越高,则可以用权重值高的风格(红色)突出。其次就是根据权重值获取对应的颜色,这里会有一个颜色表,根据不同的权重值,采用具体的差值算法获取对应的颜色。

    5

          这样则完成了热点图的渲染,这里需要注意的是,两个for循环是对热点的行列的遍历,本身是一个矩阵范围,而热点本身的范围应该是一个圆,因此在for循环中需要判断是否在圆内。最终效果如下图:

    6

    优化

          如上就是热点图的原理和实现,基本上实现都大同小异。不知道你看出来有什么问题了没有?性能!

    这个实现有一个特点,假设有N个热点,权重假设是(0,1)之间的平均值0.5,半径假设为R(像素单位),那一共要有N * 0.5 * R * R * 4(4个象限),这个计算量是惊人的,和N以及R的平方是线性增长。另外一个特点,在for循环中的规则都是一致的。

    这就让我们想到,如何能够进行优化,答案就是批次和模板。在开始前,我们先说两个技术点。

    Canvas渐变填充

    7

          如上是伪代码,最终是在canvas上绘制了一个圆,但本身是从黑到白的渐变,同时阴影在x轴上偏移d个像素,这样,该代码生成了如下一张图片,我们称它为权重图,暂时不解释,只需要明白这段代码生成该图的过程即可。

    8

    Canvas色带


    9

          这里主要有两个函数:

    • createLinearGradient()创建线性的渐变对象

    • addColorStop() 方法规定不同的颜色,以及在 gradient对象中的何处定位颜色

    10

          这样,我们便创建了一个色带,就是如上这样一个效果,当然我们也可以根据自己的需要,定义一个彩色色带,就想我们平时所见的调色板中的颜色色带一样。这里比较宽,主要是给大家看一下效果,真正程序中,宽度只需要一个像素就可以。

    实例化


          不知道大家这时候是否发现了里面的原理和巧妙之处?

          打个比方,我一直想买一个印章,这样每次买书的时候,就不用自己一笔一划的来写了,字写得比人还丑,这可以理解(不过最后还是没买,因为最后连书也买不起了)。假如我有了印章,这样每次就不用自己写了,直接盖章多省事啊,而且还一模一样。

    这里,假设这个章是圆形的,它就是一个模板,对应的就是创建好的渐变填充的纹理。以前我们需要对热点缓冲区内的所有点都进行计算,计算出权重值,现在只需要以该热点为圆心盖一下,则把该热点范围内所有点的权重值都写上去了。

          于是,逐个把所有热点都盖章,这样权重值不是都有了嘛,啪啪啪的声音就是爽。但这有一个问题,热点之间的相互影响怎么处理?这是需要叠加效果的,而不是后盖的章覆盖前者的效果。这里在盖章的时候增加一个透明度的属性,谁的权重大,谁的透明度就小,这样就可以叠加效果了,之前写的风向图的轨迹也是这个思路,还记得吗?下面是逐次盖章(贴图)的代码实现,大家过一遍,都是一个思路。

    11

          这样之后,我们就有了一个“热力图”了,不过略有遗憾,因为它是黑白相机拍出来的,都是0~255的灰度值,也就是权重信息。这样,我们就需要根据之前创建的色带来对它进行上色的过程了

    12

          可见,通过模板,我们可以极大的减少计算量,渲染也是批次的,而不是逐点赋值。这样我们可以根据不同的算法来创建对应的模板,实现不同的热点风格。下面是百度热力图采用这个方式实现的思路。

    13

     

    百度热力图&总结


          不知不觉又写了这么多,就压缩一下篇幅吧。如下,是百度热力图八小时的请求队列,从v的属性可以看出来是小时单位,而xyz和地图行列号一致。如果想要叠加百度热力图的,就可以按照这个思路来加载热力图层了。

    14

          总体来说,个人觉得有两点收获:1功能的实现不是完结,而是开始,方能深入掌握其中,就好比一锅汤,精华都在下面,沉得住寂寞,肯定有所收获。2热力图的技术点都不难,一看便知,但不看还真不知道,这些技术点看起来没关系,但结合好了性能的提升非常明显。所以,见的多了经验就多。

          今天圣诞节了,每年圣诞节我都会买一本书,然后写上”Merry Christmas to me”,然后送给自己,能看到这的人也都是真爱,再唠叨几句吧:昨天听了一个deliberate practice的概念,讲的是同等时间下,业余爱好者喜欢不断练习自己熟悉的动作,而专业选手则会专注在自己不熟悉但有机会掌握的动作,这一块在心理学中成为panic zone,属于你知道,但不精通的部分。个人觉得收获很大,如何更好的发挥时间价值,这也是我,作为一个老人家需要调整的地方。

     
     
  • 相关阅读:
    POJ 1330 Nearest Common Ancestors(LCA Tarjan算法)
    LCA 最近公共祖先 (模板)
    线段树,最大值查询位置
    带权并查集
    转负二进制
    UVA 11437 Triangle Fun
    UVA 11488 Hyper Prefix Sets (字典树)
    UVALive 3295 Counting Triangles
    POJ 2752 Seek the Name, Seek the Fame (KMP)
    UVA 11584 Partitioning by Palindromes (字符串区间dp)
  • 原文地址:https://www.cnblogs.com/arxive/p/7445995.html
Copyright © 2011-2022 走看看