当我们在做折线图、柱状图的时候经常会遇到一个问题:如果横轴元素过多该怎么展示。

比如,以时间作为横轴的,如果我们以天为维度统计,那选择一个月,横轴就需要显示30个左右日期,如果选择三个月,就需要显示90个左右日期。

一般的做法是限制横轴长度,比如以时间为维度的话,就会限制用户不能超过三个月,但这种做法不是很好,

从用户的角度分析,存在两种情况(我们以时间作为横轴):

  1. 当横轴元素不多时,用户可能会关注每天的数据
  2. 当横轴元素比较多时(比如用户选择了6个月),用户可能不会关注每天的数据,他的注意力会放到这段时间的趋势上

这给我们提供了一个解决方案:当用户选择的时间段过长时,我们可以离散的选择几个来显示

自适应算法

消减因子(reduceIndex):开始消减的阈值

实际宽度(realWidth):根据用户的选择计算出的用户的宽度,比如用户选择了一个月,那realWidth=30

消减步长(reduceStep):多少元素选取一个

消减宽度(reduceWidth):表示可以保留元素数目,这是一个估值

我们可以设 reduceIndex = 1.5 reduceWidth = 15 ,表示当 realWidth > 15*1.5 时,开始消减。

设 realWidth = 30 ,这30个元素分别是(1, 2, 3 ... , 30),由于30 > 15*1.5 所以开始消减:

  • reduceStep = (30-2)/(15-2) = 2.15     #由于要保留头尾2个元素,所以realWidth和reduceWidth都要减去2
  • 首先选取第1个元素 list = [1],
  • 计算第2个元素的位置 2*2.15 = 4.3,下取整 = 4,list = [1, 4]
  • 计算第3个元素的位置 3*2.15 = 6.45,下取整 = 6,list= [1, 2, 6]
  • 计算第4个元素的位置 4*2.15 = 8.6,下取整 = 8,list = [1, 2, 6, 8]
  • ...
  • 计算第13个元素的位置 13*2.15 = 27.95,下取整 = 29,list = [1, 2, 6, 8, ... 29]
  • 计算第14个元素的位置 14*2.15 = 30.1,由于 30.1 > 30,终止
  • 将末尾元素加入到列表,list = [1, 2, 6, 8, ... 29, 30]

通过以上算法,我们得到了消减后的,相对离散的数据,这个数据可以作为图的横轴。

php实现代码

1    /**
 2      * 消减数组
 3      * @param array $arr 待消减数组
 4      * @param int $reduceWidth 消减宽度
 5      * @return array 消减后的数组
 6      */
 7     public static function reduceIndex($arr, $reduceWidth) {
 8         $realWidth = count ( $arr );
 9         if ($reduceWidth > 0 && $realWidth > $reduceWidth * self::$reduceIndex) {
10             // 需要消减,并且满足消减系数约束
11             $newArr = array (
12                     $arr [0] // 需要保留第一条数据
13             ); 
14             $reduceStep = ($realWidth - 2) / ($reduceWidth - 2); // 消减步长,由于已经选择了首尾元素,计算时的分子分母都需要-2
15             $cnt = 2; // 计数器
16             $curP = 0; // 当前获取到的元素的下标
17             while ( $cnt * $reduceStep < $realWidth ) {
18                 $index = intval ( floor ( $cnt * $reduceStep ) );//计算本次选中的元素的下标,下标元素需要下取整
19                 $newArr [] = $arr [$index];
20                 $curP = $index;
21                 $cnt ++;
22             }
23             if ($curP != $realWidth - 1) {
24                 //最后一个元素没有取到,现在保存最后一个元素
25                 $newArr [] = $arr [$realWidth - 1];
26             }
27             $arr = $newArr;
28         }
29         return $arr;
30     }