एक ग्राफ के वाई अक्ष के लिए एक आकर्षक रैखिक पैमाने का चयन


84

हम अपने सॉफ़्टवेयर में बार (या लाइन) ग्राफ प्रदर्शित करने के लिए थोड़ा सा कोड लिख रहे हैं। सब कुछ ठीक चल रहा है। जो चीज मुझे मिली है वह स्टम्प्ड है जो वाई एक्सिस लेबल कर रही है।

कॉल करने वाला मुझे बता सकता है कि वे वाई स्केल को कितने अच्छे से लेबल करना चाहते हैं, लेकिन मुझे लगता है कि वास्तव में "आकर्षक" तरह से उन्हें लेबल करने के लिए क्या करना है। मैं "आकर्षक" का वर्णन नहीं कर सकता, और शायद न तो आप कर सकते हैं, लेकिन हम इसे जानते हैं जब हम इसे देखते हैं, है ना?

तो अगर डेटा बिंदु हैं:

   15, 234, 140, 65, 90

और उपयोगकर्ता वाई अक्ष पर 10 लेबल के लिए पूछता है, कागज और पेंसिल के साथ थोड़ा सा परिमार्जन आता है:

  0, 25, 50, 75, 100, 125, 150, 175, 200, 225, 250

तो वहाँ 10 है (0 सहित नहीं), अंतिम एक उच्चतम मूल्य (234 <250) से परे फैली हुई है, और यह 25 में से प्रत्येक का "अच्छा" वेतन वृद्धि है। यदि वे 8 लेबल मांगते हैं, तो 30 की वृद्धि अच्छी लगेगी:

  0, 30, 60, 90, 120, 150, 180, 210, 240

नन्हे ने छल किया होगा। हो सकता है कि अभी या तो 8 या 10 का उपयोग किया है और इसे पर्याप्त रूप से बंद करना ठीक होगा। और जब कुछ बिंदु नकारात्मक हों तो क्या करें?

मैं देख सकता हूँ कि एक्सेल इस समस्या से अच्छी तरह निपटता है।

क्या किसी को यह हल करने के लिए एक सामान्य-उद्देश्य एल्गोरिथ्म (यहां तक ​​कि कुछ क्रूर बल ठीक है) पता है? मुझे इसे जल्दी करने की ज़रूरत नहीं है, लेकिन यह अच्छा दिखना चाहिए।


1
एक्सेल अपनी वाई अक्ष के लिए अधिकतम और न्यूनतम मान कैसे चुनता है, इसके बारे में कुछ जानकारी है: support.microsoft.com/kb/214075
क्रिस्टोफर ओर

अच्छा कार्यान्वयन: stackoverflow.com/a/16363437/829571
assylias

जवाबों:


103

बहुत समय पहले मैंने एक ग्राफ मॉड्यूल लिखा है जो इसे अच्छी तरह से कवर करता है। धूसर द्रव्यमान में खुदाई करने पर निम्नलिखित मिलते हैं:

  • डेटा के निचले और ऊपरी हिस्से को निर्धारित करें। (विशेष मामले से सावधान रहें जहाँ निचला बाउंड = अपर बाउंड!
  • टिक की आवश्यक मात्रा में सीमा को विभाजित करें।
  • टिक रेंज को अच्छी मात्रा में गोल करें।
  • तदनुसार कम और ऊपरी बाउंड समायोजित करें।

अपना उदाहरण दें:

15, 234, 140, 65, 90 with 10 ticks
  1. लोअर बाउंड = 15
  2. ऊपरी बाध्य = 234
  3. रेंज = 234-15 = 219
  4. टिक रेंज = 21.9। यह 25.0 होना चाहिए
  5. न्यू लोअर बाउंड = 25 * राउंड (15/25) = 0
  6. नई ऊपरी सीमा = 25 * गोल (1 + 235/25) = 250

तो रेंज = 0,25,50, ..., 225,250

आप निम्न चरणों के साथ अच्छा टिक रेंज प्राप्त कर सकते हैं:

  1. 10 ^ x से विभाजित करें, जिसका परिणाम 0.1 और 1.0 के बीच होता है (0.1 को छोड़कर 1)।
  2. तदनुसार अनुवाद करें:
    • 0.1 -> 0.1
    • <= 0.2 -> 0.2
    • <= 0.25 -> 0.25
    • <= 0.3 -> 0.3
    • <= 0.4 -> 0.4
    • <= 0.5 -> 0.5
    • <= 0.6 -> 0.6
    • <= 0.7 -> 0.7
    • <= 0.75 -> 0.75
    • <= 0.8 -> 0.8
    • <= 0.9 -> 0.9
    • <= 1.0 -> 1.0
  3. 10 ^ x से गुणा करें।

इस स्थिति में, 21.9 को 0.219 प्राप्त करने के लिए 10 ^ 2 से विभाजित किया जाता है। यह <= 0.25 है इसलिए हमारे पास अब 0.25 है। 10 ^ 2 से गुणा यह 25 देता है।

8 टिक के साथ एक ही उदाहरण पर एक नज़र डालते हैं:

15, 234, 140, 65, 90 with 8 ticks
  1. लोअर बाउंड = 15
  2. ऊपरी बाध्य = 234
  3. रेंज = 234-15 = 219
  4. टिक रेंज = 27.375
    1. 0.27375 के लिए 10 ^ 2 से विभाजित करें, 0.3 में अनुवाद करता है, जो देता है (10 ^ 2 से गुणा) 30।
  5. न्यू लोअर बाउंड = 30 * राउंड (15/30) = 0
  6. नई ऊपरी सीमा = 30 * गोल (1 + 235/30) = 240

जो आपके द्वारा अनुरोधित परिणाम देते हैं ;-)।

------ केडी द्वारा जोड़ा गया ------

यहाँ कोड है जो लुकअप टेबल आदि का उपयोग किए बिना इस एल्गोरिथ्म को प्राप्त करता है ...:

double range = ...;
int tickCount = ...;
double unroundedTickSize = range/(tickCount-1);
double x = Math.ceil(Math.log10(unroundedTickSize)-1);
double pow10x = Math.pow(10, x);
double roundedTickRange = Math.ceil(unroundedTickSize / pow10x) * pow10x;
return roundedTickRange;

सामान्यतया, टिक्कों की संख्या में नीचे की टिक शामिल होती है, इसलिए वास्तविक y- अक्ष खंड टिक की संख्या से एक कम होते हैं।


1
यह सिर्फ अधिकार के बारे में था। चरण 3, मुझे X को 1 से कम करना था। 219 से .1-> 1 की सीमा प्राप्त करने के लिए मुझे 10 ^ 3 (1000) से विभाजित करना होगा 10 ^ 2 (100) नहीं। अन्यथा, मौके पर।
क्लिंटन पियर्स

2
आप 10 ^ x से विभाजित करते हैं और 10 ^ x से गुणा करते हैं। यह ध्यान दिया जाना चाहिए कि एक्स को इस तरह से पाया जा सकता है: 'डबल एक्स = मैथ.काइलिंग (मैथ.लॉग 10 (टिकैंग));'
ब्रायन

1
बहुत मददगार। हालांकि समझ में नहीं आया - 'न्यू लोअर बाउंड = 30 * राउंड (15/30) = 0' (यह 30 मुझे लगता है) आएगा और आपको 'न्यू अपर बाउंड = 30 * राउंड में 235 (1 + 235/30) = कैसे मिला 240 '235 का उल्लेख कहीं नहीं है, यह 234 होना चाहिए।
उत्परिवर्ती

4
यह एक बेहतरीन जवाब है। बहुत ज्यादा अधिमूल्यित।
जोएल अनएयर

4
@JoelAnair धन्यवाद, आपने एक दुखद दिन को थोड़ा उज्जवल बनाया है।
तून Krijthe

22

यहाँ एक PHP उदाहरण है जिसका मैं उपयोग कर रहा हूँ। यह फ़ंक्शन बहुत सारे वाई अक्ष मूल्यों की एक सरणी देता है जो न्यूनतम और अधिकतम वाई मूल्यों को शामिल करता है। बेशक, इस दिनचर्या का उपयोग एक्स अक्ष मूल्यों के लिए भी किया जा सकता है।

यह आपको "सुझाव" देने की अनुमति देता है कि आप कितने टिक्स चाहते हैं, लेकिन दिनचर्या वापस आ जाएगी जो अच्छा लगता है। मैंने कुछ नमूना डेटा जोड़े हैं और इनके लिए परिणाम दिखाए हैं।

#!/usr/bin/php -q
<?php

function makeYaxis($yMin, $yMax, $ticks = 10)
{
  // This routine creates the Y axis values for a graph.
  //
  // Calculate Min amd Max graphical labels and graph
  // increments.  The number of ticks defaults to
  // 10 which is the SUGGESTED value.  Any tick value
  // entered is used as a suggested value which is
  // adjusted to be a 'pretty' value.
  //
  // Output will be an array of the Y axis values that
  // encompass the Y values.
  $result = array();
  // If yMin and yMax are identical, then
  // adjust the yMin and yMax values to actually
  // make a graph. Also avoids division by zero errors.
  if($yMin == $yMax)
  {
    $yMin = $yMin - 10;   // some small value
    $yMax = $yMax + 10;   // some small value
  }
  // Determine Range
  $range = $yMax - $yMin;
  // Adjust ticks if needed
  if($ticks < 2)
    $ticks = 2;
  else if($ticks > 2)
    $ticks -= 2;
  // Get raw step value
  $tempStep = $range/$ticks;
  // Calculate pretty step value
  $mag = floor(log10($tempStep));
  $magPow = pow(10,$mag);
  $magMsd = (int)($tempStep/$magPow + 0.5);
  $stepSize = $magMsd*$magPow;

  // build Y label array.
  // Lower and upper bounds calculations
  $lb = $stepSize * floor($yMin/$stepSize);
  $ub = $stepSize * ceil(($yMax/$stepSize));
  // Build array
  $val = $lb;
  while(1)
  {
    $result[] = $val;
    $val += $stepSize;
    if($val > $ub)
      break;
  }
  return $result;
}

// Create some sample data for demonstration purposes
$yMin = 60;
$yMax = 330;
$scale =  makeYaxis($yMin, $yMax);
print_r($scale);

$scale = makeYaxis($yMin, $yMax,5);
print_r($scale);

$yMin = 60847326;
$yMax = 73425330;
$scale =  makeYaxis($yMin, $yMax);
print_r($scale);
?>

नमूना डेटा से परिणाम आउटपुट

# ./test1.php
Array
(
    [0] => 60
    [1] => 90
    [2] => 120
    [3] => 150
    [4] => 180
    [5] => 210
    [6] => 240
    [7] => 270
    [8] => 300
    [9] => 330
)

Array
(
    [0] => 0
    [1] => 90
    [2] => 180
    [3] => 270
    [4] => 360
)

Array
(
    [0] => 60000000
    [1] => 62000000
    [2] => 64000000
    [3] => 66000000
    [4] => 68000000
    [5] => 70000000
    [6] => 72000000
    [7] => 74000000
)

मेरे मालिक इस से खुश होंगे - मुझ से अपवित्र भी n धन्यवाद!
स्टीफन हेज़ल

बहुत बढ़िया जवाब! मैं यह करने के लिए कनवर्ट स्विफ्ट 4 stackoverflow.com/a/55151115/2670547
पेट्र Syrov

@Scott Guthrie: यह बहुत अच्छा है जब तक कि इनपुट पूर्णांक नहीं होते हैं और छोटी संख्या होते हैं, उदाहरण के लिए, यदि yMin = 0.03 और yMax = 0.11।
ग्रेग

9

इस कोड को आज़माएं। मैंने इसे कुछ चार्टिंग परिदृश्यों में उपयोग किया है और यह अच्छी तरह से काम करता है। यह बहुत तेज है।

public static class AxisUtil
{
    public static float CalculateStepSize(float range, float targetSteps)
    {
        // calculate an initial guess at step size
        float tempStep = range/targetSteps;

        // get the magnitude of the step size
        float mag = (float)Math.Floor(Math.Log10(tempStep));
        float magPow = (float)Math.Pow(10, mag);

        // calculate most significant digit of the new step size
        float magMsd = (int)(tempStep/magPow + 0.5);

        // promote the MSD to either 1, 2, or 5
        if (magMsd > 5.0)
            magMsd = 10.0f;
        else if (magMsd > 2.0)
            magMsd = 5.0f;
        else if (magMsd > 1.0)
            magMsd = 2.0f;

        return magMsd*magPow;
    }
}

6

लगता है जैसे फोन करने वाला आपको वह रेंज नहीं बताता जो वह चाहता है।

इसलिए आप अंत बिंदुओं को तब तक बदलने के लिए स्वतंत्र हैं जब तक कि आप इसे अपने लेबल की गिनती से अच्छी तरह से विभाज्य न प्राप्त कर लें।

चलो "अच्छा" परिभाषित करते हैं। यदि लेबल बंद हैं तो मुझे अच्छा लगेगा:

1. 2^n, for some integer n. eg. ..., .25, .5, 1, 2, 4, 8, 16, ...
2. 10^n, for some integer n. eg. ..., .01, .1, 1, 10, 100
3. n/5 == 0, for some positive integer n, eg, 5, 10, 15, 20, 25, ...
4. n/2 == 0, for some positive integer n, eg, 2, 4, 6, 8, 10, 12, 14, ...

अपनी डेटा श्रृंखला की अधिकतम और न्यूनतम राशि का पता लगाएं। आइए इन बिंदुओं को कॉल करें:

min_point and max_point.

अब आपको बस इतना करना है 3 मान है:

- start_label, where start_label < min_point and start_label is an integer
- end_label, where end_label > max_point and end_label is an integer
- label_offset, where label_offset is "nice"

यह समीकरण फिट बैठता है:

(end_label - start_label)/label_offset == label_count

शायद कई समाधान हैं, इसलिए बस एक को चुनें। अधिकांश समय मैं शर्त लगा सकता हूं कि आप सेट कर सकते हैं

start_label to 0

तो बस अलग पूर्णांक का प्रयास करें

end_label

जब तक ऑफसेट "अच्छा" नहीं है


3

मैं अब भी इससे जूझ रहा हूं :)

मूल गेमकैट का उत्तर अधिकतर समय काम करने लगता है, लेकिन "3 टिक्स" में प्लगिंग की कोशिश करें, क्योंकि टिक की संख्या आवश्यक है (उसी डेटा मान के लिए 15, 234, 140, 65, 90) .... यह 73 की एक टिक रेंज देने के लिए लगता है, जो 10 ^ 2 पैदावार 0.73 से विभाजित करने के बाद, जो 0.75 के लिए मैप करता है, जो 75 का 'अच्छा' टिक रेंज देता है।

फिर ऊपरी सीमा की गणना: 75 * गोल (1 + 234/75) = 300

और निचला बाउंड: 75 * राउंड (15/75) = 0

लेकिन स्पष्ट रूप से यदि आप 0 से शुरू करते हैं, और 300 के ऊपरी सीमा तक 75 के चरणों में आगे बढ़ते हैं, तो आप 0,75,150,225,300 के साथ समाप्त होते हैं .... जो कि कोई संदेह नहीं है, लेकिन यह 4 टिक है (0 सहित नहीं) नहीं 3 टिक की आवश्यकता है।

बस निराशा होती है कि यह समय के 100% काम नहीं करता है .... जो मेरी गलती के लिए नीचे हो सकता है!


मूल रूप से लगा कि ब्रायन के एक्स को प्राप्त करने की सुझाई गई विधि से समस्या कुछ हो सकती है, लेकिन यह बिल्कुल सटीक है।
स्टिलपॉन्डिंग

3

तून Krijthe द्वारा जवाब ज्यादातर समय काम करता है। लेकिन कभी-कभी यह अधिक संख्या में टिक्स का उत्पादन करेगा। यह नकारात्मक संख्या के साथ भी काम नहीं करेगा। समस्या के लिए ओवरआल दृष्टिकोण ठीक है लेकिन इसे संभालने का एक बेहतर तरीका है। आप जिस एल्गोरिथ्म का उपयोग करना चाहते हैं, वह इस बात पर निर्भर करेगा कि आप वास्तव में क्या पाना चाहते हैं। नीचे मैं आपको अपना कोड प्रस्तुत कर रहा हूं जिसका उपयोग मैंने अपने जेएस प्लॉटिंग लाइब्रेरी में किया था। मैंने इसका परीक्षण किया है और यह हमेशा काम करता है (उम्मीद है;))। यहाँ प्रमुख कदम हैं:

  • वैश्विक चरम xMin और xMax प्राप्त करें (उन सभी भूखंडों को हटा दें जिन्हें आप एल्गोरिथ्म में प्रिंट करना चाहते हैं)
  • xMin और xMax के बीच की गणना रेंज
  • अपनी सीमा के परिमाण के क्रम की गणना करें
  • टिकों की संख्या के आधार पर श्रेणी को विभाजित करके टिक आकार की गणना करें
  • यह एक वैकल्पिक है। यदि आप शून्य टिक ऑलवेज प्रिंट करना चाहते हैं तो आप सकारात्मक और नकारात्मक टिकों की संख्या की गणना करने के लिए टिक आकार का उपयोग करें। टिक्स की कुल संख्या उनकी राशि + 1 होगी (शून्य टिक)
  • यदि आपके पास शून्य टिक ऑलवेज मुद्रित है, तो इसकी आवश्यकता नहीं है। कम और ऊपरी बाध्य की गणना करें लेकिन भूखंड को केंद्र में रखना याद रखें

चलो शुरू करते हैं। पहले बुनियादी गणना

    var range = Math.abs(xMax - xMin); //both can be negative
    var rangeOrder = Math.floor(Math.log10(range)) - 1; 
    var power10 = Math.pow(10, rangeOrder);
    var maxRound = (xMax > 0) ? Math.ceil(xMax / power10) : Math.floor(xMax / power10);
    var minRound = (xMin < 0) ? Math.floor(xMin / power10) : Math.ceil(xMin / power10);

मैं न्यूनतम और अधिकतम मानों को 100% सुनिश्चित करता हूं कि मेरा प्लॉट सभी डेटा को कवर करेगा। रेंज व्हीटर के लॉग 10 को फर्श करना बहुत महत्वपूर्ण है या नहीं यह नकारात्मक है और 1 बाद में घटिया होता है। अन्यथा आपका एल्गोरिदम उन संख्याओं के लिए काम नहीं करेगा जो एक से कम हैं।

    var fullRange = Math.abs(maxRound - minRound);
    var tickSize = Math.ceil(fullRange / (this.XTickCount - 1));

    //You can set nice looking ticks if you want
    //You can find exemplary method below 
    tickSize = this.NiceLookingTick(tickSize);

    //Here you can write a method to determine if you need zero tick
    //You can find exemplary method below
    var isZeroNeeded = this.HasZeroTick(maxRound, minRound, tickSize);

मैं 7, 13, 17 आदि जैसे टिक्स से बचने के लिए "गुड लुकिंग टिक्स" का उपयोग करता हूं। यहां मैं जिस विधि का उपयोग करता हूं वह बहुत सरल है। जरूरत पड़ने पर जीरो टिक करना भी अच्छा है। प्लॉट इस तरह से बहुत अधिक पेशेवर लगता है। आपको इस उत्तर के अंत में सभी विधियाँ मिलेंगी।

अब आपको ऊपरी और निचले सीमा की गणना करनी होगी। यह शून्य टिक के साथ बहुत आसान है, लेकिन अन्य मामले में थोड़ा अधिक प्रयास की आवश्यकता है। क्यों? क्योंकि हम प्लॉट को ऊपरी और निचले हिस्से में अच्छी तरह से बांधना चाहते हैं। मेरे कोड पर एक नज़र है। कुछ चर इस दायरे के बाहर परिभाषित किए गए हैं और उनमें से कुछ एक वस्तु के गुण हैं जिसमें संपूर्ण प्रस्तुत कोड रखा गया है।

    if (isZeroNeeded) {

        var positiveTicksCount = 0;
        var negativeTickCount = 0;

        if (maxRound != 0) {

            positiveTicksCount = Math.ceil(maxRound / tickSize);
            XUpperBound = tickSize * positiveTicksCount * power10;
        }

        if (minRound != 0) {
            negativeTickCount = Math.floor(minRound / tickSize);
            XLowerBound = tickSize * negativeTickCount * power10;
        }

        XTickRange = tickSize * power10;
        this.XTickCount = positiveTicksCount - negativeTickCount + 1;
    }
    else {
        var delta = (tickSize * (this.XTickCount - 1) - fullRange) / 2.0;

        if (delta % 1 == 0) {
            XUpperBound = maxRound + delta;
            XLowerBound = minRound - delta;
        }
        else {
            XUpperBound =  maxRound + Math.ceil(delta);
            XLowerBound =  minRound - Math.floor(delta);
        }

        XTickRange = tickSize * power10;
        XUpperBound = XUpperBound * power10;
        XLowerBound = XLowerBound * power10;
    }

और यहां वे विधियां बताई गई हैं जिनके बारे में आप खुद लिख सकते हैं, लेकिन आप मेरा उपयोग भी कर सकते हैं

this.NiceLookingTick = function (tickSize) {

    var NiceArray = [1, 2, 2.5, 3, 4, 5, 10];

    var tickOrder = Math.floor(Math.log10(tickSize));
    var power10 = Math.pow(10, tickOrder);
    tickSize = tickSize / power10;

    var niceTick;
    var minDistance = 10;
    var index = 0;

    for (var i = 0; i < NiceArray.length; i++) {
        var dist = Math.abs(NiceArray[i] - tickSize);
        if (dist < minDistance) {
            minDistance = dist;
            index = i;
        }
    }

    return NiceArray[index] * power10;
}

this.HasZeroTick = function (maxRound, minRound, tickSize) {

    if (maxRound * minRound < 0)
    {
        return true;
    }
    else if (Math.abs(maxRound) < tickSize || Math.round(minRound) < tickSize) {

        return true;
    }
    else {

        return false;
    }
}

केवल एक और चीज है जो यहां शामिल नहीं है। यह "अच्छी लग रही सीमा" है। ये निचली सीमाएं हैं जो "अच्छे दिखने वाले टिक्स" में संख्याओं के समान हैं। उदाहरण के लिए, टिक आकार 5 के साथ कम से कम 5 से शुरू होना बेहतर है एक प्लॉट होने की तुलना में 6 पर एक ही टिक आकार के साथ शुरू होता है। लेकिन यह मेरा निकाल दिया मैं इसे आप पर छोड़ दें।

आशा करता हूँ की ये काम करेगा। चीयर्स!


2

इस उत्तर को स्विफ्ट 4 के रूप में परिवर्तित किया

extension Int {

    static func makeYaxis(yMin: Int, yMax: Int, ticks: Int = 10) -> [Int] {
        var yMin = yMin
        var yMax = yMax
        var ticks = ticks
        // This routine creates the Y axis values for a graph.
        //
        // Calculate Min amd Max graphical labels and graph
        // increments.  The number of ticks defaults to
        // 10 which is the SUGGESTED value.  Any tick value
        // entered is used as a suggested value which is
        // adjusted to be a 'pretty' value.
        //
        // Output will be an array of the Y axis values that
        // encompass the Y values.
        var result = [Int]()
        // If yMin and yMax are identical, then
        // adjust the yMin and yMax values to actually
        // make a graph. Also avoids division by zero errors.
        if yMin == yMax {
            yMin -= ticks   // some small value
            yMax += ticks   // some small value
        }
        // Determine Range
        let range = yMax - yMin
        // Adjust ticks if needed
        if ticks < 2 { ticks = 2 }
        else if ticks > 2 { ticks -= 2 }

        // Get raw step value
        let tempStep: CGFloat = CGFloat(range) / CGFloat(ticks)
        // Calculate pretty step value
        let mag = floor(log10(tempStep))
        let magPow = pow(10,mag)
        let magMsd = Int(tempStep / magPow + 0.5)
        let stepSize = magMsd * Int(magPow)

        // build Y label array.
        // Lower and upper bounds calculations
        let lb = stepSize * Int(yMin/stepSize)
        let ub = stepSize * Int(ceil(CGFloat(yMax)/CGFloat(stepSize)))
        // Build array
        var val = lb
        while true {
            result.append(val)
            val += stepSize
            if val > ub { break }
        }
        return result
    }

}

यह बहुत अच्छा है जब तक कि इनपुट पूर्णांक नहीं हैं और छोटी संख्या हैं, उदाहरण के लिए, यदि yMin = 0.03 और yMax = 0.11।
ग्रेग

1

यदि आप 10 चरण + शून्य चाहते हैं तो यह एक आकर्षण की तरह काम करता है

//get proper scale for y
$maximoyi_temp= max($institucion); //get max value from data array
 for ($i=10; $i< $maximoyi_temp; $i=($i*10)) {   
    if (($divisor = ($maximoyi_temp / $i)) < 2) break; //get which divisor will give a number between 1-2    
 } 
 $factor_d = $maximoyi_temp / $i;
 $factor_d = ceil($factor_d); //round up number to 2
 $maximoyi = $factor_d * $i; //get new max value for y
 if ( ($maximoyi/ $maximoyi_temp) > 2) $maximoyi = $maximoyi /2; //check if max value is too big, then split by 2

1

ES5 जावास्क्रिप्ट में जिस किसी को भी इसकी आवश्यकता है, वह थोड़ा सा कुश्ती कर रहा है, लेकिन यहां यह है:

var min=52;
var max=173;
var actualHeight=500; // 500 pixels high graph

var tickCount =Math.round(actualHeight/100); 
// we want lines about every 100 pixels.

if(tickCount <3) tickCount =3; 
var range=Math.abs(max-min);
var unroundedTickSize = range/(tickCount-1);
var x = Math.ceil(Math.log10(unroundedTickSize)-1);
var pow10x = Math.pow(10, x);
var roundedTickRange = Math.ceil(unroundedTickSize / pow10x) * pow10x;
var min_rounded=roundedTickRange * Math.floor(min/roundedTickRange);
var max_rounded= roundedTickRange * Math.ceil(max/roundedTickRange);
var nr=tickCount;
var str="";
for(var x=min_rounded;x<=max_rounded;x+=roundedTickRange)
{
    str+=x+", ";
}
console.log("nice Y axis "+str);    

Toon Krijtje द्वारा उत्कृष्ट उत्तर के आधार पर।


1

यह समाधान मुझे मिले जावा उदाहरण पर आधारित है।

const niceScale = ( minPoint, maxPoint, maxTicks) => {
    const niceNum = ( localRange,  round) => {
        var exponent,fraction,niceFraction;
        exponent = Math.floor(Math.log10(localRange));
        fraction = localRange / Math.pow(10, exponent);
        if (round) {
            if (fraction < 1.5) niceFraction = 1;
            else if (fraction < 3) niceFraction = 2;
            else if (fraction < 7) niceFraction = 5;
            else niceFraction = 10;
        } else {
            if (fraction <= 1) niceFraction = 1;
            else if (fraction <= 2) niceFraction = 2;
            else if (fraction <= 5) niceFraction = 5;
            else niceFraction = 10;
        }
        return niceFraction * Math.pow(10, exponent);
    }
    const result = [];
    const range = niceNum(maxPoint - minPoint, false);
    const stepSize = niceNum(range / (maxTicks - 1), true);
    const lBound = Math.floor(minPoint / stepSize) * stepSize;
    const uBound = Math.ceil(maxPoint / stepSize) * stepSize;
    for(let i=lBound;i<=uBound;i+=stepSize) result.push(i);
    return result;
};
console.log(niceScale(15,234,6));
// > [0, 100, 200, 300]


0

सवाल और जवाब के लिए धन्यवाद, बहुत मददगार। Gamecat, मैं सोच रहा हूं कि आप कैसे निर्धारित कर रहे हैं कि टिक रेंज को किस तरह गोल किया जाना चाहिए।

टिक रेंज = 21.9। यह 25.0 होना चाहिए

एल्गोरिथम ऐसा करने के लिए, किसी को बड़ी संख्या के लिए इस स्केल को अच्छी तरह से बनाने के लिए ऊपर दिए गए एल्गोरिथम में तर्क जोड़ना होगा? उदाहरण के लिए 10 टिक के साथ, यदि रेंज 3346 है तो टिक रेंज 334.6 का मूल्यांकन करेगी और निकटतम 10 के लिए गोलाई में 340 होगा जब 350 संभवत: अच्छा है।

तुम क्या सोचते हो?


@ गमेकैट के उदाहरण में, 334.6 => 0.3346, जो 0.4 पर जाना चाहिए। तो टिक रेंज वास्तव में 400 होगी, जो एक बहुत अच्छी संख्या है।
ब्रायन

0

@ Gamecat के एल्गोरिथ्म के आधार पर, मैंने निम्न सहायक वर्ग का उत्पादन किया

public struct Interval
{
    public readonly double Min, Max, TickRange;

    public static Interval Find(double min, double max, int tickCount, double padding = 0.05)
    {
        double range = max - min;
        max += range*padding;
        min -= range*padding;

        var attempts = new List<Interval>();
        for (int i = tickCount; i > tickCount / 2; --i)
            attempts.Add(new Interval(min, max, i));

        return attempts.MinBy(a => a.Max - a.Min);
    }

    private Interval(double min, double max, int tickCount)
    {
        var candidates = (min <= 0 && max >= 0 && tickCount <= 8) ? new[] {2, 2.5, 3, 4, 5, 7.5, 10} : new[] {2, 2.5, 5, 10};

        double unroundedTickSize = (max - min) / (tickCount - 1);
        double x = Math.Ceiling(Math.Log10(unroundedTickSize) - 1);
        double pow10X = Math.Pow(10, x);
        TickRange = RoundUp(unroundedTickSize/pow10X, candidates) * pow10X;
        Min = TickRange * Math.Floor(min / TickRange);
        Max = TickRange * Math.Ceiling(max / TickRange);
    }

    // 1 < scaled <= 10
    private static double RoundUp(double scaled, IEnumerable<double> candidates)
    {
        return candidates.First(candidate => scaled <= candidate);
    }
}

0

उपरोक्त एल्गोरिदम उस मामले को ध्यान में नहीं रखते हैं जब न्यूनतम और अधिकतम मूल्य के बीच की सीमा बहुत छोटी है। और क्या होगा यदि ये मूल्य शून्य से बहुत अधिक हैं? फिर, हमारे पास शून्य से अधिक मूल्य वाले y- अक्ष को शुरू करने की संभावना है। इसके अलावा, हमारी रेखा पूरी तरह से ग्राफ के ऊपरी या नीचे की ओर होने से बचने के लिए, हमें इसे "साँस लेने के लिए हवा" देना होगा।

उन मामलों को कवर करने के लिए जो मैंने (PHP पर) उपरोक्त कोड में लिखा था:

function calculateStartingPoint($min, $ticks, $times, $scale) {

    $starting_point = $min - floor((($ticks - $times) * $scale)/2);

    if ($starting_point < 0) {
        $starting_point = 0;
    } else {
        $starting_point = floor($starting_point / $scale) * $scale;
        $starting_point = ceil($starting_point / $scale) * $scale;
        $starting_point = round($starting_point / $scale) * $scale;
    }
    return $starting_point;
}

function calculateYaxis($min, $max, $ticks = 7)
{
    print "Min = " . $min . "\n";
    print "Max = " . $max . "\n";

    $range = $max - $min;
    $step = floor($range/$ticks);
    print "First step is " . $step . "\n";
    $available_steps = array(5, 10, 20, 25, 30, 40, 50, 100, 150, 200, 300, 400, 500);
    $distance = 1000;
    $scale = 0;

    foreach ($available_steps as $i) {
        if (($i - $step < $distance) && ($i - $step > 0)) {
            $distance = $i - $step;
            $scale = $i;
        }
    }

    print "Final scale step is " . $scale . "\n";

    $times = floor($range/$scale);
    print "range/scale = " . $times . "\n";

    print "floor(times/2) = " . floor($times/2) . "\n";

    $starting_point = calculateStartingPoint($min, $ticks, $times, $scale);

    if ($starting_point + ($ticks * $scale) < $max) {
        $ticks += 1;
    }

    print "starting_point = " . $starting_point . "\n";

    // result calculation
    $result = [];
    for ($x = 0; $x <= $ticks; $x++) {
        $result[] = $starting_point + ($x * $scale);
    }
    return $result;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.