The problem

When the author was making chart requirements recently, he encountered such a problem that the Y-axis data processing of the chart was not good-looking enough, as follows:

The data of the chart is 1005,1988,2500,3902,5530, obviously the result of this data processing is not satisfactory.

plan

Since this problem exists, a new processing scheme is needed. When we use tools such as Excel to make line charts at ordinary times, we will find that the data above are actually processed, as follows:

Develop demand

For the author, the requirement not only includes the data normalization, but also the decimal number formatting, which involves the conversion of units, such as the initial value is given 10345,21200,29001, and the y axis is formatted as 10, 20, 30, 000, then the decimal number needs to be processed, so that the final result is 10, 20, 30, 000. Therefore, for the author, after finding the interval, we also need to zero the decimal number of the final result.

steps

1. The initial value

First, we need to find the minimum min and maximum Max of the data set. Here, the data given by the author is min = 47769543, Max = 54961473, and the maximum number of decimal places that need to be retained is decimal=1. It also contains the following methods to use:

The method name role
formatDataByRanges Data unit conversion processing, such as 47769534 do “ten thousand” processing is 4776.9534

2. Calculation process

  1. Range = max-min = 7191930, then formatDataByRanges = 719.1930, OriginInterval = range/4 = 179.798 originInterval = range/4 = 179.798 First, a group of regular spacing values of 1000, 500, 200, 100 are generated through range, and the pseudo-code generated is as follows:
export function genIntervalList(originInterval: number) {
    let base = genPowNum(originInterval)
    return [10 * base, 5 * base, 2 * base, base]
}

// Get the number to the power of ten
// If 90, it is 1
 // if 0.01 = -2
function getPowBit(number) {
    return Math.floor(Math.log10(data))
}  

/** * generates a power of ten * @param data */
export function genPowNum(data: number) {
    let bit = getPowBit(data)
    return Math.pow(10, bit)
}

Copy the code

If the originInterval is 179.798 > 100, the originInterval is 200. If the originInterval is 179.798 > 100, the originInterval is 200. Interval is then used to process min, of course, min is also processed to 4776.9543, the processing process is as follows:

function handleMin(min, interval) {
    if(min < interval) {
        return 0
    } else {
         let baseNum = genPowNum(interval) * 10
       // Remove the minimum number of digits that need not be compared
       // If min is 4776.9543 interval 200 then formatMin = 4000
       // formatMin = 0.07 if min = 0.073 interval = 0.005
         let formatMin = Math.floor(min / baseNum) * baseNum
         let minRange = min - formatMin
         
          // Find the appropriate interval. Here is 500
        // The final 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
    }
Copy the code

After the above processing, the minimum value is 4500

  1. Since min was determined in Step 1, min has changed, so range needs to be recalculated, and interval also needs to be recalculated. The calculation method refers to the interval found in Step 1, and interval is 500.

  2. Min and interval can be obtained after step 1 and step 2. If min is less than interval, set min to 0 and perform Step 2 again. In this way, the last min and interval can be obtained, and then the last decimal number to be reserved needs to be calculated as follows:

function getDecimal(interval, min) {
   if(interval < 1) {
       return  Math.abs(getPowBit(interval))
   } else {
       // If the range and Max of the spacing are equal, there is no need to keep the decimal number
       // For example, interval is 15 million, Max is 40 million
       if(interval.range = max.range) {
           return 0
       } else {
             // Min must be greater than formatInterval if range is not equal
             // For example, if min is 1.211 million and interval is 2000, range is applied to 2000
             // Get the maximum number of digits to be reserved
           return Math.abs(getPowBit(formatInterval / formatMinData.range))
       }
   }
}
Copy the code
  1. And you end up with 45 million, 50 million, 55 million, 60 million, 65 million on the Y-axis

conclusion

For this scheme, min and interval are determined. For interval, you can flexibly define a regular interval, such as 550 instead of 1000 for 520, which can make the trend of the chart more obvious.