问题
笔者最近在做图表需求,遇到一个这样的问题,图表的y轴数据处理得不够好看,如下:
图表的数据为1005,1988,2500,3902,5530,很显然这个处理数据的结果不尽人意。
方案
既然有这个问题,那么就需要一个新的处理方案,平时我们在使用Excel等工具制作的折线图,会发现上面的数据其实是处理过的了,如下:
对比可知,为了让图表的数据规整,需要保证间隔的规整,如50,500等数字的倍数,其次是确定起点,只要确定好起点,通过间隔的累加即可。所以,对于这个方案有两个需要处理的,那就是确定起点min和找间隔interval。
制定需求
对于笔者而言,需求不仅包含数据的规整,还有小数位数的格式化,其中涉及到单位的转换,如初始值给了10345,21200,29001,y轴格式化为1.0万,2.0万,3.0万,那么需要对小数位数做处理,使得最后结果为1万,2万,3万。所以对于笔者而言,找出间隔之后,还需要对最终结果的小数位数做去0操作。
步骤
1. 初始值
首先,需要找出数据集的最小值min,最大值max,这里笔者给出的数据为min = 47769543,max = 54961473,最多需要保留的小数位数decimal=1。还包含以下需要用到的方法:
方法名 | 作用 |
formatDataByRanges | 对数据进行单位转换处理,如47769534做“万”处理则为 4776.9534 |
2. 计算过程
- 确定起点
首先计算range = max - min = 7191930,接着对range进行formatDataByRanges处理为719.1930,然后originInterval = range/4 = 179.798,这时候的originInterval就是没有处理过的间距值,接下来就是通过range找出规整的间距interval,首先通过range产生一组规整间距值为1000 500 200 100, 产生的伪代码如下:
export function genIntervalList(originInterval: number) {
let base = genPowNum(originInterval)
return [10 * base, 5 * base, 2 * base, base]
}
// 获取数字的十的幂次方的位数
// 如 90 则为 1
// 如 0.01则为 -2
function getPowBit(number) {
return Math.floor(Math.log10(data))
}
/**
* 生成十的幂次方
* @param data
*/
export function genPowNum(data: number) {
let bit = getPowBit(data)
return Math.pow(10, bit)
}
复制代码
接着从第二个数开始遍历1000 500 200 100, 当找到大于originInterval的数时则停止,获取当前点的上一个数为我们所需的interval,如179.798 > 100, 那么interval就为200。然后通过interval对min做处理,当然这里的min也经过处理为4776.9543,处理过程如下:
function handleMin(min, interval) {
if(min < interval) {
return 0
} else {
let baseNum = genPowNum(interval) * 10
// 去掉最小值不需要比较的位数
// 如min为4776.9543 interval为200 那么formatMin = 4000
// 如min为0.073 interval为0.005 那么formatMin = 0.07
let formatMin = Math.floor(min / baseNum) * baseNum
let minRange = min - formatMin
// 找出适合的interval 这里为500
// 所以最后的min = 4000 + 500 = 4500
return formatMin + this.findMinInterval(minRange, interval)
}
}
function findMinInterval(minRange: number, interval: number) {
let factorList = genIntervalList(interval)
for (let i = 1; i < factorList.length; i++) {
if (minRange >= factorList[i]) {
return factorList[i]
}
}
return 0
}
复制代码
经过上述的处理,可得最小值为4500
- 找间距
由于经过步骤1确定了min,min已经改变了,所以range需要重新计算,interval也需要重新计算。计算方式参考步骤1的找间距,计算可得interval为500。 - 最终计算
经过步骤1,2可以得到min和interval,如果此时min< interval,那么需要令min = 0,然后重新执行步骤2。这样就可以得到最后的min,interval,接着需要计算最后需要保留的小数位数,如下:
function getDecimal(interval, min) {
if(interval < 1) {
return Math.abs(getPowBit(interval))
} else {
// 如果间距的range和max相等 则不需要保留小数位数
// 如interval是1500万 max是4000万 range都为万 不需要保留小数
if(interval.range = max.range) {
return 0
} else {
// 当range不相等的情况 可知min一定大于formatInterval
// 如min为121.1万 interval为2000 这时候对2000做range的处理
// 则可得到最大需要保留的位数
return Math.abs(getPowBit(formatInterval / formatMinData.range))
}
}
}
复制代码
- 最后结果
最后可得y轴的数据为4500万 5000万 5500万 6000万 6500万
总结
对于这个方案,在于确定min和interval,对于interval而言,则可以自己灵活定义规整的间距,如520取的是550,而不是1000,这样的好处是可以让图表的走势更加明显。