前言
可视化开发是前端开发的重要分支之一。日常的数据需求需要我们去熟练使用Echarts
、Antv
、Highcharts
等开源可视化库。可能还有些定制化的需求,这就需要去深入了解svg
、canvas
更底层的东西。操作svg
的有D3
,canvas
则有Zrender
等。
本文以Echarts
为例,讲解如何运用基本初等函数中的对数函数
去解决极端
数据的业务场景。如果听懂了,其他图表库相信也能触类旁通地解决。
对数函数🎈
对数函数是6
种基本初等函数之一。如果a^x=N(a>0,且a≠1)
,那么数x
叫做以a
为底N
的对数,记作x=logaN
,其中a
叫做对数的底数,N
叫做真数。
一般,函数y=logax(a>0,且a≠1)
叫做对数函数。
由图可看出,随着x
增长,对数函数的增长趋势越来越平缓,本文的解决核心思想也就是利用对数函数增长缓慢的特性。
进入正题 ~
业务场景
模拟下业务场景。
产品经理:js
需要用折线图表展示这份数据。
后端:返回的真实数据类似[212, 0, 0.001, 9, 1, 133, 13200]
可以看出目前数据差异较大,拿Echarts
配置如下所示。
let option = {
xAxis: {
type: 'category',
name: 'x',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
toolbox: {
show: true,
feature: {
saveAsImage: {
type: 'png'
}
}
},
tooltip: {
show: true,
trigger: 'axis'
},
yAxis: {
type: 'value',
name: 'y',
minorSplitLine: {
show: true
}
},
series: [{
data: [212, 0, 0.001, 9, 1, 133, 13200],
type: 'line',
smooth: true
}]
};
产品经理:数据差异太大,这图感觉不太行,能不能优化下?
前端:这... 真实数据是这样,能有什么办法?(推锅)给我点时间,我先想想吧。
翻阅Echarts文档,发现轴的type
属性有log
的概念,立马替换,结果如下,跟想像中的不太一样,好像报错了。😅
仔细排查 + 搜索引擎,发现是因为0
的原因,毕竟对数x
的定义域是大于0的。所以这里可以改成null
。图是不是就好看很多了,兴奋地叫产品经理过来看效果。
let option = {
xAxis: {
type: 'category',
name: 'x',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
toolbox: {
show: true,
feature: {
saveAsImage: {
type: 'png'
}
}
},
tooltip: {
show: true,
trigger: 'axis'
},
yAxis: {
type: 'log',
name: 'y',
minorSplitLine: {
show: true
}
},
series: [{
data: [212, null, 0.001, 9, 1, 133, 13200],
type: 'line',
smooth: true
}]
};
产品经理:嗯嗯,好看多了,但怎么中间断开了?
前端:因为数据是0,没有了啊。
产品经理:需求是满足了,好想线是连一起的(得寸进尺💢)
前端:...
虽然嘴上拒绝,但还是得快速地去想方案,毕竟得优雅
处理。Echarts
没有提供相关解决方案,重新换个库,再研究,有点浪费时间了。自己用canvas
画,更不可能了。那怎么去延伸对数
的思想呢?
在翻阅对数概念时,突然看到一句话:指数函数是对数函数的反函数
,灵光乍现✨。
- 先将原始数据用对数函数转换一下。
- 显示的时候再用指数函数转换回来。
这里处理原数据的时候需要+1
处理,显示的时候-1
即可。因为对数在(0,1)
范围内是负数,趋于0
的时候又接近于负无穷。图表数据本身是正数的情况下,映射成负数,更容易误导。
改造成功,如下图所示,线也连一起了,骄傲地把产品经理叫过来。
产品经理:👏。
前端:小事小事~。
代码思路如下:
- 转换数据用
Math.log10(d + 1)
即可。 - 显示数据的时候用
Math.pow(10, d) - 1
。图表方面利用Echarts
提供的formatter
格式化显示y轴
和tooltip
。
具体代码如下,即插即用,拿去直接用!!!
// 转换原始数据
let transformToLog = (d) => {
return Math.log10(d + 1)
}
// 对数转原始数据
let transformToOrigin = (d) => {
return Math.pow(10, d) - 1
}
let orginData = [212, null, 0.001, 9, 1, 133, 13200]
let option = {
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
toolbox: {
show: true,
feature: {
saveAsImage: {
type: 'png'
}
}
},
tooltip: {
show: true,
trigger: 'axis',
formatter: function(params) {
return params[0].axisValue + ':' + Number(transformToOrigin(params[0].value).toFixed(3))
}
},
yAxis: {
type: 'value',
axisLabel: {
formatter: function(value, index) {
if(value > 0) { return Math.pow(10, value)}
return value
}
},
minorSplitLine: {
show: true
}
},
series: [{
data: logData,
type: 'line',
smooth: true
}]
};
产品需求满足了,但故事还没结束,毕竟标题还有极端
两个字。
这算是自己的疑问吧,如果数据有负数
怎么办,那这又不可行了?毕竟对数定义域是大于0
。
那怎么办?既然是负数,我们是不是可以想到取绝对值
。
- 若是负数,先取绝对值,再用对数,最后乘以
-1
。 - 显示的时候,再反向操作即可。
首先原数据改为:[212, null, 0.001, 9, 1, -133, 13200]
。先看结果图,优雅
么?
// 转换原始数据
let transformToLog = (d) => {
let operator = 1
d < 0 && (operator = -1)
return Math.log10(Math.abs(d) + 1) * operator
}
// 对数转原始数据
let transformToOrigin = (d) => {
let operator = 1
d < 0 && (operator = -1)
return (Math.pow(10, d * operator)) * operator - 1 * operator
}
let orginData = [212, null, 0.001, 9, 1, -133, 13200]
let logData = orginData.map(item => transformToLog(item))
let option = {
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
toolbox: {
show: true,
feature: {
saveAsImage: {
type: 'png'
}
}
},
tooltip: {
show: true,
trigger: 'axis',
formatter: function(params) {
return params[0].axisValue + ':' + Number(transformToOrigin(params[0].value).toFixed(3))
}
},
yAxis: {
type: 'value',
axisLabel: {
formatter: function(value, index) {
let operator = 1
if(value < 0) operator = -1
if(value === 0) return 0
return Math.pow(10, value * operator) * operator
}
},
minorSplitLine: {
show: true
}
},
series: [{
data: logData,
type: 'line',
smooth: true
}]
};
总结
💗 感谢8月掘金活动,让自己能坚持一个月,用文字记录基础知识点和一些小心得,也幸运地获取了一些点赞和关注,从lv1
到lv2
,切实感受写文章的快乐。
先前文章大多是JS
基础,接下来,我会分享的更全面一点,比如CSS
、node
、可视化
、浏览器
等,也会继续分享自己的小心得(结合真实业务场景,绝对干货),比如这一篇,看大家兴趣(其实看点赞量了,哈哈哈哈)。
如果本篇文章帮到你了,记得毫不吝啬地三连点赞
+关注
+收藏
啊!!!
当然,也欢迎各路大佬评论
交流学习,批评指正。