当我们使用 JavaScript 记录下用户鼠标的点击行为数据后,怎么把它呈现出来呢?



最简单的做法,是直接把鼠标点击的位置在图上标出来,比如像下图这样:


android 点击热区放大原理 点击热区是什么_android 点击热区放大原理


(图1)

上图的每一个小叉代表一次点击(为简单起见,这儿所有的小叉都是一种颜色,事实上,可以用不同的颜色来表示不同类型的点击,比如点在超链接上的小叉画成红色,点在非超链接上的小叉画成蓝色),从图上可以大致看出来页面上哪几个区域的点击比较密集。但很多时侯,我们其实并不想精确地了解用户具体是点在哪个像素上,——很多时候,用户是点在这个像素上,还是点在右边相邻的像素上,并没有什么区别,我们想知道的,只是一个区域内的总的点击情况,或者说用户对这个页面上各个区域的兴趣情况。这个时候,热区图(或叫热力图)就可以派上用场了。

热区图的绘制不复杂,有多种不同的实现,下面介绍一下我的一些实践。

一、基本思路

色盘

热区图上最“冷”的部分到最“热”的部分应该是什么颜色?通常我们会预先定义好若干种颜色(比如100种、256种),存放在一个数组中,最冷的部分取第一个颜色(索引为0),次冷的取第二个颜色,直到最热的部分,取最后一个颜色。这个数组即是色盘。

热区图中比较常见的色盘是从蓝色(最冷)取到红色(最热)。网页中我们通常用 RGB 色彩,这儿也可以使用 RGB,不过使用 HSL 色彩表示方法似乎更方便,只要将 H 从 240 取到 0 即可。


android 点击热区放大原理 点击热区是什么_用户兴趣_02


(图2)

当然,不同风格的热区图可以使用不同的色盘,除了色彩外,还可以加入一些透明度的变化,比如很冷的地方除了色彩偏蓝外,还可以让它更透明。

映射

我们所能得到的信息是用户的点击坐标记录。上面提到,我们真正想了解的并不是用户具体点在了哪个像素上,而是页面上哪个区域更热或更冷。当我们知道用户在位置 (x, y) 点击了一次时,我们会合理地认为,用户感兴趣的并不是 (x, y) 这一点,而是包含这一点的一个小区域,比如 (x + 1, y) 这个点很有可能也是在用户感兴趣的区域之内。

那么问题就是,如何把用户的一次点击映射到热区图上?或者更详细一点:

问题一、我们如何从用户点击的位置坐标推测出用户感兴趣的区域?
问题二、这个区域内,用户的兴趣程度是如何变化的(或者没有变化,处处相等)?

这两个问题是如何绘制热区图的关键,不同的回答对应着不同的热区图实现。很遗憾,这两个问题都很难回答。但是,我们也可以做一些假设、简化,然后尝试根据用户的点击坐标记录,还原出用户对页面各部分的感兴趣程度,并据此画出热区图来。

比如,对于第一个问题,我们可以简单地假设:如果用户在位置 (x, y) 有一次点击,那么我们认为这个用户对页面上以点 (x, y) 为圆心、r 为半径的一个圆形区域感兴趣。这个 r 可以是 10px 或其它你认为比较合理的值。

接着,对于第二个问题,我们也可以简单地假设:用户的兴趣值在圆心 (x, y) 处最大,比如值为 r,向外线性递减,离圆心每远一像素兴趣值降低 1 ,在圆的边缘处兴趣降为 0。

需要注意的是,上面两个方案仅仅是一种简化的假设,并不一定是真实的用户兴趣的情况,这么假设主要的是为了方便处理。但只要我们的假设有一定合理性,我们也能得到一些具有参考价值的结果。

让我们来看一看,在这样的假设之下,我们能得到什么样的图。编程处理的过程这儿就略去了,最后的结果如下:


android 点击热区放大原理 点击热区是什么_超链接_03


(图3)

这样,一个常见的热区图就完成了。

如果我们把它画成三维图形,大致是这个样子:


android 点击热区放大原理 点击热区是什么_python_04


(图4)

二、问题及更多尝试

上面的热区图已经基本可以使用了,不过,我们应该清楚,这个热区图的绘制是基于一定的假设的,比如我们假设用户的兴趣在圆心处为 r,向外线性递减,每过一个像素减一。或者直观一点,用户的兴趣变化如下图所示:


android 点击热区放大原理 点击热区是什么_android 点击热区放大原理_05


(图5)

我们应该清楚,这个假设不一定与真实情况相符合,只是我们为了便于处理而作的假设。如果我们改变一下这个变化的斜率会怎样呢?比如变成下面这样:


android 点击热区放大原理 点击热区是什么_用户兴趣_06


(图6)

即我们把兴趣的半径加大一倍,同时变化的斜率减小为原来的一半。在这样的条件下,我们画出来的热区图如下所示:


android 点击热区放大原理 点击热区是什么_用户兴趣_07


(图7)

可以看到,与原来的图形非常相似,但变化边缘柔和了很多。

类似地,我们也可以把这个变化曲线变为其它形状,比如下面这样:


android 点击热区放大原理 点击热区是什么_python_08


(图8)

这时,热区图将变成这样:


android 点击热区放大原理 点击热区是什么_android 点击热区放大原理_09


(图9)

或者,我们来一个大点的改变,对上面 (图3) 的每个像素的值取对数,将得到如下图形:


android 点击热区放大原理 点击热区是什么_python_10


(图10)

可以看到,仅仅是对一个假设的改变,我们也能得出很多种不同的热区图。究竟哪一种更能反映真实的用户兴趣?我们不得而知。

三、更多思考

上面我们改变的都只是对问题二的假设,对于问题一,我们的假设一直是:用户的兴趣区域为以点击位置为圆心的一个圆。但这个假设是否合理呢?

我们来看一个真实的截图:


android 点击热区放大原理 点击热区是什么_超链接_11


(图11)

这是某个页面上图片轮播广告右下角的热区图。从图上我们可以看到,右下角页码的部分非常的热,图片区域本身则好像比较冷。但进一步的统计却告诉我们,点在图片上的点击比点击右下角翻页页码上的点击要多很多。

为什么图片区域的点击更多,但看起来却比较冷,翻页页码的点击更少,但看起来却更热呢?问题就出在我们对问题一的假设上,我们假设用户的兴趣区域与页面上的元素无关,无论用户点在一个图片上还是一个超链接上,用户的兴趣区域都是一个大小相同的小圆圈。这样,对于一个图片而言,虽然对它感兴趣的人更多,它上面的点击也更多,但由于它的面积大,平均下来显得不那么热;对于一个翻页组件而言,虽然对它感兴趣的人少一些,点击也少一些,但由于它的面积小,在它上面的点击非常集中,于是它这儿反倒显得很热。

所以,更合理的热区图,不仅要考虑用户点击的位置,还要考虑用户所点击的元素是什么,是一个图片,还是一个链接?或者是一个 Flash ?前端埋点代码不仅要记录下用户鼠标的坐标,还要记录下当前被点击元素的位置以及尺寸。而对问题一的回答,也不应该只是简单地假设用户的兴趣区域是一个个的小圆圈,用户的兴趣区域应该是与被点击元素的类型以及尺寸有关的一个形状。

问题还没结束。如果我们认为,用户点击了页面上一张图片,表示他对这一整张图片都有兴趣,也即用户的兴趣区域为这个图片所在的区域,那我们马上又面临如何回答问题二:用户对这个图片的兴趣是处处相同的还是对他所点击的那个位置附近兴趣更大一些?如果是后者,兴趣程度又是如何变化的?

这些都不是能很容易回答的问题,我们只有通过不断地试验来试图作出一些假设,然后再在实践中不断地验证我们的假设。

注:
1、本文中用到的点击数据是随机生成的模拟数据,你可以点击这儿下载。
2、本文中的二维热区图是用 Python + PIL 生成的,3d 热区图及曲线图是用 Matplotlib 绘制的。