本文連接:https://blog.csdn.net/qq_26909801/article/details/96966372
數值型座標軸刻度計算算法
前言
算法描述
上代碼
代碼運行效果
結語
前言
因實習的公司是作大數據的,而個人工做恰好又是須要繪製一些數據圖表的。繪製圖表有許多現成的組件可使用,可是要想達到產品所須要的效果,只靠組件內部的一些功能是不太夠的。一些細膩的要求必須在掌握組件原理方法的狀況下,本身去寫算法來完成。例如,本文要說的這個刻度計算算法,開始正文以前,我先描述遇到的問題。
echarts自身的刻度計算有時候並很差用,例若有時候你但願讓圖表只有5條刻度線,即分紅4段,echarts提供了一個參數叫splitNumber,把splitNumber設爲4可讓圖表儘可能地分紅4段,然而當數據波動較大時,echarts會自動增長分割的段數,即即便大部分數據都能正常分出4段刻度,但仍有少部分數據實際分出的段數可能不止4段。以下面這樣:javascript
所以咱們得出一個結論,echarts的splitNumber只是預估的分割段數。若是咱們須要強制把刻度區間分爲4段,則須要咱們本身去寫算法計算刻度。另外,即便段數正確的狀況下,echarts自動計算出的刻度也可能存在區間過大,數據差別不明顯的狀況,以下面的圖片,由於刻度區間太大,致使各個數據看起來像是差很少大的,看不出差別:前端
另外,echarts自動計算出的刻度也有一些其餘的問題,例如當圖表在柱狀圖和堆疊圖中切換時,堆疊圖可能出現刻度溢出問題。不過堆疊圖的刻度計算這裏就先不說明了,下面開始正文吧。java
算法描述
刻度計算的算法以前我以前也寫了一版,解決了分割段數的問題,可是仍沒法解決刻度區間過大的問題。以前那一版算法的主要思想是取近似值,分別取最大值和最小值的最小近似整值獲得刻度,雖然不是最優的算法,可是在構思和調整算法的時候我也學到了很多東西,而這一版的算法是在咱們技術老大的點撥下結合網上的一些文章和項目的需求而寫出來的,算法以下:算法
要求: 根據一組數的最大值、最小值來肯定刻度值,肯定刻度的最大值maxi、最小值mini和刻度間隔interval。當出現異號數據時可選擇正負兩邊刻度是否須要對稱,當存在異號數據時要求其中一條刻度分割線必須在0刻度上,能夠選擇是否容許段數偏差。數組
肯定分割段數splitNumber和魔數數組magic = [10,15,20,25,30,40,50,60,70,80,90,100];
從目標數組arr中算出最大值max和最小值min,肯定初始間隔大小 tempGap = (max-min)/splitNumber;
設tempGap除以一個倍數multiple後,恰好處於魔數數組的區間[10,100]中,記錄倍數multiple;
從魔數數組中取出第一個大於tempGap縮放值的魔數,用 魔數*multiple當作理想刻度間隔(estep)進行第一次計算,計算出max和min的鄰近刻度maxi和mini,若是容許分割段數偏差,則直接結束運算,取interval=estep;
當刻度須要正負兩邊對稱且存在異號數據時,取maxi和mini中絕對值大的一方,將其相反數賦值給另一方,計算interval=(maxi-mini)/splitNumber,結束運算;
當正負刻度不須要對稱或不存在異號數據時,判斷實際分割段數是否等於splitNumber,若是不相等,則從新取較大的魔數進行運算,當魔數取完或者分割段數相等時結束運算,得出interval=(maxi-mini)/splitNumber.
上代碼
算法採用javascript語言描述,由於圖表的繪製在前端完成。app
/* 刻度計算算法,基於魔術數組 [10, 15, 20, 25, 30, 40, 50, 60, 70, 80, 90, 100]; 解釋:魔數數組是理想間隔數組,即咱們但願每一個刻度的間隔都是魔數數組中某個數的整數倍。(準確的來講是整10倍) */ //新增,解決js的浮點數存在精度問題,在計算出最後結果時能夠四捨五入一次,由於刻度過小也沒有意義,因此這裏忽略設置精度爲8位 function fixedNum(num){ if((""+num).indexOf('.')>=0) num = parseFloat(num.toFixed(8)); return num; } //1.初始化 var symmetrical = false;//是否要求正負刻度對稱。默認爲false,須要時請設置爲true var deviation = false;//是否容許偏差,即實際分出的段數不等於splitNumber var magic = [10, 15, 20, 25, 30, 40, 50, 60, 70, 80, 90, 100];//魔數數組通過擴充,放寬魔數限制避免出現取不到魔數的狀況。 var arr = [1230, 320, 20, 304, 102, 234];//測試數據 var max,min,splitNumber; splitNumber = 4;//理想的刻度間隔段數,即但願刻度區間有多少段 max = Math.max.apply(null,arr);//調用js已有函數計算出最大值 min = Math.min.apply(null,arr);//計算出最小值 //2.計算出初始間隔tempGap和縮放比例multiple var tempGap = (max - min) / splitNumber;//初始刻度間隔的大小。 //設tempGap除以multiple後剛剛處於魔數區間內,先求multiple的冪10指數,例如當tempGap爲120,想要把tempGap映射到魔數數組(即處理爲10到100之間的數),則倍數爲10,即10的1次方。 var multiple = Math.floor(Math.log10(tempGap)-1);//這裏使用Math.floor的緣由是,當Math.log10(tempGap)-1不管是正負數都須要向下取整。不能使用parseInt或其餘取整邏輯代替。 multiple = Math.pow(10,multiple);//剛纔是求出指數,這裏求出multiple的實際值。分開兩行代碼避免有人看不懂 //3.取出鄰近較大的魔數執行第一次計算 var tempStep = tempGap / multiple;//映射後的間隔大小 var estep;//指望獲得的間隔 var lastIndex = -1;//記錄上一次取到的魔數下標,避免出現死循環 for(var i = 0; i < magic.length;i++){ if(magic[i]>tempStep){ estep = magic[i]*multiple;//取出第一個大於tempStep的魔數,並乘以multiple做爲指望獲得的最佳間隔 break; } } //4.求出指望的最大刻度和最小刻度,爲estep的整數倍 var maxi,mini; function countDegree(estep){ //這裏的parseInt是我無心中寫出來的,原本我是想對maxi使用Math.floor,對mini使用Math.ceil的。這樣能向下取到鄰近的一格,不事後面發現用parseInt好像畫出來圖的比較好看 maxi = parseInt(max/estep+1) * estep;//最終效果是當max/estep屬於(-1,Infinity)區間時,向上取1格,不然取2格。 mini = parseInt(min/estep-1) * estep;//當min/estep屬於(-Infinity,1)區間時,向下取1格,不然取2格。 //若是max和min恰好在刻度線的話,則按照上面的邏輯會向上或向下多取一格 if(max===0) maxi = 0;//這裏進行了一次矯正,優先取到0刻度 if(min===0) mini = 0; if(symmetrical&&maxi*mini<0){//若是須要正負刻度對稱且存在異號數據 var tm = Math.max(Math.abs(maxi),Math.abs(mini));//取絕對值較大的一方 maxi = tm; mini = -tm; } } countDegree(estep); if(deviation){//若是容許偏差,即實際分段數能夠不等於splitNumber,則直接結束 var interval = fixedNum(estep); console.log(maxi,mini,interval); return; } //5.當正負刻度不對稱且0刻度不在刻度線上時,從新取魔數進行計算//確保其中一條分割線恰好在0刻度上。 else if(!symmetrical||maxi*mini>0){ outter:do{ //計算模擬的實際分段數 var tempSplitNumber = Math.round((maxi-mini)/estep); //當趨勢單調性發生變化時可能出現死循環,須要進行校訂 if((i-lastIndex)*(tempSplitNumber-splitNumber)<0){//此處檢查單調性變化且未取到理想分段數 //此處的校訂基於合理的均勻的魔數數組,即tempSplitNumber和splitNumber的差值較小如1和2,始終取大刻度 while(tempSplitNumber<splitNumber){//讓maxi或mini增大或減小一個estep直到取到理想分段數 if((mini-min)<=(maxi-max)&&mini!=0||maxi==0){//在儘可能保留0刻度的前提下,讓更接近最值的一邊擴展一個刻度 mini-=estep; }else{ maxi+=estep; } tempSplitNumber++; if(tempSplitNumber==splitNumber) break outter; } } //當魔數下標越界或取到理想分段數時退出循環 if(i>=magic.length-1|| i<=0 || tempSplitNumber==splitNumber) break; //記錄上一次的魔數下標 lastIndex = i; //嘗試取符合趨勢的鄰近魔數 if(tempSplitNumber>splitNumber) estep = magic[++i]*multiple; else estep = magic[--i]*multiple; //從新計算刻度 countDegree(estep); }while(tempSplitNumber!=splitNumber); } //6.不管計算始終把maxi-mini分紅splitNumber段,獲得間隔interval。不過前面的算法已經儘可能的保證刻度最優了,即interval接近或等於理想刻度estep。 maxi = fixedNum(maxi); mini = fixedNum(mini); var interval = fixedNum((maxi-mini)/splitNumber); console.log(maxi,mini,interval);
代碼運行效果
1.若是不處理小數偏差,且強制分爲4段,出來的效果是這樣的(20190722版):echarts
2.處理了小數偏差,並容許刻度偏差出來的效果是這樣的(20190723版)函數
能夠看出:測試
採用基於魔數數組的新算法計算出的刻度區間是緊挨着最大值和最小值的,算是差強人意。
js中浮點數的精度問題是咱們在設計一些通用性的算法時須要注意的,圖片右邊能夠看到,當數據幅度較小時,算出的min和interval是存在偏差的。
圖片左邊的刻度的精確度處理是我寫的邏輯,當數據爲非純小數時最多隻精確到3位小數,純小數時精確到8位,避免出現刻度過長,echarts並無自帶這個功能。
再附上一張存在異號數據時的效果和一張須要正負刻度對稱的效果大數據
能夠看出:
正負刻度不對稱且其中一條分割線恰好在0刻度上
y軸刻度的40K實際上是40000的縮寫,算法也是要本身寫的,echarts沒有提供這個功能。
結語
很久沒有寫博客,多是最近比較忙,若是有空的話其實前端有不少東西均可以寫一下,今天寫這個博客是由於文章篇幅較小,並且我恰好在檢查算法,另外這個算法比較有參考意義,就忙裏偷閒寫出來給你們看看,也方便我本身之後查閱。目前我參與的項目還在開發完善中,關於echarts我也踩了不少坑,針對項目需求編寫了一些東西,有機會再寫出來你們討論吧,另外,你們。
走過路過點個贊再走吧。
————————————————
版權聲明:本文爲CSDN博主「鄭偉斌」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/qq_26909801/article/details/96966372
所以咱們得出一個結論,echarts的splitNumber只是預估的分割段數。若是咱們須要強制把刻度區間分爲4段,則須要咱們本身去寫算法計算刻度。另外,即便段數正確的狀況下,echarts自動計算出的刻度也可能存在區間過大,數據差別不明顯的狀況,以下面的圖片,由於刻度區間太大,致使各個數據看起來像是差很少大的,看不出差別:
————————————————
版權聲明:本文爲CSDN博主「鄭偉斌」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/qq_26909801/article/details/96966372
所以咱們得出一個結論,echarts的splitNumber只是預估的分割段數。若是咱們須要強制把刻度區間分爲4段,則須要咱們本身去寫算法計算刻度。另外,即便段數正確的狀況下,echarts自動計算出的刻度也可能存在區間過大,數據差別不明顯的狀況,以下面的圖片,由於刻度區間太大,致使各個數據看起來像是差很少大的,看不出差別:
————————————————
版權聲明:本文爲CSDN博主「鄭偉斌」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/qq_26909801/article/details/96966372
所以咱們得出一個結論,echarts的splitNumber只是預估的分割段數。若是咱們須要強制把刻度區間分爲4段,則須要咱們本身去寫算法計算刻度。另外,即便段數正確的狀況下,echarts自動計算出的刻度也可能存在區間過大,數據差別不明顯的狀況,以下面的圖片,由於刻度區間太大,致使各個數據看起來像是差很少大的,看不出差別:
————————————————
版權聲明:本文爲CSDN博主「鄭偉斌」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/qq_26909801/article/details/96966372
所以咱們得出一個結論,echarts的splitNumber只是預估的分割段數。若是咱們須要強制把刻度區間分爲4段,則須要咱們本身去寫算法計算刻度。另外,即便段數正確的狀況下,echarts自動計算出的刻度也可能存在區間過大,數據差別不明顯的狀況,以下面的圖片,由於刻度區間太大,致使各個數據看起來像是差很少大的,看不出差別:————————————————版權聲明:本文爲CSDN博主「鄭偉斌」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。原文連接:https://blog.csdn.net/qq_26909801/article/details/96966372