ज्ञात न्यूनतम और अधिकतम मान के साथ संख्याओं की श्रेणी को कैसे कम करें


230

इसलिए मैं यह पता लगाने की कोशिश कर रहा हूं कि कैसे कई रेंज ले सकते हैं और एक रेंज फिट करने के लिए वैल्यूज को नीचे गिरा सकते हैं। ऐसा करने की इच्छा का कारण यह है कि मैं एक जावा स्विंग पैनल्स में दीर्घवृत्त खींचने की कोशिश कर रहा हूं। मैं चाहता हूं कि प्रत्येक दीर्घवृत्त की ऊंचाई और चौड़ाई 1-30 की सीमा में हो। मेरे पास ऐसे तरीके हैं जो मेरे डेटा सेट से न्यूनतम और अधिकतम मान पाते हैं, लेकिन मेरे पास रनटाइम तक न्यूनतम और अधिकतम नहीं होगा। क्या इसे करने का कोई आसान तरीका है?

जवाबों:


507

मान लीजिए कि आप किसी श्रेणी [min,max]को स्केल करना चाहते हैं [a,b]। आप एक (निरंतर) फ़ंक्शन की तलाश कर रहे हैं जो संतुष्ट करता है

f(min) = a
f(max) = b

आपके मामले में, a1 bहोगा और 30 होगा, लेकिन चलो कुछ सरल से शुरू [min,max]करते हैं और रेंज में मैप करने का प्रयास करते हैं [0,1]

लाना minएक समारोह में और 0 बाहर हो रही के साथ पूरा किया जा सकता

f(x) = x - min   ===>   f(min) = min - min = 0

तो हम लगभग यही चाहते हैं। लेकिन में डालने maxहमें देना होगा max - minजब हम वास्तव में 1. चाहते इसलिए हम इसे पैमाने पर करना होगा:

        x - min                                  max - min
f(x) = ---------   ===>   f(min) = 0;  f(max) =  --------- = 1
       max - min                                 max - min

जो हम चाहते हैं। इसलिए हमें अनुवाद और स्केलिंग करने की जरूरत है। अब अगर इसके बजाय हम मनमाना मूल्यों को प्राप्त करना चाहते हैं aऔर b, हमें कुछ अधिक जटिल होने की आवश्यकता है:

       (b-a)(x - min)
f(x) = --------------  + a
          max - min

आप सत्यापित करें कि में डाल सकते हैं minके लिए xअब देता है a, और में डाल maxदेता है b

आप यह भी देख सकते हैं कि (b-a)/(max-min)नई श्रेणी के आकार और मूल श्रेणी के आकार के बीच एक स्केलिंग कारक है। तो सच में हम पहले अनुवाद कर रहे हैं xद्वारा -min, सही कारक करने के लिए इसे स्केलिंग, और उसके बाद का अनुवाद इसके बारे में नए न्यूनतम मूल्य के लिए बैक अप लेने a

उम्मीद है की यह मदद करेगा।


तुम्हारी सहायता सराहनीय है। मैंने एक ऐसा समाधान निकाला, जो सौंदर्य की दृष्टि से प्रसन्न करने का काम करता है। हालाँकि मैं आपके तर्क को अधिक सटीक मॉडल देने के लिए लागू करूँगा। धन्यवाद फिर से :)
user650271

4
बस एक अनुस्मारक: max != minअन्यथा समारोह अनिश्चित परिणाम के साथ मॉडल अधिक सटीक होगा :)
marcoslhc

10
क्या यह सुनिश्चित करता है कि मेरा पुनर्विकसित चर मूल वितरण को बरकरार रखता है?
हाइजेनबर्ग

2
यह एक रेखीय पैमाने का एक अच्छा कार्यान्वयन है। क्या यह आसानी से एक लघुगणकीय पैमाने में तब्दील हो सकता है?
12

बहुत स्पष्ट व्याख्या। क्या यह काम करता है अगर minनकारात्मक है और maxसकारात्मक है, या क्या उन दोनों को सकारात्मक होना चाहिए?
एंड्रयू

48

कॉपी-पेस्ट आसानी के लिए यहां कुछ जावास्क्रिप्ट है (यह जलन का जवाब है):

function scaleBetween(unscaledNum, minAllowed, maxAllowed, min, max) {
  return (maxAllowed - minAllowed) * (unscaledNum - min) / (max - min) + minAllowed;
}

इस तरह लागू होता है, 0-100 के बीच की सीमा को 10-50 तक बढ़ा देता है।

var unscaledNums = [10, 13, 25, 28, 43, 50];

var maxRange = Math.max.apply(Math, unscaledNums);
var minRange = Math.min.apply(Math, unscaledNums);

for (var i = 0; i < unscaledNums.length; i++) {
  var unscaled = unscaledNums[i];
  var scaled = scaleBetween(unscaled, 0, 100, minRange, maxRange);
  console.log(scaled.toFixed(2));
}

0.00, 18.37, 48.98, 55.10, 85.71, 100.00

संपादित करें:

मुझे पता है कि मैंने बहुत समय पहले इसका उत्तर दिया था, लेकिन यहां एक क्लीनर फ़ंक्शन है जो अब मैं उपयोग करता हूं:

Array.prototype.scaleBetween = function(scaledMin, scaledMax) {
  var max = Math.max.apply(Math, this);
  var min = Math.min.apply(Math, this);
  return this.map(num => (scaledMax-scaledMin)*(num-min)/(max-min)+scaledMin);
}

इस तरह लागू:

[-4, 0, 5, 6, 9].scaleBetween(0, 100);

[0, 30.76923076923077, 69.23076923076923, 76.92307692307692, 100]


var arr = ["-40000.00", "2", "3.000", "4.5825", "0.00008", "1000000000.00008", "0.02008", "100", "- 5000", "- 82.0000048", "0.02" , "0,005", "- 3.0008", "5", "8", "600", "- 1000", "- 5000"]; इस स्थिति के लिए, आपकी विधि से, संख्या बहुत कम हो रही है। क्या कोई रास्ता है, इसलिए, स्केल (0,100) या (-100,100) होना चाहिए और आउटपुट के बीच का अंतर 0.5 (या कोई भी संख्या) होना चाहिए।

कृपया गिरफ्तारी [] के लिए भी मेरे परिदृश्य पर विचार करें।

1
यह एक किनारे का मामला है, लेकिन यह मर जाता है अगर सरणी में केवल एक मान होता है या एक ही मूल्य की कई प्रतियां होती हैं। तो [1] .scaleBetween (1, 100) और [1,1,1] .scaleBetween (1,100) दोनों NaN के साथ आउटपुट को भरते हैं।
मालाबार फ्रंट

1
@ मालाबारफ्रंट, अच्छा अवलोकन। मैं इसे अपरिभाषित है कि क्या उस स्थिति में परिणाम होना चाहिए लगता है [1, 1, 1], [100, 100, 100]या, यहां तक कि [50.5, 50.5, 50.5]। आप मामले में डाल सकते हैं:if (max-min == 0) return this.map(num => (scaledMin+scaledMax)/2);
चार्ल्स क्लेटन

1
@CharlesClayton शानदार, धन्यवाद। कि एक इलाज काम करता है!
मालाबार फ्रंट

27

सुविधा के लिए, यहाँ जावा फॉर्म में इरिटेट का एल्गोरिदम है। त्रुटि जाँच, अपवाद संभालना और आवश्यकतानुसार जोड़ना।

public class Algorithms { 
    public static double scale(final double valueIn, final double baseMin, final double baseMax, final double limitMin, final double limitMax) {
        return ((limitMax - limitMin) * (valueIn - baseMin) / (baseMax - baseMin)) + limitMin;
    }
}

परीक्षक:

final double baseMin = 0.0;
final double baseMax = 360.0;
final double limitMin = 90.0;
final double limitMax = 270.0;
double valueIn = 0;
System.out.println(Algorithms.scale(valueIn, baseMin, baseMax, limitMin, limitMax));
valueIn = 360;
System.out.println(Algorithms.scale(valueIn, baseMin, baseMax, limitMin, limitMax));
valueIn = 180;
System.out.println(Algorithms.scale(valueIn, baseMin, baseMax, limitMin, limitMax));

90.0
270.0
180.0

21

यहाँ मैं इसे कैसे समझ सकता हूँ:


कितने प्रतिशत xएक सीमा में है

मान लेते हैं कि आपके पास एक सीमा 0है 100। उस सीमा से एक मनमानी संख्या को देखते हुए, उस सीमा से "प्रतिशत" क्या है? यह बहुत सरल होना चाहिए, 0होगा 0%, 50होगा 50%और 100होगा 100%

अब, क्या हुआ अगर आपके रेंज था 20करने के लिए 100? हम ऊपर के समान तर्क लागू नहीं कर सकते (100 से विभाजित करें) क्योंकि:

20 / 100

हमें नहीं देना चाहिए 0( अब 20होना चाहिए 0%)। इसे ठीक करने के लिए सरल होना चाहिए, हमें केवल 0मामले के लिए अंश बनाने की आवश्यकता है 20। हम घटाकर ऐसा कर सकते हैं:

(20 - 20) / 100

हालाँकि, यह 100अब और काम नहीं करता क्योंकि:

(100 - 20) / 100

हमें नहीं देता 100%। फिर, हम इसे भाजक से घटाकर भी ठीक कर सकते हैं:

(100 - 20) / (100 - 20)

यह पता लगाने के लिए एक सामान्यीकृत समीकरण कि किस xसीमा में % निहित है:

(x - MIN) / (MAX - MIN)

स्केल रेंज से दूसरी रेंज

अब जब हम जानते हैं कि एक सीमा में कितने प्रतिशत संख्या निहित है, हम इसे संख्या को दूसरी श्रेणी में मैप करने के लिए लागू कर सकते हैं। एक उदाहरण के माध्यम से चलते हैं।

old range = [200, 1000]
new range = [10, 20]

यदि हमारे पास पुरानी श्रेणी में एक संख्या है, तो नई श्रेणी में संख्या क्या होगी? मान लीजिए कि संख्या है 400। पहले यह पता करें कि 400पुरानी सीमा के भीतर कितना प्रतिशत है। हम अपने समीकरण को ऊपर लागू कर सकते हैं।

(400 - 200) / (1000 - 200) = 0.25

तो, पुरानी सीमा 400में स्थित है 25%। हमें केवल यह पता लगाने की आवश्यकता है 25%कि नई श्रेणी की संख्या क्या है । के बारे में सोचो क्या 50%की [0, 20]है। यह 10सही होगा? आप उस उत्तर पर कैसे पहुंचे? ठीक है, हम बस कर सकते हैं:

20 * 0.5 = 10

लेकिन, इससे क्या [10, 20]? हमें 10अब तक सब कुछ स्थानांतरित करने की आवश्यकता है । उदाहरण के लिए:

((20 - 10) * 0.5) + 10

अधिक सामान्यीकृत सूत्र होगा:

((MAX - MIN) * PERCENT) + MIN

क्या है 25%के मूल उदाहरण के लिए [10, 20]:

((20 - 10) * 0.25) + 10 = 12.5

तो, 400सीमा में [200, 1000]करने के लिए नक्शे होगा 12.5रेंज में[10, 20]


TLDR

xपुरानी रेंज से नई रेंज में मैप करने के लिए:

OLD PERCENT = (x - OLD MIN) / (OLD MAX - OLD MIN)
NEW X = ((NEW MAX - NEW MIN) * OLD PERCENT) + NEW MIN

1
ठीक इसी तरह मैंने इसे काम किया। ट्रिकिएस्ट भाग उस अनुपात का पता लगाने के लिए है जहां एक संख्या एक सीमा में है। यह हमेशा [0, 1] सीमा के भीतर होना चाहिए जैसे प्रतिशत, उदाहरण के लिए 0.5 50% के लिए है। आगे आपको केवल अपनी आवश्यक सीमा में फिट होने के लिए इस नंबर का विस्तार / खिंचाव और बदलाव करना होगा।
SMUsamaShah

बहुत ही सरल तरीके से चरणों को समझाने के लिए धन्यवाद - उत्तर / एस कार्यों के ऊपर कोपिस्टा है लेकिन चरणों को जानना केवल महान है।
रोजा

11

मैं इस समाधान पर आया था लेकिन यह वास्तव में मेरी जरूरत के अनुरूप नहीं है। इसलिए मैंने d3 स्रोत कोड में थोड़ा खुदाई किया। मैं व्यक्तिगत रूप से इसे करने की सिफारिश करूंगा जैसे d3.scale करता है।

इसलिए यहां आप डोमेन को रेंज में स्केल करते हैं। लाभ यह है कि आप अपने लक्ष्य सीमा तक संकेत फ्लिप कर सकते हैं। यह उपयोगी है क्योंकि कंप्यूटर स्क्रीन पर y अक्ष ऊपर जाता है इसलिए बड़े मानों में एक छोटा y होता है।

public class Rescale {
    private final double range0,range1,domain0,domain1;

    public Rescale(double domain0, double domain1, double range0, double range1) {
        this.range0 = range0;
        this.range1 = range1;
        this.domain0 = domain0;
        this.domain1 = domain1;
    }

    private double interpolate(double x) {
        return range0 * (1 - x) + range1 * x;
    }

    private double uninterpolate(double x) {
        double b = (domain1 - domain0) != 0 ? domain1 - domain0 : 1 / domain1;
        return (x - domain0) / b;
    }

    public double rescale(double x) {
        return interpolate(uninterpolate(x));
    }
}

और यहाँ परीक्षण है जहाँ आप देख सकते हैं कि मेरा क्या मतलब है

public class RescaleTest {

    @Test
    public void testRescale() {
        Rescale r;
        r = new Rescale(5,7,0,1);
        Assert.assertTrue(r.rescale(5) == 0);
        Assert.assertTrue(r.rescale(6) == 0.5);
        Assert.assertTrue(r.rescale(7) == 1);

        r = new Rescale(5,7,1,0);
        Assert.assertTrue(r.rescale(5) == 1);
        Assert.assertTrue(r.rescale(6) == 0.5);
        Assert.assertTrue(r.rescale(7) == 0);

        r = new Rescale(-3,3,0,1);
        Assert.assertTrue(r.rescale(-3) == 0);
        Assert.assertTrue(r.rescale(0) == 0.5);
        Assert.assertTrue(r.rescale(3) == 1);

        r = new Rescale(-3,3,-1,1);
        Assert.assertTrue(r.rescale(-3) == -1);
        Assert.assertTrue(r.rescale(0) == 0);
        Assert.assertTrue(r.rescale(3) == 1);
    }
}

"लाभ यह है कि आप अपने लक्ष्य सीमा तक संकेत फ्लिप कर सकते हैं।" मैं यह नहीं समझता। क्या तुम समझा सकते हो? मुझे आपके d3-संस्करण और ऊपर से संस्करण (@irritate) से लौटाए गए मानों का अंतर नहीं मिल रहा है।
निमो

उदाहरण 1 और 2 की तुलना करें आपकी लक्ष्य सीमा स्विच की गई
KIC

2

मैंने इर्रिट के उत्तर को लिया है और इसे फिर से परिभाषित किया है ताकि बाद में गणना करने के लिए कम्प्यूटेशनल चरणों को कम से कम स्थिरांक में बदल दिया जा सके। प्रेरणा एक स्केलर को डेटा के एक सेट पर प्रशिक्षित करने की अनुमति देने के लिए है, और फिर नए डेटा (एमएल एल्गो के लिए) पर चलाया जा सकता है। वास्तव में, यह उपयोग में पायथन के लिए SciKit के प्रीप्रोसेसिंग MinMaxScaler जैसा है।

इस प्रकार, x' = (b-a)(x-min)/(max-min) + a(जहाँ b! = A) बन जाता है x' = x(b-a)/(max-min) + min(-b+a)/(max-min) + aजिसे फॉर्म में दो स्थिरांक तक घटाया जा सकता हैx' = x*Part1 + Part2

यहां दो बिल्डरों के साथ एक सी # कार्यान्वयन है: एक को प्रशिक्षित करने के लिए, और एक को एक प्रशिक्षित उदाहरण को पुनः लोड करने के लिए (जैसे, दृढ़ता का समर्थन करने के लिए)।

public class MinMaxColumnSpec
{
    /// <summary>
    /// To reduce repetitive computations, the min-max formula has been refactored so that the portions that remain constant are just computed once.
    /// This transforms the forumula from
    /// x' = (b-a)(x-min)/(max-min) + a
    /// into x' = x(b-a)/(max-min) + min(-b+a)/(max-min) + a
    /// which can be further factored into
    /// x' = x*Part1 + Part2
    /// </summary>
    public readonly double Part1, Part2;

    /// <summary>
    /// Use this ctor to train a new scaler.
    /// </summary>
    public MinMaxColumnSpec(double[] columnValues, int newMin = 0, int newMax = 1)
    {
        if (newMax <= newMin)
            throw new ArgumentOutOfRangeException("newMax", "newMax must be greater than newMin");

        var oldMax = columnValues.Max();
        var oldMin = columnValues.Min();

        Part1 = (newMax - newMin) / (oldMax - oldMin);
        Part2 = newMin + (oldMin * (newMin - newMax) / (oldMax - oldMin));
    }

    /// <summary>
    /// Use this ctor for previously-trained scalers with known constants.
    /// </summary>
    public MinMaxColumnSpec(double part1, double part2)
    {
        Part1 = part1;
        Part2 = part2;
    }

    public double Scale(double x) => (x * Part1) + Part2;
}

2

चार्ल्स क्लेटन की प्रतिक्रिया के आधार पर, मैंने कुछ JSDoc, ES6 tweaks शामिल किए, और मूल प्रतिक्रिया में टिप्पणियों से सुझाव शामिल किए।

/**
 * Returns a scaled number within its source bounds to the desired target bounds.
 * @param {number} n - Unscaled number
 * @param {number} tMin - Minimum (target) bound to scale to
 * @param {number} tMax - Maximum (target) bound to scale to
 * @param {number} sMin - Minimum (source) bound to scale from
 * @param {number} sMax - Maximum (source) bound to scale from
 * @returns {number} The scaled number within the target bounds.
 */
const scaleBetween = (n, tMin, tMax, sMin, sMax) => {
  return (tMax - tMin) * (n - sMin) / (sMax - sMin) + tMin;
}

if (Array.prototype.scaleBetween === undefined) {
  /**
   * Returns a scaled array of numbers fit to the desired target bounds.
   * @param {number} tMin - Minimum (target) bound to scale to
   * @param {number} tMax - Maximum (target) bound to scale to
   * @returns {number} The scaled array.
   */
  Array.prototype.scaleBetween = function(tMin, tMax) {
    if (arguments.length === 1 || tMax === undefined) {
      tMax = tMin; tMin = 0;
    }
    let sMax = Math.max(...this), sMin = Math.min(...this);
    if (sMax - sMin == 0) return this.map(num => (tMin + tMax) / 2);
    return this.map(num => (tMax - tMin) * (num - sMin) / (sMax - sMin) + tMin);
  }
}

// ================================================================
// Usage
// ================================================================

let nums = [10, 13, 25, 28, 43, 50], tMin = 0, tMax = 100,
    sMin = Math.min(...nums), sMax = Math.max(...nums);

// Result: [ 0.0, 7.50, 37.50, 45.00, 82.50, 100.00 ]
console.log(nums.map(n => scaleBetween(n, tMin, tMax, sMin, sMax).toFixed(2)).join(', '));

// Result: [ 0, 30.769, 69.231, 76.923, 100 ]
console.log([-4, 0, 5, 6, 9].scaleBetween(0, 100).join(', '));

// Result: [ 50, 50, 50 ]
console.log([1, 1, 1].scaleBetween(0, 100).join(', '));
.as-console-wrapper { top: 0; max-height: 100% !important; }

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.