Reasonable optimized chart scaling

前端 未结 6 1575
耶瑟儿~
耶瑟儿~ 2021-01-30 13:43

I need to make a chart with an optimized y axis maximum value.

The current method I have of making charts simply uses the maximum value of all the graphs, then

6条回答
  •  南方客
    南方客 (楼主)
    2021-01-30 14:12

    In the past I've done this in a brute force-ish sort of way. Here's a chunk of C++ code that works well... but for a hardcoded lower and upper limits (0 and 5000):

    int PickYUnits()
    {
        int MinSize[8] = {20, 20, 20, 20, 20, 20, 20, 20};
        int ItemsPerUnit[8] = {5, 10, 20, 25, 50, 100, 250, 500};
        int ItemLimits[8] = {20, 50, 100, 250, 500, 1000, 2500, 5000};
        int MaxNumUnits = 8;
        double PixelsPerY;
        int PixelsPerAxis;
        int Units;
    
        //
        // Figure out the max from the dataset
        //  - Min is always 0 for a bar chart
        //
        m_MinY = 0;
        m_MaxY = -9999999;
        m_TotalY = 0;
        for (int j = 0; j < m_DataPoints.GetSize(); j++) {
            if (m_DataPoints[j].m_y > m_MaxY) {
                m_MaxY = m_DataPoints[j].m_y;
            }
    
            m_TotalY += m_DataPoints[j].m_y;
        }
    
        //
        // Give some space at the top
        //
        m_MaxY = m_MaxY + 1;
    
    
        //
        // Figure out the size of the range
        //
        double yRange = (m_MaxY - m_MinY);
    
        //
        // Pick the initial size
        //
        Units = MaxNumUnits;
        for (int k = 0; k < MaxNumUnits; k++)
        {
            if (yRange < ItemLimits[k])
            {
                Units = k;
                break;
            }
        }
    
        //
        // Adjust it upwards based on the space available
        //
        PixelsPerY = m_rcGraph.Height() / yRange;
        PixelsPerAxis = (int)(PixelsPerY * ItemsPerUnit[Units]);
    
        while (PixelsPerAxis < MinSize[Units]){
            Units += 1;
            PixelsPerAxis = (int)(PixelsPerY * ItemsPerUnit[Units]);
            if (Units == 5)
                break;
        }
    
    
        return ItemsPerUnit[Units];
    }
    

    However something in what you've said tweaked me. To pick nice axis numbers a definition of "nice number" would help:

    • A "nice" number is one that has 3 or fewer non-zero digits (eg. 1230000)
    • A "nice" number has the same or few non-zero digits than zero digits (eg 1230 is not nice, 1200 is nice)
    • The nicest numbers are ones with multiples of 3 zeros (eg. "1,000", "1,000,000")
    • The second nicest numbers are onces with multples of 3 zeros plus 2 zeros (eg. "1,500,000", "1,200")

    Not sure if the above definition is "right" or actually helpful (but with the definition in hand it then becomes a simpler task to devise an algorithm).

提交回复
热议问题