[Python] Konvertieren Sie einen Zahlenbereich in einen anderen Bereich, wobei Sie das Verhältnis beibehalten


Answers

Das ist eine einfache lineare Konvertierung.

new_value = ( (old_value - old_min) / (old_max - old_min) ) * (new_max - new_min) + new_min

Die Umwandlung von 10000 auf der Skala von -16000 zu 16000 auf eine neue Skala von 0 bis 100 ergibt also:

old_value = 10000
old_min = -16000
old_max = 16000
new_min = 0
new_max = 100

new_value = ( ( 10000 - -16000 ) / (16000 - -16000) ) * (100 - 0) + 0
          = 81.25
Question

Ich versuche, einen Zahlenbereich in einen anderen zu konvertieren, wobei das Verhältnis erhalten bleibt. Mathe ist nicht meine Stärke.

Ich habe eine Bilddatei, in der Punktwerte von -16000.00 zu 16000.00 reichen können, obwohl der typische Bereich viel weniger sein kann. Ich möchte diese Werte in den Ganzzahlbereich 0-100 komprimieren, wobei 0 der Wert des kleinsten Punkts und 100 der Wert des größten Punkts ist. Alle Punkte dazwischen sollten ein relatives Verhältnis behalten, auch wenn etwas Genauigkeit verloren geht. Ich würde das gerne in Python machen, aber selbst ein allgemeiner Algorithmus sollte ausreichen. Ich würde einen Algorithmus bevorzugen, bei dem der Min / Max- oder jeder Bereich eingestellt werden kann (dh der zweite Bereich könnte -50 bis 800 anstelle von 0 bis 100 sein).




Es gibt eine Bedingung, wenn alle Werte, die Sie überprüfen, die gleichen sind, wobei @ jerryjvls Code NaN zurückgeben würde.

if (OldMin != OldMax && NewMin != NewMax):
    return (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin
else:
    return (NewMax + NewMin) / 2



Ich benutzte diese Lösung in einem Problem, das ich in js gelöst habe, also dachte ich, dass ich die Übersetzung teilen würde. Danke für die Erklärung und Lösung.

function remap( x, oMin, oMax, nMin, nMax ){
//range check
if (oMin == oMax){
    console.log("Warning: Zero input range");
    return None;
};

if (nMin == nMax){
    console.log("Warning: Zero output range");
    return None
}

//check reversed input range
var reverseInput = false;
oldMin = Math.min( oMin, oMax );
oldMax = Math.max( oMin, oMax );
if (oldMin != oMin){
    reverseInput = true;
}

//check reversed output range
var reverseOutput = false;  
newMin = Math.min( nMin, nMax )
newMax = Math.max( nMin, nMax )
if (newMin != nMin){
    reverseOutput = true;
};

var portion = (x-oldMin)*(newMax-newMin)/(oldMax-oldMin)
if (reverseInput){
    portion = (oldMax-x)*(newMax-newMin)/(oldMax-oldMin);
};

var result = portion + newMin
if (reverseOutput){
    result = newMax - portion;
}

return result;
}



Ich benutze persönlich die Helferklasse, die Generika unterstützt (Swift 3 kompatibel)

struct Rescale<Type : BinaryFloatingPoint> {
    typealias RescaleDomain = (lowerBound: Type, upperBound: Type)

    var fromDomain: RescaleDomain
    var toDomain: RescaleDomain

    init(from: RescaleDomain, to: RescaleDomain) {
        self.fromDomain = from
        self.toDomain = to
    }

    func interpolate(_ x: Type ) -> Type {
        return self.toDomain.lowerBound * (1 - x) + self.toDomain.upperBound * x;
    }

    func uninterpolate(_ x: Type) -> Type {
        let b = (self.fromDomain.upperBound - self.fromDomain.lowerBound) != 0 ? self.fromDomain.upperBound - self.fromDomain.lowerBound : 1 / self.fromDomain.upperBound;
        return (x - self.fromDomain.lowerBound) / b
    }

    func rescale(_ x: Type )  -> Type {
        return interpolate( uninterpolate(x) )
    }
}



Hier sind einige kurze Python-Funktionen zum Kopieren und Einfügen, einschließlich einer Funktion zum Skalieren einer ganzen Liste.

def scale_number(unscaled, to_min, to_max, from_min, from_max):
    return (to_max-to_min)*(unscaled-from_min)/(from_max-from_min)+to_min

def scale_list(l, to_min, to_max):
    return [scale_number(i, to_min, to_max, min(l), max(l)) for i in l]

Welches kann so verwendet werden:

scale_list([1,3,4,5], 0, 100)

[0.0, 50.0, 75.0, 100.0]

In meinem Fall wollte ich eine logarithmische Kurve skalieren:

scale_list([math.log(i+1) for i in range(5)], 0, 50)

[0.0, 21.533827903669653, 34.130309724299266, 43.06765580733931, 50.0]




In der Auflistung von PenguinTD verstehe ich nicht, warum die Bereiche umgekehrt sind, es funktioniert, ohne die Bereiche umzukehren. Die lineare Bereichsumwandlung basiert auf der linearen Gleichung Y=Xm+n , wobei m und n aus den gegebenen Bereichen abgeleitet sind. Anstatt sich auf die Bereiche als min und max zu beziehen, wäre es besser, sie als 1 und 2 zu bezeichnen. Die Formel wäre also:

Y = (((X - x1) * (y2 - y1)) / (x2 - x1)) + y1

Wo Y=y1 wenn X=x1 , und Y=y2 wenn X=x2 . x1 , x2 , y1 und y2 können einen beliebigen positive oder negative Wert erhalten. Das Definieren des Ausdrucks in einem Makro macht es nützlicher, es kann dann mit beliebigen Argumentnamen verwendet werden.

#define RangeConv(X, x1, x2, y1, y2) (((float)((X - x1) * (y2 - y1)) / (x2 - x1)) + y1)

Die float Darstellung würde eine Gleitkommadivision sicherstellen, wenn alle Argumente integer Werte sind. Abhängig von der Anwendung ist es möglicherweise nicht notwendig, die Bereiche x1=x2 und y1==y2 zu überprüfen.